From 5e5977c83a06959fe34aa1da86adbe663f622920 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 9 Nov 2016 15:42:51 +0100 Subject: [PATCH] Update text component and the html parser --- src/demo.js | 36 ++++++++++--------- src/dom_components/main.js | 8 ++--- src/dom_components/model/ComponentImage.js | 6 ++-- src/dom_components/model/ComponentTextNode.js | 17 +++++++++ src/dom_components/model/Components.js | 2 ++ .../view/ComponentTextNodeView.js | 9 +---- src/dom_components/view/ComponentTextView.js | 31 +++++++++++----- src/dom_components/view/ComponentView.js | 2 +- src/dom_components/view/ComponentsView.js | 3 ++ src/parser/model/ParserHtml.js | 20 ++++++----- src/rich_text_editor/config/config.js | 35 ++++++++++-------- .../view/CommandButtonsView.js | 13 ++++--- src/rich_text_editor/view/TextEditorView.js | 17 +++++---- 13 files changed, 124 insertions(+), 75 deletions(-) diff --git a/src/demo.js b/src/demo.js index 8f6042028..b5610a1df 100644 --- a/src/demo.js +++ b/src/demo.js @@ -9,27 +9,31 @@ require(['config/require-config'], function() { noticeOnUnload: 0, container : '#gjs', height: '100%', - fromElement: true, - /* + //fromElement: true, + components: [{ + type: 'text', style:{ width:'100px', - height:'100px' - }, - traits: ['title'] - },{ - style:{ - width:'150px', - height:'100px' + height:'100px', + margin: '50px auto', }, - traits: [{name:'title', value: "myTitleTest"}] - },{ - type: 'image' - },{ - type: 'link', - content: 'mylink', + traits: ['title'], + components: [{ + type: 'textnode', + content: 'text node row', + },{ + type: 'textnode', + content: ', another text node', + },{ + type: 'link', + content: 'someLink', + },{ + type: 'textnode', + content: " More text node --- ", + }], }], - */ + storageManager:{ autoload: 0, }, diff --git a/src/dom_components/main.js b/src/dom_components/main.js index 57b84d2bb..284c42bd6 100644 --- a/src/dom_components/main.js +++ b/src/dom_components/main.js @@ -58,14 +58,14 @@ define(function(require) { model: require('./model/ComponentImage'), view: require('./view/ComponentImageView'), }, - 'text': { - model: require('./model/ComponentText'), - view: require('./view/ComponentTextView'), - }, 'textnode': { model: require('./model/ComponentTextNode'), view: require('./view/ComponentTextNodeView'), }, + 'text': { + model: require('./model/ComponentText'), + view: require('./view/ComponentTextView'), + }, 'default': { model: Component, view: ComponentView, diff --git a/src/dom_components/model/ComponentImage.js b/src/dom_components/model/ComponentImage.js index 8901deb13..b5ac60eec 100644 --- a/src/dom_components/model/ComponentImage.js +++ b/src/dom_components/model/ComponentImage.js @@ -67,8 +67,10 @@ define(['./Component'], isComponent: function(el) { var result = ''; if(el.tagName == 'IMG'){ - result = {type: 'image'}; - result.src = el.src; + result = { + type: 'image', + src: el.src + }; } return result; }, diff --git a/src/dom_components/model/ComponentTextNode.js b/src/dom_components/model/ComponentTextNode.js index b34f10291..a27cd2432 100644 --- a/src/dom_components/model/ComponentTextNode.js +++ b/src/dom_components/model/ComponentTextNode.js @@ -7,5 +7,22 @@ define(['./Component'], droppable: false, }), + toHTML: function() { + return this.get('content'); + }, + + }, { + + isComponent: function(el) { + var result = ''; + if(el.nodeType === 3){ + result = { + type: 'textnode', + content: el.textContent + }; + } + return result; + }, + }); }); diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index a12ef5961..140d25ea0 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -51,6 +51,8 @@ define([ 'backbone', 'require'], if(parsed.css && cssc){ var added = cssc.addCollection(parsed.css); } + + console.log('Parsed from add', models); } return Backbone.Collection.prototype.add.apply(this, [models, opt]); diff --git a/src/dom_components/view/ComponentTextNodeView.js b/src/dom_components/view/ComponentTextNodeView.js index 9cc28f433..29abacbe2 100644 --- a/src/dom_components/view/ComponentTextNodeView.js +++ b/src/dom_components/view/ComponentTextNodeView.js @@ -1,12 +1,5 @@ define(['backbone'], function (Backbone) { - return Backbone.View.extend({ - - render: function() { - this.el.innerHTML = this.model.get('content'); - return this; - }, - - }); + return Backbone.View.extend({}); }); diff --git a/src/dom_components/view/ComponentTextView.js b/src/dom_components/view/ComponentTextView.js index 929e1b70f..71b5efde7 100644 --- a/src/dom_components/view/ComponentTextView.js +++ b/src/dom_components/view/ComponentTextView.js @@ -4,7 +4,8 @@ define(['backbone', './ComponentView'], return ComponentView.extend({ events: { - 'dblclick' : 'enableEditing', + 'dblclick': 'enableEditing', + 'change': 'parseRender', }, initialize: function(o){ @@ -14,6 +15,20 @@ define(['backbone', './ComponentView'], this.rte = this.config.rte || ''; }, + /** + * 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(); + }, + /** * Enable the component to be editable * @param {Event} e @@ -34,7 +49,8 @@ define(['backbone', './ComponentView'], if(this.rte) this.rte.detach(this); this.toggleEvents(); - this.updateContents(); + //this.updateContents(); + this.parseRender(); }, /** @@ -48,10 +64,11 @@ define(['backbone', './ComponentView'], /** * Update contents of the element + * TODO to remove * @private **/ updateContents: function(){ - this.model.set('content', this.el.innerHTML); + //this.model.set('content', this.el.innerHTML); }, /** @@ -62,16 +79,12 @@ define(['backbone', './ComponentView'], 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); }, - render: function() { - this.updateAttributes(); - this.updateClasses(); - this.el.innerHTML = this.model.get('content'); - return this; - }, }); }); diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index f23d08cd6..faac603e5 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -179,7 +179,7 @@ define(['backbone', './ComponentsView'], this.updateClasses(); this.$el.html(this.model.get('content')); var view = new ComponentsView({ - collection: this.components, + collection: this.model.get('components'), config: this.config, defaultTypes: this.opts.defaultTypes, componentTypes: this.opts.componentTypes, diff --git a/src/dom_components/view/ComponentsView.js b/src/dom_components/view/ComponentsView.js index 76f794dee..04386785f 100644 --- a/src/dom_components/view/ComponentsView.js +++ b/src/dom_components/view/ComponentsView.js @@ -36,6 +36,7 @@ function(Backbone, require) { 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; @@ -50,6 +51,8 @@ function(Backbone, require) { 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); diff --git a/src/parser/model/ParserHtml.js b/src/parser/model/ParserHtml.js index c3c40b719..994174f2c 100644 --- a/src/parser/model/ParserHtml.js +++ b/src/parser/model/ParserHtml.js @@ -97,10 +97,7 @@ define(function(require) { model.style = this.parseStyle(nodeValue); else if(nodeName == 'class') model.classes = this.parseClass(nodeValue); - else if(nodeName == 'src' && model.tagName == 'img'){ - model.type = 'image'; - model.src = nodeValue; - }else + else model.attributes[nodeName] = nodeValue; } @@ -110,7 +107,9 @@ define(function(require) { // Avoid infinite text nodes nesting var firstChild = node.childNodes[0]; if(nodeChild === 1 && firstChild.nodeType === 3){ - model.type = 'text'; + if(!model.type){ + model.type = 'text'; + } model.content = firstChild.nodeValue; }else{ var parsed = this.parseNode(node); @@ -128,19 +127,22 @@ define(function(require) { var prevIsText = prevSib && prevSib.type == 'text' && prevSib.tagName == TEXT_NODE; // Find text nodes - if(!model.tagName && node.nodeType === 3){ + /* + if(model.type == 'textnode'){ // Pass content to the previous model if it's a text node if(prevIsText){ prevSib.content += node.nodeValue; continue; } - // Make it text node only the content is not empty + // Make it text node only if the content is not empty if(node.nodeValue.trim()){ + console.log('part 1', model); model.type = 'text'; model.tagName = TEXT_NODE; model.content = node.nodeValue; } } + */ // Check if it's a text node and if it could be moved to the prevous model if(c.textTags.indexOf(model.tagName) >= 0){ @@ -156,8 +158,8 @@ define(function(require) { } } - // If tagName is still empty do not push it - if(!model.tagName) + // If tagName is still empty and is not a textnode, do not push it + if(!model.tagName && model.type != 'textnode') continue; result.push(model); diff --git a/src/rich_text_editor/config/config.js b/src/rich_text_editor/config/config.js index 8bf8a5821..3ec451379 100644 --- a/src/rich_text_editor/config/config.js +++ b/src/rich_text_editor/config/config.js @@ -4,24 +4,29 @@ define(function () { toolbarId : 'toolbar', containerId : 'wrapper', commands : [{ - command: 'bold', - title: 'Bold', - class: 'fa fa-bold', + command: 'bold', + title: 'Bold', + class: 'fa fa-bold', },{ - command: 'italic', - title: 'Italic', - class: 'fa fa-italic', + command: 'italic', + title: 'Italic', + class: 'fa fa-italic', },{ - command: 'underline', - title: 'Underline', - class: 'fa fa-underline', + command: 'underline', + title: 'Underline', + class: 'fa fa-underline', },{ - command: 'strikethrough', - title: 'Strikethrough', - class: 'fa fa-strikethrough', - group: 'format' + command: 'strikethrough', + title: 'Strikethrough', + class: 'fa fa-strikethrough', + group: 'format' + },{ + command: 'insertHTML', + title: 'Link', + class: 'fa fa-link', + args: '${content}', }/*,{ - command: 'fontSize', + command: 'fontSize', options: [ {name: 'Huge', value: '7'}, {name: 'Normal', value: '5'}, @@ -29,4 +34,4 @@ define(function () { ] }*/], }; -}); \ No newline at end of file +}); diff --git a/src/rich_text_editor/view/CommandButtonsView.js b/src/rich_text_editor/view/CommandButtonsView.js index a42f381c1..9c992ac3d 100644 --- a/src/rich_text_editor/view/CommandButtonsView.js +++ b/src/rich_text_editor/view/CommandButtonsView.js @@ -38,13 +38,16 @@ define(['backbone','./CommandButtonView', './CommandButtonSelectView'], viewObj = CommandButtonSelectView; break; } - + var args = model.get('args'); + var attrs = { + 'title': model.get('title'), + 'data-edit': model.get('command'), + }; + if(args) + attrs['data-args'] = args; var view = new viewObj({ model: model, - attributes: { - 'title': model.get('title'), - 'data-edit': model.get('command'), - }, + attributes: attrs, }, this.config); var rendered = view.render().el; diff --git a/src/rich_text_editor/view/TextEditorView.js b/src/rich_text_editor/view/TextEditorView.js index 50bfa6461..58b79a2dd 100644 --- a/src/rich_text_editor/view/TextEditorView.js +++ b/src/rich_text_editor/view/TextEditorView.js @@ -45,6 +45,7 @@ define(['jquery'], editor.get(0).ownerDocument.execCommand("styleWithCSS", false, true); editor.get(0).ownerDocument.execCommand(command, 0, args); updateToolbar(); + editor.trigger('change'); }, /* bindHotkeys = function (hotKeys) { @@ -112,7 +113,16 @@ define(['jquery'], toolbar.find(toolbarBtnSelector).unbind().click(function () { restoreSelection(); //editor.focus(); // cause defocus on selects - editor.get(0).ownerDocument.execCommand($(this).data(options.commandRole)); + var doc = editor.get(0).ownerDocument; + var el = $(this); + var comm = el.data(options.commandRole); + var args = el.data('args'); + if(args){ + args = args.replace('${content}', doc.getSelection()); + execCommand(comm, args); + }else{ + doc.execCommand(comm); + } saveSelection(); }); toolbar.find('[data-toggle=dropdown]').click(restoreSelection); @@ -124,11 +134,9 @@ define(['jquery'], editor.focus(); execCommand($(this).data(options.commandRole), newValue); } - console.log('change isolated2 ', newValue); saveSelection(); }); toolbar.find('input[type=text]'+dName,', select'+dName).on('webkitspeechchange change', function () { - console.log('on changed ', newValue); var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */ this.value = ''; restoreSelection(); @@ -138,10 +146,8 @@ define(['jquery'], } saveSelection(); }).on('focus', function () { - console.log('on focus '); var input = $(this); if (!input.data(options.selectionMarker)) { - console.log('i have no ', options.selectionMarker); markSelection(input, options.selectionColor); input.focus(); } @@ -228,4 +234,3 @@ define(['jquery'], return $; }); -