Browse Source

Remove all define in src

pull/2/merge
Artur Arseniev 9 years ago
parent
commit
fed2423726
  1. 11
      package.json
  2. 26
      src/asset_manager/config/config.js
  3. 408
      src/asset_manager/index.js
  4. 52
      src/asset_manager/model/Asset.js
  5. 24
      src/asset_manager/model/AssetImage.js
  6. 138
      src/asset_manager/model/Assets.js
  7. 174
      src/asset_manager/view/AssetImageView.js
  8. 26
      src/asset_manager/view/AssetView.js
  9. 270
      src/asset_manager/view/AssetsView.js
  10. 182
      src/asset_manager/view/FileUploader.js
  11. 8
      src/block_manager/config/config.js
  12. 166
      src/block_manager/index.js
  13. 19
      src/block_manager/model/Block.js
  14. 15
      src/block_manager/model/Blocks.js
  15. 85
      src/block_manager/view/BlockView.js
  16. 241
      src/block_manager/view/BlocksView.js
  17. 22
      src/canvas/config/config.js
  18. 652
      src/canvas/index.js
  19. 29
      src/canvas/model/Canvas.js
  20. 21
      src/canvas/model/Frame.js
  21. 481
      src/canvas/view/CanvasView.js
  22. 105
      src/canvas/view/FrameView.js
  23. 12
      src/code_manager/config/config.js
  24. 410
      src/code_manager/index.js
  25. 87
      src/code_manager/model/CodeMirrorEditor.js
  26. 283
      src/code_manager/model/CssGenerator.js
  27. 35
      src/code_manager/model/HtmlGenerator.js
  28. 107
      src/code_manager/model/JsGenerator.js
  29. 67
      src/code_manager/model/JsonGenerator.js
  30. 49
      src/code_manager/view/EditorView.js
  31. 32
      src/commands/config/config.js
  32. 388
      src/commands/index.js
  33. 23
      src/commands/model/Command.js
  34. 21
      src/commands/model/Commands.js
  35. 228
      src/commands/view/CommandAbstract.js
  36. 463
      src/commands/view/CreateComponent.js
  37. 145
      src/commands/view/DeleteComponent.js
  38. 128
      src/commands/view/ExportTemplate.js
  39. 157
      src/commands/view/Fullscreen.js
  40. 68
      src/commands/view/ImageComponent.js
  41. 142
      src/commands/view/InsertCustom.js
  42. 299
      src/commands/view/MoveComponent.js
  43. 35
      src/commands/view/OpenAssets.js
  44. 49
      src/commands/view/OpenBlocks.js
  45. 62
      src/commands/view/OpenLayers.js
  46. 142
      src/commands/view/OpenStyleManager.js
  47. 57
      src/commands/view/OpenTraitManager.js
  48. 121
      src/commands/view/Preview.js
  49. 52
      src/commands/view/Resize.js
  50. 998
      src/commands/view/SelectComponent.js
  51. 187
      src/commands/view/SelectPosition.js
  52. 296
      src/commands/view/ShowOffset.js
  53. 18
      src/commands/view/SwitchVisibility.js
  54. 62
      src/commands/view/TextComponent.js
  55. 18
      src/css_composer/config/config.js
  56. 442
      src/css_composer/index.js
  57. 188
      src/css_composer/model/CssRule.js
  58. 52
      src/css_composer/model/CssRules.js
  59. 32
      src/css_composer/model/Selectors.js
  60. 136
      src/css_composer/view/CssRuleView.js
  61. 124
      src/css_composer/view/CssRulesView.js
  62. 10
      src/device_manager/config/config.js
  63. 178
      src/device_manager/index.js
  64. 19
      src/device_manager/model/Device.js
  65. 145
      src/device_manager/view/DevicesView.js
  66. 48
      src/dom_components/config/config.js
  67. 663
      src/dom_components/index.js
  68. 727
      src/dom_components/model/Component.js
  69. 183
      src/dom_components/model/ComponentImage.js
  70. 71
      src/dom_components/model/ComponentLink.js
  71. 197
      src/dom_components/model/ComponentMap.js
  72. 43
      src/dom_components/model/ComponentScript.js
  73. 159
      src/dom_components/model/ComponentTable.js
  74. 59
      src/dom_components/model/ComponentTableCell.js
  75. 77
      src/dom_components/model/ComponentTableRow.js
  76. 19
      src/dom_components/model/ComponentText.js
  77. 45
      src/dom_components/model/ComponentTextNode.js
  78. 593
      src/dom_components/model/ComponentVideo.js
  79. 129
      src/dom_components/model/Components.js
  80. 10
      src/dom_components/model/Toolbar.js
  81. 18
      src/dom_components/model/ToolbarButton.js
  82. 121
      src/dom_components/view/ComponentImageView.js
  83. 31
      src/dom_components/view/ComponentLinkView.js
  84. 89
      src/dom_components/view/ComponentMapView.js
  85. 69
      src/dom_components/view/ComponentScriptView.js
  86. 10
      src/dom_components/view/ComponentTableCellView.js
  87. 10
      src/dom_components/view/ComponentTableRowView.js
  88. 15
      src/dom_components/view/ComponentTableView.js
  89. 7
      src/dom_components/view/ComponentTextNodeView.js
  90. 215
      src/dom_components/view/ComponentTextView.js
  91. 211
      src/dom_components/view/ComponentVideoView.js
  92. 781
      src/dom_components/view/ComponentView.js
  93. 189
      src/dom_components/view/ComponentsView.js
  94. 56
      src/dom_components/view/ToolbarButtonView.js
  95. 145
      src/domain_abstract/ui/Input.js
  96. 133
      src/domain_abstract/ui/InputColor.js
  97. 481
      src/domain_abstract/ui/InputNumber.js
  98. 107
      src/domain_abstract/view/DomainViews.js
  99. 555
      src/editor/config/config.js
  100. 836
      src/editor/index.js

11
package.json

@ -30,7 +30,9 @@
"babel-preset-es2015": "^6.24.1",
"chai": "^3.5.0",
"documentation": "^4.0.0-beta2",
"expect": "^1.20.2",
"istanbul": "^0.4.2",
"jsdom": "^11.0.0",
"mocha": "^3.1.2",
"node-sass": "^3.4.2",
"requirejs": "^2.3.2",
@ -55,14 +57,19 @@
"template",
"editor"
],
"babel": {
"presets": [
"es2015"
]
},
"scripts": {
"lint": "eslint src",
"build": "WEBPACK_ENV=prod && npm run v:patch && webpack && npm run build:css",
"build": "WEBPACK_ENV=prod && npm run test && npm run v:patch && webpack && npm run build:css",
"build:css": "node-sass styles/scss/main.scss dist/css/grapesjs.min.css",
"build:fonts": "./node_modules/.bin/grunt build:fonts",
"v:patch": "npm version --no-git-tag-version patch",
"start": "WEBPACK_ENV=dev webpack-dev-server --progress --colors & npm run build:css -- -w",
"test": "mocha --compilers js:babel-core/register --require ./test/helper.js --recursive ./test/*.js",
"test": "NODE_PATH=./src mocha --compilers js:babel-core/register --require test/helper.js --recursive test/main.js",
"test:dev": "npm test -- -R min -w"
}
}

26
src/asset_manager/config/config.js

@ -1,18 +1,16 @@
define(function () {
return {
// Default assets
assets: [],
module.exports = {
// Default assets
assets: [],
// Style prefix
stylePrefix: 'am-',
// Style prefix
stylePrefix: 'am-',
// Url where uploads will be send, set false to disable upload
upload: 'http://localhost/assets/upload',
// Url where uploads will be send, set false to disable upload
upload: 'http://localhost/assets/upload',
// Text on upload input
uploadText: 'Drop files here or click to upload',
// Text on upload input
uploadText: 'Drop files here or click to upload',
// Label for the add button
addBtnText: 'Add image',
};
});
// Label for the add button
addBtnText: 'Add image',
};

408
src/asset_manager/index.js

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

52
src/asset_manager/model/Asset.js

@ -1,32 +1,30 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
var Backbone = require('backbone');
idAttribute: 'src',
module.exports = Backbone.Model.extend({
defaults: {
type: '',
src: '',
},
idAttribute: 'src',
/**
* Get filename of the asset
* @return {string}
* @private
* */
getFilename: function(){
return this.get('src').split('/').pop();
},
defaults: {
type: '',
src: '',
},
/**
* Get extension of the asset
* @return {string}
* @private
* */
getExtension: function(){
return this.getFilename().split('.').pop();
},
/**
* Get filename of the asset
* @return {string}
* @private
* */
getFilename: function(){
return this.get('src').split('/').pop();
},
});
});
/**
* Get extension of the asset
* @return {string}
* @private
* */
getExtension: function(){
return this.getFilename().split('.').pop();
},
});

24
src/asset_manager/model/AssetImage.js

@ -1,15 +1,13 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Asset = require('./Asset');
module.exports = Asset.extend({
var Backbone = require('backbone');
var Asset = require('./Asset');
defaults: _.extend({}, Asset.prototype.defaults, {
type: 'image',
unitDim: 'px',
height: 0,
width: 0,
}),
module.exports = Asset.extend({
});
});
defaults: _.extend({}, Asset.prototype.defaults, {
type: 'image',
unitDim: 'px',
height: 0,
width: 0,
}),
});

138
src/asset_manager/model/Assets.js

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

174
src/asset_manager/view/AssetImageView.js

@ -1,99 +1,97 @@
define(function(require, exports, module){
'use strict';
var AssetView = require('./AssetView');
var assetTemplate = require('text!./../template/assetImage.html');
module.exports = AssetView.extend({
var AssetView = require('./AssetView');
var assetTemplate = require('text!./../template/assetImage.html');
events:{
'click': 'handleClick',
'dblclick': 'handleDblClick',
},
module.exports = AssetView.extend({
template: _.template(assetTemplate),
events:{
'click': 'handleClick',
'dblclick': 'handleDblClick',
},
initialize: function(o) {
AssetView.prototype.initialize.apply(this, arguments);
this.className += ' ' + this.pfx + 'asset-image';
this.events['click #' + this.pfx + 'close'] = 'removeItem';
this.delegateEvents();
},
template: _.template(assetTemplate),
/**
* Trigger when the asset is clicked
* @private
* */
handleClick: function() {
var onClick = this.config.onClick;
var model = this.model;
model.collection.trigger('deselectAll');
this.$el.addClass(this.pfx + 'highlight');
initialize: function(o) {
AssetView.prototype.initialize.apply(this, arguments);
this.className += ' ' + this.pfx + 'asset-image';
this.events['click #' + this.pfx + 'close'] = 'removeItem';
this.delegateEvents();
},
if (typeof onClick === 'function') {
onClick(model);
} else {
this.updateTarget(model.get('src'));
}
},
/**
* Trigger when the asset is clicked
* @private
* */
handleClick: function() {
var onClick = this.config.onClick;
var model = this.model;
model.collection.trigger('deselectAll');
this.$el.addClass(this.pfx + 'highlight');
/**
* Trigger when the asset is double clicked
* @private
* */
handleDblClick: function() {
var onDblClick = this.config.onDblClick;
var model = this.model;
if (typeof onClick === 'function') {
onClick(model);
} else {
this.updateTarget(model.get('src'));
}
},
if (typeof onDblClick === 'function') {
onDblClick(model);
} else {
this.updateTarget(model.get('src'));
}
/**
* Trigger when the asset is double clicked
* @private
* */
handleDblClick: function() {
var onDblClick = this.config.onDblClick;
var model = this.model;
var onSelect = model.collection.onSelect;
if(typeof onSelect == 'function'){
onSelect(this.model);
}
},
if (typeof onDblClick === 'function') {
onDblClick(model);
} else {
this.updateTarget(model.get('src'));
}
/**
* Update target if exists
* @param {String} v Value
* @private
* */
updateTarget: function(v){
var target = this.model.collection.target;
if(target && target.set) {
var attr = _.clone( target.get('attributes') );
target.set('attributes', attr );
target.set('src', v );
}
},
var onSelect = model.collection.onSelect;
if(typeof onSelect == 'function'){
onSelect(this.model);
}
},
/**
* Remove asset from collection
* @private
* */
removeItem: function(e){
e.stopPropagation();
this.model.collection.remove(this.model);
},
/**
* Update target if exists
* @param {String} v Value
* @private
* */
updateTarget: function(v){
var target = this.model.collection.target;
if(target && target.set) {
var attr = _.clone( target.get('attributes') );
target.set('attributes', attr );
target.set('src', v );
}
},
render : function(){
var name = this.model.get('name'),
dim = this.model.get('width') && this.model.get('height') ?
this.model.get('width')+' x '+this.model.get('height') : '';
name = name ? name : this.model.get('src').split("/").pop();
name = name && name.length > 30 ? name.substring(0, 30)+'...' : name;
dim = dim ? dim + (this.model.get('unitDim') ? this.model.get('unitDim') : ' px' ) : '';
this.$el.html( this.template({
name: name,
src: this.model.get('src'),
dim: dim,
pfx: this.pfx,
ppfx: this.ppfx
}));
this.$el.attr('class', this.className);
return this;
},
});
});
/**
* Remove asset from collection
* @private
* */
removeItem: function(e){
e.stopPropagation();
this.model.collection.remove(this.model);
},
render : function(){
var name = this.model.get('name'),
dim = this.model.get('width') && this.model.get('height') ?
this.model.get('width')+' x '+this.model.get('height') : '';
name = name ? name : this.model.get('src').split("/").pop();
name = name && name.length > 30 ? name.substring(0, 30)+'...' : name;
dim = dim ? dim + (this.model.get('unitDim') ? this.model.get('unitDim') : ' px' ) : '';
this.$el.html( this.template({
name: name,
src: this.model.get('src'),
dim: dim,
pfx: this.pfx,
ppfx: this.ppfx
}));
this.$el.attr('class', this.className);
return this;
},
});

26
src/asset_manager/view/AssetView.js

@ -1,16 +1,12 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.View.extend({
initialize: function(o) {
this.options = o;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.className = this.pfx + 'asset';
this.listenTo( this.model, 'destroy remove', this.remove);
},
});
});
module.exports = Backbone.View.extend({
initialize: function(o) {
this.options = o;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.className = this.pfx + 'asset';
this.listenTo( this.model, 'destroy remove', this.remove);
},
});

270
src/asset_manager/view/AssetsView.js

@ -1,136 +1,134 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var AssetView = require('./AssetView');
var AssetImageView = require('./AssetImageView');
var FileUploader = require('./FileUploader');
var assetsTemplate = require('text!./../template/assets.html');
module.exports = Backbone.View.extend({
template: _.template(assetsTemplate),
initialize: function(o) {
this.options = o;
this.config = o.config;
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.listenTo( this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll );
this.className = this.pfx + 'assets';
this.events = {};
this.events.submit = 'addFromStr';
this.delegateEvents();
},
/**
* Add new asset to the collection via string
* @param {Event} e Event object
* @return {this}
* @private
*/
addFromStr: function(e){
e.preventDefault();
var input = this.getInputUrl();
var url = input.value.trim();
if(!url)
return;
this.collection.addImg(url, {at: 0});
this.getAssetsEl().scrollTop = 0;
input.value = '';
return this;
},
/**
* Returns assets element
* @return {HTMLElement}
* @private
*/
getAssetsEl: function(){
//if(!this.assets) // Not able to cache as after the rerender it losses the ref
this.assets = this.el.querySelector('.' + this.pfx + 'assets');
return this.assets;
},
/**
* Returns input url element
* @return {HTMLElement}
* @private
*/
getInputUrl: function(){
if(!this.inputUrl || !this.inputUrl.value)
this.inputUrl = this.el.querySelector('.'+this.pfx+'add-asset input');
return this.inputUrl;
},
/**
* Add asset to collection
* @private
* */
addToAsset: function(model){
this.addAsset(model);
},
/**
* Add new asset to collection
* @param Object Model
* @param Object Fragment collection
* @return Object Object created
* @private
* */
addAsset: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = AssetView;
if(model.get('type').indexOf("image") > -1)
viewObject = AssetImageView;
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild( rendered );
}else{
var assetsEl = this.getAssetsEl();
if(assetsEl)
assetsEl.insertBefore(rendered, assetsEl.firstChild);
}
return rendered;
},
/**
* Deselect all assets
* @private
* */
deselectAll: function(){
this.$el.find('.' + this.pfx + 'highlight').removeClass(this.pfx + 'highlight');
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addAsset(model, fragment);
},this);
this.$el.html(this.template({
pfx: this.pfx,
ppfx: this.ppfx,
btnText: this.config.addBtnText,
}));
this.$el.find('.'+this.pfx + 'assets').append(fragment);
return this;
}
});
});
var Backbone = require('backbone');
var AssetView = require('./AssetView');
var AssetImageView = require('./AssetImageView');
var FileUploader = require('./FileUploader');
var assetsTemplate = require('text!./../template/assets.html');
module.exports = Backbone.View.extend({
template: _.template(assetsTemplate),
initialize: function(o) {
this.options = o;
this.config = o.config;
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.listenTo( this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll );
this.className = this.pfx + 'assets';
this.events = {};
this.events.submit = 'addFromStr';
this.delegateEvents();
},
/**
* Add new asset to the collection via string
* @param {Event} e Event object
* @return {this}
* @private
*/
addFromStr: function(e){
e.preventDefault();
var input = this.getInputUrl();
var url = input.value.trim();
if(!url)
return;
this.collection.addImg(url, {at: 0});
this.getAssetsEl().scrollTop = 0;
input.value = '';
return this;
},
/**
* Returns assets element
* @return {HTMLElement}
* @private
*/
getAssetsEl: function(){
//if(!this.assets) // Not able to cache as after the rerender it losses the ref
this.assets = this.el.querySelector('.' + this.pfx + 'assets');
return this.assets;
},
/**
* Returns input url element
* @return {HTMLElement}
* @private
*/
getInputUrl: function(){
if(!this.inputUrl || !this.inputUrl.value)
this.inputUrl = this.el.querySelector('.'+this.pfx+'add-asset input');
return this.inputUrl;
},
/**
* Add asset to collection
* @private
* */
addToAsset: function(model){
this.addAsset(model);
},
/**
* Add new asset to collection
* @param Object Model
* @param Object Fragment collection
* @return Object Object created
* @private
* */
addAsset: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = AssetView;
if(model.get('type').indexOf("image") > -1)
viewObject = AssetImageView;
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild( rendered );
}else{
var assetsEl = this.getAssetsEl();
if(assetsEl)
assetsEl.insertBefore(rendered, assetsEl.firstChild);
}
return rendered;
},
/**
* Deselect all assets
* @private
* */
deselectAll: function(){
this.$el.find('.' + this.pfx + 'highlight').removeClass(this.pfx + 'highlight');
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addAsset(model, fragment);
},this);
this.$el.html(this.template({
pfx: this.pfx,
ppfx: this.ppfx,
btnText: this.config.addBtnText,
}));
this.$el.find('.'+this.pfx + 'assets').append(fragment);
return this;
}
});

182
src/asset_manager/view/FileUploader.js

@ -1,99 +1,97 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var fileUploaderTemplate = require('text!./../template/fileUploader.html');
module.exports = Backbone.View.extend({
var Backbone = require('backbone');
var fileUploaderTemplate = require('text!./../template/fileUploader.html');
template: _.template(fileUploaderTemplate),
module.exports = Backbone.View.extend({
events: {},
template: _.template(fileUploaderTemplate),
initialize: function(o) {
this.options = o || {};
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.target = this.collection || {};
this.uploadId = this.pfx + 'uploadFile';
this.disabled = !this.config.upload;
this.events['change #' + this.uploadId] = 'uploadFile';
this.delegateEvents();
},
events: {},
/**
* Upload files
* @param {Object} e Event
* @private
* */
uploadFile : function(e){
var files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
formData = new FormData();
for (var i = 0; i < files.length; i++) {
formData.append('files[]', files[i]);
}
var target = this.target;
$.ajax({
url : this.config.upload,
type : 'POST',
data : formData,
beforeSend : this.config.beforeSend,
complete : this.config.onComplete,
xhrFields : {
onprogress: function (e) {
if (e.lengthComputable) {
/*var result = e.loaded / e.total * 100 + '%';*/
}
},
onload: function (e) {
//progress.value = 100;
}
},
cache: false, contentType: false, processData: false
}).done(function(data){
target.add(data.data);
}).always(function(){
//turnOff loading
});
},
initialize: function(o) {
this.options = o || {};
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.target = this.collection || {};
this.uploadId = this.pfx + 'uploadFile';
this.disabled = !this.config.upload;
this.events['change #' + this.uploadId] = 'uploadFile';
this.delegateEvents();
},
/**
* Make input file droppable
* @private
* */
initDrop: function(){
var that = this;
if(!this.uploadForm){
this.uploadForm = this.$el.find('form').get(0);
if( 'draggable' in this.uploadForm ){
var uploadFile = this.uploadFile;
this.uploadForm.ondragover = function(){
this.className = that.pfx + 'hover';
return false;
};
this.uploadForm.ondragleave = function(){
this.className = '';
return false;
};
this.uploadForm.ondrop = function(e){
this.className = '';
e.preventDefault();
that.uploadFile(e);
return;
};
}
}
},
/**
* Upload files
* @param {Object} e Event
* @private
* */
uploadFile : function(e){
var files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
formData = new FormData();
for (var i = 0; i < files.length; i++) {
formData.append('files[]', files[i]);
}
var target = this.target;
$.ajax({
url : this.config.upload,
type : 'POST',
data : formData,
beforeSend : this.config.beforeSend,
complete : this.config.onComplete,
xhrFields : {
onprogress: function (e) {
if (e.lengthComputable) {
/*var result = e.loaded / e.total * 100 + '%';*/
}
},
onload: function (e) {
//progress.value = 100;
}
},
cache: false, contentType: false, processData: false
}).done(function(data){
target.add(data.data);
}).always(function(){
//turnOff loading
});
},
render : function(){
this.$el.html( this.template({
title: this.config.uploadText,
uploadId: this.uploadId,
disabled: this.disabled,
pfx: this.pfx
}) );
this.initDrop();
this.$el.attr('class', this.pfx + 'file-uploader');
return this;
},
/**
* Make input file droppable
* @private
* */
initDrop: function(){
var that = this;
if(!this.uploadForm){
this.uploadForm = this.$el.find('form').get(0);
if( 'draggable' in this.uploadForm ){
var uploadFile = this.uploadFile;
this.uploadForm.ondragover = function(){
this.className = that.pfx + 'hover';
return false;
};
this.uploadForm.ondragleave = function(){
this.className = '';
return false;
};
this.uploadForm.ondrop = function(e){
this.className = '';
e.preventDefault();
that.uploadFile(e);
return;
};
}
}
},
});
});
render : function(){
this.$el.html( this.template({
title: this.config.uploadText,
uploadId: this.uploadId,
disabled: this.disabled,
pfx: this.pfx
}) );
this.initDrop();
this.$el.attr('class', this.pfx + 'file-uploader');
return this;
},
});

8
src/block_manager/config/config.js

@ -1,7 +1,5 @@
define(function () {
return {
module.exports = {
'blocks': [],
'blocks': [],
};
});
};

166
src/block_manager/index.js

@ -25,98 +25,94 @@
* }
* ...
*/
define(function(require) {
module.exports = function() {
var c = {},
defaults = require('./config/config'),
Blocks = require('./model/Blocks'),
BlocksView = require('./view/BlocksView');
var blocks, view;
return function() {
var c = {},
defaults = require('./config/config'),
Blocks = require('./model/Blocks'),
BlocksView = require('./view/BlocksView');
var blocks, view;
return {
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'BlockManager',
/**
* Name of the module
* @type {String}
* @private
*/
name: 'BlockManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @return {this}
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
blocks = new Blocks(c.blocks);
view = new BlocksView({ collection: blocks }, c);
return this;
},
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @return {this}
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
blocks = new Blocks(c.blocks);
view = new BlocksView({ collection: blocks }, c);
return this;
},
/**
* Add new block to the collection.
* @param {string} id Block id
* @param {Object} opts Options
* @param {string} opts.label Name of the block
* @param {string} opts.content HTML content
* @param {Object} [opts.attributes={}] Block attributes
* @return {Block} Added block
* @example
* blockManager.add('h1-block', {
* label: 'Heading',
* content: '<h1>Put your title here</h1>',
* attributes: {
* title: 'Insert h1 block'
* }
* });
*/
add: function(id, opts){
var obj = opts || {};
obj.id = id;
return blocks.add(obj);
},
/**
* Add new block to the collection.
* @param {string} id Block id
* @param {Object} opts Options
* @param {string} opts.label Name of the block
* @param {string} opts.content HTML content
* @param {Object} [opts.attributes={}] Block attributes
* @return {Block} Added block
* @example
* blockManager.add('h1-block', {
* label: 'Heading',
* content: '<h1>Put your title here</h1>',
* attributes: {
* title: 'Insert h1 block'
* }
* });
*/
add: function(id, opts){
var obj = opts || {};
obj.id = id;
return blocks.add(obj);
},
/**
* Return block by id
* @param {string} id Block id
* @example
* var block = blockManager.get('h1-block');
* console.log(JSON.stringify(block));
* // {label: 'Heading', content: '<h1>Put your ...', ...}
*/
get: function(id){
return blocks.get(id);
},
/**
* Return block by id
* @param {string} id Block id
* @example
* var block = blockManager.get('h1-block');
* console.log(JSON.stringify(block));
* // {label: 'Heading', content: '<h1>Put your ...', ...}
*/
get: function(id){
return blocks.get(id);
},
/**
* Return all blocks
* @return {Collection}
* @example
* var blocks = blockManager.getAll();
* console.log(JSON.stringify(blocks));
* // [{label: 'Heading', content: '<h1>Put your ...'}, ...]
*/
getAll: function(){
return blocks;
},
/**
* Return all blocks
* @return {Collection}
* @example
* var blocks = blockManager.getAll();
* console.log(JSON.stringify(blocks));
* // [{label: 'Heading', content: '<h1>Put your ...'}, ...]
*/
getAll: function(){
return blocks;
},
/**
* Render blocks
* @return {HTMLElement}
*/
render: function(){
return view.render().el;
},
};
/**
* Render blocks
* @return {HTMLElement}
*/
render: function(){
return view.render().el;
},
};
});
};

19
src/block_manager/model/Block.js

@ -1,14 +1,11 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
module.exports = Backbone.Model.extend({
defaults :{
label: '',
content: '',
attributes: {},
},
defaults :{
label: '',
content: '',
attributes: {},
},
});
});
});

15
src/block_manager/model/Blocks.js

@ -1,11 +1,6 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Block = require('./Block');
var Backbone = require('backbone');
var Block = require('./Block');
module.exports = Backbone.Collection.extend({
model: Block,
});
});
module.exports = Backbone.Collection.extend({
model: Block,
});

85
src/block_manager/view/BlockView.js

@ -1,51 +1,48 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.View.extend({
module.exports = Backbone.View.extend({
events: {
mousedown: 'onDrag'
},
events: {
mousedown: 'onDrag'
},
initialize: function(o, config) {
_.bindAll(this, 'onDrop');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.model, 'destroy', this.remove);
this.doc = $(document);
},
initialize: function(o, config) {
_.bindAll(this, 'onDrop');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.model, 'destroy', this.remove);
this.doc = $(document);
},
/**
* Start block dragging
* @private
*/
onDrag: function(e) {
if(!this.config.getSorter)
return;
this.config.em.refreshCanvas();
var sorter = this.config.getSorter();
sorter.setDragHelper(this.el, e);
sorter.startSort(this.el);
sorter.setDropContent(this.model.get('content'));
this.doc.on('mouseup', this.onDrop);
},
/**
* Start block dragging
* @private
*/
onDrag: function(e) {
if(!this.config.getSorter)
return;
this.config.em.refreshCanvas();
var sorter = this.config.getSorter();
sorter.setDragHelper(this.el, e);
sorter.startSort(this.el);
sorter.setDropContent(this.model.get('content'));
this.doc.on('mouseup', this.onDrop);
},
/**
* Drop block
* @private
*/
onDrop: function() {
this.doc.off('mouseup', this.onDrop);
this.config.getSorter().endMove();
},
/**
* Drop block
* @private
*/
onDrop: function() {
this.doc.off('mouseup', this.onDrop);
this.config.getSorter().endMove();
},
render: function() {
var className = this.ppfx + 'block';
this.$el.addClass(className);
this.el.innerHTML = '<div class="' + className + '-label">' + this.model.get('label') + '</div>';
return this;
},
render: function() {
var className = this.ppfx + 'block';
this.$el.addClass(className);
this.el.innerHTML = '<div class="' + className + '-label">' + this.model.get('label') + '</div>';
return this;
},
});
});
});

241
src/block_manager/view/BlocksView.js

@ -1,123 +1,120 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var BlockView = require('./BlockView');
module.exports = Backbone.View.extend({
initialize: function(opts, config) {
_.bindAll(this, 'getSorter', 'onDrag', 'onDrop');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.collection, 'add', this.addTo);
this.em = this.config.em;
this.tac = 'test-tac';
this.grabbingCls = this.ppfx + 'grabbing';
if(this.em){
this.config.getSorter = this.getSorter;
this.canvas = this.em.get('Canvas');
var Backbone = require('backbone');
var BlockView = require('./BlockView');
module.exports = Backbone.View.extend({
initialize: function(opts, config) {
_.bindAll(this, 'getSorter', 'onDrag', 'onDrop');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.collection, 'add', this.addTo);
this.em = this.config.em;
this.tac = 'test-tac';
this.grabbingCls = this.ppfx + 'grabbing';
if(this.em){
this.config.getSorter = this.getSorter;
this.canvas = this.em.get('Canvas');
}
},
/**
* Get sorter
* @private
*/
getSorter: function(){
if(!this.em)
return;
if(!this.sorter){
var utils = this.em.get('Utils');
var canvas = this.canvas;
this.sorter = new utils.Sorter({
container: canvas.getBody(),
placer: canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
onStart: this.onDrag,
onEndMove: this.onDrop,
document: canvas.getFrameEl().contentDocument,
direction: 'a',
wmargin: 1,
nested: 1,
em: this.em,
canvasRelative: 1,
});
}
return this.sorter;
},
/**
* Callback when block is on drag
* @private
*/
onDrag: function(){
this.em.stopDefault();
},
/**
* Callback when block is dropped
* @private
*/
onDrop: function(model){
this.em.runDefault();
if (model && model.get) {
if(model.get('activeOnRender')) {
model.trigger('active');
model.set('activeOnRender', 0);
}
},
/**
* Get sorter
* @private
*/
getSorter: function(){
if(!this.em)
return;
if(!this.sorter){
var utils = this.em.get('Utils');
var canvas = this.canvas;
this.sorter = new utils.Sorter({
container: canvas.getBody(),
placer: canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
onStart: this.onDrag,
onEndMove: this.onDrop,
document: canvas.getFrameEl().contentDocument,
direction: 'a',
wmargin: 1,
nested: 1,
em: this.em,
canvasRelative: 1,
});
}
return this.sorter;
},
/**
* Callback when block is on drag
* @private
*/
onDrag: function(){
this.em.stopDefault();
},
/**
* Callback when block is dropped
* @private
*/
onDrop: function(model){
this.em.runDefault();
if (model && model.get) {
if(model.get('activeOnRender')) {
model.trigger('active');
model.set('activeOnRender', 0);
}
// Register all its components (eg. for the Undo Manager)
this.em.initChildrenComp(model);
}
},
/**
* Add new model to the collection
* @param {Model} model
* @private
* */
addTo: function(model){
this.add(model);
},
/**
* Render new model inside the view
* @param {Model} model
* @param {Object} fragment Fragment collection
* @private
* */
add: function(model, fragment){
var frag = fragment || null;
var view = new BlockView({
model: model,
attributes: model.get('attributes'),
}, this.config);
var rendered = view.render().el;
if(frag)
frag.appendChild(rendered);
else
this.$el.append(rendered);
},
render: function() {
var frag = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.add(model, frag);
}, this);
this.$el.append(frag);
this.$el.addClass(this.ppfx + 'blocks-c');
return this;
},
});
});
// Register all its components (eg. for the Undo Manager)
this.em.initChildrenComp(model);
}
},
/**
* Add new model to the collection
* @param {Model} model
* @private
* */
addTo: function(model){
this.add(model);
},
/**
* Render new model inside the view
* @param {Model} model
* @param {Object} fragment Fragment collection
* @private
* */
add: function(model, fragment){
var frag = fragment || null;
var view = new BlockView({
model: model,
attributes: model.get('attributes'),
}, this.config);
var rendered = view.render().el;
if(frag)
frag.appendChild(rendered);
else
this.$el.append(rendered);
},
render: function() {
var frag = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.add(model, frag);
}, this);
this.$el.append(frag);
this.$el.addClass(this.ppfx + 'blocks-c');
return this;
},
});

22
src/canvas/config/config.js

@ -1,16 +1,14 @@
define(function () {
return {
module.exports = {
stylePrefix: 'cv-',
stylePrefix: 'cv-',
// Coming soon
rulers: false,
// Coming soon
rulers: false,
/*
* append scripts in head of iframe before renderBody content
* need to manually maintain the same scripts in cms's render template
*/
scripts: []
/*
* append scripts in head of iframe before renderBody content
* need to manually maintain the same scripts in cms's render template
*/
scripts: []
};
});
};

652
src/canvas/index.js

@ -1,329 +1,325 @@
define(function(require) {
return function() {
var c = {},
defaults = require('./config/config'),
Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView');
var canvas;
return {
/**
* Used inside RTE
* @private
*/
getCanvasView: function() {
return CanvasView;
},
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Canvas',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
canvas = new Canvas(config);
CanvasView = new CanvasView({
model: canvas,
config: c,
});
var cm = c.em.get('DomComponents');
if(cm)
this.setWrapper(cm);
return this;
},
/**
* Add wrapper
* @param {Object} wrp Wrapper
*
* */
setWrapper: function(wrp) {
canvas.set('wrapper', wrp);
},
/**
* Returns canvas element
* @return {HTMLElement}
*/
getElement: function(){
return CanvasView.el;
},
/**
* Returns frame element of the canvas
* @return {HTMLElement}
*/
getFrameEl: function(){
return CanvasView.frame.el;
},
/**
* Returns body element of the frame
* @return {HTMLElement}
*/
getBody: function(){
return CanvasView.frame.el.contentDocument.body;
},
/**
* Returns body wrapper element of the frame
* @return {HTMLElement}
*/
getWrapperEl: function(){
return this.getBody().querySelector('#wrapper');
},
/**
* Returns element containing canvas tools
* @return {HTMLElement}
*/
getToolsEl: function(){
return CanvasView.toolsEl;
},
/**
* Returns highlighter element
* @return {HTMLElement}
*/
getHighlighter: function(){
return CanvasView.hlEl;
},
/**
* Returns badge element
* @return {HTMLElement}
*/
getBadgeEl: function(){
return CanvasView.badgeEl;
},
/**
* Returns placer element
* @return {HTMLElement}
*/
getPlacerEl: function(){
return CanvasView.placerEl;
},
/**
* Returns ghost element
* @return {HTMLElement}
* @private
*/
getGhostEl: function(){
return CanvasView.ghostEl;
},
/**
* Returns toolbar element
* @return {HTMLElement}
*/
getToolbarEl: function() {
return CanvasView.toolbarEl;
},
/**
* Returns resizer element
* @return {HTMLElement}
*/
getResizerEl: function() {
return CanvasView.resizerEl;
},
/**
* Returns offset viewer element
* @return {HTMLElement}
*/
getOffsetViewerEl: function() {
return CanvasView.offsetEl;
},
/**
* Returns fixed offset viewer element
* @return {HTMLElement}
*/
getFixedOffsetViewerEl: function() {
return CanvasView.fixedOffsetEl;
},
/**
* Render canvas
* */
render: function() {
return CanvasView.render().el;
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffset: function() {
var frameOff = this.offset(this.getFrameEl());
var canvasOff = this.offset(this.getElement());
return {
top: frameOff.top - canvasOff.top,
left: frameOff.left - canvasOff.left
};
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
* @private
*/
offset: function(el){
var rect = el.getBoundingClientRect();
return {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
};
},
/**
* Get element position relative to the canvas
* @param {HTMLElement} el
* @return {Object}
*/
getElementPos: function(el) {
return CanvasView.getElementPos(el);
},
/**
* This method comes handy when you need to attach something like toolbars
* to elements inside the canvas, dealing with all relative position,
* offsets, etc. and returning as result the object with positions which are
* viewable by the user (when the canvas is scrolled the top edge of the element
* is not viewable by the user anymore so the new top edge is the one of the canvas)
*
* The target should be visible before being passed here as invisible elements
* return empty string as width
* @param {HTMLElement} target The target in this case could be the toolbar
* @param {HTMLElement} element The element on which I'd attach the toolbar
* @param {Object} options Custom options
* @param {Boolean} options.toRight Set to true if you want the toolbar attached to the right
* @return {Object}
*/
getTargetToElementDim: function (target, element, options) {
var opts = options || {};
var canvasPos = CanvasView.getPosition();
var pos = opts.elPos || CanvasView.getElementPos(element);
var toRight = options.toRight || 0;
var targetHeight = opts.targetHeight || target.offsetHeight;
var targetWidth = opts.targetWidth || target.offsetWidth;
var eventToTrigger = opts.event || null;
var elTop = pos.top - targetHeight;
var elLeft = pos.left;
elLeft += toRight ? pos.width : 0;
elLeft = toRight ? (elLeft - targetWidth) : elLeft;
var leftPos = elLeft < canvasPos.left ? canvasPos.left : elLeft;
var topPos = elTop < canvasPos.top ? canvasPos.top : elTop;
topPos = topPos > (pos.top + pos.height) ? (pos.top + pos.height) : topPos;
var result = {
top: topPos,
left: leftPos,
elementTop: pos.top,
elementLeft: pos.left,
elementWidth: pos.width,
elementHeight: pos.height,
targetWidth: target.offsetWidth,
targetHeight: target.offsetHeight,
canvasTop: canvasPos.top,
canvasLeft: canvasPos.left,
};
// In this way I can catch data and also change the position strategy
if(eventToTrigger && c.em) {
c.em.trigger(eventToTrigger, result);
}
return result;
},
/**
* Instead of simply returning e.clientX and e.clientY this function
* calculates also the offset based on the canvas. This is helpful when you
* need to get X and Y position while moving between the editor area and
* canvas area, which is in the iframe
* @param {Event} e
* @return {Object}
*/
getMouseRelativePos: function (e, options) {
var opts = options || {};
var addTop = 0;
var addLeft = 0;
var subWinOffset = opts.subWinOffset;
var doc = e.target.ownerDocument;
var win = doc.defaultView || doc.parentWindow;
var frame = win.frameElement;
var yOffset = subWinOffset ? win.pageYOffset : 0;
var xOffset = subWinOffset ? win.pageXOffset : 0;
if (frame) {
var frameRect = frame.getBoundingClientRect();
addTop = frameRect.top || 0;
addLeft = frameRect.left || 0;
}
return {
y: e.clientY + addTop - yOffset,
x: e.clientX + addLeft - xOffset,
};
},
/**
* X and Y mouse position relative to the canvas
* @param {Event} e
* @return {Object}
*/
getMouseRelativeCanvas: function (e, options) {
var opts = options || {};
var frame = this.getFrameEl();
var body = this.getBody();
var addTop = frame.offsetTop || 0;
var addLeft = frame.offsetLeft || 0;
var yOffset = body.scrollTop || 0;
var xOffset = body.scrollLeft || 0;
return {
y: e.clientY + addTop + yOffset,
x: e.clientX + addLeft + xOffset,
};
},
/**
* Returns wrapper element
* @return {HTMLElement}
* ????
*/
getFrameWrapperEl: function(){
return CanvasView.frame.getWrapper();
},
};
module.exports = function() {
var c = {},
defaults = require('./config/config'),
Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView');
var canvas;
return {
/**
* Used inside RTE
* @private
*/
getCanvasView: function() {
return CanvasView;
},
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Canvas',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
canvas = new Canvas(config);
CanvasView = new CanvasView({
model: canvas,
config: c,
});
var cm = c.em.get('DomComponents');
if(cm)
this.setWrapper(cm);
return this;
},
/**
* Add wrapper
* @param {Object} wrp Wrapper
*
* */
setWrapper: function(wrp) {
canvas.set('wrapper', wrp);
},
/**
* Returns canvas element
* @return {HTMLElement}
*/
getElement: function(){
return CanvasView.el;
},
/**
* Returns frame element of the canvas
* @return {HTMLElement}
*/
getFrameEl: function(){
return CanvasView.frame.el;
},
/**
* Returns body element of the frame
* @return {HTMLElement}
*/
getBody: function(){
return CanvasView.frame.el.contentDocument.body;
},
/**
* Returns body wrapper element of the frame
* @return {HTMLElement}
*/
getWrapperEl: function(){
return this.getBody().querySelector('#wrapper');
},
/**
* Returns element containing canvas tools
* @return {HTMLElement}
*/
getToolsEl: function(){
return CanvasView.toolsEl;
},
/**
* Returns highlighter element
* @return {HTMLElement}
*/
getHighlighter: function(){
return CanvasView.hlEl;
},
/**
* Returns badge element
* @return {HTMLElement}
*/
getBadgeEl: function(){
return CanvasView.badgeEl;
},
/**
* Returns placer element
* @return {HTMLElement}
*/
getPlacerEl: function(){
return CanvasView.placerEl;
},
/**
* Returns ghost element
* @return {HTMLElement}
* @private
*/
getGhostEl: function(){
return CanvasView.ghostEl;
},
/**
* Returns toolbar element
* @return {HTMLElement}
*/
getToolbarEl: function() {
return CanvasView.toolbarEl;
},
/**
* Returns resizer element
* @return {HTMLElement}
*/
getResizerEl: function() {
return CanvasView.resizerEl;
},
/**
* Returns offset viewer element
* @return {HTMLElement}
*/
getOffsetViewerEl: function() {
return CanvasView.offsetEl;
},
/**
* Returns fixed offset viewer element
* @return {HTMLElement}
*/
getFixedOffsetViewerEl: function() {
return CanvasView.fixedOffsetEl;
},
/**
* Render canvas
* */
render: function() {
return CanvasView.render().el;
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffset: function() {
var frameOff = this.offset(this.getFrameEl());
var canvasOff = this.offset(this.getElement());
return {
top: frameOff.top - canvasOff.top,
left: frameOff.left - canvasOff.left
};
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
* @private
*/
offset: function(el){
var rect = el.getBoundingClientRect();
return {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
};
},
/**
* Get element position relative to the canvas
* @param {HTMLElement} el
* @return {Object}
*/
getElementPos: function(el) {
return CanvasView.getElementPos(el);
},
/**
* This method comes handy when you need to attach something like toolbars
* to elements inside the canvas, dealing with all relative position,
* offsets, etc. and returning as result the object with positions which are
* viewable by the user (when the canvas is scrolled the top edge of the element
* is not viewable by the user anymore so the new top edge is the one of the canvas)
*
* The target should be visible before being passed here as invisible elements
* return empty string as width
* @param {HTMLElement} target The target in this case could be the toolbar
* @param {HTMLElement} element The element on which I'd attach the toolbar
* @param {Object} options Custom options
* @param {Boolean} options.toRight Set to true if you want the toolbar attached to the right
* @return {Object}
*/
getTargetToElementDim: function (target, element, options) {
var opts = options || {};
var canvasPos = CanvasView.getPosition();
var pos = opts.elPos || CanvasView.getElementPos(element);
var toRight = options.toRight || 0;
var targetHeight = opts.targetHeight || target.offsetHeight;
var targetWidth = opts.targetWidth || target.offsetWidth;
var eventToTrigger = opts.event || null;
var elTop = pos.top - targetHeight;
var elLeft = pos.left;
elLeft += toRight ? pos.width : 0;
elLeft = toRight ? (elLeft - targetWidth) : elLeft;
var leftPos = elLeft < canvasPos.left ? canvasPos.left : elLeft;
var topPos = elTop < canvasPos.top ? canvasPos.top : elTop;
topPos = topPos > (pos.top + pos.height) ? (pos.top + pos.height) : topPos;
var result = {
top: topPos,
left: leftPos,
elementTop: pos.top,
elementLeft: pos.left,
elementWidth: pos.width,
elementHeight: pos.height,
targetWidth: target.offsetWidth,
targetHeight: target.offsetHeight,
canvasTop: canvasPos.top,
canvasLeft: canvasPos.left,
};
// In this way I can catch data and also change the position strategy
if(eventToTrigger && c.em) {
c.em.trigger(eventToTrigger, result);
}
return result;
},
/**
* Instead of simply returning e.clientX and e.clientY this function
* calculates also the offset based on the canvas. This is helpful when you
* need to get X and Y position while moving between the editor area and
* canvas area, which is in the iframe
* @param {Event} e
* @return {Object}
*/
getMouseRelativePos: function (e, options) {
var opts = options || {};
var addTop = 0;
var addLeft = 0;
var subWinOffset = opts.subWinOffset;
var doc = e.target.ownerDocument;
var win = doc.defaultView || doc.parentWindow;
var frame = win.frameElement;
var yOffset = subWinOffset ? win.pageYOffset : 0;
var xOffset = subWinOffset ? win.pageXOffset : 0;
if (frame) {
var frameRect = frame.getBoundingClientRect();
addTop = frameRect.top || 0;
addLeft = frameRect.left || 0;
}
return {
y: e.clientY + addTop - yOffset,
x: e.clientX + addLeft - xOffset,
};
},
/**
* X and Y mouse position relative to the canvas
* @param {Event} e
* @return {Object}
*/
getMouseRelativeCanvas: function (e, options) {
var opts = options || {};
var frame = this.getFrameEl();
var body = this.getBody();
var addTop = frame.offsetTop || 0;
var addLeft = frame.offsetLeft || 0;
var yOffset = body.scrollTop || 0;
var xOffset = body.scrollLeft || 0;
return {
y: e.clientY + addTop + yOffset,
x: e.clientX + addLeft + xOffset,
};
},
/**
* Returns wrapper element
* @return {HTMLElement}
* ????
*/
getFrameWrapperEl: function(){
return CanvasView.frame.getWrapper();
},
};
});
};

29
src/canvas/model/Canvas.js

@ -1,20 +1,17 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Frame = require('./Frame');
var Backbone = require('backbone');
var Frame = require('./Frame');
module.exports = Backbone.Model.extend({
module.exports = Backbone.Model.extend({
defaults :{
frame: '',
wrapper: '',
rulers: false,
},
defaults :{
frame: '',
wrapper: '',
rulers: false,
},
initialize: function(config) {
var conf = this.conf || {};
this.set('frame', new Frame(conf.frame));
},
initialize: function(config) {
var conf = this.conf || {};
this.set('frame', new Frame(conf.frame));
},
});
});
});

21
src/canvas/model/Frame.js

@ -1,15 +1,12 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
module.exports = Backbone.Model.extend({
defaults :{
wrapper: '',
width: '',
height: '',
attributes: {},
},
defaults :{
wrapper: '',
width: '',
height: '',
attributes: {},
},
});
});
});

481
src/canvas/view/CanvasView.js

@ -1,271 +1,266 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var FrameView = require('./FrameView');
/**
* @class CanvasView
* */
module.exports = Backbone.View.extend({
var Backbone = require('backbone');
var FrameView = require('./FrameView');
initialize: function(o) {
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff');
this.config = o.config || {};
this.em = this.config.em || {};
this.ppfx = this.config.pStylePrefix || '';
this.className = this.config.stylePrefix + 'canvas';
this.listenTo(this.em, 'change:canvasOffset', this.clearOff);
this.frame = new FrameView({
model: this.model.get('frame'),
config: this.config
});
},
module.exports = Backbone.View.extend({
/**
* Update tools position
* @private
*/
onFrameScroll: function(){
var u = 'px';
var body = this.frame.el.contentDocument.body;
this.toolsEl.style.top = '-' + body.scrollTop + u;
this.toolsEl.style.left = '-' + body.scrollLeft + u;
this.em.trigger('canvasScroll');
},
initialize: function(o) {
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff');
this.config = o.config || {};
this.em = this.config.em || {};
this.ppfx = this.config.pStylePrefix || '';
this.className = this.config.stylePrefix + 'canvas';
this.listenTo(this.em, 'change:canvasOffset', this.clearOff);
this.frame = new FrameView({
model: this.model.get('frame'),
config: this.config
});
},
/**
* Insert scripts into head, it will call renderBody after all scripts loaded or failed
* @private
*/
renderScripts: function () {
var frame = this.frame;
var that = this;
/**
* Update tools position
* @private
*/
onFrameScroll: function(){
var u = 'px';
var body = this.frame.el.contentDocument.body;
this.toolsEl.style.top = '-' + body.scrollTop + u;
this.toolsEl.style.left = '-' + body.scrollLeft + u;
this.em.trigger('canvasScroll');
},
frame.el.onload = function () {
var scripts = that.config.scripts.slice(0), // clone
counter = 0;
/**
* Insert scripts into head, it will call renderBody after all scripts loaded or failed
* @private
*/
renderScripts: function () {
var frame = this.frame;
var that = this;
function appendScript(scripts) {
if (scripts.length > 0) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scripts.shift();
script.onerror = script.onload = appendScript.bind(null, scripts);
frame.el.contentDocument.head.appendChild(script);
} else {
that.renderBody();
}
frame.el.onload = function () {
var scripts = that.config.scripts.slice(0), // clone
counter = 0;
function appendScript(scripts) {
if (scripts.length > 0) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scripts.shift();
script.onerror = script.onload = appendScript.bind(null, scripts);
frame.el.contentDocument.head.appendChild(script);
} else {
that.renderBody();
}
appendScript(scripts);
};
},
}
appendScript(scripts);
};
},
/**
* Render inside frame's body
* @private
*/
renderBody: function() {
var wrap = this.model.get('frame').get('wrapper');
var em = this.config.em;
if(wrap) {
var ppfx = this.ppfx;
var body = this.frame.$el.contents().find('body');
var cssc = em.get('CssComposer');
var conf = em.get('Config');
body.append(wrap.render()).append(cssc.render());
var protCss = conf.protectedCss;
/**
* Render inside frame's body
* @private
*/
renderBody: function() {
var wrap = this.model.get('frame').get('wrapper');
var em = this.config.em;
if(wrap) {
var ppfx = this.ppfx;
var body = this.frame.$el.contents().find('body');
var cssc = em.get('CssComposer');
var conf = em.get('Config');
body.append(wrap.render()).append(cssc.render());
var protCss = conf.protectedCss;
// 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}' +
'.' + 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}' +
'.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+
'.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' +
'.' + ppfx + 'no-pointer{pointer-events: none}' +
'.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}' +
'.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' +
'* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' +
'* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' +
'* ::-webkit-scrollbar {width: 10px}' +
(conf.canvasCss || '');
frameCss += protCss || '';
body.append('<style>' + frameCss + '</style>');
body.append(this.getJsContainer());
em.trigger('loaded');
this.frame.el.contentWindow.onscroll = this.onFrameScroll;
this.frame.udpateOffset();
// 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}' +
'.' + 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}' +
'.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+
'.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' +
'.' + ppfx + 'no-pointer{pointer-events: none}' +
'.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}' +
'.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' +
'* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' +
'* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' +
'* ::-webkit-scrollbar {width: 10px}' +
(conf.canvasCss || '');
frameCss += protCss || '';
body.append('<style>' + frameCss + '</style>');
body.append(this.getJsContainer());
em.trigger('loaded');
this.frame.el.contentWindow.onscroll = this.onFrameScroll;
this.frame.udpateOffset();
// When the iframe is focused the event dispatcher is not the same so
// I need to delegate all events to the parent document
var doc = document;
var fdoc = this.frame.el.contentDocument;
fdoc.addEventListener('keydown', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
fdoc.addEventListener('keyup', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
}
},
// When the iframe is focused the event dispatcher is not the same so
// I need to delegate all events to the parent document
var doc = document;
var fdoc = this.frame.el.contentDocument;
fdoc.addEventListener('keydown', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
fdoc.addEventListener('keyup', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
}
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
*/
offset: function(el){
var rect = el.getBoundingClientRect();
var docBody = el.ownerDocument.body;
return {
top: rect.top + docBody.scrollTop,
left: rect.left + docBody.scrollLeft
};
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
*/
offset: function(el){
var rect = el.getBoundingClientRect();
var docBody = el.ownerDocument.body;
return {
top: rect.top + docBody.scrollTop,
left: rect.left + docBody.scrollLeft
};
},
/**
* Cleare cached offsets
* @private
*/
clearOff: function(){
this.frmOff = null;
this.cvsOff = null;
},
/**
* Cleare cached offsets
* @private
*/
clearOff: function(){
this.frmOff = null;
this.cvsOff = null;
},
/**
* Return frame offset
* @return {Object}
* @private
*/
getFrameOffset: function () {
if(!this.frmOff)
this.frmOff = this.offset(this.frame.el);
return this.frmOff;
},
/**
* Return frame offset
* @return {Object}
* @private
*/
getFrameOffset: function () {
if(!this.frmOff)
this.frmOff = this.offset(this.frame.el);
return this.frmOff;
},
/**
* Return canvas offset
* @return {Object}
* @private
*/
getCanvasOffset: function () {
if(!this.cvsOff)
this.cvsOff = this.offset(this.el);
return this.cvsOff;
},
/**
* Return canvas offset
* @return {Object}
* @private
*/
getCanvasOffset: function () {
if(!this.cvsOff)
this.cvsOff = this.offset(this.el);
return this.cvsOff;
},
/**
* Returns element's data info
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementPos: function(el) {
var frmOff = this.getFrameOffset();
var cvsOff = this.getCanvasOffset();
var eo = this.offset(el);
var top = eo.top + frmOff.top - cvsOff.top;
var left = eo.left + frmOff.left - cvsOff.left;
return {
top: top,
left: left,
height: el.offsetHeight,
width: el.offsetWidth
};
},
/**
* Returns element's data info
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementPos: function(el) {
var frmOff = this.getFrameOffset();
var cvsOff = this.getCanvasOffset();
var eo = this.offset(el);
var top = eo.top + frmOff.top - cvsOff.top;
var left = eo.left + frmOff.left - cvsOff.left;
return {
top: top,
left: left,
height: el.offsetHeight,
width: el.offsetWidth
};
},
/**
* Returns position data of the canvas element
* @return {Object} obj Position object
* @private
*/
getPosition: function() {
var bEl = this.frame.el.contentDocument.body;
var fo = this.getFrameOffset();
var co = this.getCanvasOffset();
return {
top: fo.top + bEl.scrollTop - co.top,
left: fo.left + bEl.scrollLeft - co.left
};
},
/**
* Returns position data of the canvas element
* @return {Object} obj Position object
* @private
*/
getPosition: function() {
var bEl = this.frame.el.contentDocument.body;
var fo = this.getFrameOffset();
var co = this.getCanvasOffset();
return {
top: fo.top + bEl.scrollTop - co.top,
left: fo.left + bEl.scrollLeft - co.left
};
},
/**
* Update javascript of a specific component passed by its View
* @param {View} view Component's View
* @private
*/
updateScript: function(view) {
if(!view.scriptContainer) {
view.scriptContainer = $('<div>');
this.getJsContainer().append(view.scriptContainer.get(0));
}
/**
* Update javascript of a specific component passed by its View
* @param {View} view Component's View
* @private
*/
updateScript: function(view) {
if(!view.scriptContainer) {
view.scriptContainer = $('<div>');
this.getJsContainer().append(view.scriptContainer.get(0));
}
var id = view.model.cid;
var script = view.model.get('script');
var scrStr = 'function(){' + script + '}';
scrStr = typeof script == 'function' ? script.toString() : scrStr;
var id = view.model.cid;
var script = view.model.get('script');
var scrStr = 'function(){' + script + '}';
scrStr = typeof script == 'function' ? script.toString() : scrStr;
view.el.id = id;
view.scriptContainer.html('');
view.el.id = id;
view.scriptContainer.html('');
view.scriptContainer.append('<script>' +
'var item = document.getElementById("'+id+'");' +
'(' + scrStr + '.bind(item))()</script>');
},
view.scriptContainer.append('<script>' +
'var item = document.getElementById("'+id+'");' +
'(' + scrStr + '.bind(item))()</script>');
},
/**
* Get javascript container
* @private
*/
getJsContainer: function () {
if (!this.jsContainer) {
this.jsContainer = $('<div>', {class: this.ppfx + 'js-cont'}).get(0);
}
return this.jsContainer;
},
/**
* Get javascript container
* @private
*/
getJsContainer: function () {
if (!this.jsContainer) {
this.jsContainer = $('<div>', {class: this.ppfx + 'js-cont'}).get(0);
}
return this.jsContainer;
},
render: function() {
this.wrapper = this.model.get('wrapper');
render: function() {
this.wrapper = this.model.get('wrapper');
if(this.wrapper && typeof this.wrapper.render == 'function'){
this.model.get('frame').set('wrapper', this.wrapper);
this.$el.append(this.frame.render().el);
var frame = this.frame;
if (this.config.scripts.length === 0) {
frame.el.onload = this.renderBody;
} else {
this.renderScripts(); // will call renderBody later
}
if(this.wrapper && typeof this.wrapper.render == 'function'){
this.model.get('frame').set('wrapper', this.wrapper);
this.$el.append(this.frame.render().el);
var frame = this.frame;
if (this.config.scripts.length === 0) {
frame.el.onload = this.renderBody;
} else {
this.renderScripts(); // will call renderBody later
}
var ppfx = this.ppfx;
var toolsEl = $('<div>', { id: ppfx + 'tools' }).get(0);
this.hlEl = $('<div>', { class: ppfx + 'highlighter' }).get(0);
this.badgeEl = $('<div>', {class: ppfx + 'badge'}).get(0);
this.placerEl = $('<div>', {class: ppfx + 'placeholder'}).get(0);
this.placerIntEl = $('<div>', {class: ppfx + 'placeholder-int'}).get(0);
this.ghostEl = $('<div>', {class: ppfx + 'ghost'}).get(0);
this.toolbarEl = $('<div>', {class: ppfx + 'toolbar'}).get(0);
this.resizerEl = $('<div>', {class: ppfx + 'resizer'}).get(0);
this.offsetEl = $('<div>', {class: ppfx + 'offset-v'}).get(0);
this.fixedOffsetEl = $('<div>', {class: ppfx + 'offset-fixed-v'}).get(0);
this.placerEl.appendChild(this.placerIntEl);
toolsEl.appendChild(this.hlEl);
toolsEl.appendChild(this.badgeEl);
toolsEl.appendChild(this.placerEl);
toolsEl.appendChild(this.ghostEl);
toolsEl.appendChild(this.toolbarEl);
toolsEl.appendChild(this.resizerEl);
toolsEl.appendChild(this.offsetEl);
toolsEl.appendChild(this.fixedOffsetEl);
this.$el.append(toolsEl);
var rte = this.em.get('rte');
}
var ppfx = this.ppfx;
var toolsEl = $('<div>', { id: ppfx + 'tools' }).get(0);
this.hlEl = $('<div>', { class: ppfx + 'highlighter' }).get(0);
this.badgeEl = $('<div>', {class: ppfx + 'badge'}).get(0);
this.placerEl = $('<div>', {class: ppfx + 'placeholder'}).get(0);
this.placerIntEl = $('<div>', {class: ppfx + 'placeholder-int'}).get(0);
this.ghostEl = $('<div>', {class: ppfx + 'ghost'}).get(0);
this.toolbarEl = $('<div>', {class: ppfx + 'toolbar'}).get(0);
this.resizerEl = $('<div>', {class: ppfx + 'resizer'}).get(0);
this.offsetEl = $('<div>', {class: ppfx + 'offset-v'}).get(0);
this.fixedOffsetEl = $('<div>', {class: ppfx + 'offset-fixed-v'}).get(0);
this.placerEl.appendChild(this.placerIntEl);
toolsEl.appendChild(this.hlEl);
toolsEl.appendChild(this.badgeEl);
toolsEl.appendChild(this.placerEl);
toolsEl.appendChild(this.ghostEl);
toolsEl.appendChild(this.toolbarEl);
toolsEl.appendChild(this.resizerEl);
toolsEl.appendChild(this.offsetEl);
toolsEl.appendChild(this.fixedOffsetEl);
this.$el.append(toolsEl);
var rte = this.em.get('rte');
if(rte)
toolsEl.appendChild(rte.render());
if(rte)
toolsEl.appendChild(rte.render());
this.toolsEl = toolsEl;
this.$el.attr({class: this.className});
return this;
},
this.toolsEl = toolsEl;
this.$el.attr({class: this.className});
return this;
},
});
});

105
src/canvas/view/FrameView.js

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

12
src/code_manager/config/config.js

@ -1,8 +1,6 @@
define(function () {
return {
// Style prefix
stylePrefix: 'cm-',
module.exports = {
// Style prefix
stylePrefix: 'cm-',
inlineCss: false,
};
});
inlineCss: false,
};

410
src/code_manager/index.js

@ -17,211 +17,205 @@
*
* @module CodeManager
*/
define(function(require) {
var CodeManager = function() {
var c = {},
defaults = require('./config/config'),
gHtml = require('./model/HtmlGenerator'),
gCss = require('./model/CssGenerator'),
gJson = require('./model/JsonGenerator'),
gJs = require('./model/JsGenerator'),
eCM = require('./model/CodeMirrorEditor'),
editorView = require('./view/EditorView');
var generators = {},
defGenerators = {},
viewers = {},
defViewers = {};
return {
getConfig: function() {
return c;
},
config: c,
EditorView: editorView,
/**
* Name of the module
* @type {String}
* @private
*/
name: 'CodeManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
defGenerators.html = new gHtml();
defGenerators.css = new gCss();
defGenerators.json = new gJson();
defGenerators.js = new gJs();
defViewers.CodeMirror = new eCM();
return this;
},
/**
* Callback on load
*/
onLoad: function(){
this.loadDefaultGenerators().loadDefaultViewers();
},
/**
* Add new code generator to the collection
* @param {string} id Code generator ID
* @param {Object} generator Code generator wrapper
* @param {Function} generator.build Function that builds the code
* @return {this}
* @example
* codeManager.addGenerator('html7',{
* build: function(model){
* return 'myCode';
* }
* });
* */
addGenerator: function(id, generator) {
generators[id] = generator;
return this;
},
/**
* Get code generator by id
* @param {string} id Code generator ID
* @return {Object|null}
* @example
* var generator = codeManager.getGenerator('html7');
* generator.build = function(model){
* //extend
* };
* */
getGenerator: function(id) {
return generators[id] || null;
},
/**
* Returns all code generators
* @return {Array<Object>}
* */
getGenerators: function() {
return generators;
},
/**
* Add new code viewer
* @param {string} id Code viewer ID
* @param {Object} viewer Code viewer wrapper
* @param {Function} viewer.init Set element on which viewer will be displayed
* @param {Function} viewer.setContent Set content to the viewer
* @return {this}
* @example
* codeManager.addViewer('ace',{
* init: function(el){
* var ace = require('ace-editor');
* this.editor = ace.edit(el.id);
* },
* setContent: function(code){
* this.editor.setValue(code);
* }
* });
* */
addViewer: function(id, viewer) {
viewers[id] = viewer;
return this;
},
/**
* Get code viewer by id
* @param {string} id Code viewer ID
* @return {Object|null}
* @example
* var viewer = codeManager.getViewer('ace');
* */
getViewer: function(id) {
return viewers[id] || null;
},
/**
* Returns all code viewers
* @return {Array<Object>}
* */
getViewers: function() {
return viewers;
},
/**
* Update code viewer content
* @param {Object} viewer Viewer instance
* @param {string} code Code string
* @example
* var AceViewer = codeManager.getViewer('ace');
* // ...
* var viewer = AceViewer.init(el);
* // ...
* codeManager.updateViewer(AceViewer, 'code');
* */
updateViewer: function(viewer, code) {
viewer.setContent(code);
},
/**
* Get code from model
* @param {Object} model Any kind of model that will be passed to the build method of generator
* @param {string} genId Code generator id
* @param {Object} [opt] Options
* @return {string}
* @example
* var codeStr = codeManager.getCode(model, 'html');
* */
getCode: function(model, genId, opt) {
var generator = this.getGenerator(genId);
return generator ? generator.build(model, opt) : '';
},
/**
* Load default code generators
* @return {this}
* @private
* */
loadDefaultGenerators: function() {
for (var id in defGenerators)
this.addGenerator(id, defGenerators[id]);
return this;
},
/**
* Load default code viewers
* @return {this}
* @private
* */
loadDefaultViewers: function() {
for (var id in defViewers)
this.addViewer(id, defViewers[id]);
return this;
},
};
};
return CodeManager;
});
module.exports = function() {
var c = {},
defaults = require('./config/config'),
gHtml = require('./model/HtmlGenerator'),
gCss = require('./model/CssGenerator'),
gJson = require('./model/JsonGenerator'),
gJs = require('./model/JsGenerator'),
eCM = require('./model/CodeMirrorEditor'),
editorView = require('./view/EditorView');
var generators = {},
defGenerators = {},
viewers = {},
defViewers = {};
return {
getConfig: function() {
return c;
},
config: c,
EditorView: editorView,
/**
* Name of the module
* @type {String}
* @private
*/
name: 'CodeManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
defGenerators.html = new gHtml();
defGenerators.css = new gCss();
defGenerators.json = new gJson();
defGenerators.js = new gJs();
defViewers.CodeMirror = new eCM();
return this;
},
/**
* Callback on load
*/
onLoad: function(){
this.loadDefaultGenerators().loadDefaultViewers();
},
/**
* Add new code generator to the collection
* @param {string} id Code generator ID
* @param {Object} generator Code generator wrapper
* @param {Function} generator.build Function that builds the code
* @return {this}
* @example
* codeManager.addGenerator('html7',{
* build: function(model){
* return 'myCode';
* }
* });
* */
addGenerator: function(id, generator) {
generators[id] = generator;
return this;
},
/**
* Get code generator by id
* @param {string} id Code generator ID
* @return {Object|null}
* @example
* var generator = codeManager.getGenerator('html7');
* generator.build = function(model){
* //extend
* };
* */
getGenerator: function(id) {
return generators[id] || null;
},
/**
* Returns all code generators
* @return {Array<Object>}
* */
getGenerators: function() {
return generators;
},
/**
* Add new code viewer
* @param {string} id Code viewer ID
* @param {Object} viewer Code viewer wrapper
* @param {Function} viewer.init Set element on which viewer will be displayed
* @param {Function} viewer.setContent Set content to the viewer
* @return {this}
* @example
* codeManager.addViewer('ace',{
* init: function(el){
* var ace = require('ace-editor');
* this.editor = ace.edit(el.id);
* },
* setContent: function(code){
* this.editor.setValue(code);
* }
* });
* */
addViewer: function(id, viewer) {
viewers[id] = viewer;
return this;
},
/**
* Get code viewer by id
* @param {string} id Code viewer ID
* @return {Object|null}
* @example
* var viewer = codeManager.getViewer('ace');
* */
getViewer: function(id) {
return viewers[id] || null;
},
/**
* Returns all code viewers
* @return {Array<Object>}
* */
getViewers: function() {
return viewers;
},
/**
* Update code viewer content
* @param {Object} viewer Viewer instance
* @param {string} code Code string
* @example
* var AceViewer = codeManager.getViewer('ace');
* // ...
* var viewer = AceViewer.init(el);
* // ...
* codeManager.updateViewer(AceViewer, 'code');
* */
updateViewer: function(viewer, code) {
viewer.setContent(code);
},
/**
* Get code from model
* @param {Object} model Any kind of model that will be passed to the build method of generator
* @param {string} genId Code generator id
* @param {Object} [opt] Options
* @return {string}
* @example
* var codeStr = codeManager.getCode(model, 'html');
* */
getCode: function(model, genId, opt) {
var generator = this.getGenerator(genId);
return generator ? generator.build(model, opt) : '';
},
/**
* Load default code generators
* @return {this}
* @private
* */
loadDefaultGenerators: function() {
for (var id in defGenerators)
this.addGenerator(id, defGenerators[id]);
return this;
},
/**
* Load default code viewers
* @return {this}
* @private
* */
loadDefaultViewers: function() {
for (var id in defViewers)
this.addViewer(id, defViewers[id]);
return this;
},
};
};

87
src/code_manager/model/CodeMirrorEditor.js

@ -1,51 +1,46 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var CodeMirror = require('codemirror/lib/codemirror');
var htmlMode = require('codemirror/mode/htmlmixed/htmlmixed');
var cssMode = require('codemirror/mode/css/css');
var formatting = require('formatting');
/**
* @class CodeViewer
* */
module.exports = Backbone.Model.extend({
var Backbone = require('backbone');
var CodeMirror = require('codemirror/lib/codemirror');
var htmlMode = require('codemirror/mode/htmlmixed/htmlmixed');
var cssMode = require('codemirror/mode/css/css');
var formatting = require('formatting');
defaults: {
input : '',
label : '',
codeName : '',
theme : '',
readOnly : true,
lineNumbers : true,
},
module.exports = Backbone.Model.extend({
/** @inheritdoc */
init: function(el)
{
this.editor = CodeMirror.fromTextArea(el, {
dragDrop: false,
lineWrapping: true,
lineNumbers: this.get('lineNumbers'),
readOnly: this.get('readOnly'),
mode: this.get('codeName'),
theme: this.get('theme'),
});
defaults: {
input : '',
label : '',
codeName : '',
theme : '',
readOnly : true,
lineNumbers : true,
},
return this;
},
/** @inheritdoc */
init: function(el)
{
this.editor = CodeMirror.fromTextArea(el, {
dragDrop: false,
lineWrapping: true,
lineNumbers: this.get('lineNumbers'),
readOnly: this.get('readOnly'),
mode: this.get('codeName'),
theme: this.get('theme'),
});
/** @inheritdoc */
setContent : function(v)
{
if(!this.editor)
return;
this.editor.setValue(v);
if(this.editor.autoFormatRange){
CodeMirror.commands.selectAll(this.editor);
this.editor.autoFormatRange(this.editor.getCursor(true), this.editor.getCursor(false) );
CodeMirror.commands.goDocStart(this.editor);
}
},
return this;
},
});
});
/** @inheritdoc */
setContent : function(v)
{
if(!this.editor)
return;
this.editor.setValue(v);
if(this.editor.autoFormatRange){
CodeMirror.commands.selectAll(this.editor);
this.editor.autoFormatRange(this.editor.getCursor(true), this.editor.getCursor(false) );
CodeMirror.commands.goDocStart(this.editor);
}
},
});

283
src/code_manager/model/CssGenerator.js

@ -1,152 +1,147 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
/**
* @class CssGenerator
* */
module.exports = Backbone.Model.extend({
initialize: function() {
this.compCls = [];
},
/**
* Get CSS from component
* @param {Model} model
* @return {String}
*/
buildFromModel: function (model) {
var code = '';
var style = model.get('style');
var classes = model.get('classes');
// Let's know what classes I've found
if(classes) {
classes.each(function(model){
this.compCls.push(model.get('name'));
}, this);
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
initialize: function() {
this.compCls = [];
},
/**
* Get CSS from component
* @param {Model} model
* @return {String}
*/
buildFromModel: function (model) {
var code = '';
var style = model.get('style');
var classes = model.get('classes');
// Let's know what classes I've found
if(classes) {
classes.each(function(model){
this.compCls.push(model.get('name'));
}, this);
}
if(style && Object.keys(style).length !== 0) {
code += '#' + model.cid + '{';
for(var prop in style){
if(style.hasOwnProperty(prop))
code += prop + ':' + style[prop] + ';';
}
code += '}';
}
return code;
},
/**
* Get CSS from components
* @param {Model} model
* @return {String}
*/
buildFromComp: function(model) {
var coll = model.get('components') || model,
code = '';
coll.each(function(m) {
var cln = m.get('components');
code += this.buildFromModel(m);
if(cln.length){
code += this.buildFromComp(cln);
}
}, this);
return code;
},
/** @inheritdoc */
build: function(model, cssc) {
this.compCls = [];
var code = this.buildFromModel(model);
code += this.buildFromComp(model);
var compCls = this.compCls;
if(cssc){
var rules = cssc.getAll();
var mediaRules = {};
rules.each(function(rule) {
var width = rule.get('mediaText');
// If width setted will render it later
if(width){
var mRule = mediaRules[width];
if(mRule)
mRule.push(rule);
else
mediaRules[width] = [rule];
return;
}
if(style && Object.keys(style).length !== 0) {
code += '#' + model.cid + '{';
for(var prop in style){
if(style.hasOwnProperty(prop))
code += prop + ':' + style[prop] + ';';
}
code += '}';
}
code += this.buildFromRule(rule);
}, this);
return code;
},
/**
* Get CSS from components
* @param {Model} model
* @return {String}
*/
buildFromComp: function(model) {
var coll = model.get('components') || model,
code = '';
coll.each(function(m) {
var cln = m.get('components');
code += this.buildFromModel(m);
if(cln.length){
code += this.buildFromComp(cln);
}
}, this);
return code;
},
/** @inheritdoc */
build: function(model, cssc) {
this.compCls = [];
var code = this.buildFromModel(model);
code += this.buildFromComp(model);
var compCls = this.compCls;
if(cssc){
var rules = cssc.getAll();
var mediaRules = {};
rules.each(function(rule) {
var width = rule.get('mediaText');
// If width setted will render it later
if(width){
var mRule = mediaRules[width];
if(mRule)
mRule.push(rule);
else
mediaRules[width] = [rule];
return;
}
code += this.buildFromRule(rule);
}, this);
// Get media rules
for (var ruleW in mediaRules) {
var meRules = mediaRules[ruleW];
var ruleC = '';
for(var i = 0, len = meRules.length; i < len; i++){
ruleC += this.buildFromRule(meRules[i]);
}
if (ruleC) {
code += '@media ' + ruleW + '{' + ruleC + '}';
}
}
// Get media rules
for (var ruleW in mediaRules) {
var meRules = mediaRules[ruleW];
var ruleC = '';
for(var i = 0, len = meRules.length; i < len; i++){
ruleC += this.buildFromRule(meRules[i]);
}
if (ruleC) {
code += '@media ' + ruleW + '{' + ruleC + '}';
}
return code;
},
/**
* Get CSS from the rule model
* @param {Model} rule
* @return {string} CSS string
*/
buildFromRule: function(rule) {
var result = '';
var selectorsAdd = rule.get('selectorsAdd');
var selectors = rule.get('selectors');
var ruleStyle = rule.get('style');
var state = rule.get('state');
var strSel = '';
var found = 0;
var compCls = this.compCls;
// Get string of selectors
selectors.each(function(selector){
strSel += '.' + selector.get('name');
if(compCls.indexOf(selector.get('name')) > -1)
found = 1;
});
// With 'found' will skip rules which selectors are not found in
// canvas components.
if ((strSel && found) || selectorsAdd) {
strSel += state ? ':' + state : '';
strSel += selectorsAdd ? (strSel ? ', ' : '') + selectorsAdd : '';
var strStyle = '';
// Get string of style properties
if(ruleStyle && Object.keys(ruleStyle).length !== 0){
for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
}
}
if(strStyle)
result += strSel + '{' + strStyle + '}';
}
}
return code;
},
/**
* Get CSS from the rule model
* @param {Model} rule
* @return {string} CSS string
*/
buildFromRule: function(rule) {
var result = '';
var selectorsAdd = rule.get('selectorsAdd');
var selectors = rule.get('selectors');
var ruleStyle = rule.get('style');
var state = rule.get('state');
var strSel = '';
var found = 0;
var compCls = this.compCls;
// Get string of selectors
selectors.each(function(selector){
strSel += '.' + selector.get('name');
if(compCls.indexOf(selector.get('name')) > -1)
found = 1;
});
// With 'found' will skip rules which selectors are not found in
// canvas components.
if ((strSel && found) || selectorsAdd) {
strSel += state ? ':' + state : '';
strSel += selectorsAdd ? (strSel ? ', ' : '') + selectorsAdd : '';
var strStyle = '';
// Get string of style properties
if(ruleStyle && Object.keys(ruleStyle).length !== 0){
for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
}
}
return result;
},
if(strStyle)
result += strSel + '{' + strStyle + '}';
}
});
});
return result;
},
});

35
src/code_manager/model/HtmlGenerator.js

@ -1,24 +1,19 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
/**
* @class HtmlGenerator
* */
module.exports = Backbone.Model.extend({
var Backbone = require('backbone');
/** @inheritdoc */
build: function(model, cssc){
var coll = model.get('components') || model,
code = '';
module.exports = Backbone.Model.extend({
coll.each(function(m){
code += m.toHTML({
cssc: cssc
});
}, this);
/** @inheritdoc */
build: function(model, cssc){
var coll = model.get('components') || model,
code = '';
return code;
},
coll.each(function(m){
code += m.toHTML({
cssc: cssc
});
}, this);
});
});
return code;
},
});

107
src/code_manager/model/JsGenerator.js

@ -1,64 +1,61 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
mapModel: function (model) {
var code = '';
var script = model.get('script');
var type = model.get('type');
var comps = model.get('components');
var id = model.cid;
if (script) {
// If the component has scripts we need to expose his ID
var attr = model.get('attributes');
attr = _.extend({}, attr, {id: id});
model.set('attributes', attr);
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 (model.get('scriptUpdated')) {
this.mapJs[type+'-'+id] = {ids: [id], code: scrStr};
} else {
var mapType = this.mapJs[type];
if(mapType) {
mapType.ids.push(id);
} else {
this.mapJs[type] = {ids: [id], code: scrStr};
}
}
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
mapModel: function (model) {
var code = '';
var script = model.get('script');
var type = model.get('type');
var comps = model.get('components');
var id = model.cid;
if (script) {
// If the component has scripts we need to expose his ID
var attr = model.get('attributes');
attr = _.extend({}, attr, {id: id});
model.set('attributes', attr);
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 (model.get('scriptUpdated')) {
this.mapJs[type+'-'+id] = {ids: [id], code: scrStr};
} else {
var mapType = this.mapJs[type];
if(mapType) {
mapType.ids.push(id);
} else {
this.mapJs[type] = {ids: [id], code: scrStr};
}
}
}
comps.each(function(model) {
code += this.mapModel(model);
}, this);
comps.each(function(model) {
code += this.mapModel(model);
}, this);
return code;
},
return code;
},
build: function(model) {
this.mapJs = {};
this.mapModel(model);
build: function(model) {
this.mapJs = {};
this.mapModel(model);
var code = '';
var code = '';
for(var type in this.mapJs) {
var mapType = this.mapJs[type];
var ids = '#' + mapType.ids.join(', #');
code += 'var items = document.querySelectorAll("'+ids+'");' +
'for (var i = 0, len = items.length; i < len; i++) {'+
'(' + mapType.code + '.bind(items[i]))();' +
'}';
}
for(var type in this.mapJs) {
var mapType = this.mapJs[type];
var ids = '#' + mapType.ids.join(', #');
code += 'var items = document.querySelectorAll("'+ids+'");' +
'for (var i = 0, len = items.length; i < len; i++) {'+
'(' + mapType.code + '.bind(items[i]))();' +
'}';
}
return code;
},
return code;
},
});
});
});

67
src/code_manager/model/JsonGenerator.js

@ -1,41 +1,36 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
/**
* @class JsonGenerator
* */
module.exports = Backbone.Model.extend({
var Backbone = require('backbone');
/** @inheritdoc */
build: function(model) {
var json = model.toJSON();
this.beforeEach(json);
module.exports = Backbone.Model.extend({
_.each(json,function(v, attr){
var obj = json[attr];
if(obj instanceof Backbone.Model){
json[attr] = this.build(obj);
}else if(obj instanceof Backbone.Collection){
var coll = obj;
json[attr] = [];
if(coll.length){
coll.each(function (el, index) {
json[attr][index] = this.build(el);
}, this);
}
}
}, this);
/** @inheritdoc */
build: function(model) {
var json = model.toJSON();
this.beforeEach(json);
return json;
},
_.each(json,function(v, attr){
var obj = json[attr];
if(obj instanceof Backbone.Model){
json[attr] = this.build(obj);
}else if(obj instanceof Backbone.Collection){
var coll = obj;
json[attr] = [];
if(coll.length){
coll.each(function (el, index) {
json[attr][index] = this.build(el);
}, this);
}
}
}, this);
/**
* Execute on each object
* @param {Object} obj
*/
beforeEach: function(obj) {
delete obj.status;
}
return json;
},
});
});
/**
* Execute on each object
* @param {Object} obj
*/
beforeEach: function(obj) {
delete obj.status;
}
});

49
src/code_manager/view/EditorView.js

@ -1,27 +1,22 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var vTemplate = require('text!./../template/editor.html');
/**
* @class EditorView
* */
module.exports = Backbone.View.extend({
template: _.template(vTemplate),
initialize: function(o){
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
},
render : function(){
var obj = this.model.toJSON();
obj.pfx = this.pfx;
this.$el.html( this.template(obj) );
this.$el.attr('class', this.pfx + 'editor-c');
this.$el.find('#'+this.pfx+'code').html(this.model.get('input'));
return this;
},
});
});
var Backbone = require('backbone');
var vTemplate = require('text!./../template/editor.html');
module.exports = Backbone.View.extend({
template: _.template(vTemplate),
initialize: function(o){
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
},
render: function(){
var obj = this.model.toJSON();
obj.pfx = this.pfx;
this.$el.html( this.template(obj) );
this.$el.attr('class', this.pfx + 'editor-c');
this.$el.find('#'+this.pfx+'code').html(this.model.get('input'));
return this;
},
});

32
src/commands/config/config.js

@ -1,25 +1,23 @@
define(function () {
return {
module.exports = {
ESCAPE_KEY : 27,
ESCAPE_KEY: 27,
stylePrefix : 'com-',
stylePrefix: 'com-',
defaults : [],
defaults: [],
// Editor model
em : null,
// Editor model
em: null,
// If true center new first-level components
firstCentered : true,
// If true center new first-level components
firstCentered: true,
// If true the new component will created with 'height', else 'min-height'
newFixedH : false,
// If true the new component will created with 'height', else 'min-height'
newFixedH: false,
// Minimum height (in px) of new component
minComponentH : 50,
// Minimum height (in px) of new component
minComponentH: 50,
// Minimum width (in px) of component on creation
minComponentW : 50,
};
});
// Minimum width (in px) of component on creation
minComponentW: 50,
};

388
src/commands/index.js

@ -38,201 +38,199 @@
* },
* ...
*/
define(function(require) {
return function() {
var c = {},
commands = {},
defaultCommands = {},
defaults = require('./config/config'),
AbsCommands = require('./view/CommandAbstract');
// Need it here as it would be used below
var add = function(id, obj){
delete obj.initialize;
commands[id] = AbsCommands.extend(obj);
return this;
};
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Commands',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
// Load commands passed via configuration
for( var k in c.defaults) {
var obj = c.defaults[k];
if(obj.id)
this.add(obj.id, obj);
}
defaultCommands['select-comp'] = require('./view/SelectComponent');
defaultCommands['create-comp'] = require('./view/CreateComponent');
defaultCommands['delete-comp'] = require('./view/DeleteComponent');
defaultCommands['image-comp'] = require('./view/ImageComponent');
defaultCommands['move-comp'] = require('./view/MoveComponent');
defaultCommands['text-comp'] = require('./view/TextComponent');
defaultCommands['insert-custom'] = require('./view/InsertCustom');
defaultCommands['export-template'] = require('./view/ExportTemplate');
defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
defaultCommands['open-layers'] = require('./view/OpenLayers');
defaultCommands['open-sm'] = require('./view/OpenStyleManager');
defaultCommands['open-tm'] = require('./view/OpenTraitManager');
defaultCommands['open-blocks'] = require('./view/OpenBlocks');
defaultCommands['open-assets'] = require('./view/OpenAssets');
defaultCommands['show-offset'] = require('./view/ShowOffset');
defaultCommands.fullscreen = require('./view/Fullscreen');
defaultCommands.preview = require('./view/Preview');
defaultCommands.resize = require('./view/Resize');
defaultCommands['tlb-delete'] = {
run: function(ed) {
var sel = ed.getSelected();
if(!sel || !sel.get('removable')) {
console.warn('The element is not removable');
return;
}
sel.set('status', '');
sel.destroy();
ed.trigger('component:update', sel);
ed.editor.set('selectedComponent', null);
},
};
defaultCommands['tlb-clone'] = {
run: function(ed) {
var sel = ed.getSelected();
if(!sel || !sel.get('copyable')) {
console.warn('The element is not clonable');
return;
}
var collection = sel.collection;
var index = collection.indexOf(sel);
collection.add(sel.clone(), {at: index + 1});
module.exports = function() {
var c = {},
commands = {},
defaultCommands = {},
defaults = require('./config/config'),
AbsCommands = require('./view/CommandAbstract');
// Need it here as it would be used below
var add = function(id, obj){
delete obj.initialize;
commands[id] = AbsCommands.extend(obj);
return this;
};
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Commands',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
// Load commands passed via configuration
for( var k in c.defaults) {
var obj = c.defaults[k];
if(obj.id)
this.add(obj.id, obj);
}
defaultCommands['select-comp'] = require('./view/SelectComponent');
defaultCommands['create-comp'] = require('./view/CreateComponent');
defaultCommands['delete-comp'] = require('./view/DeleteComponent');
defaultCommands['image-comp'] = require('./view/ImageComponent');
defaultCommands['move-comp'] = require('./view/MoveComponent');
defaultCommands['text-comp'] = require('./view/TextComponent');
defaultCommands['insert-custom'] = require('./view/InsertCustom');
defaultCommands['export-template'] = require('./view/ExportTemplate');
defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
defaultCommands['open-layers'] = require('./view/OpenLayers');
defaultCommands['open-sm'] = require('./view/OpenStyleManager');
defaultCommands['open-tm'] = require('./view/OpenTraitManager');
defaultCommands['open-blocks'] = require('./view/OpenBlocks');
defaultCommands['open-assets'] = require('./view/OpenAssets');
defaultCommands['show-offset'] = require('./view/ShowOffset');
defaultCommands.fullscreen = require('./view/Fullscreen');
defaultCommands.preview = require('./view/Preview');
defaultCommands.resize = require('./view/Resize');
defaultCommands['tlb-delete'] = {
run: function(ed) {
var sel = ed.getSelected();
if(!sel || !sel.get('removable')) {
console.warn('The element is not removable');
return;
}
sel.set('status', '');
sel.destroy();
ed.trigger('component:update', sel);
ed.editor.set('selectedComponent', null);
},
};
defaultCommands['tlb-clone'] = {
run: function(ed) {
var sel = ed.getSelected();
if(!sel || !sel.get('copyable')) {
console.warn('The element is not clonable');
return;
}
var collection = sel.collection;
var index = collection.indexOf(sel);
collection.add(sel.clone(), {at: index + 1});
ed.trigger('component:update', sel);
},
};
defaultCommands['tlb-move'] = {
run: function(ed){
var sel = ed.getSelected();
if(!sel || !sel.get('draggable')) {
console.warn('The element is not draggable');
return;
}
var toolbarEl = ed.Canvas.getToolbarEl();
var cmdMove = ed.Commands.get('move-comp');
cmdMove.onEndMoveFromModel = function() {
ed.editor.runDefault();
ed.editor.set('selectedComponent', sel);
ed.trigger('component:update', sel);
},
};
defaultCommands['tlb-move'] = {
run: function(ed){
var sel = ed.getSelected();
if(!sel || !sel.get('draggable')) {
console.warn('The element is not draggable');
return;
}
var toolbarEl = ed.Canvas.getToolbarEl();
var cmdMove = ed.Commands.get('move-comp');
cmdMove.onEndMoveFromModel = function() {
ed.editor.runDefault();
ed.editor.set('selectedComponent', sel);
ed.trigger('component:update', sel);
};
ed.editor.stopDefault();
cmdMove.initSorterFromModel(sel);
sel.set('status', 'selected');
toolbarEl.style.display = 'none';
},
};
if(c.em)
c.model = c.em.get('Canvas');
return this;
},
/**
* On load callback
* @private
*/
onLoad: function() {
this.loadDefaultCommands();
},
/**
* Add new command to the collection
* @param {string} id Command's ID
* @param {Object} command Object representing you command. Methods `run` and `stop` are required
* @return {this}
* @example
* commands.add('myCommand', {
* run: function(editor, sender){
* alert('Hello world!');
* },
* stop: function(editor, sender){
* },
* });
* */
add: add,
/**
* Get command by ID
* @param {string} id Command's ID
* @return {Object} Object representing the command
* @example
* var myCommand = commands.get('myCommand');
* myCommand.run();
* */
get: function(id) {
var el = commands[id];
if(typeof el == 'function'){
el = new el(c);
commands[id] = el;
}
return el;
},
/**
* Check if command exists
* @param {string} id Command's ID
* @return {Boolean}
* */
has: function(id) {
return !!commands[id];
},
/**
* Load default commands
* @return {this}
* @private
* */
loadDefaultCommands: function(){
for (var id in defaultCommands) {
this.add(id, defaultCommands[id]);
}
return this;
},
};
};
ed.editor.stopDefault();
cmdMove.initSorterFromModel(sel);
sel.set('status', 'selected');
toolbarEl.style.display = 'none';
},
};
if(c.em)
c.model = c.em.get('Canvas');
return this;
},
/**
* On load callback
* @private
*/
onLoad: function() {
this.loadDefaultCommands();
},
/**
* Add new command to the collection
* @param {string} id Command's ID
* @param {Object} command Object representing you command. Methods `run` and `stop` are required
* @return {this}
* @example
* commands.add('myCommand', {
* run: function(editor, sender){
* alert('Hello world!');
* },
* stop: function(editor, sender){
* },
* });
* */
add: add,
/**
* Get command by ID
* @param {string} id Command's ID
* @return {Object} Object representing the command
* @example
* var myCommand = commands.get('myCommand');
* myCommand.run();
* */
get: function(id) {
var el = commands[id];
if(typeof el == 'function'){
el = new el(c);
commands[id] = el;
}
return el;
},
/**
* Check if command exists
* @param {string} id Command's ID
* @return {Boolean}
* */
has: function(id) {
return !!commands[id];
},
/**
* Load default commands
* @return {this}
* @private
* */
loadDefaultCommands: function(){
for (var id in defaultCommands) {
this.add(id, defaultCommands[id]);
}
return this;
},
};
});
};

23
src/commands/model/Command.js

@ -1,14 +1,9 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
/**
* @class Command
* */
module.exports = Backbone.Model.extend({
defaults :{
id : '',
}
});
});
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
defaults :{
id: '',
}
});

21
src/commands/model/Commands.js

@ -1,13 +1,8 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Command = require('./Command');
/**
* @class Commands
* */
module.exports = Backbone.Collection.extend({
model: Command,
});
});
var Backbone = require('backbone');
var Command = require('./Command');
module.exports = Backbone.Collection.extend({
model: Command,
});

228
src/commands/view/CommandAbstract.js

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

463
src/commands/view/CreateComponent.js

@ -1,233 +1,230 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var SelectPosition = require('./SelectPosition');
module.exports = _.extend({}, SelectPosition, {
init: function(opt) {
_.bindAll(this,'startDraw','draw','endDraw','rollback');
this.config = opt || {};
this.hType = this.config.newFixedH ? 'height' : 'min-height';
this.allowDraw = 1;
},
/**
* Start with enabling to select position and listening to start drawning
* @private
* */
enable: function() {
SelectPosition.enable.apply(this, arguments);
this.$wr.css('cursor','crosshair');
if(this.allowDraw)
this.$wr.on('mousedown', this.startDraw);
this.ghost = this.canvas.getGhostEl();
},
/**
* Start drawing component
* @param {Object} e Event
* @private
* */
startDraw : function(e) {
e.preventDefault();
this.stopSelectPosition();
this.ghost.style.display = 'block';
this.frameOff = this.getOffsetDim();
this.startPos = {
top : e.pageY + this.frameOff.top,
left: e.pageX + this.frameOff.left
};
this.isDragged = false;
this.tempComponent = {style: {}};
this.beforeDraw(this.tempComponent);
this.updateSize(this.startPos.top, this.startPos.left, 0, 0);
this.toggleEvents(1);
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
this.$wr[method]('mousemove', this.draw);
this.$wr[method]('mouseup', this.endDraw);
this.$canvas[method]('mousemove', this.draw);
$(document)[method]('mouseup', this.endDraw);
$(document)[method]('keypress', this.rollback);
},
/**
* While drawing the component
* @param {Object} e Event
* @private
* */
draw: function(e) {
this.isDragged = true;
this.updateComponentSize(e);
},
/**
* End drawing component
* @param {Object} e Event
* @private
* */
endDraw : function(e) {
this.toggleEvents();
var model = {};
// Only if the mouse was moved
if(this.isDragged){
this.updateComponentSize(e);
this.setRequirements(this.tempComponent);
var lp = this.sorter.lastPos;
model = this.create(this.sorter.target, this.tempComponent, lp.index, lp.method);
this.sorter.prevTarget = null;
}
this.ghost.style.display = 'none';
this.startSelectPosition();
this.afterDraw(model);
},
/**
* Create new component inside the target
* @param {Object} target Tha target collection
* @param {Object} component New component to create
* @param {number} index Index inside the collection, 0 if no children inside
* @param {string} method Before or after of the children
* @param {Object} opts Options
*/
create: function(target, component, index, method, opts) {
index = method === 'after' ? index + 1 : index;
var opt = opts || {};
var $trg = $(target);
var trgModel = $trg.data('model');
var trgCollection = $trg.data('collection');
var droppable = trgModel ? trgModel.get('droppable') : 1;
opt.at = index;
if(trgCollection && droppable)
return trgCollection.add(component, opt);
else
console.warn("Invalid target position");
},
/**
* Check and set basic requirements for the component
* @param {Object} component New component to be created
* @return {Object} Component updated
* @private
* */
setRequirements: function(component) {
var c = this.config;
var compStl = component.style;
// Check min width
if(compStl.width.replace(/\D/g,'') < c.minComponentW)
compStl.width = c.minComponentW +'px';
// Check min height
if(compStl[this.hType].replace(/\D/g,'') < c.minComponentH)
compStl[this.hType] = c.minComponentH +'px';
// Set overflow in case of fixed height
if(c.newFixedH)
compStl.overflow = 'auto';
if(!this.absoluteMode){
delete compStl.left;
delete compStl.top;
}else
compStl.position = 'absolute';
var lp = this.sorter.lastPos;
if(this.nearFloat(lp.index, lp.method, this.sorter.lastDims))
compStl.float = 'left';
if(this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target){
compStl.margin = '0 auto';
}
return component;
},
/**
* Update new component size while drawing
* @param {Object} e Event
* @private
* */
updateComponentSize : function (e) {
var y = e.pageY + this.frameOff.top;
var x = e.pageX + this.frameOff.left;
var start = this.startPos;
var top = start.top;
var left = start.left;
var height = y - top;
var width = x - left;
if (x < left) {
left = x;
width = start.left - x;
}
if (y < top) {
top = y;
height = start.top - y;
}
this.updateSize(top, left, width, height);
},
/**
* Update size
* @private
*/
updateSize: function(top, left, width, height){
var u = 'px';
var ghStl = this.ghost.style;
var compStl = this.tempComponent.style;
ghStl.top = compStl.top = top + u;
ghStl.left = compStl.left = left + u;
ghStl.width = compStl.width = width + u;
ghStl[this.hType] = compStl[this.hType] = height + u;
},
/**
* Used to bring the previous situation before event started
* @param {Object} e Event
* @param {Boolean} forse Indicates if rollback in anycase
* @private
* */
rollback: function(e, force) {
var key = e.which || e.keyCode;
if(key == this.config.ESCAPE_KEY || force){
this.isDragged = false;
this.endDraw();
}
return;
},
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw: function(component){
component.editable = false;//set this component editable
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw: function(model){},
run: function(editor, sender, opts){
this.editor = editor;
this.sender = sender;
this.$wr = this.$wrapper;
this.enable();
},
stop: function(){
this.stopSelectPosition();
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
});
});
var Backbone = require('backbone');
var SelectPosition = require('./SelectPosition');
module.exports = _.extend({}, SelectPosition, {
init: function(opt) {
_.bindAll(this,'startDraw','draw','endDraw','rollback');
this.config = opt || {};
this.hType = this.config.newFixedH ? 'height' : 'min-height';
this.allowDraw = 1;
},
/**
* Start with enabling to select position and listening to start drawning
* @private
* */
enable: function() {
SelectPosition.enable.apply(this, arguments);
this.$wr.css('cursor','crosshair');
if(this.allowDraw)
this.$wr.on('mousedown', this.startDraw);
this.ghost = this.canvas.getGhostEl();
},
/**
* Start drawing component
* @param {Object} e Event
* @private
* */
startDraw : function(e) {
e.preventDefault();
this.stopSelectPosition();
this.ghost.style.display = 'block';
this.frameOff = this.getOffsetDim();
this.startPos = {
top : e.pageY + this.frameOff.top,
left: e.pageX + this.frameOff.left
};
this.isDragged = false;
this.tempComponent = {style: {}};
this.beforeDraw(this.tempComponent);
this.updateSize(this.startPos.top, this.startPos.left, 0, 0);
this.toggleEvents(1);
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
this.$wr[method]('mousemove', this.draw);
this.$wr[method]('mouseup', this.endDraw);
this.$canvas[method]('mousemove', this.draw);
$(document)[method]('mouseup', this.endDraw);
$(document)[method]('keypress', this.rollback);
},
/**
* While drawing the component
* @param {Object} e Event
* @private
* */
draw: function(e) {
this.isDragged = true;
this.updateComponentSize(e);
},
/**
* End drawing component
* @param {Object} e Event
* @private
* */
endDraw : function(e) {
this.toggleEvents();
var model = {};
// Only if the mouse was moved
if(this.isDragged){
this.updateComponentSize(e);
this.setRequirements(this.tempComponent);
var lp = this.sorter.lastPos;
model = this.create(this.sorter.target, this.tempComponent, lp.index, lp.method);
this.sorter.prevTarget = null;
}
this.ghost.style.display = 'none';
this.startSelectPosition();
this.afterDraw(model);
},
/**
* Create new component inside the target
* @param {Object} target Tha target collection
* @param {Object} component New component to create
* @param {number} index Index inside the collection, 0 if no children inside
* @param {string} method Before or after of the children
* @param {Object} opts Options
*/
create: function(target, component, index, method, opts) {
index = method === 'after' ? index + 1 : index;
var opt = opts || {};
var $trg = $(target);
var trgModel = $trg.data('model');
var trgCollection = $trg.data('collection');
var droppable = trgModel ? trgModel.get('droppable') : 1;
opt.at = index;
if(trgCollection && droppable)
return trgCollection.add(component, opt);
else
console.warn("Invalid target position");
},
/**
* Check and set basic requirements for the component
* @param {Object} component New component to be created
* @return {Object} Component updated
* @private
* */
setRequirements: function(component) {
var c = this.config;
var compStl = component.style;
// Check min width
if(compStl.width.replace(/\D/g,'') < c.minComponentW)
compStl.width = c.minComponentW +'px';
// Check min height
if(compStl[this.hType].replace(/\D/g,'') < c.minComponentH)
compStl[this.hType] = c.minComponentH +'px';
// Set overflow in case of fixed height
if(c.newFixedH)
compStl.overflow = 'auto';
if(!this.absoluteMode){
delete compStl.left;
delete compStl.top;
}else
compStl.position = 'absolute';
var lp = this.sorter.lastPos;
if(this.nearFloat(lp.index, lp.method, this.sorter.lastDims))
compStl.float = 'left';
if(this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target){
compStl.margin = '0 auto';
}
return component;
},
/**
* Update new component size while drawing
* @param {Object} e Event
* @private
* */
updateComponentSize : function (e) {
var y = e.pageY + this.frameOff.top;
var x = e.pageX + this.frameOff.left;
var start = this.startPos;
var top = start.top;
var left = start.left;
var height = y - top;
var width = x - left;
if (x < left) {
left = x;
width = start.left - x;
}
if (y < top) {
top = y;
height = start.top - y;
}
this.updateSize(top, left, width, height);
},
/**
* Update size
* @private
*/
updateSize: function(top, left, width, height){
var u = 'px';
var ghStl = this.ghost.style;
var compStl = this.tempComponent.style;
ghStl.top = compStl.top = top + u;
ghStl.left = compStl.left = left + u;
ghStl.width = compStl.width = width + u;
ghStl[this.hType] = compStl[this.hType] = height + u;
},
/**
* Used to bring the previous situation before event started
* @param {Object} e Event
* @param {Boolean} forse Indicates if rollback in anycase
* @private
* */
rollback: function(e, force) {
var key = e.which || e.keyCode;
if(key == this.config.ESCAPE_KEY || force){
this.isDragged = false;
this.endDraw();
}
return;
},
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw: function(component){
component.editable = false;//set this component editable
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw: function(model){},
run: function(editor, sender, opts){
this.editor = editor;
this.sender = sender;
this.$wr = this.$wrapper;
this.enable();
},
stop: function(){
this.stopSelectPosition();
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
});

145
src/commands/view/DeleteComponent.js

@ -1,90 +1,79 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var SelectComponent = require('./SelectComponent');
/**
* @class DeleteComponent
* @private
* */
module.exports = _.extend({},SelectComponent,{
var Backbone = require('backbone');
var SelectComponent = require('./SelectComponent');
init: function(o){
_.bindAll(this, 'startDelete', 'stopDelete', 'onDelete');
this.hoverClass = this.pfx + 'hover-delete';
this.badgeClass = this.pfx + 'badge-red';
},
module.exports = _.extend({},SelectComponent,{
enable: function()
{
var that = this;
this.$el.find('*')
.mouseover(this.startDelete)
.mouseout(this.stopDelete)
.click(this.onDelete);
},
init: function(o){
_.bindAll(this, 'startDelete', 'stopDelete', 'onDelete');
this.hoverClass = this.pfx + 'hover-delete';
this.badgeClass = this.pfx + 'badge-red';
},
/**
* Start command
* @param {Object} e
* @private
*/
startDelete: function(e)
{
e.stopPropagation();
var $this = $(e.target);
enable: function() {
var that = this;
this.$el.find('*')
.mouseover(this.startDelete)
.mouseout(this.stopDelete)
.click(this.onDelete);
},
// Show badge if possible
if($this.data('model').get('removable')){
$this.addClass(this.hoverClass);
this.attachBadge($this.get(0));
}
/**
* Start command
* @param {Object} e
* @private
*/
startDelete: function(e) {
e.stopPropagation();
var $this = $(e.target);
},
// Show badge if possible
if($this.data('model').get('removable')){
$this.addClass(this.hoverClass);
this.attachBadge($this.get(0));
}
/**
* Stop command
* @param {Object} e
* @private
*/
stopDelete: function(e)
{
e.stopPropagation();
var $this = $(e.target);
$this.removeClass(this.hoverClass);
},
// Hide badge if possible
if(this.badge)
this.badge.css({ left: -1000, top:-1000 });
},
/**
* Stop command
* @param {Object} e
* @private
*/
stopDelete: function(e) {
e.stopPropagation();
var $this = $(e.target);
$this.removeClass(this.hoverClass);
/**
* Delete command
* @param {Object} e
* @private
*/
onDelete: function(e)
{
e.stopPropagation();
var $this = $(e.target);
// Hide badge if possible
if(this.badge)
this.badge.css({ left: -1000, top:-1000 });
},
// Do nothing in case can't remove
if(!$this.data('model').get('removable'))
return;
/**
* Delete command
* @param {Object} e
* @private
*/
onDelete: function(e) {
e.stopPropagation();
var $this = $(e.target);
$this.data('model').destroy();
this.removeBadge();
this.clean();
},
// Do nothing in case can't remove
if(!$this.data('model').get('removable'))
return;
/**
* Updates badge label
* @param {Object} model
* @private
* */
updateBadgeLabel: function (model)
{
this.badge.html( 'Remove ' + model.getName() );
},
$this.data('model').destroy();
this.removeBadge();
this.clean();
},
});
});
/**
* Updates badge label
* @param {Object} model
* @private
* */
updateBadgeLabel: function (model) {
this.badge.html( 'Remove ' + model.getName() );
},
});

128
src/commands/view/ExportTemplate.js

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

157
src/commands/view/Fullscreen.js

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

68
src/commands/view/ImageComponent.js

@ -1,41 +1,35 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var InsertCustom = require('./InsertCustom');
/**
* @class ImageComponent
* @private
* */
module.exports = _.extend({}, InsertCustom, {
var Backbone = require('backbone');
var InsertCustom = require('./InsertCustom');
/**
* Trigger before insert
* @param {Object} object
* @private
*
* */
beforeInsert: function(object){
object.type = 'image';
object.style = {};
object.attributes = {};
object.attributes.onmousedown = 'return false';
if (this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target ) {
object.style.margin = '0 auto';
}
},
module.exports = _.extend({}, InsertCustom, {
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert: function(model){
model.trigger('dblclick');
if(this.sender)
this.sender.set('active', false);
},
/**
* Trigger before insert
* @param {Object} object
* @private
*
* */
beforeInsert: function(object){
object.type = 'image';
object.style = {};
object.attributes = {};
object.attributes.onmousedown = 'return false';
if (this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target ) {
object.style.margin = '0 auto';
}
},
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert: function(model){
model.trigger('dblclick');
if(this.sender)
this.sender.set('active', false);
},
});
});
});

142
src/commands/view/InsertCustom.js

@ -1,85 +1,79 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var CreateComponent = require('./CreateComponent');
/**
* @class InsertCustom
* @private
* */
module.exports = _.extend({}, CreateComponent, {
var Backbone = require('backbone');
var CreateComponent = require('./CreateComponent');
init: function(){
CreateComponent.init.apply(this, arguments);
_.bindAll(this, 'insertComponent');
this.allowDraw = 0;
},
module.exports = _.extend({}, CreateComponent, {
/**
* Run method
* @private
* */
run: function(em, sender, options) {
this.em = em;
this.sender = sender;
this.opt = options || {};
this.$wr = this.$wrapper;
this.enable();
},
init: function(){
CreateComponent.init.apply(this, arguments);
_.bindAll(this, 'insertComponent');
this.allowDraw = 0;
},
enable: function(){
CreateComponent.enable.apply(this, arguments);
this.$wr.on('click', this.insertComponent);
},
/**
* Run method
* @private
* */
run: function(em, sender, options) {
this.em = em;
this.sender = sender;
this.opt = options || {};
this.$wr = this.$wrapper;
this.enable();
},
/**
* Start insert event
* @private
* */
insertComponent: function(){
this.$wr.off('click', this.insertComponent);
this.stopSelectPosition();
var object = this.buildContent();
this.beforeInsert(object);
var index = this.sorter.lastPos.index;
// By default, collections do not trigger add event, so silent is used
var model = this.create(this.sorter.target, object, index, null, {silent: false});
enable: function(){
CreateComponent.enable.apply(this, arguments);
this.$wr.on('click', this.insertComponent);
},
if(this.opt.terminateAfterInsert && this.sender)
this.sender.set('active', false);
else
this.enable();
/**
* Start insert event
* @private
* */
insertComponent: function(){
this.$wr.off('click', this.insertComponent);
this.stopSelectPosition();
var object = this.buildContent();
this.beforeInsert(object);
var index = this.sorter.lastPos.index;
// By default, collections do not trigger add event, so silent is used
var model = this.create(this.sorter.target, object, index, null, {silent: false});
if(!model)
return;
if(this.opt.terminateAfterInsert && this.sender)
this.sender.set('active', false);
else
this.enable();
if(this.em)
this.em.editor.initChildrenComp(model);
if(!model)
return;
this.afterInsert(model, this);
},
if(this.em)
this.em.editor.initChildrenComp(model);
/**
* Trigger before insert
* @param {Object} obj
* @private
* */
beforeInsert: function(obj){},
this.afterInsert(model, this);
},
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert: function(model){},
/**
* Trigger before insert
* @param {Object} obj
* @private
* */
beforeInsert: function(obj){},
/**
* Create different object, based on content, to insert inside canvas
*
* @return {Object}
* @private
* */
buildContent: function(){
return this.opt.content || {};
},
});
});
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert: function(model){},
/**
* Create different object, based on content, to insert inside canvas
*
* @return {Object}
* @private
* */
buildContent: function(){
return this.opt.content || {};
},
});

299
src/commands/view/MoveComponent.js

@ -1,151 +1,148 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var SelectComponent = require('./SelectComponent');
var SelectPosition = require('./SelectPosition');
module.exports = _.extend({}, SelectPosition, SelectComponent, {
init: function(o){
SelectComponent.init.apply(this, arguments);
_.bindAll(this, 'initSorter','rollback', 'onEndMove');
this.opt = o;
this.hoverClass = this.ppfx + 'highlighter-warning';
this.badgeClass = this.ppfx + 'badge-warning';
this.noSelClass = this.ppfx + 'no-select';
},
enable: function() {
SelectComponent.enable.apply(this, arguments);
this.getBadgeEl().addClass(this.badgeClass);
this.getHighlighterEl().addClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor','move');
wp.on('mousedown', this.initSorter);
// Avoid strange moving behavior
wp.addClass(this.noSelClass);
},
/**
* Overwrite for doing nothing
* @private
*/
toggleClipboard: function(){},
/**
* Delegate sorting
* @param {Event} e
* @private
* */
initSorter: function(e){
var el = $(e.target).data('model');
var drag = el.get('draggable');
if(!drag)
return;
// Avoid badge showing on move
this.cacheEl = null;
this.startSelectPosition(e.target, this.frameEl.contentDocument);
this.sorter.draggable = drag;
this.sorter.onEndMove = this.onEndMove.bind(this);
this.stopSelectComponent();
this.$wrapper.off('mousedown', this.initSorter);
this.getContentWindow().on('keydown', this.rollback);
},
/**
* Init sorter from model
* @param {Object} model
* @private
*/
initSorterFromModel: function(model) {
var drag = model.get('draggable');
if(!drag)
return;
// Avoid badge showing on move
this.cacheEl = null;
var el = model.view.el;
this.startSelectPosition(el, this.frameEl.contentDocument);
this.sorter.draggable = drag;
this.sorter.onEndMove = this.onEndMoveFromModel.bind(this);
/*
this.sorter.setDragHelper(el);
var dragHelper = this.sorter.dragHelper;
dragHelper.className = this.ppfx + 'drag-helper';
dragHelper.innerHTML = '';
dragHelper.backgroundColor = 'white';
*/
this.stopSelectComponent();
this.getContentWindow().on('keydown', this.rollback);
},
onEndMoveFromModel: function() {
this.getContentWindow().off('keydown', this.rollback);
},
/**
* Callback after sorting
* @private
*/
onEndMove: function(){
this.enable();
this.getContentWindow().off('keydown', this.rollback);
},
/**
* Say what to do after the component was selected (selectComponent)
* @param {Event} e
* @param {Object} Selected element
* @private
* */
onSelect: function(e,el){},
/**
* Used to bring the previous situation before start moving the component
* @param {Event} e
* @param {Boolean} Indicates if rollback in anycase
* @private
* */
rollback: function(e, force){
var key = e.which || e.keyCode;
if(key == this.opt.ESCAPE_KEY || force){
this.sorter.moved = false;
this.sorter.endMove();
}
return;
},
/**
* Returns badge element
* @return {HTMLElement}
* @private
*/
getBadgeEl: function(){
if(!this.$badge)
this.$badge = $(this.getBadge());
return this.$badge;
},
/**
* Returns highlighter element
* @return {HTMLElement}
* @private
*/
getHighlighterEl: function(){
if(!this.$hl)
this.$hl = $(this.canvas.getHighlighter());
return this.$hl;
},
stop: function(){
SelectComponent.stop.apply(this, arguments);
this.getBadgeEl().removeClass(this.badgeClass);
this.getHighlighterEl().removeClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor', '').unbind().removeClass(this.noSelClass);
}
});
});
var Backbone = require('backbone');
var SelectComponent = require('./SelectComponent');
var SelectPosition = require('./SelectPosition');
module.exports = _.extend({}, SelectPosition, SelectComponent, {
init: function(o){
SelectComponent.init.apply(this, arguments);
_.bindAll(this, 'initSorter','rollback', 'onEndMove');
this.opt = o;
this.hoverClass = this.ppfx + 'highlighter-warning';
this.badgeClass = this.ppfx + 'badge-warning';
this.noSelClass = this.ppfx + 'no-select';
},
enable: function() {
SelectComponent.enable.apply(this, arguments);
this.getBadgeEl().addClass(this.badgeClass);
this.getHighlighterEl().addClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor','move');
wp.on('mousedown', this.initSorter);
// Avoid strange moving behavior
wp.addClass(this.noSelClass);
},
/**
* Overwrite for doing nothing
* @private
*/
toggleClipboard: function(){},
/**
* Delegate sorting
* @param {Event} e
* @private
* */
initSorter: function(e){
var el = $(e.target).data('model');
var drag = el.get('draggable');
if(!drag)
return;
// Avoid badge showing on move
this.cacheEl = null;
this.startSelectPosition(e.target, this.frameEl.contentDocument);
this.sorter.draggable = drag;
this.sorter.onEndMove = this.onEndMove.bind(this);
this.stopSelectComponent();
this.$wrapper.off('mousedown', this.initSorter);
this.getContentWindow().on('keydown', this.rollback);
},
/**
* Init sorter from model
* @param {Object} model
* @private
*/
initSorterFromModel: function(model) {
var drag = model.get('draggable');
if(!drag)
return;
// Avoid badge showing on move
this.cacheEl = null;
var el = model.view.el;
this.startSelectPosition(el, this.frameEl.contentDocument);
this.sorter.draggable = drag;
this.sorter.onEndMove = this.onEndMoveFromModel.bind(this);
/*
this.sorter.setDragHelper(el);
var dragHelper = this.sorter.dragHelper;
dragHelper.className = this.ppfx + 'drag-helper';
dragHelper.innerHTML = '';
dragHelper.backgroundColor = 'white';
*/
this.stopSelectComponent();
this.getContentWindow().on('keydown', this.rollback);
},
onEndMoveFromModel: function() {
this.getContentWindow().off('keydown', this.rollback);
},
/**
* Callback after sorting
* @private
*/
onEndMove: function(){
this.enable();
this.getContentWindow().off('keydown', this.rollback);
},
/**
* Say what to do after the component was selected (selectComponent)
* @param {Event} e
* @param {Object} Selected element
* @private
* */
onSelect: function(e,el){},
/**
* Used to bring the previous situation before start moving the component
* @param {Event} e
* @param {Boolean} Indicates if rollback in anycase
* @private
* */
rollback: function(e, force){
var key = e.which || e.keyCode;
if(key == this.opt.ESCAPE_KEY || force){
this.sorter.moved = false;
this.sorter.endMove();
}
return;
},
/**
* Returns badge element
* @return {HTMLElement}
* @private
*/
getBadgeEl: function(){
if(!this.$badge)
this.$badge = $(this.getBadge());
return this.$badge;
},
/**
* Returns highlighter element
* @return {HTMLElement}
* @private
*/
getHighlighterEl: function(){
if(!this.$hl)
this.$hl = $(this.canvas.getHighlighter());
return this.$hl;
},
stop: function(){
SelectComponent.stop.apply(this, arguments);
this.getBadgeEl().removeClass(this.badgeClass);
this.getHighlighterEl().removeClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor', '').unbind().removeClass(this.noSelClass);
}
});

35
src/commands/view/OpenAssets.js

@ -1,24 +1,21 @@
define(function() {
module.exports = {
return {
run: function(editor, sender, opts) {
var opt = opts || {};
var config = editor.getConfig();
var modal = editor.Modal;
var assetManager = editor.AssetManager;
run: function(editor, sender, opts) {
var opt = opts || {};
var config = editor.getConfig();
var modal = editor.Modal;
var assetManager = editor.AssetManager;
assetManager.onClick(opt.onClick);
assetManager.onDblClick(opt.onDblClick);
assetManager.onClick(opt.onClick);
assetManager.onDblClick(opt.onDblClick);
// old API
assetManager.setTarget(opt.target);
assetManager.onSelect(opt.onSelect);
// old API
assetManager.setTarget(opt.target);
assetManager.onSelect(opt.onSelect);
modal.setTitle(opt.modalTitle || 'Select image');
modal.setContent(assetManager.render());
modal.open();
},
modal.setTitle(opt.modalTitle || 'Select image');
modal.setContent(assetManager.render());
modal.open();
},
};
});
};

49
src/commands/view/OpenBlocks.js

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

62
src/commands/view/OpenLayers.js

@ -1,40 +1,34 @@
define(function(require, exports, module){
'use strict';
var Layers = require('Navigator');
/**
* @class OpenStyleManager
* @private
* */
module.exports = {
var Layers = require('Navigator');
run: function(em, sender) {
if(!this.$layers) {
var collection = em.DomComponents.getComponent().get('components'),
config = em.getConfig(),
panels = em.Panels,
lyStylePfx = config.layers.stylePrefix || 'nv-';
module.exports = {
config.layers.stylePrefix = config.stylePrefix + lyStylePfx;
config.layers.pStylePrefix = config.stylePrefix;
config.layers.em = em.editor;
config.layers.opened = em.editor.get('opened');
var layers = new Layers(collection, config.layers);
this.$layers = layers.render();
run: function(em, sender) {
if(!this.$layers) {
var collection = em.DomComponents.getComponent().get('components'),
config = em.getConfig(),
panels = em.Panels,
lyStylePfx = config.layers.stylePrefix || 'nv-';
// Check if panel exists otherwise crate it
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
config.layers.stylePrefix = config.stylePrefix + lyStylePfx;
config.layers.pStylePrefix = config.stylePrefix;
config.layers.em = em.editor;
config.layers.opened = em.editor.get('opened');
var layers = new Layers(collection, config.layers);
this.$layers = layers.render();
this.panel.set('appendContent', this.$layers).trigger('change:appendContent');
}
this.$layers.show();
},
// Check if panel exists otherwise crate it
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
stop: function() {
if(this.$layers)
this.$layers.hide();
this.panel.set('appendContent', this.$layers).trigger('change:appendContent');
}
};
});
this.$layers.show();
},
stop: function() {
if(this.$layers)
this.$layers.hide();
}
};

142
src/commands/view/OpenStyleManager.js

@ -1,84 +1,78 @@
define(function(require, exports, module){
'use strict';
var StyleManager = require('StyleManager');
/**
* @class OpenStyleManager
* @private
* */
module.exports = {
var StyleManager = require('StyleManager');
run: function(em, sender) {
this.sender = sender;
if(!this.$cn){
var config = em.getConfig(),
panels = em.Panels;
// Main container
this.$cn = $('<div/>');
// Secondary container
this.$cn2 = $('<div/>');
this.$cn.append(this.$cn2);
module.exports = {
// Device Manager
var dvm = em.DeviceManager;
if(dvm && config.showDevices){
var devicePanel = panels.addPanel({ id: 'devices-c'});
devicePanel.set('appendContent', dvm.render()).trigger('change:appendContent');
}
run: function(em, sender) {
this.sender = sender;
if(!this.$cn){
var config = em.getConfig(),
panels = em.Panels;
// Main container
this.$cn = $('<div/>');
// Secondary container
this.$cn2 = $('<div/>');
this.$cn.append(this.$cn2);
// Class Manager container
var clm = em.SelectorManager;
if(clm)
this.$cn2.append(clm.render([]));
// Device Manager
var dvm = em.DeviceManager;
if(dvm && config.showDevices){
var devicePanel = panels.addPanel({ id: 'devices-c'});
devicePanel.set('appendContent', dvm.render()).trigger('change:appendContent');
}
this.$cn2.append(em.StyleManager.render());
var smConfig = em.StyleManager.getConfig();
// Create header
this.$header = $('<div>', {
class: smConfig.stylePrefix + 'header',
text: smConfig.textNoElement,
});
//this.$cn = this.$cn.add(this.$header);
this.$cn.append(this.$header);
// Class Manager container
var clm = em.SelectorManager;
if(clm)
this.$cn2.append(clm.render([]));
// Create panel if not exists
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({ id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
this.$cn2.append(em.StyleManager.render());
var smConfig = em.StyleManager.getConfig();
// Create header
this.$header = $('<div>', {
class: smConfig.stylePrefix + 'header',
text: smConfig.textNoElement,
});
//this.$cn = this.$cn.add(this.$header);
this.$cn.append(this.$header);
// Add all containers to the panel
this.panel.set('appendContent', this.$cn).trigger('change:appendContent');
// Create panel if not exists
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({ id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
this.target = em.editor;
this.listenTo( this.target ,'change:selectedComponent', this.toggleSm);
}
this.toggleSm();
},
// Add all containers to the panel
this.panel.set('appendContent', this.$cn).trigger('change:appendContent');
/**
* Toggle Style Manager visibility
* @private
*/
toggleSm: function() {
if(!this.sender.get('active'))
return;
if(this.target.get('selectedComponent')){
this.$cn2.show();
this.$header.hide();
}else{
this.$cn2.hide();
this.$header.show();
}
},
this.target = em.editor;
this.listenTo( this.target ,'change:selectedComponent', this.toggleSm);
}
this.toggleSm();
},
stop: function() {
// Hide secondary container if exists
if(this.$cn2)
this.$cn2.hide();
/**
* Toggle Style Manager visibility
* @private
*/
toggleSm: function() {
if(!this.sender.get('active'))
return;
if(this.target.get('selectedComponent')){
this.$cn2.show();
this.$header.hide();
}else{
this.$cn2.hide();
this.$header.show();
}
},
// Hide header container if exists
if(this.$header)
this.$header.hide();
}
};
});
stop: function() {
// Hide secondary container if exists
if(this.$cn2)
this.$cn2.hide();
// Hide header container if exists
if(this.$header)
this.$header.hide();
}
};

57
src/commands/view/OpenTraitManager.js

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

121
src/commands/view/Preview.js

@ -1,68 +1,65 @@
define(function() {
module.exports = {
return {
getPanels: function(editor){
if(!this.panels)
this.panels = editor.Panels.getPanelsEl();
return this.panels;
},
getPanels: function(editor){
if(!this.panels)
this.panels = editor.Panels.getPanelsEl();
return this.panels;
},
tglPointers: function(editor, v) {
var elP = editor.Canvas.getBody().querySelectorAll('.' + this.ppfx + 'no-pointer');
_.each(elP, function(item){
item.style.pointerEvents = v ? '' : 'all';
});
},
tglPointers: function(editor, v) {
var elP = editor.Canvas.getBody().querySelectorAll('.' + this.ppfx + 'no-pointer');
_.each(elP, function(item){
item.style.pointerEvents = v ? '' : 'all';
});
},
run: function(editor, sender) {
if(sender && sender.set)
sender.set('active', false);
editor.stopCommand('sw-visibility');
var that = this;
var panels = this.getPanels(editor);
var canvas = editor.Canvas.getElement();
var editorEl = editor.getEl();
var pfx = editor.Config.stylePrefix;
if(!this.helper) {
this.helper = document.createElement('span');
this.helper.className = pfx + 'off-prv fa fa-eye-slash';
editorEl.appendChild(this.helper);
this.helper.onclick = function(){
that.stop(editor);
};
}
this.helper.style.display = 'inline-block';
this.tglPointers(editor);
run: function(editor, sender) {
if(sender && sender.set)
sender.set('active', false);
editor.stopCommand('sw-visibility');
var that = this;
var panels = this.getPanels(editor);
var canvas = editor.Canvas.getElement();
var editorEl = editor.getEl();
var pfx = editor.Config.stylePrefix;
if(!this.helper) {
this.helper = document.createElement('span');
this.helper.className = pfx + 'off-prv fa fa-eye-slash';
editorEl.appendChild(this.helper);
this.helper.onclick = function(){
that.stop(editor);
};
}
this.helper.style.display = 'inline-block';
this.tglPointers(editor);
/*
editor.Canvas.getBody().querySelectorAll('.' + pfx + 'no-pointer').forEach(function(){
this.style.pointerEvents = 'all';
});*/
/*
editor.Canvas.getBody().querySelectorAll('.' + pfx + 'no-pointer').forEach(function(){
this.style.pointerEvents = 'all';
});*/
panels.style.display = 'none';
var canvasS = canvas.style;
canvasS.width = '100%';
canvasS.height = '100%';
canvasS.top = '0';
canvasS.left = '0';
canvasS.padding = '0';
canvasS.margin = '0';
editor.trigger('change:canvasOffset');
},
panels.style.display = 'none';
var canvasS = canvas.style;
canvasS.width = '100%';
canvasS.height = '100%';
canvasS.top = '0';
canvasS.left = '0';
canvasS.padding = '0';
canvasS.margin = '0';
editor.trigger('change:canvasOffset');
},
stop: function(editor, sender) {
var panels = this.getPanels(editor);
editor.runCommand('sw-visibility');
editor.getModel().runDefault();
panels.style.display = 'block';
var canvas = editor.Canvas.getElement();
canvas.setAttribute('style', '');
if(this.helper) {
this.helper.style.display = 'none';
}
editor.trigger('change:canvasOffset');
this.tglPointers(editor, 1);
}
};
});
stop: function(editor, sender) {
var panels = this.getPanels(editor);
editor.runCommand('sw-visibility');
editor.getModel().runDefault();
panels.style.display = 'block';
var canvas = editor.Canvas.getElement();
canvas.setAttribute('style', '');
if(this.helper) {
this.helper.style.display = 'none';
}
editor.trigger('change:canvasOffset');
this.tglPointers(editor, 1);
}
};

52
src/commands/view/Resize.js

@ -1,32 +1,30 @@
define(function() {
return {
module.exports = {
run: function(editor, sender, opts) {
var el = (opts && opts.el) || '';
var canvas = editor.Canvas;
var canvasResizer = this.canvasResizer;
var options = opts.options || {};
run: function(editor, sender, opts) {
var el = (opts && opts.el) || '';
var canvas = editor.Canvas;
var canvasResizer = this.canvasResizer;
var options = opts.options || {};
// Create the resizer for the canvas if not yet created
if(!canvasResizer) {
var canvasView = canvas.getCanvasView();
options.ratioDefault = 1;
options.appendTo = canvas.getResizerEl();
options.prefix = editor.getConfig().stylePrefix;
options.posFetcher = canvasView.getElementPos.bind(canvasView);
options.mousePosFetcher = canvas.getMouseRelativePos;
this.canvasResizer = editor.Utils.Resizer.init(options);
canvasResizer = this.canvasResizer;
}
// Create the resizer for the canvas if not yet created
if(!canvasResizer) {
var canvasView = canvas.getCanvasView();
options.ratioDefault = 1;
options.appendTo = canvas.getResizerEl();
options.prefix = editor.getConfig().stylePrefix;
options.posFetcher = canvasView.getElementPos.bind(canvasView);
options.mousePosFetcher = canvas.getMouseRelativePos;
this.canvasResizer = editor.Utils.Resizer.init(options);
canvasResizer = this.canvasResizer;
}
canvasResizer.setOptions(options);
canvasResizer.focus(el);
},
canvasResizer.setOptions(options);
canvasResizer.focus(el);
},
stop: function() {
if(this.canvasResizer)
this.canvasResizer.blur();
},
stop: function() {
if(this.canvasResizer)
this.canvasResizer.blur();
},
};
});
};

998
src/commands/view/SelectComponent.js

File diff suppressed because it is too large

187
src/commands/view/SelectPosition.js

@ -1,103 +1,100 @@
define(function() {
module.exports = {
return {
/**
* Start select position event
* @param {HTMLElement} trg
* @private
* */
startSelectPosition: function(trg, doc) {
this.isPointed = false;
var utils = this.editorModel.get('Utils');
if(utils && !this.sorter)
this.sorter = new utils.Sorter({
container: this.getCanvasBody(),
placer: this.canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
direction: 'a',
document: doc,
wmargin: 1,
nested: 1,
em: this.editorModel,
canvasRelative: 1,
});
this.sorter.startSort(trg);
},
/**
* Start select position event
* @param {HTMLElement} trg
* @private
* */
startSelectPosition: function(trg, doc) {
this.isPointed = false;
var utils = this.editorModel.get('Utils');
if(utils && !this.sorter)
this.sorter = new utils.Sorter({
container: this.getCanvasBody(),
placer: this.canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
direction: 'a',
document: doc,
wmargin: 1,
nested: 1,
em: this.editorModel,
canvasRelative: 1,
});
this.sorter.startSort(trg);
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffsetDim: function() {
var frameOff = this.offset(this.canvas.getFrameEl());
var canvasOff = this.offset(this.canvas.getElement());
var top = frameOff.top - canvasOff.top;
var left = frameOff.left - canvasOff.left;
return { top: top, left: left };
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffsetDim: function() {
var frameOff = this.offset(this.canvas.getFrameEl());
var canvasOff = this.offset(this.canvas.getElement());
var top = frameOff.top - canvasOff.top;
var left = frameOff.left - canvasOff.left;
return { top: top, left: left };
},
/**
* Stop select position event
* @private
* */
stopSelectPosition: function() {
this.posTargetCollection = null;
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
if(this.sorter){
this.sorter.moved = 0;
this.sorter.endMove();
}
if(this.cDim){
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length;
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) :
(!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) ));
this.posTargetModel = this.posTargetEl.data("model");
this.posTargetCollection = this.posTargetEl.data("model-comp");
}
},
/**
* Stop select position event
* @private
* */
stopSelectPosition: function() {
this.posTargetCollection = null;
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
if(this.sorter){
this.sorter.moved = 0;
this.sorter.endMove();
}
if(this.cDim){
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length;
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) :
(!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) ));
this.posTargetModel = this.posTargetEl.data("model");
this.posTargetCollection = this.posTargetEl.data("model-comp");
}
},
/**
* Enabel select position
* @private
*/
enable: function() {
this.startSelectPosition();
},
/**
* Enabel select position
* @private
*/
enable: function() {
this.startSelectPosition();
},
/**
* Check if the pointer is near to the float component
* @param {number} index
* @param {string} method
* @param {Array<Array>} dims
* @return {Boolean}
* @private
* */
nearFloat: function(index, method, dims) {
var i = index || 0;
var m = method || 'before';
var len = dims.length;
var isLast = len !== 0 && m == 'after' && i == len;
if(len !== 0 && (
(!isLast && !dims[i][4]) ||
(dims[i-1] && !dims[i-1][4]) ||
(isLast && !dims[i-1][4]) ) )
return 1;
return 0;
},
/**
* Check if the pointer is near to the float component
* @param {number} index
* @param {string} method
* @param {Array<Array>} dims
* @return {Boolean}
* @private
* */
nearFloat: function(index, method, dims) {
var i = index || 0;
var m = method || 'before';
var len = dims.length;
var isLast = len !== 0 && m == 'after' && i == len;
if(len !== 0 && (
(!isLast && !dims[i][4]) ||
(dims[i-1] && !dims[i-1][4]) ||
(isLast && !dims[i-1][4]) ) )
return 1;
return 0;
},
run: function() {
this.enable();
},
run: function() {
this.enable();
},
stop: function() {
this.stopSelectPosition();
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
};
});
stop: function() {
this.stopSelectPosition();
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
};

296
src/commands/view/ShowOffset.js

@ -1,149 +1,147 @@
define(function() {
return {
getOffsetMethod: function(state) {
var method = state || '';
return 'get' + method + 'OffsetViewerEl';
},
run: function(editor, sender, opts) {
var opt = opts || {};
var state = opt.state || '';
var config = editor.getConfig();
if (!config.showOffsets ||
(!config.showOffsetsSelected && state == 'Fixed') ) {
return;
}
var canvas = editor.Canvas;
var el = opt.el || '';
var pos = opt.elPos || canvas.getElementPos(el);
var style = window.getComputedStyle(el);
var ppfx = this.ppfx;
var stateVar = state + 'State';
var method = this.getOffsetMethod(state);
var offsetViewer = canvas[method]();
offsetViewer.style.display = 'block';
var marginT = this['marginT' + state];
var marginB = this['marginB' + state];
var marginL = this['marginL' + state];
var marginR = this['marginR' + state];
var padT = this['padT' + state];
var padB = this['padB' + state];
var padL = this['padL' + state];
var padR = this['padR' + state];
if(!this[stateVar]) {
var stateLow = state.toLowerCase();
var marginName = stateLow + 'margin-v';
var paddingName = stateLow + 'padding-v';
var marginV = $('<div>', {class: ppfx + marginName}).get(0);
var paddingV = $('<div>', {class: ppfx + paddingName}).get(0);
var marginEls = ppfx + marginName + '-el';
var paddingEls = ppfx + paddingName + '-el';
marginT = $('<div>', {class: ppfx + marginName + '-top ' + marginEls}).get(0);
marginB = $('<div>', {class: ppfx + marginName + '-bottom ' + marginEls}).get(0);
marginL = $('<div>', {class: ppfx + marginName + '-left ' + marginEls}).get(0);
marginR = $('<div>', {class: ppfx + marginName + '-right ' + marginEls}).get(0);
padT = $('<div>', {class: ppfx + paddingName + '-top ' + paddingEls}).get(0);
padB = $('<div>', {class: ppfx + paddingName + '-bottom ' + paddingEls}).get(0);
padL = $('<div>', {class: ppfx + paddingName + '-left ' + paddingEls}).get(0);
padR = $('<div>', {class: ppfx + paddingName + '-right ' + paddingEls}).get(0);
this['marginT' + state] = marginT;
this['marginB' + state] = marginB;
this['marginL' + state] = marginL;
this['marginR' + state] = marginR;
this['padT' + state] = padT;
this['padB' + state] = padB;
this['padL' + state] = padL;
this['padR' + state] = padR;
marginV.appendChild(marginT);
marginV.appendChild(marginB);
marginV.appendChild(marginL);
marginV.appendChild(marginR);
paddingV.appendChild(padT);
paddingV.appendChild(padB);
paddingV.appendChild(padL);
paddingV.appendChild(padR);
offsetViewer.appendChild(marginV);
offsetViewer.appendChild(paddingV);
this[stateVar] = '1';
}
var unit = 'px';
var marginLeftSt = style.marginLeft.replace(unit, '');
var marginTopSt = parseInt(style.marginTop.replace(unit, ''));
var marginBottomSt = parseInt(style.marginBottom.replace(unit, ''));
var mtStyle = marginT.style;
var mbStyle = marginB.style;
var mlStyle = marginL.style;
var mrStyle = marginR.style;
var ptStyle = padT.style;
var pbStyle = padB.style;
var plStyle = padL.style;
var prStyle = padR.style;
var posLeft = parseInt(pos.left);
// Margin style
mtStyle.height = style.marginTop;
mtStyle.width = style.width;
mtStyle.top = pos.top - style.marginTop.replace(unit, '') + unit;
mtStyle.left = posLeft + unit;
mbStyle.height = style.marginBottom;
mbStyle.width = style.width;
mbStyle.top = pos.top + pos.height + unit;
mbStyle.left = posLeft + unit;
var marginSideH = pos.height + marginTopSt + marginBottomSt + unit;
var marginSideT = pos.top - marginTopSt + unit;
mlStyle.height = marginSideH;
mlStyle.width = style.marginLeft;
mlStyle.top = marginSideT;
mlStyle.left = posLeft - marginLeftSt + unit;
mrStyle.height = marginSideH;
mrStyle.width = style.marginRight;
mrStyle.top = marginSideT;
mrStyle.left = posLeft + pos.width + unit;
// Padding style
var padTop = parseInt(style.paddingTop.replace(unit, ''));
ptStyle.height = style.paddingTop;
ptStyle.width = style.width;
ptStyle.top = pos.top + unit;
ptStyle.left = posLeft + unit;
var padBot = parseInt(style.paddingBottom.replace(unit, ''));
pbStyle.height = style.paddingBottom;
pbStyle.width = style.width;
pbStyle.top = pos.top + pos.height - padBot + unit;
pbStyle.left = posLeft + unit;
var padSideH = (pos.height - padBot - padTop) + unit;
var padSideT = pos.top + padTop + unit;
plStyle.height = padSideH;
plStyle.width = style.paddingLeft;
plStyle.top = padSideT;
plStyle.left = pos.left + unit;
var padRight = parseInt(style.paddingRight.replace(unit, ''));
prStyle.height = padSideH;
prStyle.width = style.paddingRight;
prStyle.top = padSideT;
prStyle.left = pos.left + pos.width - padRight + unit;
},
stop: function(editor, sender, opts) {
var opt = opts || {};
var state = opt.state || '';
var method = this.getOffsetMethod(state);
var canvas = editor.Canvas;
var offsetViewer = canvas[method]();
offsetViewer.style.display = 'none';
},
};
});
module.exports = {
getOffsetMethod: function(state) {
var method = state || '';
return 'get' + method + 'OffsetViewerEl';
},
run: function(editor, sender, opts) {
var opt = opts || {};
var state = opt.state || '';
var config = editor.getConfig();
if (!config.showOffsets ||
(!config.showOffsetsSelected && state == 'Fixed') ) {
return;
}
var canvas = editor.Canvas;
var el = opt.el || '';
var pos = opt.elPos || canvas.getElementPos(el);
var style = window.getComputedStyle(el);
var ppfx = this.ppfx;
var stateVar = state + 'State';
var method = this.getOffsetMethod(state);
var offsetViewer = canvas[method]();
offsetViewer.style.display = 'block';
var marginT = this['marginT' + state];
var marginB = this['marginB' + state];
var marginL = this['marginL' + state];
var marginR = this['marginR' + state];
var padT = this['padT' + state];
var padB = this['padB' + state];
var padL = this['padL' + state];
var padR = this['padR' + state];
if(!this[stateVar]) {
var stateLow = state.toLowerCase();
var marginName = stateLow + 'margin-v';
var paddingName = stateLow + 'padding-v';
var marginV = $('<div>', {class: ppfx + marginName}).get(0);
var paddingV = $('<div>', {class: ppfx + paddingName}).get(0);
var marginEls = ppfx + marginName + '-el';
var paddingEls = ppfx + paddingName + '-el';
marginT = $('<div>', {class: ppfx + marginName + '-top ' + marginEls}).get(0);
marginB = $('<div>', {class: ppfx + marginName + '-bottom ' + marginEls}).get(0);
marginL = $('<div>', {class: ppfx + marginName + '-left ' + marginEls}).get(0);
marginR = $('<div>', {class: ppfx + marginName + '-right ' + marginEls}).get(0);
padT = $('<div>', {class: ppfx + paddingName + '-top ' + paddingEls}).get(0);
padB = $('<div>', {class: ppfx + paddingName + '-bottom ' + paddingEls}).get(0);
padL = $('<div>', {class: ppfx + paddingName + '-left ' + paddingEls}).get(0);
padR = $('<div>', {class: ppfx + paddingName + '-right ' + paddingEls}).get(0);
this['marginT' + state] = marginT;
this['marginB' + state] = marginB;
this['marginL' + state] = marginL;
this['marginR' + state] = marginR;
this['padT' + state] = padT;
this['padB' + state] = padB;
this['padL' + state] = padL;
this['padR' + state] = padR;
marginV.appendChild(marginT);
marginV.appendChild(marginB);
marginV.appendChild(marginL);
marginV.appendChild(marginR);
paddingV.appendChild(padT);
paddingV.appendChild(padB);
paddingV.appendChild(padL);
paddingV.appendChild(padR);
offsetViewer.appendChild(marginV);
offsetViewer.appendChild(paddingV);
this[stateVar] = '1';
}
var unit = 'px';
var marginLeftSt = style.marginLeft.replace(unit, '');
var marginTopSt = parseInt(style.marginTop.replace(unit, ''));
var marginBottomSt = parseInt(style.marginBottom.replace(unit, ''));
var mtStyle = marginT.style;
var mbStyle = marginB.style;
var mlStyle = marginL.style;
var mrStyle = marginR.style;
var ptStyle = padT.style;
var pbStyle = padB.style;
var plStyle = padL.style;
var prStyle = padR.style;
var posLeft = parseInt(pos.left);
// Margin style
mtStyle.height = style.marginTop;
mtStyle.width = style.width;
mtStyle.top = pos.top - style.marginTop.replace(unit, '') + unit;
mtStyle.left = posLeft + unit;
mbStyle.height = style.marginBottom;
mbStyle.width = style.width;
mbStyle.top = pos.top + pos.height + unit;
mbStyle.left = posLeft + unit;
var marginSideH = pos.height + marginTopSt + marginBottomSt + unit;
var marginSideT = pos.top - marginTopSt + unit;
mlStyle.height = marginSideH;
mlStyle.width = style.marginLeft;
mlStyle.top = marginSideT;
mlStyle.left = posLeft - marginLeftSt + unit;
mrStyle.height = marginSideH;
mrStyle.width = style.marginRight;
mrStyle.top = marginSideT;
mrStyle.left = posLeft + pos.width + unit;
// Padding style
var padTop = parseInt(style.paddingTop.replace(unit, ''));
ptStyle.height = style.paddingTop;
ptStyle.width = style.width;
ptStyle.top = pos.top + unit;
ptStyle.left = posLeft + unit;
var padBot = parseInt(style.paddingBottom.replace(unit, ''));
pbStyle.height = style.paddingBottom;
pbStyle.width = style.width;
pbStyle.top = pos.top + pos.height - padBot + unit;
pbStyle.left = posLeft + unit;
var padSideH = (pos.height - padBot - padTop) + unit;
var padSideT = pos.top + padTop + unit;
plStyle.height = padSideH;
plStyle.width = style.paddingLeft;
plStyle.top = padSideT;
plStyle.left = pos.left + unit;
var padRight = parseInt(style.paddingRight.replace(unit, ''));
prStyle.height = padSideH;
prStyle.width = style.paddingRight;
prStyle.top = padSideT;
prStyle.left = pos.left + pos.width - padRight + unit;
},
stop: function(editor, sender, opts) {
var opt = opts || {};
var state = opt.state || '';
var method = this.getOffsetMethod(state);
var canvas = editor.Canvas;
var offsetViewer = canvas[method]();
offsetViewer.style.display = 'none';
},
};

18
src/commands/view/SwitchVisibility.js

@ -1,13 +1,11 @@
define(function() {
return {
module.exports = {
run: function(ed) {
ed.Canvas.getBody().className = this.ppfx + 'dashed';
},
run: function(ed) {
ed.Canvas.getBody().className = this.ppfx + 'dashed';
},
stop: function(ed) {
ed.Canvas.getBody().className = "";
}
stop: function(ed) {
ed.Canvas.getBody().className = "";
}
};
});
};

62
src/commands/view/TextComponent.js

@ -1,37 +1,31 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var CreateComponent = require('./CreateComponent');
/**
* @class TextComponent
* @private
* */
module.exports = _.extend({}, CreateComponent, {
var Backbone = require('backbone');
var CreateComponent = require('./CreateComponent');
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw: function(component){
component.type = 'text';
if(!component.style)
component.style = {};
component.style.padding = '10px';
},
module.exports = _.extend({}, CreateComponent, {
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw: function(model){
if(!model || !model.set)
return;
model.trigger('focus');
if(this.sender)
this.sender.set('active', false);
},
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw: function(component){
component.type = 'text';
if(!component.style)
component.style = {};
component.style.padding = '10px';
},
});
});
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw: function(model){
if(!model || !model.set)
return;
model.trigger('focus');
if(this.sender)
this.sender.set('active', false);
},
});

18
src/css_composer/config/config.js

@ -1,14 +1,12 @@
define(function () {
return {
module.exports = {
// Style prefix
stylePrefix: 'css-',
// Style prefix
stylePrefix: 'css-',
// Custom CSS string to render on top
'staticRules': '',
// Custom CSS string to render on top
'staticRules': '',
// Default CSS style
rules: [],
// Default CSS style
rules: [],
};
});
};

442
src/css_composer/index.js

@ -21,254 +21,252 @@
* rules: '.myClass{ color: red}',
* }
*/
define(function(require) {
return function() {
var c = {},
defaults = require('./config/config'),
CssRule = require('./model/CssRule'),
CssRules = require('./model/CssRules'),
Selectors = require('./model/Selectors'),
CssRulesView = require('./view/CssRulesView');
var rules, rulesView;
module.exports = function() {
var c = {},
defaults = require('./config/config'),
CssRule = require('./model/CssRule'),
CssRules = require('./model/CssRules'),
Selectors = require('./model/Selectors'),
CssRulesView = require('./view/CssRulesView');
return {
var rules, rulesView;
Selectors: Selectors,
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'CssComposer',
Selectors: Selectors,
/**
* Mandatory for the storage manager
* @type {String}
* @private
*/
storageKey: function(){
var keys = [];
var smc = (c.stm && c.stm.getConfig()) || {};
if(smc.storeCss)
keys.push('css');
if(smc.storeStyles)
keys.push('styles');
return keys;
},
/**
* Name of the module
* @type {String}
* @private
*/
name: 'CssComposer',
/**
* Initializes module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
/**
* Mandatory for the storage manager
* @type {String}
* @private
*/
storageKey: function(){
var keys = [];
var smc = (c.stm && c.stm.getConfig()) || {};
if(smc.storeCss)
keys.push('css');
if(smc.storeStyles)
keys.push('styles');
return keys;
},
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
/**
* Initializes module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var elStyle = (c.em && c.em.config.style) || '';
c.rules = elStyle || c.rules;
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
c.sm = c.em; // TODO Refactor
rules = new CssRules([], c);
rules.add(c.rules);
var elStyle = (c.em && c.em.config.style) || '';
c.rules = elStyle || c.rules;
rulesView = new CssRulesView({
collection: rules,
config: c,
});
return this;
},
c.sm = c.em; // TODO Refactor
rules = new CssRules([], c);
rules.add(c.rules);
/**
* On load callback
* @private
*/
onLoad: function(){
if(c.stm && c.stm.getConfig().autoload)
this.load();
rulesView = new CssRulesView({
collection: rules,
config: c,
});
return this;
},
if(c.stm && c.stm.isAutosave())
c.em.listenRules(this.getAll());
},
/**
* On load callback
* @private
*/
onLoad: function(){
if(c.stm && c.stm.getConfig().autoload)
this.load();
/**
* Load data from the passed object, if the object is empty will try to fetch them
* autonomously from the storage manager.
* The fetched data will be added to the collection
* @param {Object} data Object of data to load
* @return {Object} Loaded rules
*/
load: function(data) {
var d = data || '';
if(!d && c.stm)
d = c.em.getCacheLoad();
var obj = '';
if(d.styles) {
try{
obj = JSON.parse(d.styles);
}catch(err){}
} else if (d.css) {
obj = c.em.get('Parser').parseCss(d.css);
}
if(c.stm && c.stm.isAutosave())
c.em.listenRules(this.getAll());
},
if(obj)
rules.reset(obj);
return obj;
},
/**
* Load data from the passed object, if the object is empty will try to fetch them
* autonomously from the storage manager.
* The fetched data will be added to the collection
* @param {Object} data Object of data to load
* @return {Object} Loaded rules
*/
load: function(data) {
var d = data || '';
if(!d && c.stm)
d = c.em.getCacheLoad();
var obj = '';
if(d.styles) {
try{
obj = JSON.parse(d.styles);
}catch(err){}
} else if (d.css) {
obj = c.em.get('Parser').parseCss(d.css);
}
/**
* Store data to the selected storage
* @param {Boolean} noStore If true, won't store
* @return {Object} Data to store
*/
store: function(noStore){
if(!c.stm)
return;
var obj = {};
var keys = this.storageKey();
if(keys.indexOf('css') >= 0)
obj.css = c.em.getCss();
if(keys.indexOf('styles') >= 0)
obj.styles = JSON.stringify(rules);
if(!noStore)
c.stm.store(obj);
return obj;
},
if(obj)
rules.reset(obj);
return obj;
},
/**
* Add new rule to the collection, if not yet exists with the same selectors
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} opts Other options for the rule
* @return {Model}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.add([sel1, sel2], 'hover');
* rule.set('style', {
* width: '100px',
* color: '#fff',
* });
* */
add: function(selectors, state, width, opts) {
var s = state || '';
var w = width || '';
var opt = opts || {};
var rule = this.get(selectors, s, w, opt);
if(rule)
return rule;
else {
opt.state = s;
opt.mediaText = w;
opt.selectors = '';
rule = new CssRule(opt);
rule.get('selectors').add(selectors);
rules.add(rule);
return rule;
}
},
/**
* Store data to the selected storage
* @param {Boolean} noStore If true, won't store
* @return {Object} Data to store
*/
store: function(noStore){
if(!c.stm)
return;
var obj = {};
var keys = this.storageKey();
if(keys.indexOf('css') >= 0)
obj.css = c.em.getCss();
if(keys.indexOf('styles') >= 0)
obj.styles = JSON.stringify(rules);
if(!noStore)
c.stm.store(obj);
return obj;
},
/**
* Get the rule
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} ruleProps Other rule props
* @return {Model|null}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.get([sel1, sel2], 'hover');
* // Update the style
* rule.set('style', {
* width: '300px',
* color: '#000',
* });
* */
get: function(selectors, state, width, ruleProps) {
var rule = null;
rules.each(function(m) {
if(rule)
return;
if(m.compare(selectors, state, width, ruleProps))
rule = m;
});
/**
* Add new rule to the collection, if not yet exists with the same selectors
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} opts Other options for the rule
* @return {Model}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.add([sel1, sel2], 'hover');
* rule.set('style', {
* width: '100px',
* color: '#fff',
* });
* */
add: function(selectors, state, width, opts) {
var s = state || '';
var w = width || '';
var opt = opts || {};
var rule = this.get(selectors, s, w, opt);
if(rule)
return rule;
},
/**
* Get the collection of rules
* @return {Collection}
* */
getAll: function() {
return rules;
},
else {
opt.state = s;
opt.mediaText = w;
opt.selectors = '';
rule = new CssRule(opt);
rule.get('selectors').add(selectors);
rules.add(rule);
return rule;
}
},
/**
* Add a raw collection of rule objects
* This method overrides styles, in case, of already defined rule
* @param {Array<Object>} data Array of rule objects, eg . [{selectors: ['class1'], style: {....}}, ..]
* @param {Object} opts Options
* @return {Array<Model>}
* @private
*/
addCollection: function(data, opts) {
var opt = opts || {};
var result = [];
var d = data instanceof Array ? data : [data];
/**
* Get the rule
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} ruleProps Other rule props
* @return {Model|null}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.get([sel1, sel2], 'hover');
* // Update the style
* rule.set('style', {
* width: '300px',
* color: '#000',
* });
* */
get: function(selectors, state, width, ruleProps) {
var rule = null;
rules.each(function(m) {
if(rule)
return;
if(m.compare(selectors, state, width, ruleProps))
rule = m;
});
return rule;
},
for (var i = 0, l = d.length; i < l; i++) {
var rule = d[i] || {};
if(!rule.selectors)
continue;
var sm = c.em && c.em.get('SelectorManager');
if(!sm)
console.warn('Selector Manager not found');
var sl = rule.selectors;
var sels = sl instanceof Array ? sl : [sl];
var newSels = [];
/**
* Get the collection of rules
* @return {Collection}
* */
getAll: function() {
return rules;
},
for (var j = 0, le = sels.length; j < le; j++) {
var selec = sm.add(sels[j]);
newSels.push(selec);
}
/**
* Add a raw collection of rule objects
* This method overrides styles, in case, of already defined rule
* @param {Array<Object>} data Array of rule objects, eg . [{selectors: ['class1'], style: {....}}, ..]
* @param {Object} opts Options
* @return {Array<Model>}
* @private
*/
addCollection: function(data, opts) {
var opt = opts || {};
var result = [];
var d = data instanceof Array ? data : [data];
var model = this.add(newSels, rule.state, rule.mediaText, rule);
if (opt.extend) {
var newStyle = _.extend({}, model.get('style'), rule.style || {});
model.set('style', newStyle);
} else {
model.set('style', rule.style || {});
}
for (var i = 0, l = d.length; i < l; i++) {
var rule = d[i] || {};
if(!rule.selectors)
continue;
var sm = c.em && c.em.get('SelectorManager');
if(!sm)
console.warn('Selector Manager not found');
var sl = rule.selectors;
var sels = sl instanceof Array ? sl : [sl];
var newSels = [];
result.push(model);
for (var j = 0, le = sels.length; j < le; j++) {
var selec = sm.add(sels[j]);
newSels.push(selec);
}
return result;
},
var model = this.add(newSels, rule.state, rule.mediaText, rule);
if (opt.extend) {
var newStyle = _.extend({}, model.get('style'), rule.style || {});
model.set('style', newStyle);
} else {
model.set('style', rule.style || {});
}
/**
* Render the block of CSS rules
* @return {HTMLElement}
* @private
*/
render: function() {
return rulesView.render().el;
result.push(model);
}
};
};
return result;
},
/**
* Render the block of CSS rules
* @return {HTMLElement}
* @private
*/
render: function() {
return rulesView.render().el;
}
});
};
};

188
src/css_composer/model/CssRule.js

@ -1,95 +1,93 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Selectors = require('./Selectors');
module.exports = Backbone.Model.extend({
defaults: {
// Css selectors
selectors: {},
// Additional string css selectors
selectorsAdd: '',
// Css properties style
style: {},
// On which device width this rule should be rendered, eg. @media (max-width: 1000px)
mediaText: '',
// State of the rule, eg: hover | pressed | focused
state: '',
// Indicates if the rule is stylable
stylable: true,
},
initialize: function(c, opt) {
this.config = c || {};
this.sm = opt ? opt.sm || {} : {};
this.slct = this.config.selectors || [];
if(this.sm.get){
var slct = [];
for(var i = 0; i < this.slct.length; i++)
slct.push(this.sm.get('SelectorManager').add(this.slct[i].name || this.slct[i]));
this.slct = slct;
}
this.set('selectors', new Selectors(this.slct));
},
/**
* Compare the actual model with parameters
* @param {Object} selectors Collection of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} ruleProps Other rule props
* @return {Boolean}
* @private
*/
compare: function(selectors, state, width, ruleProps){
var otherRule = ruleProps || {};
var st = state || '';
var wd = width || '';
var selectorsAdd = otherRule.selectorsAdd || '';
var cId = 'cid';
//var a1 = _.pluck(selectors.models || selectors, cId);
//var a2 = _.pluck(this.get('selectors').models, cId);
if(!(selectors instanceof Array) && !selectors.models)
selectors = [selectors];
var a1 = _.map((selectors.models || selectors), function(model) {
return model.get('name');
});
var a2 = _.map(this.get('selectors').models, function(model) {
return model.get('name');
});
var f = false;
if(a1.length !== a2.length)
return f;
for (var i = 0; i < a1.length; i++) {
var re = 0;
for (var j = 0; j < a2.length; j++) {
if (a1[i] === a2[j])
re = 1;
}
if(re === 0)
return f;
}
if(this.get('state') !== st)
return f;
if(this.get('mediaText') !== wd)
return f;
if(this.get('selectorsAdd') !== selectorsAdd)
return f;
return true;
},
});
});
var Backbone = require('backbone');
var Selectors = require('./Selectors');
module.exports = Backbone.Model.extend({
defaults: {
// Css selectors
selectors: {},
// Additional string css selectors
selectorsAdd: '',
// Css properties style
style: {},
// On which device width this rule should be rendered, eg. @media (max-width: 1000px)
mediaText: '',
// State of the rule, eg: hover | pressed | focused
state: '',
// Indicates if the rule is stylable
stylable: true,
},
initialize: function(c, opt) {
this.config = c || {};
this.sm = opt ? opt.sm || {} : {};
this.slct = this.config.selectors || [];
if(this.sm.get){
var slct = [];
for(var i = 0; i < this.slct.length; i++)
slct.push(this.sm.get('SelectorManager').add(this.slct[i].name || this.slct[i]));
this.slct = slct;
}
this.set('selectors', new Selectors(this.slct));
},
/**
* Compare the actual model with parameters
* @param {Object} selectors Collection of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @param {Object} ruleProps Other rule props
* @return {Boolean}
* @private
*/
compare: function(selectors, state, width, ruleProps){
var otherRule = ruleProps || {};
var st = state || '';
var wd = width || '';
var selectorsAdd = otherRule.selectorsAdd || '';
var cId = 'cid';
//var a1 = _.pluck(selectors.models || selectors, cId);
//var a2 = _.pluck(this.get('selectors').models, cId);
if(!(selectors instanceof Array) && !selectors.models)
selectors = [selectors];
var a1 = _.map((selectors.models || selectors), function(model) {
return model.get('name');
});
var a2 = _.map(this.get('selectors').models, function(model) {
return model.get('name');
});
var f = false;
if(a1.length !== a2.length)
return f;
for (var i = 0; i < a1.length; i++) {
var re = 0;
for (var j = 0; j < a2.length; j++) {
if (a1[i] === a2[j])
re = 1;
}
if(re === 0)
return f;
}
if(this.get('state') !== st)
return f;
if(this.get('mediaText') !== wd)
return f;
if(this.get('selectorsAdd') !== selectorsAdd)
return f;
return true;
},
});

52
src/css_composer/model/CssRules.js

@ -1,36 +1,34 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var CssRule = require('./CssRule');
module.exports = Backbone.Collection.extend({
var Backbone = require('backbone');
var CssRule = require('./CssRule');
initialize: function(models, opt){
module.exports = Backbone.Collection.extend({
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
initialize: function(models, opt){
this.model = function(attrs, options) {
var model;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
this.model = function(attrs, options) {
var model;
switch(1){
default:
model = new CssRule(attrs, options);
}
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
return model;
};
switch(1){
default:
model = new CssRule(attrs, options);
}
},
return model;
};
add: function(models, opt){
if(typeof models === 'string')
models = this.editor.get('Parser').parseCss(models);
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
},
});
});
add: function(models, opt){
if(typeof models === 'string')
models = this.editor.get('Parser').parseCss(models);
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
});

32
src/css_composer/model/Selectors.js

@ -1,26 +1,24 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
module.exports = Backbone.Collection.extend({
var Backbone = require('backbone');
initialize: function(models, opt){
module.exports = Backbone.Collection.extend({
this.model = function(attrs, opts) {
var model;
initialize: function(models, opt){
switch(1){
this.model = function(attrs, opts) {
var model;
default:
if(!this.ClassTag)
this.ClassTag = require("SelectorManager/model/Selector");
model = new this.ClassTag(attrs, opts);
switch(1){
}
default:
if(!this.ClassTag)
this.ClassTag = require("SelectorManager/model/Selector");
model = new this.ClassTag(attrs, opts);
return model;
};
}
},
return model;
};
},
});
});

136
src/css_composer/view/CssRuleView.js

@ -1,79 +1,77 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
module.exports = Backbone.View.extend({
var Backbone = require('backbone');
tagName: 'style',
module.exports = Backbone.View.extend({
initialize: function(o) {
this.config = o.config || {};
this.listenTo(this.model, 'change:style', this.render);
this.listenTo(this.model, 'change:state', this.render);
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:mediaText', this.render);
this.listenTo(this.model.get('selectors'), 'change', this.selChanged);
},
tagName: 'style',
/**
* Triggered when some selector is changed
* @private
*/
selChanged: function(){
this.selStr = this.renderSelectors();
this.render();
},
initialize: function(o) {
this.config = o.config || {};
this.listenTo(this.model, 'change:style', this.render);
this.listenTo(this.model, 'change:state', this.render);
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:mediaText', this.render);
this.listenTo(this.model.get('selectors'), 'change', this.selChanged);
},
/**
* Triggered when some selector is changed
* @private
*/
selChanged: function(){
this.selStr = this.renderSelectors();
this.render();
},
/**
* Returns string of selectors
* @return {String}
* @private
*/
renderSelectors: function() {
var sel = [];
var model = this.model;
var add = model.get('selectorsAdd');
model.get('selectors').each(function(m){
sel.push('.' + m.get('name'));
});
var sels = sel.join('');
return sels + (sels && add ? ', ' : '') + add;
},
/**
* Returns string of selectors
* @return {String}
* @private
*/
renderSelectors: function() {
var sel = [];
var model = this.model;
var add = model.get('selectorsAdd');
model.get('selectors').each(function(m){
sel.push('.' + m.get('name'));
});
var sels = sel.join('');
return sels + (sels && add ? ', ' : '') + add;
},
/**
* Returns string of properties
* @return {String}
* @private
*/
renderProperties: function(){
var sel = [],
props = this.model.get('style');
for (var prop in props){
sel.push(prop + ':' + props[prop] + ';');
}
return sel.join('');
},
/**
* Returns string of properties
* @return {String}
* @private
*/
renderProperties: function(){
var sel = [],
props = this.model.get('style');
for (var prop in props){
sel.push(prop + ':' + props[prop] + ';');
}
return sel.join('');
},
render : function() {
var block = '',
selStr = '',
o = '';
if(!this.selStr)
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
var stateStr = this.model.get('state');
var mediaText = this.model.get('mediaText');
if(this.selStr){
stateStr = stateStr ? ':' + stateStr : '';
block = prpStr !== '' ? '{' + prpStr + '}' : '';
}
o = this.selStr && block ? this.selStr + stateStr + block : '';
render : function() {
var block = '',
selStr = '',
o = '';
if(!this.selStr)
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
var stateStr = this.model.get('state');
var mediaText = this.model.get('mediaText');
if(this.selStr){
stateStr = stateStr ? ':' + stateStr : '';
block = prpStr !== '' ? '{' + prpStr + '}' : '';
}
o = this.selStr && block ? this.selStr + stateStr + block : '';
if(mediaText && o)
o = '@media ' + mediaText + '{' + o + '}';
if(mediaText && o)
o = '@media ' + mediaText + '{' + o + '}';
this.$el.html(o);
return this;
},
this.$el.html(o);
return this;
},
});
});

124
src/css_composer/view/CssRulesView.js

@ -1,63 +1,61 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var CssRuleView = require('./CssRuleView');
module.exports = Backbone.View.extend({
initialize: function(o) {
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'rules';
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} model
* @private
* */
addTo: function(model){
//console.log('Added');
this.addToCollection(model);
},
/**
* Add new object to collection
* @param {Object} model
* @param {Object} fragmentEl
* @return {Object}
* @private
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = CssRuleView;
var view = new viewObject({
model: model,
config: this.config,
});
var rendered = view.render().el;
if(fragment)
fragment.appendChild( rendered );
else
this.$el.append(rendered);
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
return this;
}
});
});
var Backbone = require('backbone');
var CssRuleView = require('./CssRuleView');
module.exports = Backbone.View.extend({
initialize: function(o) {
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'rules';
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} model
* @private
* */
addTo: function(model){
//console.log('Added');
this.addToCollection(model);
},
/**
* Add new object to collection
* @param {Object} model
* @param {Object} fragmentEl
* @return {Object}
* @private
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = CssRuleView;
var view = new viewObject({
model: model,
config: this.config,
});
var rendered = view.render().el;
if(fragment)
fragment.appendChild( rendered );
else
this.$el.append(rendered);
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
return this;
}
});

10
src/device_manager/config/config.js

@ -1,9 +1,7 @@
define(function () {
return {
module.exports = {
devices: [],
devices: [],
deviceLabel: 'Device',
deviceLabel: 'Device',
};
});
};

178
src/device_manager/index.js

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

19
src/device_manager/model/Device.js

@ -1,15 +1,12 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
module.exports = Backbone.Model.extend({
idAttribute: 'name',
idAttribute: 'name',
defaults :{
name: '',
width: '',
},
defaults :{
name: '',
width: '',
},
});
});
});

145
src/device_manager/view/DevicesView.js

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

48
src/dom_components/config/config.js

@ -1,32 +1,30 @@
define(function () {
return {
stylePrefix: 'comp-',
module.exports = {
stylePrefix: 'comp-',
wrapperId: 'wrapper',
wrapperId: 'wrapper',
// Default wrapper configuration
wrapper: {
//classes: ['body'],
removable: false,
copyable: false,
stylable: ['background','background-color','background-image', 'background-repeat','background-attachment','background-position'],
draggable: false,
badgable: false,
components: [],
},
// Default wrapper configuration
wrapper: {
//classes: ['body'],
removable: false,
copyable: false,
stylable: ['background','background-color','background-image', 'background-repeat','background-attachment','background-position'],
draggable: false,
badgable: false,
components: [],
},
// Could be used for default components
components: [],
// Could be used for default components
components: [],
rte: {},
rte: {},
// Class for new image component
imageCompClass : 'fa fa-picture-o',
// Class for new image component
imageCompClass : 'fa fa-picture-o',
// Open assets manager on create of image component
oAssetsOnCreate : true,
// Open assets manager on create of image component
oAssetsOnCreate : true,
// List of void elements
voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],
};
});
// List of void elements
voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],
};

663
src/dom_components/index.js

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

727
src/dom_components/model/Component.js

@ -1,375 +1,372 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Components = require('./Components');
var Selectors = require('SelectorManager/model/Selectors');
var Traits = require('TraitManager/model/Traits');
var Backbone = require('backbone');
var Components = require('./Components');
var Selectors = require('SelectorManager/model/Selectors');
var Traits = require('TraitManager/model/Traits');
module.exports = Backbone.Model.extend({
defaults: {
// HTML tag of the component
tagName: 'div',
// Component type, eg. 'text', 'image', 'video', etc.
type: '',
// True if the component is removable from the canvas
removable: true,
// Indicates if it's possible to drag the component inside other
// Tip: Indicate an array of selectors where it could be dropped inside
draggable: true,
// Indicates if it's possible to drop other components inside
// Tip: Indicate an array of selectors which could be dropped inside
droppable: true,
// Set false if don't want to see the badge (with the name) over the component
badgable: true,
// True if it's possible to style it
// Tip: Indicate an array of CSS properties which is possible to style
stylable: true,
// Highlightable with 'dotted' style if true
highlightable: true,
// True if it's possible to clone the component
copyable: true,
// Indicates if it's possible to resize the component (at the moment implemented only on Image Components)
resizable: false,
// Allow to edit the content of the component (used on Text components)
editable: false,
// Hide the component inside Layers
hiddenLayer: false,
// This property is used by the HTML exporter as void elements do not
// have closing tag, eg. <br/>, <hr/>, etc.
void: false,
// Indicates if the component is in some CSS state like ':hover', ':active', etc.
state: '',
// State, eg. 'selected'
status: '',
// Content of the component (not escaped) which will be appended before children rendering
content: '',
// Component related style
style: {},
// Key-value object of the component's attributes
attributes: '',
// Array of classes
classes: '',
// Component's javascript
script: '',
// Traits
traits: ['id', 'title'],
/**
* Set an array of items to show up inside the toolbar (eg. move, clone, delete)
* when the component is selected
* toolbar: [{
* attributes: {class: 'fa fa-arrows'},
* command: 'tlb-move',
* },{
* attributes: {class: 'fa fa-clone'},
* command: 'tlb-clone',
* }]
*/
toolbar: null,
// TODO
previousModel: '',
mirror: '',
},
initialize: function(o, opt) {
// Check void elements
if(opt && opt.config && opt.config.voidElements.indexOf(this.get('tagName')) >= 0)
this.set('void', true);
this.opt = opt;
this.sm = opt ? opt.sm || {} : {};
this.config = o || {};
this.defaultC = this.config.components || [];
this.defaultCl = this.normalizeClasses(this.get('classes') || this.config.classes || []);
this.components = new Components(this.defaultC, opt);
this.components.parent = this;
this.listenTo(this, 'change:script', this.scriptUpdated);
this.set('attributes', this.get('attributes') || {});
this.set('components', this.components);
this.set('classes', new Selectors(this.defaultCl));
var traits = new Traits();
traits.setTarget(this);
traits.add(this.get('traits'));
this.set('traits', traits);
this.initToolbar();
// Normalize few properties from strings to arrays
var toNormalize = ['stylable'];
toNormalize.forEach(function(name) {
var value = this.get(name);
if (typeof value == 'string') {
var newValue = value.split(',').map(function(prop) {
return prop.trim();
});
this.set(name, newValue);
}
}, this);
this.init();
},
/**
* Initialize callback
*/
init: function () {},
/**
* Script updated
*/
scriptUpdated: function() {
this.set('scriptUpdated', 1);
},
/**
* Init toolbar
*/
initToolbar: function () {
var model = this;
if(!model.get('toolbar')) {
var tb = [];
if(model.get('draggable')) {
tb.push({
attributes: {class: 'fa fa-arrows'},
command: 'tlb-move',
});
}
if(model.get('copyable')) {
tb.push({
attributes: {class: 'fa fa-clone'},
command: 'tlb-clone',
});
}
if(model.get('removable')) {
tb.push({
attributes: {class: 'fa fa-trash-o'},
command: 'tlb-delete',
});
}
model.set('toolbar', tb);
}
},
/**
* Load traits
* @param {Array} traits
* @private
*/
loadTraits: function(traits) {
var trt = new Traits();
trt.setTarget(this);
trt.add(traits);
this.set('traits', trt);
},
/**
* Normalize input classes from array to array of objects
* @param {Array} arr
* @return {Array}
* @private
*/
normalizeClasses: function(arr) {
var res = [];
if(!this.sm.get)
return;
var clm = this.sm.get('SelectorManager');
if(!clm)
return;
arr.forEach(function(val){
var name = '';
if(typeof val === 'string')
name = val;
else
name = val.name;
var model = clm.add(name);
res.push(model);
});
return res;
},
/**
* Override original clone method
* @private
*/
clone: function(reset) {
var attr = _.clone(this.attributes),
comp = this.get('components'),
traits = this.get('traits'),
cls = this.get('classes');
attr.components = [];
attr.classes = [];
attr.traits = [];
comp.each(function(md,i) {
attr.components[i] = md.clone(1);
});
traits.each(function(md, i) {
attr.traits[i] = md.clone();
module.exports = Backbone.Model.extend({
defaults: {
// HTML tag of the component
tagName: 'div',
// Component type, eg. 'text', 'image', 'video', etc.
type: '',
// True if the component is removable from the canvas
removable: true,
// Indicates if it's possible to drag the component inside other
// Tip: Indicate an array of selectors where it could be dropped inside
draggable: true,
// Indicates if it's possible to drop other components inside
// Tip: Indicate an array of selectors which could be dropped inside
droppable: true,
// Set false if don't want to see the badge (with the name) over the component
badgable: true,
// True if it's possible to style it
// Tip: Indicate an array of CSS properties which is possible to style
stylable: true,
// Highlightable with 'dotted' style if true
highlightable: true,
// True if it's possible to clone the component
copyable: true,
// Indicates if it's possible to resize the component (at the moment implemented only on Image Components)
resizable: false,
// Allow to edit the content of the component (used on Text components)
editable: false,
// Hide the component inside Layers
hiddenLayer: false,
// This property is used by the HTML exporter as void elements do not
// have closing tag, eg. <br/>, <hr/>, etc.
void: false,
// Indicates if the component is in some CSS state like ':hover', ':active', etc.
state: '',
// State, eg. 'selected'
status: '',
// Content of the component (not escaped) which will be appended before children rendering
content: '',
// Component related style
style: {},
// Key-value object of the component's attributes
attributes: '',
// Array of classes
classes: '',
// Component's javascript
script: '',
// Traits
traits: ['id', 'title'],
/**
* Set an array of items to show up inside the toolbar (eg. move, clone, delete)
* when the component is selected
* toolbar: [{
* attributes: {class: 'fa fa-arrows'},
* command: 'tlb-move',
* },{
* attributes: {class: 'fa fa-clone'},
* command: 'tlb-clone',
* }]
*/
toolbar: null,
// TODO
previousModel: '',
mirror: '',
},
initialize: function(o, opt) {
// Check void elements
if(opt && opt.config && opt.config.voidElements.indexOf(this.get('tagName')) >= 0)
this.set('void', true);
this.opt = opt;
this.sm = opt ? opt.sm || {} : {};
this.config = o || {};
this.defaultC = this.config.components || [];
this.defaultCl = this.normalizeClasses(this.get('classes') || this.config.classes || []);
this.components = new Components(this.defaultC, opt);
this.components.parent = this;
this.listenTo(this, 'change:script', this.scriptUpdated);
this.set('attributes', this.get('attributes') || {});
this.set('components', this.components);
this.set('classes', new Selectors(this.defaultCl));
var traits = new Traits();
traits.setTarget(this);
traits.add(this.get('traits'));
this.set('traits', traits);
this.initToolbar();
// Normalize few properties from strings to arrays
var toNormalize = ['stylable'];
toNormalize.forEach(function(name) {
var value = this.get(name);
if (typeof value == 'string') {
var newValue = value.split(',').map(function(prop) {
return prop.trim();
});
cls.each(function(md,i) {
attr.classes[i] = md.get('name');
this.set(name, newValue);
}
}, this);
this.init();
},
/**
* Initialize callback
*/
init: function () {},
/**
* Script updated
*/
scriptUpdated: function() {
this.set('scriptUpdated', 1);
},
/**
* Init toolbar
*/
initToolbar: function () {
var model = this;
if(!model.get('toolbar')) {
var tb = [];
if(model.get('draggable')) {
tb.push({
attributes: {class: 'fa fa-arrows'},
command: 'tlb-move',
});
attr.status = '';
attr.view = '';
if(reset){
this.opt.collection = null;
}
return new this.constructor(attr, this.opt);
},
/**
* Get name of the component
* @return {string}
* @private
* */
getName: function() {
if(!this.name){
var id = this.cid.replace(/\D/g,''),
type = this.get('type');
var tag = this.get('tagName');
tag = tag == 'div' ? 'box' : tag;
tag = type ? type : tag;
this.name = tag.charAt(0).toUpperCase() + tag.slice(1);
}
return this.name;
},
/**
* Return HTML string of the component
* @param {Object} opts Options
* @return {string} HTML string
* @private
*/
toHTML: function(opts) {
var code = '';
var m = this;
var tag = m.get('tagName'),
sTag = m.get('void'),
attrId = '';
// Build the string of attributes
var strAttr = '';
var attr = this.getAttrToHTML();
for(var prop in attr){
var val = attr[prop];
strAttr += typeof val !== undefined && val !== '' ?
' ' + prop + '="' + val + '"' : '';
}
// Build the string of classes
var strCls = '';
m.get('classes').each(function(m){
strCls += ' ' + m.get('name');
}
if(model.get('copyable')) {
tb.push({
attributes: {class: 'fa fa-clone'},
command: 'tlb-clone',
});
strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : '';
// 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')))
attrId = ' id="' + m.cid + '" ';
code += '<' + tag + strCls + attrId + strAttr + (sTag ? '/' : '') + '>' + m.get('content');
m.get('components').each(function(m) {
code += m.toHTML();
}
if(model.get('removable')) {
tb.push({
attributes: {class: 'fa fa-trash-o'},
command: 'tlb-delete',
});
if(!sTag)
code += '</'+tag+'>';
return code;
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = this.get('attributes') || {};
delete attr.style;
return attr;
},
/**
* Return a shallow copy of the model's attributes for JSON
* stringification.
* @return {Object}
* @private
*/
toJSON: function() {
var obj = Backbone.Model.prototype.toJSON.apply(this, arguments);
var scriptStr = this.getScriptString();
if (scriptStr) {
obj.script = scriptStr;
}
return obj;
},
/**
* Return script in string format, cleans 'function() {..' from scripts
* if it's a function
* @param {string|Function} script
* @return {string}
* @private
*/
getScriptString: function (script) {
var scr = script || this.get('script');
// Need to cast script functions to string
if (typeof scr == 'function') {
var scrStr = scr.toString().trim();
scrStr = scrStr.replace(/^function\s?\(\)\s?\{/, '');
scrStr = scrStr.replace(/\}$/, '');
scr = scrStr;
}
return scr;
}
model.set('toolbar', tb);
}
},
/**
* Load traits
* @param {Array} traits
* @private
*/
loadTraits: function(traits) {
var trt = new Traits();
trt.setTarget(this);
trt.add(traits);
this.set('traits', trt);
},
/**
* Normalize input classes from array to array of objects
* @param {Array} arr
* @return {Array}
* @private
*/
normalizeClasses: function(arr) {
var res = [];
if(!this.sm.get)
return;
var clm = this.sm.get('SelectorManager');
if(!clm)
return;
arr.forEach(function(val){
var name = '';
if(typeof val === 'string')
name = val;
else
name = val.name;
var model = clm.add(name);
res.push(model);
});
return res;
},
/**
* Override original clone method
* @private
*/
clone: function(reset) {
var attr = _.clone(this.attributes),
comp = this.get('components'),
traits = this.get('traits'),
cls = this.get('classes');
attr.components = [];
attr.classes = [];
attr.traits = [];
comp.each(function(md,i) {
attr.components[i] = md.clone(1);
});
traits.each(function(md, i) {
attr.traits[i] = md.clone();
});
cls.each(function(md,i) {
attr.classes[i] = md.get('name');
});
attr.status = '';
attr.view = '';
if(reset){
this.opt.collection = null;
}
return new this.constructor(attr, this.opt);
},
/**
* Get name of the component
* @return {string}
* @private
* */
getName: function() {
if(!this.name){
var id = this.cid.replace(/\D/g,''),
type = this.get('type');
var tag = this.get('tagName');
tag = tag == 'div' ? 'box' : tag;
tag = type ? type : tag;
this.name = tag.charAt(0).toUpperCase() + tag.slice(1);
}
return this.name;
},
/**
* Return HTML string of the component
* @param {Object} opts Options
* @return {string} HTML string
* @private
*/
toHTML: function(opts) {
var code = '';
var m = this;
var tag = m.get('tagName'),
sTag = m.get('void'),
attrId = '';
// Build the string of attributes
var strAttr = '';
var attr = this.getAttrToHTML();
for(var prop in attr){
var val = attr[prop];
strAttr += typeof val !== undefined && val !== '' ?
' ' + prop + '="' + val + '"' : '';
}
// Build the string of classes
var strCls = '';
m.get('classes').each(function(m){
strCls += ' ' + m.get('name');
});
strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : '';
},{
// 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')))
attrId = ' id="' + m.cid + '" ';
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
return {tagName: el.tagName ? el.tagName.toLowerCase() : ''};
},
code += '<' + tag + strCls + attrId + strAttr + (sTag ? '/' : '') + '>' + m.get('content');
m.get('components').each(function(m) {
code += m.toHTML();
});
});
if(!sTag)
code += '</'+tag+'>';
return code;
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = this.get('attributes') || {};
delete attr.style;
return attr;
},
/**
* Return a shallow copy of the model's attributes for JSON
* stringification.
* @return {Object}
* @private
*/
toJSON: function() {
var obj = Backbone.Model.prototype.toJSON.apply(this, arguments);
var scriptStr = this.getScriptString();
if (scriptStr) {
obj.script = scriptStr;
}
return obj;
},
/**
* Return script in string format, cleans 'function() {..' from scripts
* if it's a function
* @param {string|Function} script
* @return {string}
* @private
*/
getScriptString: function (script) {
var scr = script || this.get('script');
// Need to cast script functions to string
if (typeof scr == 'function') {
var scrStr = scr.toString().trim();
scrStr = scrStr.replace(/^function\s?\(\)\s?\{/, '');
scrStr = scrStr.replace(/\}$/, '');
scr = scrStr;
}
return scr;
}
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
return {tagName: el.tagName ? el.tagName.toLowerCase() : ''};
},
});

183
src/dom_components/model/ComponentImage.js

@ -1,104 +1,101 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'image',
tagName: 'img',
src: '',
void: 1,
droppable: false,
resizable: true,
traits: ['alt']
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'image',
tagName: 'img',
src: '',
void: 1,
droppable: false,
resizable: true,
traits: ['alt']
}),
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
var attr = this.get('attributes');
if(attr.src)
this.set('src', attr.src);
},
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
var attr = this.get('attributes');
if(attr.src)
this.set('src', attr.src);
},
initToolbar: function() {
Component.prototype.initToolbar.apply(this, arguments);
initToolbar: function() {
Component.prototype.initToolbar.apply(this, arguments);
if (this.sm && this.sm.get) {
var cmd = this.sm.get('Commands');
var cmdName = 'image-editor';
if (this.sm && this.sm.get) {
var cmd = this.sm.get('Commands');
var cmdName = 'image-editor';
// Add Image Editor button only if the default command exists
if (cmd.has(cmdName)) {
var tb = this.get('toolbar');
tb.push({
attributes: {class: 'fa fa-pencil'},
command: cmdName,
});
this.set('toolbar', tb);
}
}
},
// Add Image Editor button only if the default command exists
if (cmd.has(cmdName)) {
var tb = this.get('toolbar');
tb.push({
attributes: {class: 'fa fa-pencil'},
command: cmdName,
});
this.set('toolbar', tb);
}
}
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
delete attr.onmousedown;
var src = this.get('src');
if(src)
attr.src = src;
return attr;
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
delete attr.onmousedown;
var src = this.get('src');
if(src)
attr.src = src;
return attr;
},
/**
* Parse uri
* @param {string} uri
* @return {object}
* @private
*/
parseUri: function(uri) {
var el = document.createElement('a');
el.href = uri;
var query = {};
var qrs = el.search.substring(1).split('&');
for (var i = 0; i < qrs.length; i++) {
var pair = qrs[i].split('=');
var name = decodeURIComponent(pair[0]);
if(name)
query[name] = decodeURIComponent(pair[1]);
}
return {
hostname: el.hostname,
pathname: el.pathname,
protocol: el.protocol,
search: el.search,
hash: el.hash,
port: el.port,
query: query,
};
},
/**
* Parse uri
* @param {string} uri
* @return {object}
* @private
*/
parseUri: function(uri) {
var el = document.createElement('a');
el.href = uri;
var query = {};
var qrs = el.search.substring(1).split('&');
for (var i = 0; i < qrs.length; i++) {
var pair = qrs[i].split('=');
var name = decodeURIComponent(pair[0]);
if(name)
query[name] = decodeURIComponent(pair[1]);
}
return {
hostname: el.hostname,
pathname: el.pathname,
protocol: el.protocol,
search: el.search,
hash: el.hash,
port: el.port,
query: query,
};
},
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'IMG'){
result = {type: 'image'};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'IMG'){
result = {type: 'image'};
}
return result;
},
});
});
});

71
src/dom_components/model/ComponentLink.js

@ -1,43 +1,40 @@
define(function(require, exports, module){
'use strict';
var Component = require('./ComponentText');
var Component = require('./ComponentText');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'link',
tagName: 'a',
traits: ['title', 'href', 'target'],
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'link',
tagName: 'a',
traits: ['title', 'href', 'target'],
}),
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
delete attr.onmousedown;
return attr;
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
delete attr.onmousedown;
return attr;
},
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'A'){
result = {type: 'link'};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'A'){
result = {type: 'link'};
}
return result;
},
});
});
});

197
src/dom_components/model/ComponentMap.js

@ -1,110 +1,107 @@
define(function(require, exports, module){
'use strict';
var Component = require('./ComponentImage');
var OComponent = require('./Component');
var Component = require('./ComponentImage');
var OComponent = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'map',
void: 0,
mapUrl: 'https://maps.google.com/maps',
tagName: 'iframe',
mapType: 'q',
address: '',
zoom: '1',
attributes: {frameborder: 0},
toolbar: OComponent.prototype.defaults.toolbar,
traits: [{
label: 'Address',
name: 'address',
placeholder: 'eg. London, UK',
changeProp: 1,
},{
type: 'select',
label: 'Map type',
name: 'mapType',
changeProp: 1,
options: [
{value: 'q', name: 'Roadmap'},
{value: 'w', name: 'Satellite'}
]
},{
label: 'Zoom',
name: 'zoom',
type: 'range',
min: '1',
max: '20',
changeProp: 1,
}],
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'map',
void: 0,
mapUrl: 'https://maps.google.com/maps',
tagName: 'iframe',
mapType: 'q',
address: '',
zoom: '1',
attributes: {frameborder: 0},
toolbar: OComponent.prototype.defaults.toolbar,
traits: [{
label: 'Address',
name: 'address',
placeholder: 'eg. London, UK',
changeProp: 1,
},{
type: 'select',
label: 'Map type',
name: 'mapType',
changeProp: 1,
options: [
{value: 'q', name: 'Roadmap'},
{value: 'w', name: 'Satellite'}
]
},{
label: 'Zoom',
name: 'zoom',
type: 'range',
min: '1',
max: '20',
changeProp: 1,
}],
}),
initialize: function(o, opt) {
if(this.get('src'))
this.parseFromSrc();
else
this.updateSrc();
Component.prototype.initialize.apply(this, arguments);
this.listenTo(this, 'change:address change:zoom change:mapType', this.updateSrc);
},
initialize: function(o, opt) {
if(this.get('src'))
this.parseFromSrc();
else
this.updateSrc();
Component.prototype.initialize.apply(this, arguments);
this.listenTo(this, 'change:address change:zoom change:mapType', this.updateSrc);
},
updateSrc: function() {
this.set('src', this.getMapUrl());
},
updateSrc: function() {
this.set('src', this.getMapUrl());
},
/**
* Returns url of the map
* @return {string}
* @private
*/
getMapUrl: function() {
var md = this;
var addr = md.get('address');
var zoom = md.get('zoom');
var type = md.get('mapType');
var size = '';
addr = addr ? '&q=' + addr : '';
zoom = zoom ? '&z=' + zoom : '';
type = type ? '&t=' + type : '';
var result = md.get('mapUrl')+'?' + addr + zoom + type;
result += '&output=embed';
return result;
},
/**
* Returns url of the map
* @return {string}
* @private
*/
getMapUrl: function() {
var md = this;
var addr = md.get('address');
var zoom = md.get('zoom');
var type = md.get('mapType');
var size = '';
addr = addr ? '&q=' + addr : '';
zoom = zoom ? '&z=' + zoom : '';
type = type ? '&t=' + type : '';
var result = md.get('mapUrl')+'?' + addr + zoom + type;
result += '&output=embed';
return result;
},
/**
* Set attributes by src string
* @private
*/
parseFromSrc: function() {
var uri = this.parseUri(this.get('src'));
var qr = uri.query;
if(qr.q)
this.set('address', qr.q);
if(qr.z)
this.set('zoom', qr.z);
if(qr.t)
this.set('mapType', qr.t);
},
/**
* Set attributes by src string
* @private
*/
parseFromSrc: function() {
var uri = this.parseUri(this.get('src'));
var qr = uri.query;
if(qr.q)
this.set('address', qr.q);
if(qr.z)
this.set('zoom', qr.z);
if(qr.t)
this.set('mapType', qr.t);
},
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'IFRAME' &&
/maps\.google\.com/.test(el.src) ){
result = {type: 'map', src: el.src};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'IFRAME' &&
/maps\.google\.com/.test(el.src) ){
result = {type: 'map', src: el.src};
}
return result;
},
});
});
});

43
src/dom_components/model/ComponentScript.js

@ -1,30 +1,27 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'script',
droppable: false,
draggable: false,
hiddenLayer: true,
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'script',
droppable: false,
draggable: false,
hiddenLayer: true,
}),
}, {
}, {
isComponent: function(el) {
if (el.tagName == 'SCRIPT') {
var result = {type: 'script'};
isComponent: function(el) {
if (el.tagName == 'SCRIPT') {
var result = {type: 'script'};
if (el.src) {
result.src = el.src;
result.onload = el.onload;
}
if (el.src) {
result.src = el.src;
result.onload = el.onload;
}
return result;
}
},
return result;
}
},
});
});
});

159
src/dom_components/model/ComponentTable.js

@ -1,92 +1,89 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'table',
tagName: 'table',
droppable: ['tr', 'tbody', 'thead', 'tfoot'],
columns: 3,
rows: 2,
/*
traits: [{
label: 'Columns',
name: 'columns',
changeProp: 1,
},{
label: 'Rows',
name: 'rows',
changeProp: 1,
}]
*/
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'table',
tagName: 'table',
droppable: ['tr', 'tbody', 'thead', 'tfoot'],
columns: 3,
rows: 2,
/*
traits: [{
label: 'Columns',
name: 'columns',
changeProp: 1,
},{
label: 'Rows',
name: 'rows',
changeProp: 1,
}]
*/
}),
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
var components = this.get('components');
var rows = this.get('rows');
var columns = this.get('columns');
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
var components = this.get('components');
var rows = this.get('rows');
var columns = this.get('columns');
// Init components if empty
if(!components.length){
var rowsToAdd = [];
// Init components if empty
if(!components.length){
var rowsToAdd = [];
while(rows--){
var columnsToAdd = [];
var clm = columns;
while(rows--){
var columnsToAdd = [];
var clm = columns;
while (clm--) {
columnsToAdd.push({
type: 'cell',
classes: ['cell']
});
}
while (clm--) {
columnsToAdd.push({
type: 'cell',
classes: ['cell']
});
}
rowsToAdd.push({
type: 'row',
classes: ['row'],
components: columnsToAdd
});
}
components.add(rowsToAdd);
}
rowsToAdd.push({
type: 'row',
classes: ['row'],
components: columnsToAdd
});
}
components.add(rowsToAdd);
}
// Clean table from non rows
var rowsColl = [];
components.each(function(model){
if(model.get('type') != 'row'){
model.get('components').each(function(row) {
if(row.get('type') == 'row'){
row.collection = components;
rowsColl.push(row);
}
});
}else{
rowsColl.push(model);
}
});
components.reset(rowsColl);
},
// Clean table from non rows
var rowsColl = [];
components.each(function(model){
if(model.get('type') != 'row'){
model.get('components').each(function(row) {
if(row.get('type') == 'row'){
row.collection = components;
rowsColl.push(row);
}
});
}else{
rowsColl.push(model);
}
});
components.reset(rowsColl);
},
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'TABLE'){
result = {type: 'table'};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'TABLE'){
result = {type: 'table'};
}
return result;
},
});
});
});

59
src/dom_components/model/ComponentTableCell.js

@ -1,36 +1,33 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'cell',
tagName: 'td',
draggable: ['tr'],
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'cell',
tagName: 'td',
draggable: ['tr'],
}),
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
var tag = el.tagName;
if(tag == 'TD' || tag == 'TH'){
result = {
type: 'cell',
tagName: tag.toLowerCase()
};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
var tag = el.tagName;
if(tag == 'TD' || tag == 'TH'){
result = {
type: 'cell',
tagName: tag.toLowerCase()
};
}
return result;
},
});
});
});

77
src/dom_components/model/ComponentTableRow.js

@ -1,47 +1,44 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'row',
tagName: 'tr',
draggable: ['table', 'tbody', 'thead'],
droppable: ['th', 'td']
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'row',
tagName: 'tr',
draggable: ['table', 'tbody', 'thead'],
droppable: ['th', 'td']
}),
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
initialize: function(o, opt) {
Component.prototype.initialize.apply(this, arguments);
// Clean the row from non cell components
var cells = [];
var components = this.get('components');
components.each(function(model){
if(model.get('type') == 'cell'){
cells.push(model);
}
});
components.reset(cells);
}
// Clean the row from non cell components
var cells = [];
var components = this.get('components');
components.each(function(model){
if(model.get('type') == 'cell'){
cells.push(model);
}
});
components.reset(cells);
}
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'TR'){
result = {type: 'row'};
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
if(el.tagName == 'TR'){
result = {type: 'row'};
}
return result;
},
});
});
});

19
src/dom_components/model/ComponentText.js

@ -1,14 +1,11 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'text',
droppable: false,
editable: true,
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'text',
droppable: false,
editable: true,
}),
});
});
});

45
src/dom_components/model/ComponentTextNode.js

@ -1,30 +1,27 @@
define(function(require, exports, module){
'use strict';
var Component = require('./Component');
var Component = require('./Component');
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
droppable: false,
editable: true,
}),
defaults: _.extend({}, Component.prototype.defaults, {
droppable: false,
editable: true,
}),
toHTML: function() {
return this.get('content');
},
toHTML: function() {
return this.get('content');
},
}, {
}, {
isComponent: function(el) {
var result = '';
if(el.nodeType === 3){
result = {
type: 'textnode',
content: el.textContent
};
}
return result;
},
isComponent: function(el) {
var result = '';
if(el.nodeType === 3){
result = {
type: 'textnode',
content: el.textContent
};
}
return result;
},
});
});
});

593
src/dom_components/model/ComponentVideo.js

@ -1,319 +1,316 @@
define(function(require, exports, module){
'use strict';
var Component = require('./ComponentImage');
var OComponent = require('./Component');
var Component = require('./ComponentImage');
var OComponent = require('./Component');
var yt = 'yt';
var vi = 'vi';
var yt = 'yt';
var vi = 'vi';
module.exports = Component.extend({
module.exports = Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'video',
tagName: 'video',
videoId: '',
void: 0,
provider: '', // on change of provider, traits are switched
ytUrl: 'https://www.youtube.com/embed/',
viUrl: 'https://player.vimeo.com/video/',
loop: 0,
muted: 0,
autoplay: 0,
controls: 1,
color: '',
sources: [],
attributes:{allowfullscreen:'allowfullscreen'},
toolbar: OComponent.prototype.defaults.toolbar,
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'video',
tagName: 'video',
videoId: '',
void: 0,
provider: '', // on change of provider, traits are switched
ytUrl: 'https://www.youtube.com/embed/',
viUrl: 'https://player.vimeo.com/video/',
loop: 0,
muted: 0,
autoplay: 0,
controls: 1,
color: '',
sources: [],
attributes:{allowfullscreen:'allowfullscreen'},
toolbar: OComponent.prototype.defaults.toolbar,
}),
initialize: function(o, opt) {
var traits = [];
var prov = this.get('provider');
switch (prov) {
case yt:
traits = this.getYoutubeTraits();
break;
case vi:
traits = this.getVimeoTraits();
break;
default:
traits = this.getSourceTraits();
}
if(this.get('src'))
this.parseFromSrc();
this.set('traits', traits);
Component.prototype.initialize.apply(this, arguments);
this.listenTo(this, 'change:provider', this.updateTraits);
this.listenTo(this, 'change:videoId', this.updateSrc);
},
initialize: function(o, opt) {
var traits = [];
var prov = this.get('provider');
switch (prov) {
case yt:
traits = this.getYoutubeTraits();
break;
case vi:
traits = this.getVimeoTraits();
break;
default:
traits = this.getSourceTraits();
}
if(this.get('src'))
this.parseFromSrc();
this.set('traits', traits);
Component.prototype.initialize.apply(this, arguments);
this.listenTo(this, 'change:provider', this.updateTraits);
this.listenTo(this, 'change:videoId', this.updateSrc);
},
/**
* Set attributes by src string
*/
parseFromSrc: function() {
var prov = this.get('provider');
var uri = this.parseUri(this.get('src'));
var qr = uri.query;
switch (prov) {
case yt: case vi:
var videoId = uri.pathname.split('/').pop();
this.set('videoId', videoId);
if(qr.autoplay)
this.set('autoplay', 1);
if(qr.loop)
this.set('loop', 1);
if(parseInt(qr.controls) === 0)
this.set('controls', 0);
if(qr.color)
this.set('color', qr.color);
break;
default:
}
},
/**
* Set attributes by src string
*/
parseFromSrc: function() {
var prov = this.get('provider');
var uri = this.parseUri(this.get('src'));
var qr = uri.query;
switch (prov) {
case yt: case vi:
var videoId = uri.pathname.split('/').pop();
this.set('videoId', videoId);
if(qr.autoplay)
this.set('autoplay', 1);
if(qr.loop)
this.set('loop', 1);
if(parseInt(qr.controls) === 0)
this.set('controls', 0);
if(qr.color)
this.set('color', qr.color);
break;
default:
}
},
/**
* Update src on change of video ID
* @private
*/
updateSrc: function() {
var prov = this.get('provider');
switch (prov) {
case yt:
this.set('src',this.getYoutubeSrc());
break;
case vi:
this.set('src',this.getVimeoSrc());
break;
}
},
/**
* Update src on change of video ID
* @private
*/
updateSrc: function() {
var prov = this.get('provider');
switch (prov) {
case yt:
this.set('src',this.getYoutubeSrc());
break;
case vi:
this.set('src',this.getVimeoSrc());
break;
}
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
var prov = this.get('provider');
switch (prov) {
case yt: case vi:
break;
default:
if(this.get('loop'))
attr.loop = 'loop';
if(this.get('autoplay'))
attr.autoplay = 'autoplay';
if(this.get('controls'))
attr.controls = 'controls';
}
return attr;
},
/**
* Returns object of attributes for HTML
* @return {Object}
* @private
*/
getAttrToHTML: function() {
var attr = Component.prototype.getAttrToHTML.apply(this, arguments);
var prov = this.get('provider');
switch (prov) {
case yt: case vi:
break;
default:
if(this.get('loop'))
attr.loop = 'loop';
if(this.get('autoplay'))
attr.autoplay = 'autoplay';
if(this.get('controls'))
attr.controls = 'controls';
}
return attr;
},
/**
* Update traits by provider
* @private
*/
updateTraits: function() {
var prov = this.get('provider');
var traits = this.getSourceTraits();
switch (prov) {
case yt:
this.set('tagName', 'iframe');
traits = this.getYoutubeTraits();
break;
case vi:
this.set('tagName', 'iframe');
traits = this.getVimeoTraits();
break;
default:
this.set('tagName', 'video');
}
this.loadTraits(traits);
this.sm.trigger('change:selectedComponent');
},
/**
* Update traits by provider
* @private
*/
updateTraits: function() {
var prov = this.get('provider');
var traits = this.getSourceTraits();
switch (prov) {
case yt:
this.set('tagName', 'iframe');
traits = this.getYoutubeTraits();
break;
case vi:
this.set('tagName', 'iframe');
traits = this.getVimeoTraits();
break;
default:
this.set('tagName', 'video');
}
this.loadTraits(traits);
this.sm.trigger('change:selectedComponent');
},
// Listen provider change and switch traits, in TraitView listen traits change
// Listen provider change and switch traits, in TraitView listen traits change
/**
* Return the provider trait
* @return {Object}
* @private
*/
getProviderTrait: function() {
return {
type: 'select',
label: 'Provider',
name: 'provider',
changeProp: 1,
value: this.get('provider'),
options: [
{value: 'so', name: 'HTML5 Source'},
{value: yt, name: 'Youtube'},
{value: vi, name: 'Vimeo'}
]
};
},
/**
* Return the provider trait
* @return {Object}
* @private
*/
getProviderTrait: function() {
return {
type: 'select',
label: 'Provider',
name: 'provider',
changeProp: 1,
value: this.get('provider'),
options: [
{value: 'so', name: 'HTML5 Source'},
{value: yt, name: 'Youtube'},
{value: vi, name: 'Vimeo'}
]
};
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getSourceTraits: function() {
return [
this.getProviderTrait(), {
label: 'Source',
name: 'src',
placeholder: 'eg. ./media/video.mp4',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()];
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getYoutubeTraits: function() {
return [
this.getProviderTrait(), {
label: 'Video ID',
name: 'videoId',
placeholder: 'eg. jNQXAC9IVRw',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()
];
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getSourceTraits: function() {
return [
this.getProviderTrait(), {
label: 'Source',
name: 'src',
placeholder: 'eg. ./media/video.mp4',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()];
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getYoutubeTraits: function() {
return [
this.getProviderTrait(), {
label: 'Video ID',
name: 'videoId',
placeholder: 'eg. jNQXAC9IVRw',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()
];
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getVimeoTraits: function() {
return [
this.getProviderTrait(), {
label: 'Video ID',
name: 'videoId',
placeholder: 'eg. 123456789',
changeProp: 1,
},{
label: 'Color',
name: 'color',
placeholder: 'eg. FF0000',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()];
},
/**
* Return traits for the source provider
* @return {Array<Object>}
* @private
*/
getVimeoTraits: function() {
return [
this.getProviderTrait(), {
label: 'Video ID',
name: 'videoId',
placeholder: 'eg. 123456789',
changeProp: 1,
},{
label: 'Color',
name: 'color',
placeholder: 'eg. FF0000',
changeProp: 1,
},
this.getAutoplayTrait(),
this.getLoopTrait(),
this.getControlsTrait()];
},
/**
* Return object trait
* @return {Object}
* @private
*/
getAutoplayTrait: function(){
return {
type: 'checkbox',
label: 'Autoplay',
name: 'autoplay',
changeProp: 1,
};
},
/**
* Return object trait
* @return {Object}
* @private
*/
getAutoplayTrait: function(){
return {
type: 'checkbox',
label: 'Autoplay',
name: 'autoplay',
changeProp: 1,
};
},
/**
* Return object trait
* @return {Object}
* @private
*/
getLoopTrait: function(){
return {
type: 'checkbox',
label: 'Loop',
name: 'loop',
changeProp: 1,
};
},
/**
* Return object trait
* @return {Object}
* @private
*/
getLoopTrait: function(){
return {
type: 'checkbox',
label: 'Loop',
name: 'loop',
changeProp: 1,
};
},
/**
* Return object trait
* @return {Object}
* @private
*/
getControlsTrait: function(){
return {
type: 'checkbox',
label: 'Controls',
name: 'controls',
changeProp: 1,
};
},
/**
* Return object trait
* @return {Object}
* @private
*/
getControlsTrait: function(){
return {
type: 'checkbox',
label: 'Controls',
name: 'controls',
changeProp: 1,
};
},
/**
* Returns url to youtube video
* @return {string}
* @private
*/
getYoutubeSrc: function() {
var url = this.get('ytUrl');
url += this.get('videoId') + '?';
url += this.get('autoplay') ? '&autoplay=1' : '';
url += !this.get('controls') ? '&controls=0' : '';
url += this.get('loop') ? '&loop=1' : '';
return url;
},
/**
* Returns url to youtube video
* @return {string}
* @private
*/
getYoutubeSrc: function() {
var url = this.get('ytUrl');
url += this.get('videoId') + '?';
url += this.get('autoplay') ? '&autoplay=1' : '';
url += !this.get('controls') ? '&controls=0' : '';
url += this.get('loop') ? '&loop=1' : '';
return url;
},
/**
* Returns url to vimeo video
* @return {string}
* @private
*/
getVimeoSrc: function() {
var url = this.get('viUrl');
url += this.get('videoId') + '?';
url += this.get('autoplay') ? '&autoplay=1' : '';
url += this.get('loop') ? '&loop=1' : '';
url += !this.get('controls') ? '&title=0&portrait=0&badge=0' : '';
url += this.get('color') ? '&color=' + this.get('color') : '';
return url;
},
/**
* Returns url to vimeo video
* @return {string}
* @private
*/
getVimeoSrc: function() {
var url = this.get('viUrl');
url += this.get('videoId') + '?';
url += this.get('autoplay') ? '&autoplay=1' : '';
url += this.get('loop') ? '&loop=1' : '';
url += !this.get('controls') ? '&title=0&portrait=0&badge=0' : '';
url += this.get('color') ? '&color=' + this.get('color') : '';
return url;
},
},{
},{
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
var isYtProv = /youtube\.com\/embed/.test(el.src);
var isViProv = /player\.vimeo\.com\/video/.test(el.src);
var isExtProv = isYtProv || isViProv;
if(el.tagName == 'VIDEO' ||
(el.tagName == 'IFRAME' && isExtProv) ){
result = {type: 'video'};
if(el.src)
result.src = el.src;
if(isExtProv){
if(isYtProv)
result.provider = yt;
else if(isViProv)
result.provider = vi;
}
}
return result;
},
/**
* Detect if the passed element is a valid component.
* In case the element is valid an object abstracted
* from the element will be returned
* @param {HTMLElement}
* @return {Object}
* @private
*/
isComponent: function(el) {
var result = '';
var isYtProv = /youtube\.com\/embed/.test(el.src);
var isViProv = /player\.vimeo\.com\/video/.test(el.src);
var isExtProv = isYtProv || isViProv;
if(el.tagName == 'VIDEO' ||
(el.tagName == 'IFRAME' && isExtProv) ){
result = {type: 'video'};
if(el.src)
result.src = el.src;
if(isExtProv){
if(isYtProv)
result.provider = yt;
else if(isViProv)
result.provider = vi;
}
}
return result;
},
});
});
});

129
src/dom_components/model/Components.js

@ -1,81 +1,78 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.Collection.extend({
module.exports = Backbone.Collection.extend({
initialize: function(models, opt){
initialize: function(models, opt){
this.on('add', this.onAdd);
this.on('add', this.onAdd);
this.config = opt && opt.config ? opt.config : null;
this.config = opt && opt.config ? opt.config : null;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
this.model = function(attrs, options) {
var model;
this.model = function(attrs, options) {
var model;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(opt && opt.config)
options.config = opt.config;
if(opt && opt.config)
options.config = opt.config;
if(opt && opt.defaultTypes)
options.defaultTypes = opt.defaultTypes;
if(opt && opt.defaultTypes)
options.defaultTypes = opt.defaultTypes;
if(opt && opt.componentTypes)
options.componentTypes = opt.componentTypes;
if(opt && opt.componentTypes)
options.componentTypes = opt.componentTypes;
var df = opt.defaultTypes;
var df = opt.defaultTypes;
for (var it = 0; it < df.length; it++) {
var dfId = df[it].id;
if(dfId == attrs.type) {
model = df[it].model;
break;
}
}
if(!model) {
// get the last one
model = df[df.length - 1].model;
}
return new model(attrs, options);
};
},
add: function(models, opt){
if(typeof models === 'string'){
var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html;
var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css, {extend: 1});
}
}
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
onAdd: function(model, c, opts){
var style = model.get('style');
var em = this.editor;
if(!_.isEmpty(style) && em && em.get('Config').forceClass){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.add(newClass);
rule.set('style', style);
for (var it = 0; it < df.length; it++) {
var dfId = df[it].id;
if(dfId == attrs.type) {
model = df[it].model;
break;
}
},
}
if(!model) {
// get the last one
model = df[df.length - 1].model;
}
return new model(attrs, options);
};
},
add: function(models, opt){
if(typeof models === 'string'){
var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html;
var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css, {extend: 1});
}
}
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
onAdd: function(model, c, opts){
var style = model.get('style');
var em = this.editor;
if(!_.isEmpty(style) && em && em.get('Config').forceClass){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.add(newClass);
rule.set('style', style);
}
},
});
});

10
src/dom_components/model/Toolbar.js

@ -1,8 +1,4 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ToolbarButton = require('./ToolbarButton');
var Backbone = require('backbone');
var ToolbarButton = require('./ToolbarButton');
module.exports = Backbone.Collection.extend({model: ToolbarButton});
});
module.exports = Backbone.Collection.extend({model: ToolbarButton});

18
src/dom_components/model/ToolbarButton.js

@ -1,14 +1,10 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
module.exports = Backbone.Model.extend({
defaults: {
command: '',
attributes: {},
},
defaults: {
command: '',
attributes: {},
},
});
});
});

121
src/dom_components/view/ComponentImageView.js

@ -1,74 +1,71 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
module.exports = ComponentView.extend({
tagName: 'img',
tagName: 'img',
events: {
'dblclick': 'openModal',
'click': 'initResize',
},
events: {
'dblclick': 'openModal',
'click': 'initResize',
},
initialize: function(o) {
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:src', this.updateSrc);
this.listenTo(this.model, 'dblclick active', this.openModal);
this.classEmpty = this.ppfx + 'plh-image';
initialize: function(o) {
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:src', this.updateSrc);
this.listenTo(this.model, 'dblclick active', this.openModal);
this.classEmpty = this.ppfx + 'plh-image';
if(this.config.modal)
this.modal = this.config.modal;
if(this.config.modal)
this.modal = this.config.modal;
if(this.config.am)
this.am = this.config.am;
},
if(this.config.am)
this.am = this.config.am;
},
/**
* Update src attribute
* @private
* */
updateSrc: function() {
var src = this.model.get("src");
this.$el.attr('src', src);
if(!src)
this.$el.addClass(this.classEmpty);
else
this.$el.removeClass(this.classEmpty);
},
/**
* Update src attribute
* @private
* */
updateSrc: function() {
var src = this.model.get("src");
this.$el.attr('src', src);
if(!src)
this.$el.addClass(this.classEmpty);
else
this.$el.removeClass(this.classEmpty);
},
/**
* Open dialog for image changing
* @param {Object} e Event
* @private
* */
openModal: function(e) {
var em = this.opts.config.em;
var editor = em ? em.get('Editor') : '';
/**
* Open dialog for image changing
* @param {Object} e Event
* @private
* */
openModal: function(e) {
var em = this.opts.config.em;
var editor = em ? em.get('Editor') : '';
if(editor) {
editor.runCommand('open-assets', {
target: this.model,
onSelect: function() {
editor.Modal.close();
editor.AssetManager.setTarget(null);
}
});
}
},
if(editor) {
editor.runCommand('open-assets', {
target: this.model,
onSelect: function() {
editor.Modal.close();
editor.AssetManager.setTarget(null);
}
});
}
},
render: function() {
this.updateAttributes();
this.updateClasses();
render: function() {
this.updateAttributes();
this.updateClasses();
var actCls = this.$el.attr('class') || '';
if(!this.model.get('src'))
this.$el.attr('class', (actCls + ' ' + this.classEmpty).trim());
var actCls = this.$el.attr('class') || '';
if(!this.model.get('src'))
this.$el.attr('class', (actCls + ' ' + this.classEmpty).trim());
// Avoid strange behaviours while try to drag
this.$el.attr('onmousedown', 'return false');
return this;
},
});
});
// Avoid strange behaviours while try to drag
this.$el.attr('onmousedown', 'return false');
return this;
},
});

31
src/dom_components/view/ComponentLinkView.js

@ -1,23 +1,20 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentTextView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentTextView');
module.exports = ComponentView.extend({
module.exports = ComponentView.extend({
events: {
'dblclick': 'enableEditing',
},
events: {
'dblclick': 'enableEditing',
},
render: function() {
ComponentView.prototype.render.apply(this, arguments);
render: function() {
ComponentView.prototype.render.apply(this, arguments);
// I need capturing instead of bubbling as bubbled clicks from other
// children will execute the link event
this.el.addEventListener('click', this.prevDef, true);
// I need capturing instead of bubbling as bubbled clicks from other
// children will execute the link event
this.el.addEventListener('click', this.prevDef, true);
return this;
},
return this;
},
});
});
});

89
src/dom_components/view/ComponentMapView.js

@ -1,46 +1,43 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
module.exports = ComponentView.extend({
tagName: 'div',
events: {},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.classEmpty = this.ppfx + 'plh-map';
},
/**
* Update the map on the canvas
* @private
*/
updateSrc: function() {
this.getIframe().src = this.model.get('src');
},
getIframe: function() {
if(!this.iframe){
var ifrm = document.createElement("iframe");
ifrm.src = this.model.get('src');
ifrm.frameBorder = 0;
ifrm.style.height = '100%';
ifrm.style.width = '100%';
ifrm.className = this.ppfx + 'no-pointer';
this.iframe = ifrm;
}
return this.iframe;
},
render: function() {
ComponentView.prototype.render.apply(this, arguments);
this.updateClasses();
this.el.appendChild(this.getIframe());
return this;
},
});
});
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
module.exports = ComponentView.extend({
tagName: 'div',
events: {},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.classEmpty = this.ppfx + 'plh-map';
},
/**
* Update the map on the canvas
* @private
*/
updateSrc: function() {
this.getIframe().src = this.model.get('src');
},
getIframe: function() {
if(!this.iframe){
var ifrm = document.createElement("iframe");
ifrm.src = this.model.get('src');
ifrm.frameBorder = 0;
ifrm.style.height = '100%';
ifrm.style.width = '100%';
ifrm.className = this.ppfx + 'no-pointer';
this.iframe = ifrm;
}
return this.iframe;
},
render: function() {
ComponentView.prototype.render.apply(this, arguments);
this.updateClasses();
this.el.appendChild(this.getIframe());
return this;
},
});

69
src/dom_components/view/ComponentScriptView.js

@ -1,44 +1,41 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
module.exports = ComponentView.extend({
module.exports = ComponentView.extend({
tagName: 'script',
tagName: 'script',
events: {},
events: {},
render: function() {
var model = this.model;
var src = model.get('src');
var em = this.em;
var scriptCount = em && em.get('scriptCount') ? em.get('scriptCount') : 0;
var content = '';
render: function() {
var model = this.model;
var src = model.get('src');
var em = this.em;
var scriptCount = em && em.get('scriptCount') ? em.get('scriptCount') : 0;
var content = '';
// If it's an external script
if(src) {
var onload = model.get('onload');
var svar = 'script' + scriptCount;
var svarNext = 'script' + (scriptCount + 1);
content = "var "+svar+" = document.createElement('script');\n" +
svar+".onload = function(){\n" +
(onload ? onload + "();\n" : '') +
"typeof "+svarNext+"Start == 'function' && "+svarNext+"Start();\n" +
"};\n" +
svar+".src = '" + src + "';\n"+
"function "+svar+"Start() { document.body.appendChild("+svar+"); };\n" +
(!scriptCount ? svar+"Start();" : '');
if(em){
em.set('scriptCount', scriptCount + 1);
}
} else {
content = model.get('content');
// If it's an external script
if(src) {
var onload = model.get('onload');
var svar = 'script' + scriptCount;
var svarNext = 'script' + (scriptCount + 1);
content = "var "+svar+" = document.createElement('script');\n" +
svar+".onload = function(){\n" +
(onload ? onload + "();\n" : '') +
"typeof "+svarNext+"Start == 'function' && "+svarNext+"Start();\n" +
"};\n" +
svar+".src = '" + src + "';\n"+
"function "+svar+"Start() { document.body.appendChild("+svar+"); };\n" +
(!scriptCount ? svar+"Start();" : '');
if(em){
em.set('scriptCount', scriptCount + 1);
}
} else {
content = model.get('content');
}
this.el.innerHTML = content;
return this;
},
this.el.innerHTML = content;
return this;
},
});
});
});

10
src/dom_components/view/ComponentTableCellView.js

@ -1,8 +1,4 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
});
});
module.exports = ComponentView.extend({});

10
src/dom_components/view/ComponentTableRowView.js

@ -1,8 +1,4 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
});
});
module.exports = ComponentView.extend({});

15
src/dom_components/view/ComponentTableView.js

@ -1,11 +1,6 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
events: {},
});
});
module.exports = ComponentView.extend({
events: {},
});

7
src/dom_components/view/ComponentTextNodeView.js

@ -1,6 +1,3 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.View.extend({});
});
module.exports = Backbone.View.extend({});

215
src/dom_components/view/ComponentTextView.js

@ -1,113 +1,110 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
events: {
'dblclick': 'enableEditing',
'change': 'parseRender',
},
initialize: function(o) {
ComponentView.prototype.initialize.apply(this, arguments);
_.bindAll(this,'disableEditing');
this.listenTo(this.model, 'focus active', this.enableEditing);
this.rte = this.config.rte || '';
this.activeRte = null;
this.em = this.config.em;
},
/**
* Enable the component to be editable
* @param {Event} e
* @private
* */
enableEditing: function(e) {
var editable = this.model.get('editable');
if(this.rte && editable) {
try {
this.activeRte = this.rte.attach(this, this.activeRte);
this.rte.focus(this, this.activeRte);
} catch (err) {
console.error(err);
}
}
this.toggleEvents(1);
},
/**
* Disable this component to be editable
* @param {Event}
* @private
* */
disableEditing: function(e) {
var model = this.model;
var editable = model.get('editable');
if(this.rte && editable) {
try {
this.rte.detach(this, this.activeRte);
} catch (err) {
var Backbone = require('backbone');
var ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
events: {
'dblclick': 'enableEditing',
'change': 'parseRender',
},
initialize: function(o) {
ComponentView.prototype.initialize.apply(this, arguments);
_.bindAll(this,'disableEditing');
this.listenTo(this.model, 'focus active', this.enableEditing);
this.rte = this.config.rte || '';
this.activeRte = null;
this.em = this.config.em;
},
/**
* Enable the component to be editable
* @param {Event} e
* @private
* */
enableEditing: function(e) {
var editable = this.model.get('editable');
if(this.rte && editable) {
try {
this.activeRte = this.rte.attach(this, this.activeRte);
this.rte.focus(this, this.activeRte);
} catch (err) {
console.error(err);
}
var el = this.getChildrenContainer();
model.set('content', el.innerHTML);
}
if(!this.rte.customRte && editable) {
this.parseRender();
}
this.toggleEvents(1);
},
/**
* Disable this component to be editable
* @param {Event}
* @private
* */
disableEditing: function(e) {
var model = this.model;
var editable = model.get('editable');
if(this.rte && editable) {
try {
this.rte.detach(this, this.activeRte);
} catch (err) {
console.error(err);
}
this.toggleEvents();
},
/**
* Isolate disable propagation method
* @param {Event}
* @private
* */
disablePropagation: function(e){
e.stopPropagation();
},
/**
* Parse content and re-render it
* @private
*/
parseRender: function() {
var el = this.getChildrenContainer();
var comps = this.model.get('components');
var opts = {silent: true};
// Avoid re-render on reset with silent option
comps.reset(null, opts);
comps.add(el.innerHTML, opts);
this.model.set('content', '');
this.render();
// As the reset was in silent mode I need to notify
// the navigator about the change
comps.trigger('resetNavigator');
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
// The ownerDocument is from the frame
var elDocs = [this.el.ownerDocument, document, this.rte];
$(elDocs).off('mousedown', this.disableEditing);
$(elDocs)[method]('mousedown', this.disableEditing);
// Avoid closing edit mode on component click
this.$el.off('mousedown', this.disablePropagation);
this.$el[method]('mousedown', this.disablePropagation);
},
});
});
model.set('content', el.innerHTML);
}
if(!this.rte.customRte && editable) {
this.parseRender();
}
this.toggleEvents();
},
/**
* Isolate disable propagation method
* @param {Event}
* @private
* */
disablePropagation: function(e){
e.stopPropagation();
},
/**
* Parse content and re-render it
* @private
*/
parseRender: function() {
var el = this.getChildrenContainer();
var comps = this.model.get('components');
var opts = {silent: true};
// Avoid re-render on reset with silent option
comps.reset(null, opts);
comps.add(el.innerHTML, opts);
this.model.set('content', '');
this.render();
// As the reset was in silent mode I need to notify
// the navigator about the change
comps.trigger('resetNavigator');
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
// The ownerDocument is from the frame
var elDocs = [this.el.ownerDocument, document, this.rte];
$(elDocs).off('mousedown', this.disableEditing);
$(elDocs)[method]('mousedown', this.disableEditing);
// Avoid closing edit mode on component click
this.$el.off('mousedown', this.disablePropagation);
this.$el[method]('mousedown', this.disablePropagation);
},
});

211
src/dom_components/view/ComponentVideoView.js

@ -1,121 +1,118 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
var Backbone = require('backbone');
var ComponentView = require('./ComponentImageView');
module.exports = ComponentView.extend({
module.exports = ComponentView.extend({
tagName: 'div',
tagName: 'div',
events: {},
events: {},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:loop change:autoplay change:controls change:color', this.updateVideo);
this.listenTo(this.model, 'change:provider', this.updateProvider);
},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:loop change:autoplay change:controls change:color', this.updateVideo);
this.listenTo(this.model, 'change:provider', this.updateProvider);
},
/**
* Rerender on update of the provider
* @private
*/
updateProvider: function() {
var prov = this.model.get('provider');
this.el.innerHTML = '';
this.el.appendChild(this.renderByProvider(prov));
},
/**
* Rerender on update of the provider
* @private
*/
updateProvider: function() {
var prov = this.model.get('provider');
this.el.innerHTML = '';
this.el.appendChild(this.renderByProvider(prov));
},
/**
* Update the source of the video
* @private
*/
updateSrc: function() {
var prov = this.model.get('provider');
var src = this.model.get('src');
switch (prov) {
case 'yt':
src = this.model.getYoutubeSrc();
break;
case 'vi':
src = this.model.getVimeoSrc();
break;
}
this.videoEl.src = src;
},
/**
* Update the source of the video
* @private
*/
updateSrc: function() {
var prov = this.model.get('provider');
var src = this.model.get('src');
switch (prov) {
case 'yt':
src = this.model.getYoutubeSrc();
break;
case 'vi':
src = this.model.getVimeoSrc();
break;
}
this.videoEl.src = src;
},
/**
* Update video parameters
* @private
*/
updateVideo: function() {
var prov = this.model.get('provider');
var videoEl = this.videoEl;
var md = this.model;
switch (prov) {
case 'yt': case 'vi':
this.model.trigger('change:videoId');
break;
default:
videoEl.loop = md.get('loop');
videoEl.autoplay = md.get('autoplay');
videoEl.controls = md.get('controls');
}
},
/**
* Update video parameters
* @private
*/
updateVideo: function() {
var prov = this.model.get('provider');
var videoEl = this.videoEl;
var md = this.model;
switch (prov) {
case 'yt': case 'vi':
this.model.trigger('change:videoId');
break;
default:
videoEl.loop = md.get('loop');
videoEl.autoplay = md.get('autoplay');
videoEl.controls = md.get('controls');
}
},
renderByProvider: function(prov) {
var videoEl;
switch (prov) {
case 'yt':
videoEl = this.renderYoutube();
break;
case 'vi':
videoEl = this.renderVimeo();
break;
default:
videoEl = this.renderSource();
}
this.videoEl = videoEl;
return videoEl;
},
renderByProvider: function(prov) {
var videoEl;
switch (prov) {
case 'yt':
videoEl = this.renderYoutube();
break;
case 'vi':
videoEl = this.renderVimeo();
break;
default:
videoEl = this.renderSource();
}
this.videoEl = videoEl;
return videoEl;
},
renderSource: function() {
var el = document.createElement('video');
el.src = this.model.get('src');
this.initVideoEl(el);
return el;
},
renderSource: function() {
var el = document.createElement('video');
el.src = this.model.get('src');
this.initVideoEl(el);
return el;
},
renderYoutube: function() {
var el = document.createElement('iframe');
el.src = this.model.getYoutubeSrc();
el.frameBorder = 0;
el.setAttribute('allowfullscreen', true);
this.initVideoEl(el);
return el;
},
renderYoutube: function() {
var el = document.createElement('iframe');
el.src = this.model.getYoutubeSrc();
el.frameBorder = 0;
el.setAttribute('allowfullscreen', true);
this.initVideoEl(el);
return el;
},
renderVimeo: function() {
var el = document.createElement('iframe');
el.src = this.model.getVimeoSrc();
el.frameBorder = 0;
el.setAttribute('allowfullscreen', true);
this.initVideoEl(el);
return el;
},
renderVimeo: function() {
var el = document.createElement('iframe');
el.src = this.model.getVimeoSrc();
el.frameBorder = 0;
el.setAttribute('allowfullscreen', true);
this.initVideoEl(el);
return el;
},
initVideoEl: function(el){
el.className = this.ppfx + 'no-pointer';
el.style.height = '100%';
el.style.width = '100%';
},
initVideoEl: function(el){
el.className = this.ppfx + 'no-pointer';
el.style.height = '100%';
el.style.width = '100%';
},
render: function() {
ComponentView.prototype.render.apply(this, arguments);
this.updateClasses();
var prov = this.model.get('provider');
this.el.appendChild(this.renderByProvider(prov));
return this;
},
render: function() {
ComponentView.prototype.render.apply(this, arguments);
this.updateClasses();
var prov = this.model.get('provider');
this.el.appendChild(this.renderByProvider(prov));
return this;
},
});
});
});

781
src/dom_components/view/ComponentView.js

@ -1,397 +1,394 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var ComponentsView = require('./ComponentsView');
module.exports = Backbone.View.extend({
events: {
'click': 'initResize',
},
className : function(){
return this.getClasses();
},
tagName: function(){
return this.model.get('tagName');
},
initialize: function(opt) {
var model = this.model;
this.opts = opt || {};
this.config = this.opts.config || {};
this.em = this.config.em || '';
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.components = model.get('components');
this.attr = model.get("attributes");
this.classe = this.attr.class || [];
this.listenTo(model, 'destroy remove', this.remove);
this.listenTo(model, 'change:style', this.updateStyle);
this.listenTo(model, 'change:attributes', this.updateAttributes);
this.listenTo(model, 'change:status', this.updateStatus);
this.listenTo(model, 'change:state', this.updateState);
this.listenTo(model, 'change:script', this.render);
this.listenTo(model, 'change', this.handleChange);
this.listenTo(model.get('classes'), 'add remove change', this.updateClasses);
this.$el.data('model', model);
model.view = this;
this.$el.data("collection", this.components);
if(model.get('classes').length)
this.importClasses();
this.init();
},
/**
* Initialize callback
*/
init: function () {},
/**
* Handle any property change
* @private
*/
handleChange: function () {
var em = this.em;
if(em) {
var model = this.model;
em.trigger('component:update', model);
for(var prop in model.changed) {
em.trigger('component:update:' + prop, model);
var Backbone = require('backbone');
var ComponentsView = require('./ComponentsView');
module.exports = Backbone.View.extend({
events: {
'click': 'initResize',
},
className : function(){
return this.getClasses();
},
tagName: function(){
return this.model.get('tagName');
},
initialize: function(opt) {
var model = this.model;
this.opts = opt || {};
this.config = this.opts.config || {};
this.em = this.config.em || '';
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.components = model.get('components');
this.attr = model.get("attributes");
this.classe = this.attr.class || [];
this.listenTo(model, 'destroy remove', this.remove);
this.listenTo(model, 'change:style', this.updateStyle);
this.listenTo(model, 'change:attributes', this.updateAttributes);
this.listenTo(model, 'change:status', this.updateStatus);
this.listenTo(model, 'change:state', this.updateState);
this.listenTo(model, 'change:script', this.render);
this.listenTo(model, 'change', this.handleChange);
this.listenTo(model.get('classes'), 'add remove change', this.updateClasses);
this.$el.data('model', model);
model.view = this;
this.$el.data("collection", this.components);
if(model.get('classes').length)
this.importClasses();
this.init();
},
/**
* Initialize callback
*/
init: function () {},
/**
* Handle any property change
* @private
*/
handleChange: function () {
var em = this.em;
if(em) {
var model = this.model;
em.trigger('component:update', model);
for(var prop in model.changed) {
em.trigger('component:update:' + prop, model);
}
}
},
/**
* Import, if possible, classes inside main container
* @private
* */
importClasses: function(){
var clm = this.config.em.get('SelectorManager');
if(clm){
this.model.get('classes').each(function(m){
clm.add(m.get('name'));
});
}
},
/**
* Fires on state update. If the state is not empty will add a helper class
* @param {Event} e
* @private
* */
updateState: function(e){
var cl = 'hc-state';
var state = this.model.get('state');
if(state){
this.$el.addClass(cl);
}else{
this.$el.removeClass(cl);
}
},
/**
* Update item on status change
* @param {Event} e
* @private
* */
updateStatus: function(e){
var s = this.model.get('status'),
pfx = this.pfx;
switch(s) {
case 'selected':
this.$el.addClass(pfx + 'selected');
break;
case 'moving':
break;
default:
this.$el.removeClass(pfx + 'selected');
}
},
/**
* Get classes from attributes.
* This method is called before initialize
*
* @return {Array}|null
* @private
* */
getClasses: function(){
var attr = this.model.get("attributes"),
classes = attr['class'] || [];
if(classes.length){
return classes.join(" ");
}else
return null;
},
/**
* Update attributes
* @private
* */
updateAttributes: function() {
var model = this.model;
var attributes = {},
attr = model.get("attributes");
for(var key in attr) {
if(attr.hasOwnProperty(key))
attributes[key] = attr[key];
}
// Update src
if(model.get('src'))
attributes.src = model.get('src');
if(model.get('highlightable'))
attributes['data-highlightable'] = 1;
var styleStr = this.getStyleString();
if(styleStr)
attributes.style = styleStr;
this.$el.attr(attributes);
},
/**
* Update style attribute
* @private
* */
updateStyle: function(){
this.$el.attr('style', this.getStyleString());
},
/**
* Return style string
* @return {string}
* @private
* */
getStyleString: function(){
var style = '';
this.style = this.model.get('style');
for(var key in this.style) {
if(this.style.hasOwnProperty(key))
style += key + ':' + this.style[key] + ';';
}
return style;
},
/**
* Update classe attribute
* @private
* */
updateClasses: function(){
var str = '';
this.model.get('classes').each(function(model){
str += model.get('name') + ' ';
});
str = str.trim();
if(str)
this.$el.attr('class', str);
else
this.$el.removeAttr('class');
// Regenerate status class
this.updateStatus();
},
/**
* Reply to event call
* @param object Event that generated the request
* @private
* */
eventCall: function(event){
event.viewResponse = this;
},
/**
* Init component for resizing
*/
initResize: function () {
var em = this.opts.config.em;
var editor = em ? em.get('Editor') : '';
var config = em ? em.get('Config') : '';
var pfx = config.stylePrefix || '';
var attrName = 'data-' + pfx + 'handler';
var resizeClass = pfx + 'resizing';
var model = this.model;
var modelToStyle;
var toggleBodyClass = function(method, e, opts) {
var handlerAttr = e.target.getAttribute(attrName);
var resizeHndClass = pfx + 'resizing-' + handlerAttr;
var classToAdd = resizeClass;// + ' ' +resizeHndClass;
if (opts.docs) {
opts.docs.find('body')[method](classToAdd);
}
};
if(editor && this.model.get('resizable')) {
editor.runCommand('resize', {
el: this.el,
options: {
onStart: function (e, opts) {
toggleBodyClass('addClass', e, opts);
modelToStyle = em.get('StyleManager').getModelToStyle(model);
},
// Update all positioned elements (eg. component toolbar)
onMove: function () {
editor.trigger('change:canvasOffset');
},
onEnd: function (e, opts) {
toggleBodyClass('removeClass', e, opts);
editor.trigger('change:canvasOffset');
},
updateTarget: function(el, rect, store) {
if (!modelToStyle) {
return;
}
var unit = 'px';
var style = _.clone(modelToStyle.get('style'));
var width = rect.w + (store ? 1 : 0);
style.width = width + unit;
style.height = rect.h + unit;
modelToStyle.set('style', style, {avoidStore: 1});
em.trigger('targetStyleUpdated');
// This trick will trigger the Undo Manager. To trigger "change:style"
// on the Model you need to provide a new object and after that
// Undo Manager will trigger only if values are different (this is why
// above I've added + 1 to width if store required)
if(store) {
var style3 = _.clone(style);
style3.width = (width - 1) + unit;
modelToStyle.set('style', style3);
}
}
}
},
/**
* Import, if possible, classes inside main container
* @private
* */
importClasses: function(){
var clm = this.config.em.get('SelectorManager');
if(clm){
this.model.get('classes').each(function(m){
clm.add(m.get('name'));
});
}
},
/**
* Fires on state update. If the state is not empty will add a helper class
* @param {Event} e
* @private
* */
updateState: function(e){
var cl = 'hc-state';
var state = this.model.get('state');
if(state){
this.$el.addClass(cl);
}else{
this.$el.removeClass(cl);
}
},
/**
* Update item on status change
* @param {Event} e
* @private
* */
updateStatus: function(e){
var s = this.model.get('status'),
pfx = this.pfx;
switch(s) {
case 'selected':
this.$el.addClass(pfx + 'selected');
break;
case 'moving':
break;
default:
this.$el.removeClass(pfx + 'selected');
}
},
/**
* Get classes from attributes.
* This method is called before initialize
*
* @return {Array}|null
* @private
* */
getClasses: function(){
var attr = this.model.get("attributes"),
classes = attr['class'] || [];
if(classes.length){
return classes.join(" ");
}else
return null;
},
/**
* Update attributes
* @private
* */
updateAttributes: function() {
var model = this.model;
var attributes = {},
attr = model.get("attributes");
for(var key in attr) {
if(attr.hasOwnProperty(key))
attributes[key] = attr[key];
}
// Update src
if(model.get('src'))
attributes.src = model.get('src');
if(model.get('highlightable'))
attributes['data-highlightable'] = 1;
var styleStr = this.getStyleString();
if(styleStr)
attributes.style = styleStr;
this.$el.attr(attributes);
},
/**
* Update style attribute
* @private
* */
updateStyle: function(){
this.$el.attr('style', this.getStyleString());
},
/**
* Return style string
* @return {string}
* @private
* */
getStyleString: function(){
var style = '';
this.style = this.model.get('style');
for(var key in this.style) {
if(this.style.hasOwnProperty(key))
style += key + ':' + this.style[key] + ';';
}
return style;
},
/**
* Update classe attribute
* @private
* */
updateClasses: function(){
var str = '';
});
}
},
/**
* Prevent default helper
* @param {Event} e
* @private
*/
prevDef: function (e) {
e.preventDefault();
},
/**
* Render component's script
* @private
*/
updateScript: function () {
var em = this.em;
if(em) {
var canvas = em.get('Canvas');
canvas.getCanvasView().updateScript(this);
}
},
/**
* Return children container
* Differently from a simple component where children container is the
* component itself
* <my-comp>
* <!--
* <child></child> ...
* -->
* </my-comp>
* You could have the children container more deeper
* <my-comp>
* <div></div>
* <div></div>
* <div>
* <div>
* <!--
* <child></child> ...
* -->
* </div>
* </div>
* </my-comp>
* @return HTMLElement
* @private
*/
getChildrenContainer: function() {
var container = this.el;
if (typeof this.getChildrenSelector == 'function') {
container = this.el.querySelector(this.getChildrenSelector());
} else if (typeof this.getTemplate == 'function') {
// Need to find deepest first child
}
return container;
},
/**
* Render children components
* @private
*/
renderChildren: function() {
var view = new ComponentsView({
collection: this.model.get('components'),
config: this.config,
defaultTypes: this.opts.defaultTypes,
componentTypes: this.opts.componentTypes,
});
this.model.get('classes').each(function(model){
str += model.get('name') + ' ';
});
str = str.trim();
if(str)
this.$el.attr('class', str);
else
this.$el.removeAttr('class');
// Regenerate status class
this.updateStatus();
},
/**
* Reply to event call
* @param object Event that generated the request
* @private
* */
eventCall: function(event){
event.viewResponse = this;
},
/**
* Init component for resizing
*/
initResize: function () {
var em = this.opts.config.em;
var editor = em ? em.get('Editor') : '';
var config = em ? em.get('Config') : '';
var pfx = config.stylePrefix || '';
var attrName = 'data-' + pfx + 'handler';
var resizeClass = pfx + 'resizing';
var model = this.model;
var modelToStyle;
var toggleBodyClass = function(method, e, opts) {
var handlerAttr = e.target.getAttribute(attrName);
var resizeHndClass = pfx + 'resizing-' + handlerAttr;
var classToAdd = resizeClass;// + ' ' +resizeHndClass;
if (opts.docs) {
opts.docs.find('body')[method](classToAdd);
var container = this.getChildrenContainer();
var childNodes = view.render($(container)).el.childNodes;
childNodes = Array.prototype.slice.call(childNodes);
for (var i = 0, len = childNodes.length ; i < len; i++) {
container.appendChild(childNodes.shift());
}
// If the children container is not the same as the component
// (so likely fetched with getChildrenSelector()) is necessary
// to disable pointer-events for all nested components as they
// might prevent the component to be selected
if (container !== this.el) {
var disableNode = function(el) {
var children = Array.prototype.slice.call(el.children);
children.forEach(function (el) {
el.style['pointer-events'] = 'none';
if (container !== el) {
disableNode(el);
}
};
if(editor && this.model.get('resizable')) {
editor.runCommand('resize', {
el: this.el,
options: {
onStart: function (e, opts) {
toggleBodyClass('addClass', e, opts);
modelToStyle = em.get('StyleManager').getModelToStyle(model);
},
// Update all positioned elements (eg. component toolbar)
onMove: function () {
editor.trigger('change:canvasOffset');
},
onEnd: function (e, opts) {
toggleBodyClass('removeClass', e, opts);
editor.trigger('change:canvasOffset');
},
updateTarget: function(el, rect, store) {
if (!modelToStyle) {
return;
}
var unit = 'px';
var style = _.clone(modelToStyle.get('style'));
var width = rect.w + (store ? 1 : 0);
style.width = width + unit;
style.height = rect.h + unit;
modelToStyle.set('style', style, {avoidStore: 1});
em.trigger('targetStyleUpdated');
// This trick will trigger the Undo Manager. To trigger "change:style"
// on the Model you need to provide a new object and after that
// Undo Manager will trigger only if values are different (this is why
// above I've added + 1 to width if store required)
if(store) {
var style3 = _.clone(style);
style3.width = (width - 1) + unit;
modelToStyle.set('style', style3);
}
}
}
});
}
},
/**
* Prevent default helper
* @param {Event} e
* @private
*/
prevDef: function (e) {
e.preventDefault();
},
/**
* Render component's script
* @private
*/
updateScript: function () {
var em = this.em;
if(em) {
var canvas = em.get('Canvas');
canvas.getCanvasView().updateScript(this);
}
},
/**
* Return children container
* Differently from a simple component where children container is the
* component itself
* <my-comp>
* <!--
* <child></child> ...
* -->
* </my-comp>
* You could have the children container more deeper
* <my-comp>
* <div></div>
* <div></div>
* <div>
* <div>
* <!--
* <child></child> ...
* -->
* </div>
* </div>
* </my-comp>
* @return HTMLElement
* @private
*/
getChildrenContainer: function() {
var container = this.el;
if (typeof this.getChildrenSelector == 'function') {
container = this.el.querySelector(this.getChildrenSelector());
} else if (typeof this.getTemplate == 'function') {
// Need to find deepest first child
}
return container;
},
/**
* Render children components
* @private
*/
renderChildren: function() {
var view = new ComponentsView({
collection: this.model.get('components'),
config: this.config,
defaultTypes: this.opts.defaultTypes,
componentTypes: this.opts.componentTypes,
});
var container = this.getChildrenContainer();
var childNodes = view.render($(container)).el.childNodes;
childNodes = Array.prototype.slice.call(childNodes);
for (var i = 0, len = childNodes.length ; i < len; i++) {
container.appendChild(childNodes.shift());
}
// If the children container is not the same as the component
// (so likely fetched with getChildrenSelector()) is necessary
// to disable pointer-events for all nested components as they
// might prevent the component to be selected
if (container !== this.el) {
var disableNode = function(el) {
var children = Array.prototype.slice.call(el.children);
children.forEach(function (el) {
el.style['pointer-events'] = 'none';
if (container !== el) {
disableNode(el);
}
});
};
disableNode(this.el);
}
},
renderAttributes: function() {
this.updateAttributes();
this.updateClasses();
},
render: function() {
this.renderAttributes();
var model = this.model;
var container = this.getChildrenContainer();
container.innerHTML = model.get('content');
this.renderChildren();
// Render script
if(model.get('script')) {
this.updateScript();
}
return this;
},
});
});
};
disableNode(this.el);
}
},
renderAttributes: function() {
this.updateAttributes();
this.updateClasses();
},
render: function() {
this.renderAttributes();
var model = this.model;
var container = this.getChildrenContainer();
container.innerHTML = model.get('content');
this.renderChildren();
// Render script
if(model.get('script')) {
this.updateScript();
}
return this;
},
});

189
src/dom_components/view/ComponentsView.js

@ -1,115 +1,112 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.View.extend({
module.exports = Backbone.View.extend({
initialize: function(o) {
this.opts = o || {};
this.config = o.config || {};
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
initialize: function(o) {
this.opts = o || {};
this.config = o.config || {};
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} Model
*
* @return void
* @private
* */
addTo: function(model) {
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
/**
* Add to collection
* @param {Object} Model
*
* @return void
* @private
* */
addTo: function(model) {
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
var em = this.config.em;
if(em) {
// OLD
em.trigger('add:component', model);
em.trigger('component:add', model);
}
},
var em = this.config.em;
if(em) {
// OLD
em.trigger('add:component', model);
em.trigger('component:add', model);
}
},
/**
* Add new object to collection
* @param {Object} Model
* @param {Object} Fragment collection
* @param {Integer} Index of append
*
* @return {Object} Object rendered
* @private
* */
addToCollection: function(model, fragmentEl, index){
if(!this.compView)
this.compView = require('./ComponentView');
var fragment = fragmentEl || null,
viewObject = this.compView;
//console.log('Add to collection', model, 'Index',i);
/**
* Add new object to collection
* @param {Object} Model
* @param {Object} Fragment collection
* @param {Integer} Index of append
*
* @return {Object} Object rendered
* @private
* */
addToCollection: function(model, fragmentEl, index){
if(!this.compView)
this.compView = require('./ComponentView');
var fragment = fragmentEl || null,
viewObject = this.compView;
//console.log('Add to collection', model, 'Index',i);
var dt = this.opts.defaultTypes;
var ct = this.opts.componentTypes;
var dt = this.opts.defaultTypes;
var ct = this.opts.componentTypes;
var type = model.get('type');
var type = model.get('type');
for (var it = 0; it < dt.length; it++) {
var dtId = dt[it].id;
if(dtId == type) {
viewObject = dt[it].view;
break;
}
for (var it = 0; it < dt.length; it++) {
var dtId = dt[it].id;
if(dtId == type) {
viewObject = dt[it].view;
break;
}
//viewObject = dt[type] ? dt[type].view : dt.default.view;
}
//viewObject = dt[type] ? dt[type].view : dt.default.view;
var view = new viewObject({
model: model,
config: this.config,
defaultTypes: dt,
componentTypes: ct,
});
var rendered = view.render().el;
if(view.model.get('type') == 'textnode')
rendered = document.createTextNode(view.model.get('content'));
var view = new viewObject({
model: model,
config: this.config,
defaultTypes: dt,
componentTypes: ct,
});
var rendered = view.render().el;
if(view.model.get('type') == 'textnode')
rendered = document.createTextNode(view.model.get('content'));
if(fragment){
fragment.appendChild(rendered);
}else{
var p = this.$parent;
var pc = p.children;
if(typeof index != 'undefined'){
var method = 'before';
// If the added model is the last of collection
// need to change the logic of append
if(pc && p.children().length == index){
index--;
method = 'after';
}
// In case the added is new in the collection index will be -1
if(index < 0) {
p.append(rendered);
}else {
if(pc) {
p.children().eq(index)[method](rendered);
}
}
}else{
if(fragment){
fragment.appendChild(rendered);
}else{
var p = this.$parent;
var pc = p.children;
if(typeof index != 'undefined'){
var method = 'before';
// If the added model is the last of collection
// need to change the logic of append
if(pc && p.children().length == index){
index--;
method = 'after';
}
// In case the added is new in the collection index will be -1
if(index < 0) {
p.append(rendered);
}else {
if(pc) {
p.children().eq(index)[method](rendered);
}
}
}else{
p.append(rendered);
}
}
return rendered;
},
return rendered;
},
render: function($p) {
var fragment = document.createDocumentFragment();
this.$parent = $p || this.$el;
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
render: function($p) {
var fragment = document.createDocumentFragment();
this.$parent = $p || this.$el;
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
return this;
}
return this;
}
});
});

56
src/dom_components/view/ToolbarButtonView.js

@ -1,38 +1,34 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone');
module.exports = Backbone.View.extend({
module.exports = Backbone.View.extend({
events: {
'mousedown': 'handleClick',
},
events: {
'mousedown': 'handleClick',
},
attributes: function () {
return this.model.get('attributes');
},
attributes: function () {
return this.model.get('attributes');
},
initialize: function(opts) {
this.editor = opts.config.editor;
},
initialize: function(opts) {
this.editor = opts.config.editor;
},
handleClick: function() {
var command = this.model.get('command');
handleClick: function() {
var command = this.model.get('command');
if (typeof command === 'function') {
command(this.editor);
}
if (typeof command === 'function') {
command(this.editor);
}
if (typeof command === 'string') {
this.editor.runCommand(command);
}
},
if (typeof command === 'string') {
this.editor.runCommand(command);
}
},
render: function () {
var config = this.editor.getConfig();
this.el.className += ' ' + config.stylePrefix + 'toolbar-item';
return this;
},
render: function () {
var config = this.editor.getConfig();
this.el.className += ' ' + config.stylePrefix + 'toolbar-item';
return this;
},
});
});
});

145
src/domain_abstract/ui/Input.js

@ -1,85 +1,82 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var inputTemplate = require('text!./templates/input.html');
var Backbone = require('backbone');
var inputTemplate = require('text!./templates/input.html');
module.exports = Backbone.View.extend({
module.exports = Backbone.View.extend({
events: {
'change': 'handleChange',
},
events: {
'change': 'handleChange',
},
template: _.template(inputTemplate),
template: _.template(inputTemplate),
initialize: function(opts) {
var opt = opts || {};
var ppfx = opt.ppfx || '';
this.target = opt.target || {};
this.inputClass = ppfx + 'field';
this.inputHolderClass = ppfx + 'input-holder';
this.ppfx = ppfx;
this.listenTo(this.model, 'change:value', this.handleModelChange);
},
initialize: function(opts) {
var opt = opts || {};
var ppfx = opt.ppfx || '';
this.target = opt.target || {};
this.inputClass = ppfx + 'field';
this.inputHolderClass = ppfx + 'input-holder';
this.ppfx = ppfx;
this.listenTo(this.model, 'change:value', this.handleModelChange);
},
/**
* Handled when the view is changed
*/
handleChange: function (e) {
e.stopPropagation();
this.setValue(this.getInputEl().value);
},
/**
* Handled when the view is changed
*/
handleChange: function (e) {
e.stopPropagation();
this.setValue(this.getInputEl().value);
},
/**
* Set value to the model
* @param {string} value
* @param {Object} opts
*/
setValue: function(value, opts) {
var opt = opts || {};
var model = this.model;
model.set({
value: value || model.get('defaults')
}, opt);
/**
* Set value to the model
* @param {string} value
* @param {Object} opts
*/
setValue: function(value, opts) {
var opt = opts || {};
var model = this.model;
model.set({
value: value || model.get('defaults')
}, opt);
// Generally I get silent when I need to reflect data to view without
// reupdating the target
if(opt.silent) {
this.handleModelChange();
}
},
// Generally I get silent when I need to reflect data to view without
// reupdating the target
if(opt.silent) {
this.handleModelChange();
}
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
this.getInputEl().value = this.model.get('value');
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
this.getInputEl().value = this.model.get('value');
},
/**
* Get the input element
* @return {HTMLElement}
*/
getInputEl: function() {
if(!this.inputEl) {
this.inputEl = $('<input>', {
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
}
return this.inputEl.get(0);
},
/**
* Get the input element
* @return {HTMLElement}
*/
getInputEl: function() {
if(!this.inputEl) {
this.inputEl = $('<input>', {
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
}
return this.inputEl.get(0);
},
render: function() {
var el = this.$el;
el.addClass(this.inputClass);
el.html(this.template({
holderClass: this.inputHolderClass,
ppfx: this.ppfx
}));
el.find('.'+ this.inputHolderClass).html(this.getInputEl());
return this;
}
render: function() {
var el = this.$el;
el.addClass(this.inputClass);
el.html(this.template({
holderClass: this.inputHolderClass,
ppfx: this.ppfx
}));
el.find('.'+ this.inputHolderClass).html(this.getInputEl());
return this;
}
});
});
});

133
src/domain_abstract/ui/InputColor.js

@ -1,78 +1,75 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var Input = require('./Input');
var Spectrum = require('Spectrum');
var inputTemplate = require('text!./templates/inputColor.html');
var Backbone = require('backbone');
var Input = require('./Input');
var Spectrum = require('Spectrum');
var inputTemplate = require('text!./templates/inputColor.html');
module.exports = Input.extend({
module.exports = Input.extend({
template: _.template(inputTemplate),
template: _.template(inputTemplate),
initialize: function(opts) {
Input.prototype.initialize.apply(this, arguments);
var ppfx = this.ppfx;
this.colorCls = ppfx + 'field-color-picker';
this.inputClass = ppfx + 'field ' + ppfx + 'field-color';
this.colorHolderClass = ppfx + 'field-colorp-c';
initialize: function(opts) {
Input.prototype.initialize.apply(this, arguments);
var ppfx = this.ppfx;
this.colorCls = ppfx + 'field-color-picker';
this.inputClass = ppfx + 'field ' + ppfx + 'field-color';
this.colorHolderClass = ppfx + 'field-colorp-c';
this.listenTo(this.model, 'change:value', this.handleModelChange);
},
this.listenTo(this.model, 'change:value', this.handleModelChange);
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
Input.prototype.handleModelChange.apply(this, arguments);
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
Input.prototype.handleModelChange.apply(this, arguments);
var value = this.model.get('value');
var colorEl = this.getColorEl();
var value = this.model.get('value');
var colorEl = this.getColorEl();
// If no color selected I will set white for the picker
value = value === 'none' ? '#fff' : value;
colorEl.spectrum('set', value);
colorEl.get(0).style.backgroundColor = value;
},
// If no color selected I will set white for the picker
value = value === 'none' ? '#fff' : value;
colorEl.spectrum('set', value);
colorEl.get(0).style.backgroundColor = value;
},
/**
* Get the color input element
* @return {HTMLElement}
*/
getColorEl: function() {
if(!this.colorEl) {
var model = this.model;
var colorEl = $('<div>', {class: this.colorCls});
var cpStyle = colorEl.get(0).style;
var elToAppend = this.target && this.target.config ? this.target.config.el : '';
colorEl.spectrum({
appendTo: elToAppend || 'body',
maxSelectionSize: 8,
showPalette: true,
showAlpha: true,
chooseText: 'Ok',
cancelText: '⨯',
palette: [],
move: function(color) {
var c = color.getAlpha() == 1 ? color.toHexString() : color.toRgbString();
cpStyle.backgroundColor = c;
},
change: function(color) {
var c = color.getAlpha() == 1 ? color.toHexString() : color.toRgbString();
c = c.replace(/ /g,'');
cpStyle.backgroundColor = c;
model.set('value', c);
}
});
this.colorEl = colorEl;
}
return this.colorEl;
},
/**
* Get the color input element
* @return {HTMLElement}
*/
getColorEl: function() {
if(!this.colorEl) {
var model = this.model;
var colorEl = $('<div>', {class: this.colorCls});
var cpStyle = colorEl.get(0).style;
var elToAppend = this.target && this.target.config ? this.target.config.el : '';
colorEl.spectrum({
appendTo: elToAppend || 'body',
maxSelectionSize: 8,
showPalette: true,
showAlpha: true,
chooseText: 'Ok',
cancelText: '⨯',
palette: [],
move: function(color) {
var c = color.getAlpha() == 1 ? color.toHexString() : color.toRgbString();
cpStyle.backgroundColor = c;
},
change: function(color) {
var c = color.getAlpha() == 1 ? color.toHexString() : color.toRgbString();
c = c.replace(/ /g,'');
cpStyle.backgroundColor = c;
model.set('value', c);
}
});
this.colorEl = colorEl;
}
return this.colorEl;
},
render: function() {
Input.prototype.render.apply(this, arguments);
this.$el.find('.' + this.colorHolderClass).html(this.getColorEl());
return this;
}
render: function() {
Input.prototype.render.apply(this, arguments);
this.$el.find('.' + this.colorHolderClass).html(this.getColorEl());
return this;
}
});
});
});

481
src/domain_abstract/ui/InputNumber.js

@ -1,250 +1,247 @@
define(function(require, exports, module){
'use strict';
var Backbone = require('backbone');
var inputTemplate = require('text!./templates/inputNumber.html');
module.exports = Backbone.View.extend({
events: {},
template: _.template(inputTemplate),
initialize: function(opts) {
_.bindAll(this, 'moveIncrement', 'upIncrement');
var opt = opts || {};
var ppfx = opt.ppfx || '';
var contClass = opt.contClass || (ppfx + 'field');
this.ppfx = ppfx;
this.docEl = $(document);
this.inputCls = ppfx + 'input-number';
this.unitCls = ppfx + 'input-unit';
this.contClass = contClass;
this.events['click .' + ppfx + 'field-arrow-u'] = 'upArrowClick';
this.events['click .' + ppfx + 'field-arrow-d'] = 'downArrowClick';
this.events['mousedown .' + ppfx + 'field-arrows'] = 'downIncrement';
this.events['change .' + this.inputCls] = 'handleChange';
this.events['change .' + this.unitCls] = 'handleUnitChange';
this.listenTo(this.model, 'change:unit change:value', this.handleModelChange);
this.delegateEvents();
},
/**
* Set value to the model
* @param {string} value
* @param {Object} opts
*/
setValue: function(value, opts) {
var opt = opts || {};
var valid = this.validateInputValue(value, {deepCheck: 1});
var validObj = {value: valid.value};
// If found some unit value
if(valid.unit || valid.force) {
validObj.unit = valid.unit;
}
var Backbone = require('backbone');
var inputTemplate = require('text!./templates/inputNumber.html');
module.exports = Backbone.View.extend({
events: {},
template: _.template(inputTemplate),
initialize: function(opts) {
_.bindAll(this, 'moveIncrement', 'upIncrement');
var opt = opts || {};
var ppfx = opt.ppfx || '';
var contClass = opt.contClass || (ppfx + 'field');
this.ppfx = ppfx;
this.docEl = $(document);
this.inputCls = ppfx + 'input-number';
this.unitCls = ppfx + 'input-unit';
this.contClass = contClass;
this.events['click .' + ppfx + 'field-arrow-u'] = 'upArrowClick';
this.events['click .' + ppfx + 'field-arrow-d'] = 'downArrowClick';
this.events['mousedown .' + ppfx + 'field-arrows'] = 'downIncrement';
this.events['change .' + this.inputCls] = 'handleChange';
this.events['change .' + this.unitCls] = 'handleUnitChange';
this.listenTo(this.model, 'change:unit change:value', this.handleModelChange);
this.delegateEvents();
},
/**
* Set value to the model
* @param {string} value
* @param {Object} opts
*/
setValue: function(value, opts) {
var opt = opts || {};
var valid = this.validateInputValue(value, {deepCheck: 1});
var validObj = {value: valid.value};
// If found some unit value
if(valid.unit || valid.force) {
validObj.unit = valid.unit;
}
this.model.set(validObj, opt);
this.model.set(validObj, opt);
// Generally I get silent when I need to reflect data to view without
// reupdating the target
if(opt.silent) {
this.handleModelChange();
}
},
/**
* Handled when the view is changed
*/
handleChange: function (e) {
e.stopPropagation();
this.setValue(this.getInputEl().value);
},
/**
* Handled when the view is changed
*/
handleUnitChange: function (e) {
e.stopPropagation();
var value = this.getUnitEl().value;
this.model.set('unit', value);
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
var m = this.model;
this.getInputEl().value = m.get('value');
var unit = this.getUnitEl();
if (unit) {
unit.value = m.get('unit');
}
},
/**
* Get the input element
* @return {HTMLElement}
*/
getInputEl: function() {
if(!this.inputEl) {
this.inputEl = $('<input>', {
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
}
return this.inputEl.get(0);
},
/**
* Get the unit element
* @return {HTMLElement}
*/
getUnitEl: function() {
if(!this.unitEl) {
var model = this.model;
var units = model.get('units') || [];
if(units.length){
var unitStr = '<select class="' + this.unitCls + '">';
_.each(units, function(unit){
var selected = unit == model.get('unit') ? 'selected': '';
unitStr += '<option ' + selected + ' >' + unit + '</option>';
});
unitStr += '</select>';
this.unitEl = $(unitStr);
}
}
return this.unitEl && this.unitEl.get(0);
},
/**
* Invoked when the up arrow is clicked
* */
upArrowClick: function() {
var value = this.model.get('value');
value = isNaN(value) ? 1 : parseInt(value, 10) + 1;
var valid = this.validateInputValue(value);
this.model.set('value', valid.value);
},
/**
* Invoked when the down arrow is clicked
* */
downArrowClick: function(){
var value = this.model.get('value');
value = isNaN(value) ? 0 : parseInt(value, 10) - 1;
var valid = this.validateInputValue(value);
this.model.set('value', valid.value);
},
/**
* Change easily integer input value with click&drag method
* @param Event
*
* @return void
* */
downIncrement: function(e) {
e.preventDefault();
this.moved = 0;
var value = this.model.get('value');
value = isNaN(value) ? 0 : parseInt(value, 10);
var current = {y: e.pageY, val: value };
this.docEl.mouseup(current, this.upIncrement);
this.docEl.mousemove(current, this.moveIncrement);
},
/** While the increment is clicked, moving the mouse will update input value
* @param Object
*
* @return bool
* */
moveIncrement: function (ev) {
this.moved = 1;
var pos = parseInt(ev.data.val - ev.pageY + ev.data.y, 10);
this.prValue = this.validateInputValue(pos).value;//Math.max(this.min, Math.min(this.max, pos) );
this.model.set('value', this.prValue, {avoidStore: 1});
return false;
},
/**
* Stop moveIncrement method
* @param Object
*
* @return void
* */
upIncrement: function (e) {
this.docEl.off('mouseup', this.upIncrement);
this.docEl.off('mousemove', this.moveIncrement);
if(this.prValue && this.moved) {
var value = this.prValue - 1;
this.model.set('value', value, {avoidStore: 1})
.set('value', value + 1);
}
},
/**
* Validate input value
* @param {String} value Raw value
* @param {Object} opts Options
* @return {Object} Validated string
*/
validateInputValue: function(value, opts) {
var force = 0;
var opt = opts || {};
// Generally I get silent when I need to reflect data to view without
// reupdating the target
if(opt.silent) {
this.handleModelChange();
}
},
/**
* Handled when the view is changed
*/
handleChange: function (e) {
e.stopPropagation();
this.setValue(this.getInputEl().value);
},
/**
* Handled when the view is changed
*/
handleUnitChange: function (e) {
e.stopPropagation();
var value = this.getUnitEl().value;
this.model.set('unit', value);
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
var m = this.model;
this.getInputEl().value = m.get('value');
var unit = this.getUnitEl();
if (unit) {
unit.value = m.get('unit');
}
},
/**
* Get the input element
* @return {HTMLElement}
*/
getInputEl: function() {
if(!this.inputEl) {
this.inputEl = $('<input>', {
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
}
return this.inputEl.get(0);
},
/**
* Get the unit element
* @return {HTMLElement}
*/
getUnitEl: function() {
if(!this.unitEl) {
var model = this.model;
var val = value || model.get('defaults');
var units = model.get('units') || [];
var unit = model.get('unit') || (units.length && units[0]) || '';
var max = model.get('max');
var min = model.get('min');
if(opt.deepCheck) {
var fixed = model.get('fixedValues') || [];
if (val) {
// If the value is one of the fixed values I leave it as it is
var regFixed = new RegExp('^' + fixed.join('|'), 'g');
if (fixed.length && regFixed.test(val)) {
val = val.match(regFixed)[0];
unit = '';
force = 1;
} else {
var valCopy = val + '';
val += ''; // Make it suitable for replace
val = parseFloat(val.replace(',', '.'));
val = !isNaN(val) ? val : model.get('defaults');
var uN = valCopy.replace(val, '');
// Check if exists as unit
if(_.indexOf(units, uN) >= 0)
unit = uN;
}
if(units.length){
var unitStr = '<select class="' + this.unitCls + '">';
_.each(units, function(unit){
var selected = unit == model.get('unit') ? 'selected': '';
unitStr += '<option ' + selected + ' >' + unit + '</option>';
});
unitStr += '</select>';
this.unitEl = $(unitStr);
}
}
return this.unitEl && this.unitEl.get(0);
},
/**
* Invoked when the up arrow is clicked
* */
upArrowClick: function() {
var value = this.model.get('value');
value = isNaN(value) ? 1 : parseInt(value, 10) + 1;
var valid = this.validateInputValue(value);
this.model.set('value', valid.value);
},
/**
* Invoked when the down arrow is clicked
* */
downArrowClick: function(){
var value = this.model.get('value');
value = isNaN(value) ? 0 : parseInt(value, 10) - 1;
var valid = this.validateInputValue(value);
this.model.set('value', valid.value);
},
/**
* Change easily integer input value with click&drag method
* @param Event
*
* @return void
* */
downIncrement: function(e) {
e.preventDefault();
this.moved = 0;
var value = this.model.get('value');
value = isNaN(value) ? 0 : parseInt(value, 10);
var current = {y: e.pageY, val: value };
this.docEl.mouseup(current, this.upIncrement);
this.docEl.mousemove(current, this.moveIncrement);
},
/** While the increment is clicked, moving the mouse will update input value
* @param Object
*
* @return bool
* */
moveIncrement: function (ev) {
this.moved = 1;
var pos = parseInt(ev.data.val - ev.pageY + ev.data.y, 10);
this.prValue = this.validateInputValue(pos).value;//Math.max(this.min, Math.min(this.max, pos) );
this.model.set('value', this.prValue, {avoidStore: 1});
return false;
},
/**
* Stop moveIncrement method
* @param Object
*
* @return void
* */
upIncrement: function (e) {
this.docEl.off('mouseup', this.upIncrement);
this.docEl.off('mousemove', this.moveIncrement);
if(this.prValue && this.moved) {
var value = this.prValue - 1;
this.model.set('value', value, {avoidStore: 1})
.set('value', value + 1);
}
},
/**
* Validate input value
* @param {String} value Raw value
* @param {Object} opts Options
* @return {Object} Validated string
*/
validateInputValue: function(value, opts) {
var force = 0;
var opt = opts || {};
var model = this.model;
var val = value || model.get('defaults');
var units = model.get('units') || [];
var unit = model.get('unit') || (units.length && units[0]) || '';
var max = model.get('max');
var min = model.get('min');
if(opt.deepCheck) {
var fixed = model.get('fixedValues') || [];
if (val) {
// If the value is one of the fixed values I leave it as it is
var regFixed = new RegExp('^' + fixed.join('|'), 'g');
if (fixed.length && regFixed.test(val)) {
val = val.match(regFixed)[0];
unit = '';
force = 1;
} else {
var valCopy = val + '';
val += ''; // Make it suitable for replace
val = parseFloat(val.replace(',', '.'));
val = !isNaN(val) ? val : model.get('defaults');
var uN = valCopy.replace(val, '');
// Check if exists as unit
if(_.indexOf(units, uN) >= 0)
unit = uN;
}
}
if(typeof max !== 'undefined' && max !== '')
val = val > max ? max : val;
if(typeof min !== 'undefined' && min !== '')
val = val < min ? min : val;
return {
force: force,
value: val,
unit: unit
};
},
render: function() {
var ppfx = this.ppfx;
this.$el.html(this.template({ppfx: ppfx}));
this.$el.find('.'+ ppfx +'input-holder').html(this.getInputEl());
this.$el.find('.' + ppfx + 'field-units').html(this.getUnitEl());
this.$el.addClass(this.contClass);
return this;
}
});
});
if(typeof max !== 'undefined' && max !== '')
val = val > max ? max : val;
if(typeof min !== 'undefined' && min !== '')
val = val < min ? min : val;
return {
force: force,
value: val,
unit: unit
};
},
render: function() {
var ppfx = this.ppfx;
this.$el.html(this.template({ppfx: ppfx}));
this.$el.find('.'+ ppfx +'input-holder').html(this.getInputEl());
this.$el.find('.' + ppfx + 'field-units').html(this.getUnitEl());
this.$el.addClass(this.contClass);
return this;
}
});

107
src/domain_abstract/view/DomainViews.js

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

555
src/editor/config/config.js

@ -1,295 +1,292 @@
define(function () {
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}';
return {
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}';
module.exports = {
// Style prefix
stylePrefix: 'gjs-',
// Style prefix
stylePrefix: 'gjs-',
//TEMP
components: '',
//TEMP
components: '',
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
copyPaste: true,
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
copyPaste: true,
// Show an alert before unload the page with unsaved changes
noticeOnUnload: true,
// Show an alert before unload the page with unsaved changes
noticeOnUnload: true,
// Enable/Disable undo manager
undoManager: true,
// Enable/Disable undo manager
undoManager: true,
// Show paddings and margins
showOffsets: false,
// Show paddings and margins
showOffsets: false,
// Show paddings and margins on selected component
showOffsetsSelected: false,
// Show paddings and margins on selected component
showOffsetsSelected: false,
// Clear the canvas when editor.render() is called
clearOnRender: false,
// Clear the canvas when editor.render() is called
clearOnRender: false,
// Return JS of components inside HTML from 'editor.getHtml()'
jsInHtml: true,
// Return JS of components inside HTML from 'editor.getHtml()'
jsInHtml: true,
// On creation of a new Component (via object), if the 'style' attribute is not
// empty, all those roles will be moved in its new class
forceClass: true,
// On creation of a new Component (via object), if the 'style' attribute is not
// empty, all those roles will be moved in its new class
forceClass: true,
// Height for the editor container
height: '900px',
// Height for the editor container
height: '900px',
// Width for the editor container
width: '100%',
// Width for the editor container
width: '100%',
// CSS that could only be seen (for instance, inside the code viewer)
protectedCss: '',
// CSS that could only be seen (for instance, inside the code viewer)
protectedCss: '',
// CSS for the iframe which containing the canvas, useful if you need to custom something inside
// (eg. the style of the selected component)
canvasCss: '',
// Default command
defaultCommand: 'select-comp',
// Show a toolbar when the component is selected
showToolbar: 1,
// Allow script tag importing
allowScripts: 0,
// If true render a select of available devices
showDevices: 1,
// When enabled, on device change media rules won't be created
devicePreviewMode: 0,
// This option makes available custom component types also for loaded
// elements inside canvas
loadCompsOnRender: 1,
// Dom element
el: '',
//Configurations for Asset Manager
assetManager: {},
//Configurations for Canvas
canvas: {},
//Configurations for Layers
layers: {},
//Configurations for Storage Manager
storageManager: {},
//Configurations for Rich Text Editor
rte: {},
//Configurations for DomComponents
domComponents: {},
//Configurations for Modal Dialog
modal: {},
//Configurations for Code Manager
codeManager: {},
//Configurations for Panels
panels: {},
//Configurations for Commands
commands: {},
//Configurations for Css Composer
cssComposer: {},
//Configurations for Selector Manager
selectorManager: {},
//Configurations for Device Manager
deviceManager: {
'devices': [{
name: 'Desktop',
width: '',
},{
name: 'Tablet',
width: '992px',
},{
name: 'Mobile landscape',
width: '768px',
},{
name: 'Mobile portrait',
width: '480px',
// CSS for the iframe which containing the canvas, useful if you need to custom something inside
// (eg. the style of the selected component)
canvasCss: '',
// Default command
defaultCommand: 'select-comp',
// Show a toolbar when the component is selected
showToolbar: 1,
// Allow script tag importing
allowScripts: 0,
// If true render a select of available devices
showDevices: 1,
// When enabled, on device change media rules won't be created
devicePreviewMode: 0,
// This option makes available custom component types also for loaded
// elements inside canvas
loadCompsOnRender: 1,
// Dom element
el: '',
//Configurations for Asset Manager
assetManager: {},
//Configurations for Canvas
canvas: {},
//Configurations for Layers
layers: {},
//Configurations for Storage Manager
storageManager: {},
//Configurations for Rich Text Editor
rte: {},
//Configurations for DomComponents
domComponents: {},
//Configurations for Modal Dialog
modal: {},
//Configurations for Code Manager
codeManager: {},
//Configurations for Panels
panels: {},
//Configurations for Commands
commands: {},
//Configurations for Css Composer
cssComposer: {},
//Configurations for Selector Manager
selectorManager: {},
//Configurations for Device Manager
deviceManager: {
'devices': [{
name: 'Desktop',
width: '',
},{
name: 'Tablet',
width: '992px',
},{
name: 'Mobile landscape',
width: '768px',
},{
name: 'Mobile portrait',
width: '480px',
}],
},
//Configurations for Style Manager
styleManager: {
sectors: [{
name: 'General',
open: false,
buildProps: ['float', 'display', 'position', 'top', 'right', 'left', 'bottom'],
},{
name: 'Dimension',
open: false,
buildProps: ['width', 'height', 'max-width', 'min-height', 'margin', 'padding'],
},{
name: 'Typography',
open: false,
buildProps: ['font-family', 'font-size', 'font-weight', 'letter-spacing', 'color', 'line-height', 'text-align', 'text-shadow'],
properties: [{
property: 'text-align',
list : [
{value: 'left', className: 'fa fa-align-left'},
{value: 'center', className: 'fa fa-align-center' },
{value: 'right', className: 'fa fa-align-right'},
{value: 'justify', className: 'fa fa-align-justify'}
],
}]
},{
name: 'Decorations',
open: false,
buildProps: ['border-radius-c', 'background-color', 'border-radius', 'border', 'box-shadow', 'background'],
},{
name: 'Extra',
open: false,
buildProps: ['transition', 'perspective', 'transform'],
}],
},
//Configurations for Style Manager
styleManager: {
sectors: [{
name: 'General',
open: false,
buildProps: ['float', 'display', 'position', 'top', 'right', 'left', 'bottom'],
},{
name: 'Dimension',
open: false,
buildProps: ['width', 'height', 'max-width', 'min-height', 'margin', 'padding'],
},{
name: 'Typography',
open: false,
buildProps: ['font-family', 'font-size', 'font-weight', 'letter-spacing', 'color', 'line-height', 'text-align', 'text-shadow'],
properties: [{
property: 'text-align',
list : [
{value: 'left', className: 'fa fa-align-left'},
{value: 'center', className: 'fa fa-align-center' },
{value: 'right', className: 'fa fa-align-right'},
{value: 'justify', className: 'fa fa-align-justify'}
],
}]
},{
name: 'Decorations',
open: false,
buildProps: ['border-radius-c', 'background-color', 'border-radius', 'border', 'box-shadow', 'background'],
},{
name: 'Extra',
open: false,
buildProps: ['transition', 'perspective', 'transform'],
}],
},
//Configurations for Block Manager
blockManager: {
'blocks': [{
id: 'b1',
label: '1 Block',
content: '<div class="blk-row"><div class="blk1"></div></div><style>'+ blkStyle +'.blk1{width: 100%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b1'}
},{
id: 'b2',
label: '2 Blocks',
content: '<div class="blk-row"><div class="blk2"></div><div class="blk2"></div></div><style>'+ blkStyle +'.blk2{float: left;width: 50%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b2'}
},{
id: 'b3',
label: '3 Blocks',
content: '<div class="blk-row"><div class="blk3"></div><div class="blk3"></div><div class="blk3"></div></div><style>'+ blkStyle +'.blk3{float: left;width: 33.3333%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b3'}
},{
id: 'b4',
label: '3/7 Block',
content: '<div class="blk-row"><div class="blk37l"></div><div class="blk37r"></div></div></div><style>'+ blkStyle +'.blk37l{float: left;width: 30%;padding: 10px;min-height: 75px;}.blk37r{float: left;width: 70%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b37'}
},{
id: 'hero',
label: 'Hero section',
content: '<header class="header-banner"> <div class="container-width">'+
'<div class="logo-container"><div class="logo">GrapesJS</div></div>'+
'<nav class="navbar">'+
'<div class="menu-item">BUILDER</div><div class="menu-item">TEMPLATE</div><div class="menu-item">WEB</div>'+
'</nav><div class="clearfix"></div>'+
'<div class="lead-title">Build your templates without coding</div>'+
'<div class="lead-btn">Try it now</div></div></header>',
attributes: {class:'gjs-fonts gjs-f-hero'}
},{
id: 'h1p',
label: 'Text section',
content: '<h1 class="heading">Insert title here</h1><p class="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</p>',
attributes: {class:'gjs-fonts gjs-f-h1p'}
},{
id: '3ba',
label: 'Badges',
content: '<div class="badges">'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team1.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Adam Smith</div><div class="badge-role">CEO</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+
},
//Configurations for Block Manager
blockManager: {
'blocks': [{
id: 'b1',
label: '1 Block',
content: '<div class="blk-row"><div class="blk1"></div></div><style>'+ blkStyle +'.blk1{width: 100%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b1'}
},{
id: 'b2',
label: '2 Blocks',
content: '<div class="blk-row"><div class="blk2"></div><div class="blk2"></div></div><style>'+ blkStyle +'.blk2{float: left;width: 50%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b2'}
},{
id: 'b3',
label: '3 Blocks',
content: '<div class="blk-row"><div class="blk3"></div><div class="blk3"></div><div class="blk3"></div></div><style>'+ blkStyle +'.blk3{float: left;width: 33.3333%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b3'}
},{
id: 'b4',
label: '3/7 Block',
content: '<div class="blk-row"><div class="blk37l"></div><div class="blk37r"></div></div></div><style>'+ blkStyle +'.blk37l{float: left;width: 30%;padding: 10px;min-height: 75px;}.blk37r{float: left;width: 70%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b37'}
},{
id: 'hero',
label: 'Hero section',
content: '<header class="header-banner"> <div class="container-width">'+
'<div class="logo-container"><div class="logo">GrapesJS</div></div>'+
'<nav class="navbar">'+
'<div class="menu-item">BUILDER</div><div class="menu-item">TEMPLATE</div><div class="menu-item">WEB</div>'+
'</nav><div class="clearfix"></div>'+
'<div class="lead-title">Build your templates without coding</div>'+
'<div class="lead-btn">Try it now</div></div></header>',
attributes: {class:'gjs-fonts gjs-f-hero'}
},{
id: 'h1p',
label: 'Text section',
content: '<h1 class="heading">Insert title here</h1><p class="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</p>',
attributes: {class:'gjs-fonts gjs-f-h1p'}
},{
id: '3ba',
label: 'Badges',
content: '<div class="badges">'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team1.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Adam Smith</div><div class="badge-role">CEO</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+
'</div>'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team2.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">John Black</div><div class="badge-role">Software Engineer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+
'</div>'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team3.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Jessica White</div><div class="badge-role">Web Designer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge">'+
'<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 class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span>'+
'</div>'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team3.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Jessica White</div><div class="badge-role">Web Designer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span>'+
'</div>'+
'</div></div>',
attributes: {class:'gjs-fonts gjs-f-3ba'}
},{
id: 'text',
label: 'Text',
attributes: {class:'gjs-fonts gjs-f-text'},
content: {
type:'text',
content:'Insert your text here',
style: {padding: '10px' },
activeOnRender: 1
},
},{
id: 'image',
label: 'Image',
attributes: {class:'gjs-fonts gjs-f-image'},
content: {
style: {color: 'black'},
type:'image',
activeOnRender: 1
},
},{
id: 'quo',
label: 'Quote',
content: '<blockquote class="quote">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</blockquote>',
attributes: {class:'fa fa-quote-right'}
},{
id: 'link',
label: 'Link',
attributes: {class:'fa fa-link'},
content: {
type:'link',
content:'Link',
style:{color: '#d983a6'}
},
},{
id: 'map',
label: 'Map',
attributes: {class:'fa fa-map-o'},
content: {
type: 'map',
style: {height: '350px'}
},
},{
id: 'video',
label: 'Video',
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%'}
},
}*/],
},
};
});
'</div></div>',
attributes: {class:'gjs-fonts gjs-f-3ba'}
},{
id: 'text',
label: 'Text',
attributes: {class:'gjs-fonts gjs-f-text'},
content: {
type:'text',
content:'Insert your text here',
style: {padding: '10px' },
activeOnRender: 1
},
},{
id: 'image',
label: 'Image',
attributes: {class:'gjs-fonts gjs-f-image'},
content: {
style: {color: 'black'},
type:'image',
activeOnRender: 1
},
},{
id: 'quo',
label: 'Quote',
content: '<blockquote class="quote">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</blockquote>',
attributes: {class:'fa fa-quote-right'}
},{
id: 'link',
label: 'Link',
attributes: {class:'fa fa-link'},
content: {
type:'link',
content:'Link',
style:{color: '#d983a6'}
},
},{
id: 'map',
label: 'Map',
attributes: {class:'fa fa-map-o'},
content: {
type: 'map',
style: {height: '350px'}
},
},{
id: 'video',
label: 'Video',
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%'}
},
}*/],
},
};

836
src/editor/index.js

@ -68,425 +68,421 @@
* style: '.txt-red{color: red}',
* });
*/
define(function (require){
var Editor = function(config) {
var c = config || {},
defaults = require('./config/config'),
EditorModel = require('./model/Editor'),
EditorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
module.exports = function(config) {
var c = config || {},
defaults = require('./config/config'),
EditorModel = require('./model/Editor'),
EditorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
c.pStylePrefix = c.stylePrefix;
var em = new EditorModel(c);
var editorView = new EditorView({
model: em,
config: c,
});
return {
/**
* @property {EditorModel}
* @private
*/
editor: em,
/**
* @property {DomComponents}
*/
DomComponents: em.get('DomComponents'),
/**
* @property {CssComposer}
*/
CssComposer: em.get('CssComposer'),
/**
* @property {StorageManager}
*/
StorageManager: em.get('StorageManager'),
/**
* @property {AssetManager}
*/
AssetManager: em.get('AssetManager'),
/**
* @property {BlockManager}
*/
BlockManager: em.get('BlockManager'),
/**
* @property {TraitManager}
*/
TraitManager: em.get('TraitManager'),
/**
* @property {SelectorManager}
*/
SelectorManager: em.get('SelectorManager'),
/**
* @property {CodeManager}
*/
CodeManager: em.get('CodeManager'),
/**
* @property {Commands}
*/
Commands: em.get('Commands'),
/**
* @property {Modal}
*/
Modal: em.get('Modal'),
/**
* @property {Panels}
*/
Panels: em.get('Panels'),
/**
* @property {StyleManager}
*/
StyleManager: em.get('StyleManager'),
/**
* @property {Canvas}
*/
Canvas: em.get('Canvas'),
/**
* @property {UndoManager}
*/
UndoManager: em.get('UndoManager'),
/**
* @property {DeviceManager}
*/
DeviceManager: em.get('DeviceManager'),
/**
* @property {RichTextEditor}
*/
RichTextEditor: em.get('rte'),
/**
* @property {Utils}
*/
Utils: em.get('Utils'),
/**
* @property {Utils}
*/
Config: em.get('Config'),
/**
* Initialize editor model
* @return {this}
* @private
*/
init: function(){
em.init(this);
return this;
},
/**
* Returns configuration object
* @return {Object}
*/
getConfig: function(){
return c;
},
/**
* Returns HTML built inside canvas
* @return {string} HTML string
*/
getHtml: function(){
return em.getHtml();
},
/**
* Returns CSS built inside canvas
* @return {string} CSS string
*/
getCss: function(){
return em.getCss();
},
/**
* Returns JS of all components
* @return {string} JS string
*/
getJs: function(){
return em.getJs();
},
/**
* Returns components in JSON format object
* @return {Object}
*/
getComponents: function(){
return em.get('DomComponents').getComponents();
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {this}
* @example
* editor.setComponents('<div class="cls">New component</div>');
* // or
* editor.setComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
setComponents: function(components){
em.setComponents(components);
return this;
},
/**
* Add components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {Model|Array<Model>}
* @example
* editor.addComponents('<div class="cls">New component</div>');
* // or
* editor.addComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
addComponents: function(components) {
return this.getComponents().add(components);
},
/**
* Returns style in JSON format object
* @return {Object}
*/
getStyle: function(){
return em.get('CssComposer').getAll();
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Array<Object>|Object|string} style CSS string or style model
* @return {this}
* @example
* editor.setStyle('.cls{color: red}');
* //or
* editor.setStyle({
* selectors: ['cls']
* style: { color: 'red' }
* });
*/
setStyle: function(style){
em.setStyle(style);
return this;
},
/**
* Returns selected component, if there is one
* @return {grapesjs.Component}
*/
getSelected: function(){
return em.getSelected();
},
/**
* Set device to the editor. If the device exists it will
* change the canvas to the proper width
* @return {this}
* @example
* editor.setDevice('Tablet');
*/
setDevice: function(name){
return em.set('device', name);
},
/**
* Return the actual active device
* @return {string} Device name
* @example
* var device = editor.getDevice();
* console.log(device);
* // 'Tablet'
*/
getDevice: function(){
return em.get('device');
},
/**
* Execute command
* @param {string} id Command ID
* @param {Object} options Custom options
* @return {*} The return is defined by the command
* @example
* editor.runCommand('myCommand', {someValue: 1});
*/
runCommand: function(id, options) {
var result;
var command = em.get('Commands').get(id);
if(command){
result = command.run(this, this, options);
this.trigger('run:' + id);
}
return result;
},
/**
* Stop the command if stop method was provided
* @param {string} id Command ID
* @param {Object} options Custom options
* @return {*} The return is defined by the command
* @example
* editor.stopCommand('myCommand', {someValue: 1});
*/
stopCommand: function(id, options) {
var result;
var command = em.get('Commands').get(id);
if(command){
result = command.stop(this, this, options);
this.trigger('stop:' + id);
}
c.pStylePrefix = c.stylePrefix;
var em = new EditorModel(c);
var editorView = new EditorView({
model: em,
config: c,
});
return {
/**
* @property {EditorModel}
* @private
*/
editor: em,
/**
* @property {DomComponents}
*/
DomComponents: em.get('DomComponents'),
/**
* @property {CssComposer}
*/
CssComposer: em.get('CssComposer'),
/**
* @property {StorageManager}
*/
StorageManager: em.get('StorageManager'),
/**
* @property {AssetManager}
*/
AssetManager: em.get('AssetManager'),
/**
* @property {BlockManager}
*/
BlockManager: em.get('BlockManager'),
/**
* @property {TraitManager}
*/
TraitManager: em.get('TraitManager'),
/**
* @property {SelectorManager}
*/
SelectorManager: em.get('SelectorManager'),
/**
* @property {CodeManager}
*/
CodeManager: em.get('CodeManager'),
/**
* @property {Commands}
*/
Commands: em.get('Commands'),
/**
* @property {Modal}
*/
Modal: em.get('Modal'),
/**
* @property {Panels}
*/
Panels: em.get('Panels'),
/**
* @property {StyleManager}
*/
StyleManager: em.get('StyleManager'),
/**
* @property {Canvas}
*/
Canvas: em.get('Canvas'),
/**
* @property {UndoManager}
*/
UndoManager: em.get('UndoManager'),
/**
* @property {DeviceManager}
*/
DeviceManager: em.get('DeviceManager'),
/**
* @property {RichTextEditor}
*/
RichTextEditor: em.get('rte'),
/**
* @property {Utils}
*/
Utils: em.get('Utils'),
/**
* @property {Utils}
*/
Config: em.get('Config'),
/**
* Initialize editor model
* @return {this}
* @private
*/
init: function(){
em.init(this);
return this;
},
/**
* Returns configuration object
* @return {Object}
*/
getConfig: function(){
return c;
},
/**
* Returns HTML built inside canvas
* @return {string} HTML string
*/
getHtml: function(){
return em.getHtml();
},
/**
* Returns CSS built inside canvas
* @return {string} CSS string
*/
getCss: function(){
return em.getCss();
},
/**
* Returns JS of all components
* @return {string} JS string
*/
getJs: function(){
return em.getJs();
},
/**
* Returns components in JSON format object
* @return {Object}
*/
getComponents: function(){
return em.get('DomComponents').getComponents();
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {this}
* @example
* editor.setComponents('<div class="cls">New component</div>');
* // or
* editor.setComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
setComponents: function(components){
em.setComponents(components);
return this;
},
/**
* Add components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {Model|Array<Model>}
* @example
* editor.addComponents('<div class="cls">New component</div>');
* // or
* editor.addComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
addComponents: function(components) {
return this.getComponents().add(components);
},
/**
* Returns style in JSON format object
* @return {Object}
*/
getStyle: function(){
return em.get('CssComposer').getAll();
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Array<Object>|Object|string} style CSS string or style model
* @return {this}
* @example
* editor.setStyle('.cls{color: red}');
* //or
* editor.setStyle({
* selectors: ['cls']
* style: { color: 'red' }
* });
*/
setStyle: function(style){
em.setStyle(style);
return this;
},
/**
* Returns selected component, if there is one
* @return {grapesjs.Component}
*/
getSelected: function(){
return em.getSelected();
},
/**
* Set device to the editor. If the device exists it will
* change the canvas to the proper width
* @return {this}
* @example
* editor.setDevice('Tablet');
*/
setDevice: function(name){
return em.set('device', name);
},
/**
* Return the actual active device
* @return {string} Device name
* @example
* var device = editor.getDevice();
* console.log(device);
* // 'Tablet'
*/
getDevice: function(){
return em.get('device');
},
/**
* Execute command
* @param {string} id Command ID
* @param {Object} options Custom options
* @return {*} The return is defined by the command
* @example
* editor.runCommand('myCommand', {someValue: 1});
*/
runCommand: function(id, options) {
var result;
var command = em.get('Commands').get(id);
if(command){
result = command.run(this, this, options);
this.trigger('run:' + id);
}
return result;
},
/**
* Stop the command if stop method was provided
* @param {string} id Command ID
* @param {Object} options Custom options
* @return {*} The return is defined by the command
* @example
* editor.stopCommand('myCommand', {someValue: 1});
*/
stopCommand: function(id, options) {
var result;
var command = em.get('Commands').get(id);
if(command){
result = command.stop(this, this, options);
this.trigger('stop:' + id);
}
return result;
},
/**
* Store data to the current storage
* @param {Function} clb Callback function
* @return {Object} Stored data
*/
store: function(clb) {
return em.store(clb);
},
/**
* Load data from the current storage
* @return {Object} Stored data
*/
load: function() {
return em.load();
},
/**
* Returns container element. The one which was indicated as 'container'
* on init method
* @return {HTMLElement}
*/
getContainer: function(){
return c.el;
},
/**
* Update editor dimensions and refresh data useful for positioning of tools
*
* This method could be useful when you update, for example, some position
* of the editor element (eg. canvas, panels, etc.) with CSS, where without
* refresh you'll get misleading position of tools (eg. rich text editor,
* component highlighter, etc.)
*
* @private
*/
refresh: function () {
em.refreshCanvas();
},
/**
* Replace the built-in Rich Text Editor with a custom one.
* @param {Object} obj Custom RTE Interface
* @example
* editor.setCustomRte({
* // Function for enabling custom RTE
* // el is the HTMLElement of the double clicked Text Component
* // rte is the same instance you have returned the first time you call
* // enable(). This is useful if need to check if the RTE is already enabled so
* // ion this case you'll need to return the RTE and the end of the function
* enable: function(el, rte) {
* rte = new MyCustomRte(el, {}); // this depends on the Custom RTE API
* ...
* return rte; // return the RTE instance
* },
*
* // Disable the editor, called for example when you unfocus the Text Component
* disable: function(el, rte) {
* rte.blur(); // this depends on the Custom RTE API
* }
*
* // Called when the Text Component is focused again. If you returned the RTE instance
* // from the enable function, the enable won't be called again instead will call focus,
* // in this case to avoid double binding of the editor
* focus: function (el, rte) {
* rte.focus(); // this depends on the Custom RTE API
* }
* });
*/
setCustomRte: function (obj) {
this.RichTextEditor.customRte = obj;
},
/**
* Attach event
* @param {string} event Event name
* @param {Function} callback Callback function
* @return {this}
*/
on: function(event, callback){
return em.on(event, callback);
},
/**
* Trigger event
* @param {string} event Event to trigger
* @return {this}
*/
trigger: function(event){
return em.trigger(event);
},
/**
* Returns editor element
* @return {HTMLElement}
* @private
*/
getEl: function(){
return editorView.el;
},
/**
* Returns editor model
* @return {Model}
* @private
*/
getModel: function(){
return em;
},
/**
* Render editor
* @return {HTMLElement}
*/
render: function() {
return editorView.render().el;
},
};
};
return Editor;
});
return result;
},
/**
* Store data to the current storage
* @param {Function} clb Callback function
* @return {Object} Stored data
*/
store: function(clb) {
return em.store(clb);
},
/**
* Load data from the current storage
* @return {Object} Stored data
*/
load: function() {
return em.load();
},
/**
* Returns container element. The one which was indicated as 'container'
* on init method
* @return {HTMLElement}
*/
getContainer: function(){
return c.el;
},
/**
* Update editor dimensions and refresh data useful for positioning of tools
*
* This method could be useful when you update, for example, some position
* of the editor element (eg. canvas, panels, etc.) with CSS, where without
* refresh you'll get misleading position of tools (eg. rich text editor,
* component highlighter, etc.)
*
* @private
*/
refresh: function () {
em.refreshCanvas();
},
/**
* Replace the built-in Rich Text Editor with a custom one.
* @param {Object} obj Custom RTE Interface
* @example
* editor.setCustomRte({
* // Function for enabling custom RTE
* // el is the HTMLElement of the double clicked Text Component
* // rte is the same instance you have returned the first time you call
* // enable(). This is useful if need to check if the RTE is already enabled so
* // ion this case you'll need to return the RTE and the end of the function
* enable: function(el, rte) {
* rte = new MyCustomRte(el, {}); // this depends on the Custom RTE API
* ...
* return rte; // return the RTE instance
* },
*
* // Disable the editor, called for example when you unfocus the Text Component
* disable: function(el, rte) {
* rte.blur(); // this depends on the Custom RTE API
* }
*
* // Called when the Text Component is focused again. If you returned the RTE instance
* // from the enable function, the enable won't be called again instead will call focus,
* // in this case to avoid double binding of the editor
* focus: function (el, rte) {
* rte.focus(); // this depends on the Custom RTE API
* }
* });
*/
setCustomRte: function (obj) {
this.RichTextEditor.customRte = obj;
},
/**
* Attach event
* @param {string} event Event name
* @param {Function} callback Callback function
* @return {this}
*/
on: function(event, callback){
return em.on(event, callback);
},
/**
* Trigger event
* @param {string} event Event to trigger
* @return {this}
*/
trigger: function(event){
return em.trigger(event);
},
/**
* Returns editor element
* @return {HTMLElement}
* @private
*/
getEl: function(){
return editorView.el;
},
/**
* Returns editor model
* @return {Model}
* @private
*/
getModel: function(){
return em;
},
/**
* Render editor
* @return {HTMLElement}
*/
render: function() {
return editorView.render().el;
},
};
};

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

Loading…
Cancel
Save