Browse Source

Components improvements, Traits improvements, add script property

pull/67/head
Artur Arseniev 9 years ago
parent
commit
a678532ae4
  1. 2
      bower.json
  2. 22
      dist/grapes.min.js
  3. 32
      index.html
  4. 2
      package.json
  5. 13
      src/canvas/view/CanvasView.js
  6. 7
      src/commands/view/SelectComponent.js
  7. 155
      src/demo.js
  8. 20
      src/dom_components/view/ComponentScriptView.js
  9. 371
      src/domain_abstract/ui/InputNumber.js
  10. 3
      src/editor/config/config.js
  11. 793
      src/editor/main.js
  12. 8
      src/editor/model/Editor.js
  13. 4
      src/editor/view/EditorView.js
  14. 365
      src/storage_manager/main.js
  15. 112
      src/storage_manager/model/LocalStorage.js
  16. 112
      src/storage_manager/model/RemoteStorage.js
  17. 76
      src/trait_manager/view/TraitCheckboxView.js
  18. 41
      src/trait_manager/view/TraitNumberView.js
  19. 110
      src/trait_manager/view/TraitSelectView.js
  20. 228
      src/trait_manager/view/TraitView.js
  21. 78
      src/trait_manager/view/TraitsView.js

2
bower.json

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

22
dist/grapes.min.js

File diff suppressed because one or more lines are too long

32
index.html

@ -14,6 +14,38 @@
<div id="gjs" style="height:0px; overflow:hidden">
<header class="header-banner">
<section class="section-testimonials">
<div class="container">
<div class="row">
<div class="col-sm-12">
<h1 class="h1-with-decor text-center">Testimonials</h1>
<div class="slider carousel" data-gjs-type="slider">
<div class="slider-mask" data-gjs-custom-name="Slider Mask" data-gjs-droppable="false" data-gjs-draggable="false">
<div class="slider-slideset" data-gjs-type="slider-set" data-gjs-custom-name="Slider Set">
<div class="slider-slide" data-gjs-type="slide">
<cite class="cite">
<img src="https://cdn.filestackcontent.com/o54S2SKtSF2n3QHyzLjF" alt="Image description" width="130" height="130" class="cite-img"><strong class="cite-name">Henry Palmer</strong>App Professional Editor
</cite>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
<div class="slider-slide" data-gjs-type="slide">
<cite class="cite"><img src="https://cdn.filestackcontent.com/o54S2SKtSF2n3QHyzLjF" alt="Image description" width="130" height="130" class="cite-img"><strong class="cite-name">Henry Palmer</strong>App Professional Editor</cite>
<p>#2 Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
<div class="slider-slide" data-gjs-type="slide">
<cite class="cite"><img src="https://cdn.filestackcontent.com/o54S2SKtSF2n3QHyzLjF" alt="Image description" width="130" height="130" class="cite-img"><strong class="cite-name">Henry Palmer</strong>App Professional Editor</cite>
<p>#3 Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
</div>
</div>
<a href="#" class="btn-slider-prev" data-gjs-type="slider-nav"></a>
<a href="#" class="btn-slider-next" data-gjs-type="slider-nav"></a>
<div class="slider-pagination text-center"></div>
</div>
</div>
</div>
</div>
</section>
<div class="container-width">
<!--
<table>

2
package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.4.33",
"version": "0.4.35",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",

13
src/canvas/view/CanvasView.js

@ -161,23 +161,22 @@ function(Backbone, FrameView) {
* @private
*/
updateScript: function(view) {
var scriptsContainer = this.getJsContainer();
if(!view.scriptContainer) {
view.scriptContainer = $('<div>');
scriptsContainer.append(view.scriptContainer.get(0));
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;
view.el.id = id;
view.scriptContainer.html('');
// TODO isolate
view.scriptContainer.append('<script>'+
'var item = document.getElementById("'+id+'");'+
'(function(){' + script + '}.bind(item))()</script>');
view.scriptContainer.append('<script>' +
'var item = document.getElementById("'+id+'");' +
'(' + scrStr + '.bind(item))()</script>');
},
/**

7
src/commands/view/SelectComponent.js

@ -274,6 +274,12 @@ define(function(require) {
* @private
*/
updateHighlighter: function(el, pos) {
var $el = $(el);
var model = $el.data('model');
if(!model || (model && model.get('status') == 'selected')) {
return;
}
var hlEl = this.canvas.getHighlighter();
var hlStyle = hlEl.style;
var unit = 'px';
@ -318,6 +324,7 @@ define(function(require) {
nMd.set('status','selected');
this.showFixedElementOffset(el);
this.hideElementOffset();
this.hideHighlighter();
}
},

155
src/demo.js

@ -13,6 +13,7 @@ require(['config/require-config'], function() {
container : '#gjs',
height: '100%',
fromElement: true,
clearOnRender: 0,
/*
components: [{
@ -399,8 +400,160 @@ require(['config/require-config'], function() {
attributes: { title: 'Empty canvas' }
}]);
editor.render();
var domc = editor.DomComponents;
var defaultType = domc.getType('default');
var defaultModel = defaultType.model;
var defaultView = defaultType.view;
// Slider
domc.addType('slider', {
model: defaultModel.extend({
defaults: _.extend({}, defaultModel.prototype.defaults, {
autoRotation: false, // Autoplay
switchTime: 3000, // Switch time
animSpeed: 600, // Animation Speed/Duration
droppable: false,
traits: [{
type: 'number',
label: 'Duration',
name: 'animSpeed',
changeProp: 1,
}/*,{
type: 'checkbox',
label: 'Autoplay',
name: 'autoRotation',
changeProp: 1,
},{
type: 'number',
label: 'Autoplay time',
name: 'switchTime',
changeProp: 1,
}*/],
}),
init: function () {
this.listenTo(this, 'change:animSpeed change:autoRotation change:switchTime',
this.buildScript);
},
buildScript: function() {
///prevSlide, nextSlide
var opts = {
mask: '.slider-mask',
slider: '.slider-slideset',
slides: '.slider-slide',
btnPrev: '.btn-slider-prev',
btnNext: '.btn-slider-next',
generatePagination: '.slider-pagination',
pagerLinks: '.slider-pagination li a',
stretchSlideToMask: true,
animSpeed: parseInt(this.get('animSpeed')),
//switchTime: parseInt(this.get('switchTime')),
//autoRotation: this.get('autoRotation'),
};
var optsStr = JSON.stringify(opts);
var script = 'var $el = window.$(this); $el.scrollGallery('+ optsStr + ')';
console.log(script);
this.set('script', script);
},
},{
isComponent: function(el) {
if(el.getAttribute &&
el.getAttribute('data-gjs-type') == 'slider'){
return {type: 'slider'};
}
},
}),
view: defaultView.extend({
init: function () {
this.listenTo(this.model, 'active', this.updateScript);
},
}),
});
domc.addType('slide', {
model: defaultModel.extend({
defaults: _.extend({}, defaultModel.prototype.defaults, {
droppable: false,
draggable: false,
}),
},{
isComponent: function(el) {
if(el.getAttribute &&
el.getAttribute('data-gjs-type') == 'slide'){
return {type: 'slide'};
}
},
}),
view: defaultView.extend({
remove: function() {
var slider = this.$el.closest('.slider');
defaultView.prototype.remove.apply(this, arguments);
var model = slider.data('model');
if(model) model.trigger('active');
},
}),
});
domc.addType('slider-nav', {
model: defaultModel.extend({
defaults: _.extend({}, defaultModel.prototype.defaults, {
editable: false,
droppable: false,
draggable: false,
removable: false,
copyable: false,
}),
},{
isComponent: function(el) {
if(el.getAttribute &&
el.getAttribute('data-gjs-type') == 'slider-nav'){
return {type: 'slider-nav'};
}
},
}),
view: defaultView,
});
domc.addType('slider-set', {
model: defaultModel.extend({
defaults: _.extend({}, defaultModel.prototype.defaults, {
droppable: '[data-gjs-type=slide]',
draggable: false,
removable: false,
copyable: false,
}),
},{
isComponent: function(el) {
if(el.getAttribute &&
el.getAttribute('data-gjs-type') == 'slider-set'){
return {type: 'slider-set'};
}
},
}),
view: defaultView.extend({
init: function () {
var comps = this.model.get('components');
this.listenTo(comps, 'add remove change', this.updateSliders);
},
updateSliders: function() {
var slider = this.$el.closest('.slider');
var model = slider.data('model');
if(model)
model.trigger('active');
},
}),
});
var dc = editor.DomComponents.getComponents();
//http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js
dc.add('<link rel="stylesheet" href="private/main.css">'+
'<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'+
'<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>' +
'<script src="private/jquery.main.js" defer=""></script>');
editor.render();
});
});

20
src/dom_components/view/ComponentScriptView.js

@ -10,14 +10,26 @@ define(['backbone', './ComponentImageView'],
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) {
content = "var script = document.createElement('script');" +
"script.onload = " + model.get('onload') + ";" +
"script.src = '" + src + "';"+
"document.body.appendChild(script);";
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');
}

371
src/domain_abstract/ui/InputNumber.js

@ -1,58 +1,61 @@
define(['backbone', 'text!./templates/inputNumber.html'],
function (Backbone, inputTemplate) {
function (Backbone, inputTemplate) {
return Backbone.View.extend({
return Backbone.View.extend({
events: {},
events: {},
template: _.template(inputTemplate),
template: _.template(inputTemplate),
initialize: function(opts) {
_.bindAll(this, 'moveIncrement', 'upIncrement');
var ppfx = opts.ppfx || '';
this.ppfx = ppfx;
this.docEl = $(document);
this.inputCls = ppfx + 'input-number';
this.unitCls = ppfx + 'input-unit';
this.events['click .' + ppfx + 'field-arrow-u'] = 'upArrowClick';
this.events['click .' + ppfx + 'field-arrow-d'] = 'downArrowClick';
this.events['mousedown .' + ppfx + 'field-arrows'] = 'downIncrement';
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
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();
}
},
// 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();
e.stopPropagation();
this.setValue(this.getInputEl().value);
},
@ -60,186 +63,186 @@ define(['backbone', 'text!./templates/inputNumber.html'],
* Handled when the view is changed
*/
handleUnitChange: function (e) {
e.stopPropagation();
e.stopPropagation();
var value = this.getUnitEl().value;
this.model.set('unit', value);
},
/**
* Updates the view when the model is changed
* */
handleModelChange: function() {
* Updates the view when the model is changed
* */
handleModelChange: function() {
var m = this.model;
this.getInputEl().value = m.get('value');
var unit = this.getUnitEl();
var unit = this.getUnitEl();
if (unit) {
unit.value = m.get('unit');
}
},
if (unit) {
unit.value = m.get('unit');
}
},
/**
* Get the input element
* @return {HTMLElement}
*/
/**
* Get the input element
* @return {HTMLElement}
*/
getInputEl: function() {
if(!this.inputEl) {
this.inputEl = $('<input>', {
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
type: 'text',
class: this.inputCls,
placeholder: this.model.get('defaults')
});
}
return this.inputEl.get(0);
},
/**
* Get the unit element
* @return {HTMLElement}
*/
/**
* 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);
}
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;
/**
* 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 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;
}
}
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')
val = val > max ? max : val;
if(typeof max !== 'undefined' && max !== '')
val = val > max ? max : val;
if(typeof min !== 'undefined')
val = val < min ? min : val;
if(typeof min !== 'undefined' && min !== '')
val = val < min ? min : val;
return {
force: force,
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(ppfx + 'field');
return this;
}
});
},
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;
}
});
});

3
src/editor/config/config.js

@ -23,6 +23,9 @@ define(function () {
// Show paddings and margins on selected component
showOffsetsSelected: false,
// Clear the canvas when editor.render() is called
clearOnRender: true,
// Height for the editor container
height: '900px',

793
src/editor/main.js

@ -60,404 +60,405 @@
* @param {Object} [config.pluginsOpts={}] Custom options for plugins
* @example
* var editor = grapesjs.init({
* container : '#gjs',
* components: '<div class="txt-red">Hello world!</div>',
* style: '.txt-red{color: red}',
* container : '#gjs',
* components: '<div class="txt-red">Hello world!</div>',
* 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];
}
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 components in JSON format object
* @return {Object}
*/
getComponents: function(){
return em.get('DomComponents').getComponents();
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {this}
* @example
* editor.setComponents('<div class="cls">New component</div>');
* // or
* editor.setComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
setComponents: function(components){
em.setComponents(components);
return this;
},
/**
* Returns style in JSON format object
* @return {Object}
*/
getStyle: function(){
return em.get('CssComposer').getAll();
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Array<Object>|Object|string} style CSS string or style model
* @return {this}
* @example
* editor.setStyle('.cls{color: red}');
* //or
* editor.setStyle({
* selectors: ['cls']
* style: { color: 'red' }
* });
*/
setStyle: function(style){
em.setStyle(style);
return this;
},
/**
* Returns selected component, if there is one
* @return {grapesjs.Component}
*/
getSelected: function(){
return em.getSelected();
},
/**
* Set device to the editor. If the device exists it will
* change the canvas to the proper width
* @return {this}
* @example
* editor.setDevice('Tablet');
*/
setDevice: function(name){
return em.set('device', name);
},
/**
* Return the actual active device
* @return {string} Device name
* @example
* var device = editor.getDevice();
* console.log(device);
* // 'Tablet'
*/
getDevice: function(){
return em.get('device');
},
/**
* Execute command
* @param {string} id Command ID
* @param {Object} options Custom options
* @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
* @return {Object} Stored data
*/
store: function(){
return em.store();
},
/**
* Load data from the current storage
* @return {Object} Stored data
*/
load: function(){
return em.load();
},
/**
* Returns container element. The one which was indicated as 'container'
* on init method
* @return {HTMLElement}
*/
getContainer: function(){
return c.el;
},
/**
* 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;
var Editor = function(config) {
var c = config || {},
defaults = require('./config/config'),
EditorModel = require('./model/Editor'),
EditorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
c.pStylePrefix = c.stylePrefix;
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 components in JSON format object
* @return {Object}
*/
getComponents: function(){
return em.get('DomComponents').getComponents();
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {this}
* @example
* editor.setComponents('<div class="cls">New component</div>');
* // or
* editor.setComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
setComponents: function(components){
em.setComponents(components);
return this;
},
/**
* Returns style in JSON format object
* @return {Object}
*/
getStyle: function(){
return em.get('CssComposer').getAll();
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Array<Object>|Object|string} style CSS string or style model
* @return {this}
* @example
* editor.setStyle('.cls{color: red}');
* //or
* editor.setStyle({
* selectors: ['cls']
* style: { color: 'red' }
* });
*/
setStyle: function(style){
em.setStyle(style);
return this;
},
/**
* Returns selected component, if there is one
* @return {grapesjs.Component}
*/
getSelected: function(){
return em.getSelected();
},
/**
* Set device to the editor. If the device exists it will
* change the canvas to the proper width
* @return {this}
* @example
* editor.setDevice('Tablet');
*/
setDevice: function(name){
return em.set('device', name);
},
/**
* Return the actual active device
* @return {string} Device name
* @example
* var device = editor.getDevice();
* console.log(device);
* // 'Tablet'
*/
getDevice: function(){
return em.get('device');
},
/**
* Execute command
* @param {string} id Command ID
* @param {Object} options Custom options
* @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;
});

8
src/editor/model/Editor.js

@ -393,10 +393,11 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
/**
* Store data to the current storage
* @param {Function} clb Callback function
* @return {Object} Stored data
* @private
*/
store: function() {
store: function(clb) {
var sm = this.get('StorageManager');
var store = {};
if(!sm)
@ -409,7 +410,7 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
store[el] = obj[el];
});
sm.store(store);
sm.store(store, clb);
this.set('changesCount', 0);
return store;
@ -417,10 +418,11 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
/**
* Load data from the current storage
* @param {Function} clb Callback function
* @return {Object} Loaded data
* @private
*/
load: function(){
load: function(clb) {
var result = this.getCacheLoad(1);
this.get('storables').forEach(function(m){
m.load(result);

4
src/editor/view/EditorView.js

@ -21,7 +21,9 @@ function(Backbone){
var config = model.get('Config');
if(config.loadCompsOnRender) {
dComps.clear();
if (config.clearOnRender) {
dComps.clear();
}
dComps.getComponents().add(config.components);
um.clear();
dComps.onLoad();

365
src/storage_manager/main.js

@ -22,18 +22,18 @@
*/
define(function(require) {
var StorageManager = function() {
var c = {},
defaults = require('./config/config'),
LocalStorage = require('./model/LocalStorage'),
RemoteStorage = require('./model/RemoteStorage');
var StorageManager = function() {
var c = {},
defaults = require('./config/config'),
LocalStorage = require('./model/LocalStorage'),
RemoteStorage = require('./model/RemoteStorage');
var storages = {};
var defaultStorages = {};
var storages = {};
var defaultStorages = {};
return {
return {
/**
/**
* Name of the module
* @type {String}
* @private
@ -43,30 +43,30 @@ define(function(require) {
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @param {string} [config.id='gjs-'] The prefix for the fields, useful to differentiate storing/loading
* with multiple editors on the same page. For example, in local storage, the item of HTML will be saved like 'gjs-html'
* @param {Boolean} [config.autosave=true] Indicates if autosave mode is enabled, works in conjunction with stepsBeforeSave
* @param {number} [config.stepsBeforeSave=1] If autosave enabled, indicates how many steps/changes are necessary
* before autosave is triggered
* @param {string} [config.type='local'] Default storage type. Available: 'local' | 'remote' | ''(do not store)
* @example
* ...
* {
* autosave: false,
* type: 'remote',
* }
* ...
* @param {string} [config.id='gjs-'] The prefix for the fields, useful to differentiate storing/loading
* with multiple editors on the same page. For example, in local storage, the item of HTML will be saved like 'gjs-html'
* @param {Boolean} [config.autosave=true] Indicates if autosave mode is enabled, works in conjunction with stepsBeforeSave
* @param {number} [config.stepsBeforeSave=1] If autosave enabled, indicates how many steps/changes are necessary
* before autosave is triggered
* @param {string} [config.type='local'] Default storage type. Available: 'local' | 'remote' | ''(do not store)
* @example
* ...
* {
* autosave: false,
* type: 'remote',
* }
* ...
*/
init: function(config) {
c = config || {};
for (var name in defaults){
if (!(name in c))
c[name] = defaults[name];
}
if (!(name in c))
c[name] = defaults[name];
}
defaultStorages.remote = new RemoteStorage(c);
defaultStorages.local = new LocalStorage(c);
c.currentStorage = c.type;
defaultStorages.remote = new RemoteStorage(c);
defaultStorages.local = new LocalStorage(c);
c.currentStorage = c.type;
return this;
},
@ -75,182 +75,183 @@ define(function(require) {
* @private
*/
onLoad: function(){
this.loadDefaultProviders().setCurrent(c.type);
this.loadDefaultProviders().setCurrent(c.type);
},
/**
* Checks if autosave is enabled
* @return {Boolean}
* */
isAutosave: function(){
return !!c.autosave;
},
/**
* Checks if autosave is enabled
* @return {Boolean}
* */
isAutosave: function(){
return !!c.autosave;
},
/**
* Set autosave value
* @param {Boolean} v
* @return {this}
* */
setAutosave : function(v){
c.autosave = !!v;
return this;
},
/**
* Set autosave value
* @param {Boolean} v
* @return {this}
* */
setAutosave : function(v){
c.autosave = !!v;
return this;
},
/**
* Returns number of steps required before trigger autosave
* @return {number}
* */
getStepsBeforeSave: function(){
return c.stepsBeforeSave;
},
/**
* Returns number of steps required before trigger autosave
* @return {number}
* */
getStepsBeforeSave: function(){
return c.stepsBeforeSave;
},
/**
* Set steps required before trigger autosave
* @param {number} v
* @return {this}
* */
setStepsBeforeSave: function(v){
c.stepsBeforeSave = v;
return this;
},
/**
* Set steps required before trigger autosave
* @param {number} v
* @return {this}
* */
setStepsBeforeSave: function(v){
c.stepsBeforeSave = v;
return this;
},
/**
* Add new storage
* @param {string} id Storage ID
* @param {Object} storage Storage wrapper
* @param {Function} storage.load Load method
* @param {Function} storage.store Store method
* @return {this}
* @example
* storageManager.add('local2', {
* load: function(keys){
* var res = {};
* for (var i = 0, len = keys.length; i < len; i++){
* var v = localStorage.getItem(keys[i]);
* if(v) res[keys[i]] = v;
* }
* return res;
* },
* store: function(data){
* for(var key in data)
* localStorage.setItem(key, data[key]);
* }
* });
* */
add: function(id, storage) {
storages[id] = storage;
return this;
},
/**
* Add new storage
* @param {string} id Storage ID
* @param {Object} storage Storage wrapper
* @param {Function} storage.load Load method
* @param {Function} storage.store Store method
* @return {this}
* @example
* storageManager.add('local2', {
* load: function(keys){
* var res = {};
* for (var i = 0, len = keys.length; i < len; i++){
* var v = localStorage.getItem(keys[i]);
* if(v) res[keys[i]] = v;
* }
* return res;
* },
* store: function(data){
* for(var key in data)
* localStorage.setItem(key, data[key]);
* }
* });
* */
add: function(id, storage) {
storages[id] = storage;
return this;
},
/**
* Returns storage by id
* @param {string} id Storage ID
* @return {Object|null}
* */
get: function(id){
return storages[id] || null;
},
/**
* Returns storage by id
* @param {string} id Storage ID
* @return {Object|null}
* */
get: function(id){
return storages[id] || null;
},
/**
* Returns all storages
* @return {Array}
* */
getStorages: function() {
return storages;
},
/**
* Returns all storages
* @return {Array}
* */
getStorages: function() {
return storages;
},
/**
* Returns current storage type
* @return {string}
* */
getCurrent: function() {
return c.currentStorage;
},
/**
* Returns current storage type
* @return {string}
* */
getCurrent: function() {
return c.currentStorage;
},
/**
* Set current storage type
* @param {string} id Storage ID
* @return {this}
* */
setCurrent: function(id) {
c.currentStorage = id;
return this;
},
/**
* Set current storage type
* @param {string} id Storage ID
* @return {this}
* */
setCurrent: function(id) {
c.currentStorage = id;
return this;
},
/**
* Store key-value resources in the current storage
* @param {Object} data Data in key-value format, eg. {item1: value1, item2: value2}
* @return {Object|null}
* @example
* storageManager.store({item1: value1, item2: value2});
* */
store: function(data){
var st = this.get(this.getCurrent());
var dataF = {};
/**
* Store key-value resources in the current storage
* @param {Object} data Data in key-value format, eg. {item1: value1, item2: value2}
* @param {Function} clb Callback function
* @return {Object|null}
* @example
* storageManager.store({item1: value1, item2: value2});
* */
store: function(data, clb) {
var st = this.get(this.getCurrent());
var dataF = {};
for(var key in data)
dataF[c.id + key] = data[key];
for(var key in data)
dataF[c.id + key] = data[key];
return st ? st.store(dataF) : null;
},
return st ? st.store(dataF, clb) : null;
},
/**
* Load resource from the current storage by keys
* @param {string|Array<string>} keys Keys to load
* @return {Object|null} Loaded resources
* @example
* var data = storageManager.load(['item1', 'item2']);
* // data -> {item1: value1, item2: value2}
* var data2 = storageManager.load('item1');
* // data2 -> {item1: value1}
* */
load: function(keys){
var st = this.get(this.getCurrent());
var keysF = [];
var result = {};
/**
* Load resource from the current storage by keys
* @param {string|Array<string>} keys Keys to load
* @return {Object|null} Loaded resources
* @example
* var data = storageManager.load(['item1', 'item2']);
* // data -> {item1: value1, item2: value2}
* var data2 = storageManager.load('item1');
* // data2 -> {item1: value1}
* */
load: function(keys){
var st = this.get(this.getCurrent());
var keysF = [];
var result = {};
if(typeof keys === 'string')
keys = [keys];
if(typeof keys === 'string')
keys = [keys];
for (var i = 0, len = keys.length; i < len; i++)
keysF.push(c.id + keys[i]);
for (var i = 0, len = keys.length; i < len; i++)
keysF.push(c.id + keys[i]);
var loaded = st ? st.load(keysF) : {};
var loaded = st ? st.load(keysF) : {};
// Restore keys name
for (var itemKey in loaded){
var reg = new RegExp('^' + c.id + '');
var itemKeyR = itemKey.replace(reg, '');
result[itemKeyR] = loaded[itemKey];
}
// Restore keys name
for (var itemKey in loaded){
var reg = new RegExp('^' + c.id + '');
var itemKeyR = itemKey.replace(reg, '');
result[itemKeyR] = loaded[itemKey];
}
return result;
},
return result;
},
/**
* Load default storages
* @return {this}
* @private
* */
loadDefaultProviders : function() {
for (var id in defaultStorages)
this.add(id, defaultStorages[id]);
return this;
},
/**
* Load default storages
* @return {this}
* @private
* */
loadDefaultProviders : function() {
for (var id in defaultStorages)
this.add(id, defaultStorages[id]);
return this;
},
/**
* Get configuration object
* @return {Object}
* @private
* */
getConfig : function() {
return c;
},
/**
* Get configuration object
* @return {Object}
* @private
* */
getConfig : function() {
return c;
},
};
};
};
};
return StorageManager;
return StorageManager;
});
});

112
src/storage_manager/model/LocalStorage.js

@ -1,56 +1,60 @@
define(['backbone'],
function (Backbone) {
return Backbone.Model.extend({
defaults: {
checkLocal: true,
},
/**
* @private
*/
store: function(data) {
this.checkStorageEnvironment();
for(var key in data)
localStorage.setItem(key, data[key]);
},
/**
* @private
*/
load: function(keys){
this.checkStorageEnvironment();
var result = {};
for (var i = 0, len = keys.length; i < len; i++){
var value = localStorage.getItem(keys[i]);
if(value)
result[keys[i]] = value;
}
return result;
},
/**
* @private
*/
remove: function(keys) {
this.checkStorageEnvironment();
for (var i = 0, len = keys.length; i < len; i++)
localStorage.removeItem(keys[i]);
},
/**
* Check storage environment
* @private
* */
checkStorageEnvironment: function() {
if(this.get('checkLocal') && !localStorage)
console.warn("Your browser doesn't support localStorage");
},
});
function (Backbone) {
return Backbone.Model.extend({
defaults: {
checkLocal: true,
},
/**
* @private
*/
store: function(data, clb) {
this.checkStorageEnvironment();
for(var key in data)
localStorage.setItem(key, data[key]);
if (typeof clb == 'function') {
clb();
}
},
/**
* @private
*/
load: function(keys){
this.checkStorageEnvironment();
var result = {};
for (var i = 0, len = keys.length; i < len; i++){
var value = localStorage.getItem(keys[i]);
if(value)
result[keys[i]] = value;
}
return result;
},
/**
* @private
*/
remove: function(keys) {
this.checkStorageEnvironment();
for (var i = 0, len = keys.length; i < len; i++)
localStorage.removeItem(keys[i]);
},
/**
* Check storage environment
* @private
* */
checkStorageEnvironment: function() {
if(this.get('checkLocal') && !localStorage)
console.warn("Your browser doesn't support localStorage");
},
});
});

112
src/storage_manager/model/RemoteStorage.js

@ -1,66 +1,70 @@
define(['backbone'],
function (Backbone) {
/**
* @class RemoteStorage
* */
return Backbone.Model.extend({
function (Backbone) {
/**
* @class RemoteStorage
* */
return Backbone.Model.extend({
defaults: {
urlStore: '',
urlLoad: '',
params: {},
beforeSend: function(){},
onComplete: function(){},
},
defaults: {
urlStore: '',
urlLoad: '',
params: {},
beforeSend: function(){},
onComplete: function(){},
},
/**
* @private
*/
store: function(data) {
var fd = {},
params = this.get('params');
/**
* @private
*/
store: function(data, clb) {
var fd = {},
params = this.get('params');
for(var k in data)
fd[k] = data[k];
for(var k in data)
fd[k] = data[k];
for(var key in params)
fd[key] = params[key];
for(var key in params)
fd[key] = params[key];
$.ajax({
url: this.get('urlStore'),
beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'),
method: 'POST',
dataType: 'json',
data: fd,
});
},
$.ajax({
url: this.get('urlStore'),
beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'),
method: 'POST',
dataType: 'json',
data: fd,
}).always(function(){
if (typeof clb == 'function') {
clb();
}
});
},
/**
* @private
*/
load: function(keys){
var result = {},
fd = {},
params = this.get('params');
/**
* @private
*/
load: function(keys){
var result = {},
fd = {},
params = this.get('params');
for(var key in params)
fd[key] = params[key];
for(var key in params)
fd[key] = params[key];
fd.keys = keys;
fd.keys = keys;
$.ajax({
url: this.get('urlLoad'),
beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'),
data: fd,
async: false,
method: 'GET',
}).done(function(d){
result = d;
});
return result;
},
$.ajax({
url: this.get('urlLoad'),
beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'),
data: fd,
async: false,
method: 'GET',
}).done(function(d){
result = d;
});
return result;
},
});
});
});

76
src/trait_manager/view/TraitCheckboxView.js

@ -1,45 +1,45 @@
define(['./TraitView'],
function (TraitView) {
function (TraitView) {
return TraitView.extend({
return TraitView.extend({
initialize: function(o) {
TraitView.prototype.initialize.apply(this, arguments);
var iconCls = this.ppfx + 'chk-icon';
this.tmpl = '<div class="' + this.fieldClass +'"><label class="' + this.inputhClass +'"><i class="' + iconCls +'"></i></label></div>';
},
initialize: function(o) {
TraitView.prototype.initialize.apply(this, arguments);
var iconCls = this.ppfx + 'chk-icon';
this.tmpl = '<div class="' + this.fieldClass +'"><label class="' + this.inputhClass +'"><i class="' + iconCls +'"></i></label></div>';
},
/**
* Fires when the input is changed
* @private
*/
onChange: function() {
this.model.set('value', this.getInputEl().checked);
},
/**
* Fires when the input is changed
* @private
*/
onChange: function() {
this.model.set('value', this.getInputEl().checked);
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
var first;
if(!this.$input)
first = 1;
var el = TraitView.prototype.getInputEl.apply(this, arguments);
if(first){
var md = this.model;
var name = md.get('name');
var target = this.target;
if(md.get('changeProp')){
el.checked = target.get(name);
} else {
var attrs = target.get('attributes');
el.checked = !!attrs[name];
}
}
return el;
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
var first;
if(!this.$input)
first = 1;
var el = TraitView.prototype.getInputEl.apply(this, arguments);
if(first){
var md = this.model;
var name = md.get('name');
var target = this.target;
if(md.get('changeProp')){
el.checked = target.get(name);
} else {
var attrs = target.get('attributes');
el.checked = !!attrs[name];
}
}
return el;
},
});
});
});

41
src/trait_manager/view/TraitNumberView.js

@ -0,0 +1,41 @@
define(['./TraitView', 'Abstract/ui/InputNumber'],
function (TraitView, InputNumber) {
return TraitView.extend({
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
if (!this.$input) {
var value = this.getModelValue();
var inputNumber = new InputNumber({
contClass: this.ppfx + 'field-int',
model: this.model,
ppfx: this.ppfx
});
this.input = inputNumber.render();
this.$input = this.input.inputEl;
this.$unit = this.input.unitEl;
this.model.set('value', value);
this.$input.val(value);
}
return this.$input.get(0);
},
/**
* Renders input
* @private
* */
renderField: function() {
if(!this.$input){
this.$el.append(this.tmpl);
this.getInputEl();
this.$el.find('.' + this.inputhClass).prepend(this.input.el);
}
},
});
});

110
src/trait_manager/view/TraitSelectView.js

@ -1,61 +1,61 @@
define(['backbone','./TraitView'],
function (Backbone, TraitView) {
function (Backbone, TraitView) {
return TraitView.extend({
return TraitView.extend({
initialize: function(o) {
TraitView.prototype.initialize.apply(this, arguments);
var ppfx = this.ppfx;
this.tmpl = '<div class="' + this.fieldClass +'"><div class="' + this.inputhClass +'"></div>'+
'<div class="' + ppfx + 'sel-arrow"><div class="' + ppfx + 'd-s-arrow"></div></div> </div>';
},
initialize: function(o) {
TraitView.prototype.initialize.apply(this, arguments);
var ppfx = this.ppfx;
this.tmpl = '<div class="' + this.fieldClass +'"><div class="' + this.inputhClass +'"></div>'+
'<div class="' + ppfx + 'sel-arrow"><div class="' + ppfx + 'd-s-arrow"></div></div> </div>';
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
if(!this.$input){
var md = this.model;
var opts = md.get('options') || [];
this.input = '<select>';
if(opts.length){
_.each(opts, function(el){
var name, value, style;
var attrs = '';
if(typeof el === 'string'){
name = el;
value = el;
}else{
name = el.name ? el.name : el.value;
value = el.value.replace(/"/g,'&quot;');
style = el.style ? el.style.replace(/"/g,'&quot;') : '';
attrs += style ? 'style="' + style + '"' : '';
}
this.input += '<option value="' + value + '" ' + attrs + '>' + name + '</option>';
}, this);
}
this.input += '</select>';
this.$input = $(this.input);
var target = this.target;
var name = md.get('name');
var val = md.get('value');
if (md.get('changeProp')) {
val = val || target.get(name);
} else {
var attrs = target.get('attributes');
val = attrs[name];
}
if(val)
this.$input.val(val);
}
return this.$input.get(0);
},
});
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
if(!this.$input){
var md = this.model;
var opts = md.get('options') || [];
this.input = '<select>';
if(opts.length){
_.each(opts, function(el){
var name, value, style;
var attrs = '';
if(typeof el === 'string'){
name = el;
value = el;
}else{
name = el.name ? el.name : el.value;
value = el.value.replace(/"/g,'&quot;');
style = el.style ? el.style.replace(/"/g,'&quot;') : '';
attrs += style ? 'style="' + style + '"' : '';
}
this.input += '<option value="' + value + '" ' + attrs + '>' + name + '</option>';
}, this);
}
this.input += '</select>';
this.$input = $(this.input);
var target = this.target;
var name = md.get('name');
var val = md.get('value');
if (md.get('changeProp')) {
val = val || target.get(name);
} else {
var attrs = target.get('attributes');
val = attrs[name];
}
if(val)
this.$input.val(val);
}
return this.$input.get(0);
},
});
});

228
src/trait_manager/view/TraitView.js

@ -1,120 +1,136 @@
define(['backbone'], function (Backbone) {
return Backbone.View.extend({
return Backbone.View.extend({
events:{
'change': 'onChange'
},
events:{
'change': 'onChange'
},
initialize: function(o) {
var md = this.model;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.target = md.target;
this.className = this.pfx + 'trait';
this.labelClass = this.ppfx + 'label';
this.fieldClass = this.ppfx + 'field ' + this.ppfx + 'field-' + md.get('type');
this.inputhClass = this.ppfx + 'input-holder';
md.off('change:value', this.onValueChange);
this.listenTo(md, 'change:value', this.onValueChange);
this.tmpl = '<div class="' + this.fieldClass +'"><div class="' + this.inputhClass +'"></div></div>';
},
initialize: function(o) {
var md = this.model;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.target = md.target;
this.className = this.pfx + 'trait';
this.labelClass = this.ppfx + 'label';
this.fieldClass = this.ppfx + 'field ' + this.ppfx + 'field-' + md.get('type');
this.inputhClass = this.ppfx + 'input-holder';
md.off('change:value', this.onValueChange);
this.listenTo(md, 'change:value', this.onValueChange);
this.tmpl = '<div class="' + this.fieldClass +'"><div class="' + this.inputhClass +'"></div></div>';
},
/**
* Fires when the input is changed
* @private
*/
onChange: function() {
this.model.set('value', this.getInputEl().value);
},
/**
* Fires when the input is changed
* @private
*/
onChange: function() {
this.model.set('value', this.getInputEl().value);
},
/**
* On change callback
* @private
*/
onValueChange: function() {
var m = this.model;
var trg = this.target;
var name = m.get('name');
var value = m.get('value');
// Chabge property if requested otherwise attributes
if(m.get('changeProp')){
trg.set(name, value);
}else{
var attrs = _.clone(trg.get('attributes'));
attrs[name] = value;
trg.set('attributes', attrs);
}
},
/**
* On change callback
* @private
*/
onValueChange: function() {
var m = this.model;
var trg = this.target;
var name = m.get('name');
var value = m.get('value');
// Chabge property if requested otherwise attributes
if(m.get('changeProp')){
trg.set(name, value);
}else{
var attrs = _.clone(trg.get('attributes'));
attrs[name] = value;
trg.set('attributes', attrs);
}
},
/**
* Render label
* @private
*/
renderLabel: function() {
this.$el.html('<div class="' + this.labelClass + '">' + this.getLabel() + '</div>');
},
/**
* Render label
* @private
*/
renderLabel: function() {
this.$el.html('<div class="' + this.labelClass + '">' + this.getLabel() + '</div>');
},
/**
* Returns label for the input
* @return {string}
* @private
*/
getLabel: function() {
var model = this.model;
var label = model.get('label') || model.get('name');
return label.charAt(0).toUpperCase() + label.slice(1).replace(/-/g,' ');
},
/**
* Returns label for the input
* @return {string}
* @private
*/
getLabel: function() {
var model = this.model;
var label = model.get('label') || model.get('name');
return label.charAt(0).toUpperCase() + label.slice(1).replace(/-/g,' ');
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
if(!this.$input){
var md = this.model;
var trg = this.target;
var name = md.get('name');
var opts = {
placeholder: md.get('placeholder') || md.get('default'),
type: md.get('type') || 'text'
};
if(md.get('changeProp')){
opts.value = trg.get(name);
}else{
var attrs = trg.get('attributes');
opts.value = md.get('value') || attrs[name];
}
if(md.get('min'))
opts.min = md.get('min');
if(md.get('max'))
opts.max = md.get('max');
this.$input = $('<input>', opts);
}
return this.$input.get(0);
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl: function() {
if(!this.$input) {
var md = this.model;
var trg = this.target;
var name = md.get('name');
var opts = {
placeholder: md.get('placeholder') || md.get('default'),
type: md.get('type') || 'text'
};
if(md.get('changeProp')){
opts.value = trg.get(name);
}else{
var attrs = trg.get('attributes');
opts.value = md.get('value') || attrs[name];
}
if(md.get('min'))
opts.min = md.get('min');
if(md.get('max'))
opts.max = md.get('max');
this.$input = $('<input>', opts);
}
return this.$input.get(0);
},
/**
* Renders input
* @private
* */
renderField: function(){
if(!this.$input){
this.$el.append(this.tmpl);
var el = this.getInputEl();
this.$el.find('.' + this.inputhClass).prepend(el);
}
},
getModelValue: function () {
var value;
var model = this.model;
var target = this.target;
var name = model.get('name');
render : function() {
this.renderLabel();
this.renderField();
this.el.className = this.className;
return this;
},
if (model.get('changeProp')) {
value = target.get(name);
} else {
var attrs = target.get('attributes');
value = model.get('value') || attrs[name];
}
});
return value;
},
/**
* Renders input
* @private
* */
renderField: function(){
if(!this.$input){
this.$el.append(this.tmpl);
var el = this.getInputEl();
this.$el.find('.' + this.inputhClass).prepend(el);
}
},
render : function() {
this.renderLabel();
this.renderField();
this.el.className = this.className;
return this;
},
});
});

78
src/trait_manager/view/TraitsView.js

@ -1,38 +1,44 @@
define(['backbone', 'Abstract/view/DomainViews', './TraitView', './TraitSelectView', './TraitCheckboxView'],
function (Backbone, DomainViews, TraitView, TraitSelectView, TraitCheckboxView) {
return DomainViews.extend({
itemView: TraitView,
itemsView: {
'text': TraitView,
'select': TraitSelectView,
'checkbox': TraitCheckboxView,
},
initialize: function(o) {
this.config = o.config || {};
this.em = o.editor;
this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'traits';
this.listenTo(this.em, 'change:selectedComponent', this.updatedCollection);
this.updatedCollection();
},
/**
* Update view collection
* @private
*/
updatedCollection: function() {
this.el.className = this.className;
var comp = this.em.get('selectedComponent');
if(comp){
this.collection = comp.get('traits');
this.render();
}
},
});
define(function(require) {
var DomainViews = require('Abstract/view/DomainViews');
var TraitView = require('./TraitView');
var TraitSelectView = require('./TraitSelectView');
var TraitCheckboxView = require('./TraitCheckboxView');
var TraitNumberView = require('./TraitNumberView');
return DomainViews.extend({
itemView: TraitView,
itemsView: {
'text': TraitView,
'number': TraitNumberView,
'select': TraitSelectView,
'checkbox': TraitCheckboxView,
},
initialize: function(o) {
this.config = o.config || {};
this.em = o.editor;
this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'traits';
this.listenTo(this.em, 'change:selectedComponent', this.updatedCollection);
this.updatedCollection();
},
/**
* Update view collection
* @private
*/
updatedCollection: function() {
this.el.className = this.className;
var comp = this.em.get('selectedComponent');
if(comp){
this.collection = comp.get('traits');
this.render();
}
},
});
});

Loading…
Cancel
Save