Browse Source

Make parser understand how to manage text nodes

pull/36/head
Artur Arseniev 10 years ago
parent
commit
d6ae05ea6f
  1. 2
      src/code_manager/model/HtmlGenerator.js
  2. 3
      src/dom_components/config/config.js
  3. 2
      src/dom_components/main.js
  4. 5
      src/dom_components/model/Component.js
  5. 7
      src/dom_components/model/Components.js
  6. 7
      src/parser/config/config.js
  7. 7
      src/parser/main.js
  8. 55
      src/parser/model/ParserHtml.js
  9. 43
      test/specs/parser/model/ParserHtml.js

2
src/code_manager/model/HtmlGenerator.js

@ -12,12 +12,12 @@ define(['backbone'],
coll.each(function(m){
var tag = m.get('tagName'), // Tag name
sTag = 0, // Single tag
attr = '', // Attributes string
attrId = '',
strCls = '',
cln = m.get('components'), // Children
attrs = m.get('attributes'),
sTag = m.get('void'),
classes = m.get('classes');
_.each(attrs,function(value, prop){
if(prop == 'onmousedown')

3
src/dom_components/config/config.js

@ -26,5 +26,8 @@ define(function () {
// Open assets manager on create of image component
oAssetsOnCreate : true,
// List of void elements
voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],
};
});

2
src/dom_components/main.js

@ -60,7 +60,7 @@ define(function(require) {
c[name] = defaults[name];
}
var component = new Component(c.wrapper, { sm: c.em });
var component = new Component(c.wrapper, { sm: c.em, config: c });
component.set({
attributes: {id: 'wrapper'}

5
src/dom_components/model/Component.js

@ -13,6 +13,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
badgable: true,
stylable: true,
copyable: true,
void: false,
state: '',
status: '',
previousModel: '',
@ -22,6 +23,10 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
},
initialize: function(o, opt) {
// Check void elements
if(opt && opt.config && opt.config.voidElements.indexOf(this.get('tagName')) >= 0)
this.set('void', true);
this.sm = opt ? opt.sm || {} : {};
this.config = o || {};
this.defaultC = this.config.components || [];

7
src/dom_components/model/Components.js

@ -7,6 +7,8 @@ define([ 'backbone', 'require'],
this.on('add', this.onAdd);
this.config = opt && opt.config ? opt.config : null;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
@ -17,6 +19,9 @@ define([ 'backbone', 'require'],
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(opt && opt.config)
options.config = opt.config;
switch(attrs.type){
case 'text':
@ -53,7 +58,7 @@ define([ 'backbone', 'require'],
onAdd: function(model, c, opts){
var style = model.get('style');
if(!_.isEmpty(style)){
if(!_.isEmpty(style) && this.editor){
var newClass = this.editor.get('ClassManager').addClass(model.cid);
model.get('classes').add(newClass);
var rule = this.editor.get('CssComposer').newRule(newClass);

7
src/parser/config/config.js

@ -0,0 +1,7 @@
define(function () {
return {
textTags: ['br', 'b', 'i', 'u'],
};
});

7
src/parser/main.js

@ -3,9 +3,16 @@ define(function(require) {
var Parser = function(config) {
var c = config || {},
defaults = require('./config/config'),
parserCss = require('./model/ParserCss'),
parserHtml = require('./model/ParserHtml');
// Set default options
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var pHtml = new parserHtml(c);
var pCss = new parserCss(c);

55
src/parser/model/ParserHtml.js

@ -3,6 +3,7 @@ define(function(require) {
return function(config) {
var TEXT_NODE = 'span';
var c = config;
return {
@ -63,6 +64,8 @@ define(function(require) {
var model = {};
var attrs = node.attributes || [];
var attrsLen = attrs.length;
var prevI = result.length - 1;
var prevSib = result[prevI];
model.tagName = node.tagName ? node.tagName.toLowerCase() : '';
if(attrsLen)
@ -74,11 +77,11 @@ define(function(require) {
var nodeValue = attrs[j].nodeValue;
//Isolate style, class and src attributes
if(nodeName === 'style')
if(nodeName == 'style')
model.style = this.parseStyle(nodeValue);
else if(nodeName === 'class')
else if(nodeName == 'class')
model.classes = this.parseClass(nodeValue);
else if(nodeName === 'src' && model.tagName === 'img'){
else if(nodeName == 'src' && model.tagName == 'img'){
model.type = 'image';
model.src = nodeValue;
}else
@ -90,18 +93,50 @@ define(function(require) {
if(nodeChild){
// Avoid infinite text nodes nesting
var firstChild = node.childNodes[0];
if(nodeChild === 1 && firstChild.nodeType === 3 && model.tagName === TEXT_NODE){
if(nodeChild === 1 && firstChild.nodeType === 3){
model.type = 'text';
model.content = firstChild.nodeValue;
}else
model.components = this.parseNode(node);
}else{
var parsed = this.parseNode(node);
// From: <div> <span>TEST</span> </div> <-- span is text type
// TO: <div> TEST </div> <-- div become text type
// With 'nodeChild > 1' I know that nodes were merged
if(parsed.length == 1 && parsed[0].type == 'text' && nodeChild > 1){
model.type = 'text';
model.content = parsed[0].content;
}else
model.components = parsed;
}
}
var prevIsText = prevSib && prevSib.type == 'text' && prevSib.tagName == TEXT_NODE;
// Find text nodes
if(!model.tagName && node.nodeType === 3 && node.nodeValue.trim()){
model.type = 'text';
model.tagName = TEXT_NODE;
model.content = node.nodeValue;
if(!model.tagName && node.nodeType === 3){
// 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
if(node.nodeValue.trim()){
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){
if(prevIsText){
prevSib.content += node.outerHTML;
continue;
}else{
model = {
type: 'text',
tagName: TEXT_NODE,
content: node.outerHTML,
};
}
}
// If tagName is still empty do not push it

43
test/specs/parser/model/ParserHtml.js

@ -9,7 +9,9 @@ define([path + 'model/ParserHtml',],
var obj;
beforeEach(function () {
obj = new ParserHtml();
obj = new ParserHtml({
textTags: ['br', 'b', 'i', 'u'],
});
});
afterEach(function () {
@ -95,13 +97,43 @@ define([path + 'model/ParserHtml',],
it('Parse text nodes', function() {
var str = '<div id="test1">test2 </div>';
var result = {
tagName: 'div',
attributes: { id: 'test1'},
type: 'text',
content: 'test2 ',
};
obj.parse(str).should.deep.equal(result);
});
it('Parse text with few text tags', function() {
var str = '<div id="test1"><br/> test2 <br/> a b <b>b</b> <i>i</i> <u>u</u> test </div>';
var result = {
tagName: 'div',
attributes: { id: 'test1'},
type: 'text',
content: '<br> test2 <br> a b <b>b</b> <i>i</i> <u>u</u> test ',
};
obj.parse(str).should.deep.equal(result);
});
it('Parse text with few text tags and nested node', function() {
var str = '<div id="test1">a b <b>b</b> <i>i</i>c <div>ABC</div> <i>i</i> <u>u</u> test </div>';
var result = {
tagName: 'div',
attributes: { id: 'test1'},
components: [{
tagName: 'span',
type: 'text',
content: 'test2 ',
content: 'a b <b>b</b> <i>i</i>c ',
},{
tagName: 'div',
type: 'text',
content: 'ABC',
},{
tagName: 'span',
type: 'text',
content: '<i>i</i> <u>u</u> test ',
}],
};
obj.parse(str).should.deep.equal(result);
@ -141,11 +173,8 @@ define([path + 'model/ParserHtml',],
content: 'content1 ',
},{
tagName: 'div',
components: [{
tagName: 'span',
type: 'text',
content: 'nested',
}]
type: 'text',
content: 'nested',
},{
tagName: 'span',
type: 'text',

Loading…
Cancel
Save