diff --git a/README.md b/README.md
index d4c05c9de..5cca10d31 100644
--- a/README.md
+++ b/README.md
@@ -210,11 +210,23 @@ If you like the project support it with a donation of your choice or become a ba
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/grapes.js b/dist/grapes.js
index 243d4d62e..2d78524c8 100644
--- a/dist/grapes.js
+++ b/dist/grapes.js
@@ -93,7 +93,7 @@ return /******/ (function(modules) { // webpackBootstrap
// Set up Backbone appropriately for the environment. Start with AMD.
if (true) {
- !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1), __webpack_require__(8), exports], __WEBPACK_AMD_DEFINE_RESULT__ = function(_, $, exports) {
+ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1), __webpack_require__(9), exports], __WEBPACK_AMD_DEFINE_RESULT__ = function(_, $, exports) {
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backbone.
root.Backbone = factory(root, exports, _, $);
@@ -3686,6 +3686,369 @@ exports.getUnitFromValue = getUnitFromValue;
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
+"use strict";
+/* WEBPACK VAR INJECTION */(function(Backbone) {
+
+var _underscore = __webpack_require__(1);
+
+var ComponentsView = __webpack_require__(50);
+
+module.exports = Backbone.View.extend({
+ className: function className() {
+ return this.getClasses();
+ },
+ tagName: function tagName() {
+ return this.model.get('tagName');
+ },
+ initialize: function initialize() {
+ var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ var model = this.model;
+ var config = opt.config || {};
+ this.opts = opt;
+ this.config = config;
+ this.em = config.em || '';
+ this.pfx = config.stylePrefix || '';
+ this.ppfx = config.pStylePrefix || '';
+ this.attr = model.get('attributes');
+ this.classe = this.attr.class || [];
+ var $el = this.$el;
+ var classes = model.get('classes');
+ 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:highlightable', this.updateHighlight);
+ 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(classes, 'add remove change', this.updateClasses);
+ $el.data('model', model);
+ $el.data('collection', model.get('components'));
+ model.view = this;
+ classes.length && this.importClasses();
+ this.init();
+ },
+ remove: function remove() {
+ Backbone.View.prototype.remove.apply(this);
+ var children = this.childrenView;
+ children && children.stopListening();
+ },
+
+
+ /**
+ * Initialize callback
+ */
+ init: function init() {},
+
+
+ /**
+ * Handle any property change
+ * @private
+ */
+ handleChange: function handleChange() {
+ 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 importClasses() {
+ 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 updateState(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 updateStatus(e) {
+ var el = this.el;
+ var status = this.model.get('status');
+ var pfx = this.pfx;
+ var ppfx = this.ppfx;
+ var selectedCls = pfx + 'selected';
+ var selectedParentCls = selectedCls + '-parent';
+ var freezedCls = ppfx + 'freezed';
+ var actualCls = el.getAttribute('class') || '';
+ var cls = '';
+
+ switch (status) {
+ case 'selected':
+ cls = actualCls + ' ' + selectedCls;
+ break;
+ case 'selected-parent':
+ cls = actualCls + ' ' + selectedParentCls;
+ break;
+ case 'freezed':
+ cls = actualCls + ' ' + freezedCls;
+ break;
+ default:
+ this.$el.removeClass(selectedCls + ' ' + selectedParentCls + ' ' + freezedCls);
+ }
+
+ cls = cls.trim();
+
+ if (cls) {
+ el.setAttribute('class', cls);
+ }
+ },
+
+
+ /**
+ * Update highlight attribute
+ * @private
+ * */
+ updateHighlight: function updateHighlight() {
+ var hl = this.model.get('highlightable');
+ this.setAttribute('data-highlightable', hl ? 1 : '');
+ },
+
+
+ /**
+ * Update style attribute
+ * @private
+ * */
+ updateStyle: function updateStyle() {
+ var em = this.em;
+ var model = this.model;
+
+ if (em && em.get('avoidInlineStyle')) {
+ this.el.id = model.getId();
+ model.setStyle(model.getStyle());
+ } else {
+ this.setAttribute('style', model.styleToString());
+ }
+ },
+
+
+ /**
+ * Update classe attribute
+ * @private
+ * */
+ updateClasses: function updateClasses() {
+ var str = this.model.get('classes').pluck('name').join(' ');
+ this.setAttribute('class', str);
+
+ // Regenerate status class
+ this.updateStatus();
+ },
+
+
+ /**
+ * Update single attribute
+ * @param {[type]} name [description]
+ * @param {[type]} value [description]
+ */
+ setAttribute: function setAttribute(name, value) {
+ var el = this.$el;
+ value ? el.attr(name, value) : el.removeAttr(name);
+ },
+
+
+ /**
+ * Get classes from attributes.
+ * This method is called before initialize
+ *
+ * @return {Array}|null
+ * @private
+ * */
+ getClasses: function getClasses() {
+ var attr = this.model.get("attributes"),
+ classes = attr['class'] || [];
+ classes = (0, _underscore.isArray)(classes) ? classes : [classes];
+
+ if (classes.length) {
+ return classes.join(' ');
+ } else {
+ return null;
+ }
+ },
+
+
+ /**
+ * Update attributes
+ * @private
+ * */
+ updateAttributes: function updateAttributes() {
+ var model = this.model;
+ var attrs = {};
+ var attr = model.get('attributes');
+ var src = model.get('src');
+
+ for (var key in attr) {
+ attrs[key] = attr[key];
+ }
+
+ src && (attrs.src = src);
+ this.$el.attr(attrs);
+ this.updateHighlight();
+ this.updateStyle();
+ },
+
+
+ /**
+ * Update component content
+ * @private
+ * */
+ updateContent: function updateContent() {
+ this.getChildrenContainer().innerHTML = this.model.get('content');
+ },
+
+
+ /**
+ * Prevent default helper
+ * @param {Event} e
+ * @private
+ */
+ prevDef: function prevDef(e) {
+ e.preventDefault();
+ },
+
+
+ /**
+ * Render component's script
+ * @private
+ */
+ updateScript: function updateScript() {
+ if (!this.model.get('script')) {
+ return;
+ }
+
+ 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
+ *