Browse Source

Fix text component content update, add new component events

pull/67/head v0.4.33
Artur Arseniev 9 years ago
parent
commit
453bd35344
  1. 2
      bower.json
  2. 12
      dist/grapes.min.js
  3. 2
      package.json
  4. 174
      src/dom_components/model/ComponentImage.js
  5. 116
      src/dom_components/view/ComponentImageView.js
  6. 193
      src/dom_components/view/ComponentTextView.js
  7. 42
      src/dom_components/view/ComponentView.js
  8. 7
      src/dom_components/view/ComponentsView.js
  9. 6
      src/editor/main.js
  10. 6
      src/editor/model/Editor.js

2
bower.json

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

12
dist/grapes.min.js

File diff suppressed because one or more lines are too long

2
package.json

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

174
src/dom_components/model/ComponentImage.js

@ -1,97 +1,97 @@
define(['./Component'],
function (Component) {
function (Component) {
return Component.extend({
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
type: 'image',
tagName: 'img',
src: '',
void: 1,
droppable: false,
resizable: true,
traits: ['alt'],
toolbar: [{
attributes: {class: 'fa fa-arrows'},
command: 'tlb-move',
},{
attributes: {class: 'fa fa-clone'},
command: 'tlb-clone',
},{
attributes: {class: 'fa fa-pencil'},
command: 'tlb-edit',
},{
attributes: {class: 'fa fa-trash-o'},
command: 'tlb-delete',
}],
}),
defaults: _.extend({}, Component.prototype.defaults, {
type: 'image',
tagName: 'img',
src: '',
void: 1,
droppable: false,
resizable: true,
traits: ['alt'],
toolbar: [{
attributes: {class: 'fa fa-arrows'},
command: 'tlb-move',
},{
attributes: {class: 'fa fa-clone'},
command: 'tlb-clone',
},{
attributes: {class: 'fa fa-pencil'},
command: 'tlb-edit',
},{
attributes: {class: 'fa fa-trash-o'},
command: 'tlb-delete',
}],
}),
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);
},
/**
* 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;
},
});
});
});

116
src/dom_components/view/ComponentImageView.js

@ -1,72 +1,72 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
function (Backbone, ComponentView) {
return ComponentView.extend({
return 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;
},
});
});

193
src/dom_components/view/ComponentTextView.js

@ -1,96 +1,101 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
return 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) {
this.activeRte = this.rte.attach(this, this.activeRte);
this.rte.focus(this, this.activeRte);
}
this.toggleEvents(1);
},
/**
* Disable this component to be editable
* @param {Event}
* @private
* */
disableEditing: function(e) {
var editable = this.model.get('editable');
if(this.rte && editable) {
this.rte.detach(this, this.activeRte);
}
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 comps = this.model.get('components');
var opts = {silent: true};
// Avoid re-render on reset with silent option
comps.reset(null, opts);
comps.add(this.$el.html(), 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);
},
});
function (Backbone, ComponentView) {
return 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) {
this.activeRte = this.rte.attach(this, this.activeRte);
this.rte.focus(this, this.activeRte);
}
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) {
this.rte.detach(this, this.activeRte);
model.set('content', this.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 comps = this.model.get('components');
var opts = {silent: true};
// Avoid re-render on reset with silent option
comps.reset(null, opts);
comps.add(this.$el.html(), 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);
},
});
});

42
src/dom_components/view/ComponentView.js

@ -16,26 +16,28 @@ define(['backbone', './ComponentsView'],
},
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 = this.model.get('components');
this.attr = this.model.get("attributes");
this.components = model.get('components');
this.attr = model.get("attributes");
this.classe = this.attr.class || [];
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:style', this.updateStyle);
this.listenTo(this.model, 'change:attributes', this.updateAttributes);
this.listenTo(this.model, 'change:status', this.updateStatus);
this.listenTo(this.model, 'change:state', this.updateState);
this.listenTo(this.model, 'change:script', this.render);
this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses);
this.$el.data('model', this.model);
this.model.view = this;
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(this.model.get('classes').length)
if(model.get('classes').length)
this.importClasses();
this.init();
@ -46,6 +48,22 @@ define(['backbone', './ComponentsView'],
*/
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

7
src/dom_components/view/ComponentsView.js

@ -21,8 +21,11 @@ function(Backbone, require) {
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
if(this.config.em) {
this.config.em.trigger('add:component', model);
var em = this.config.em;
if(em) {
// OLD
em.trigger('add:component', model);
em.trigger('component:add', model);
}
},

6
src/editor/main.js

@ -26,10 +26,12 @@
* ```js
* var editor = grapesjs.init({...});
* ```
* Available events
* #add:component - Triggered when a new component is added to the editor, the model is passed as an argument to the callback
*
* **Available events**
* #component:add - Triggered when a new component is added to the editor, the model is passed as an argument to the callback
* #component:update - Triggered when a component is, generally, updated (moved, styled, etc.)
* #component:styleUpdate - Triggered when the style of the component is updated
* #component:update:{propertyName} - Listen any property change
* #canvasScroll - Triggered when the canvas is scrolled
* #run:{commandName} - Triggered when some command is called to run (eg. editor.runCommand('preview'))
* #stop:{commandName} - Triggered when some command is called to stop (eg. editor.stopCommand('preview'))

6
src/editor/model/Editor.js

@ -139,7 +139,6 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
if(!avSt){
this.store();
this.set('changesCount', 0);
}
},
@ -220,7 +219,6 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
if(!avSt){
this.store();
this.set('changesCount', 0);
}
},
@ -398,7 +396,7 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
* @return {Object} Stored data
* @private
*/
store: function(){
store: function() {
var sm = this.get('StorageManager');
var store = {};
if(!sm)
@ -412,6 +410,8 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
});
sm.store(store);
this.set('changesCount', 0);
return store;
},

Loading…
Cancel
Save