diff --git a/src/config/require-config.js b/src/config/require-config.js index 9d4656c78..b55d859de 100644 --- a/src/config/require-config.js +++ b/src/config/require-config.js @@ -46,6 +46,7 @@ require.config({ { name: 'Commands', location: 'commands', }, { name: 'Canvas', location: 'canvas', }, { name: 'Panels', location: 'panels', }, + { name: 'Parser', location: 'parser', }, { name: 'Utils', location: 'utils', } ] }); \ No newline at end of file diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 584dd7914..c989fd27c 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -13,6 +13,7 @@ define([ 'DomComponents', 'ClassManager', 'Panels', + 'Parser', 'Utils'], function( Backbone, @@ -29,6 +30,7 @@ define([ DomComponents, ClassManager, Panels, + Parser, Utils ){ return Backbone.Model.extend({ @@ -61,10 +63,19 @@ define([ this.initUndoManager(); this.initCssComposer(); this.initUtils(); + this.initParser(); this.on('change:selectedComponent', this.componentSelected, this); }, + /** + * Initialize Parser + * */ + initParser: function() { + this.parser = new Parser(); + this.set('parser', this.parser); + }, + /** * Initialize Utils * */ diff --git a/src/parser/main.js b/src/parser/main.js new file mode 100644 index 000000000..611f69a52 --- /dev/null +++ b/src/parser/main.js @@ -0,0 +1,27 @@ +define(function(require) { + + var Parser = function(config) { + + var c = config || {}, + parserCss = require('./model/ParserCss'), + parserHtml = require('./model/ParserHtml'); + + var pHtml = new parserHtml(c); + var pCss = new parserCss(c); + + return { + + parseHtml: function(str){ + return pHtml.parse(str); + }, + + parseCss: function(str){ + return pCss.parse(str); + }, + + }; + }; + + return Parser; + +}); \ No newline at end of file diff --git a/src/parser/model/ParserCss.js b/src/parser/model/ParserCss.js new file mode 100644 index 000000000..6cfd90132 --- /dev/null +++ b/src/parser/model/ParserCss.js @@ -0,0 +1,15 @@ +define(function(require) { + + return function(config) { + + return { + + parse: function(str){ + return {parsed: 'CSS '+str}; + }, + + }; + + }; + +}); \ No newline at end of file diff --git a/src/parser/model/ParserHtml.js b/src/parser/model/ParserHtml.js new file mode 100644 index 000000000..2d91eab8f --- /dev/null +++ b/src/parser/model/ParserHtml.js @@ -0,0 +1,99 @@ +define(function(require) { + + return function(config) { + + return { + + /** + * Parse style string to object + * @param {string} str + * @return {Object} + * @example + * var stl = ParserHtml.parseStyle('color:black; width:100px; test:value;'); + * console.log(stl); + * // {color: 'black', width: '100px', test: 'value'} + */ + parseStyle: function(str){ + var result = {}; + var decls = str.split(';'); + for (var i = 0, len = decls.length; i < len; i++) { + var decl = decls[i].trim(); + if(!decl) + continue; + var prop = decl.split(':'); + result[prop[0].trim()] = prop[1].trim(); + } + return result; + }, + + /** + * Parse class string to array + * @param {string} str + * @return {Array} + * @example + * var res = ParserHtml.parseClass('test1 test2 test3'); + * console.log(res); + * // ['test1', 'test2', 'test3'] + */ + parseClass: function(str){ + var result = []; + var cls = str.split(' '); + for (var i = 0, len = cls.length; i < len; i++) { + var cl = cls[i].trim(); + if(!cl) + continue; + result.push(cl); + } + return result; + }, + + /** + * Parse HTML string to a desired model object + * @param {string} str HTML string + * @return {Object} + */ + parse: function(str){ + var el = document.createElement('div'); + el.innerHTML = str; + var nodes = el.childNodes; + var result = []; + + // Iterate all nodes + for (var i = 0, len = nodes.length; i < len; i++) { + var node = nodes[i]; + var model = {}; + var attrs = node.attributes; + var attrsLen = attrs.length; + model.tagName = node.tagName.toLowerCase(); + + if(attrsLen) + model.attributes = {}; + + // Store attributes + for (var j = 0; j < attrsLen; j++){ + var nodeName = attrs[j].nodeName; + var nodeValue = attrs[j].nodeValue; + + //Isolate style and class attributes + if(nodeName === 'style') + model.style = this.parseStyle(nodeValue); + else if(nodeName === 'class') + model.classes = this.parseClass(nodeValue); + else + model.attributes[nodeName] = nodeValue; + } + + result.push(model); + } + + if(result.length == 1) + result = result[0]; + + return result; + }, + + }; + + }; + +}); \ No newline at end of file diff --git a/test/runner/main.js b/test/runner/main.js index 4c69e4edd..7282656c7 100644 --- a/test/runner/main.js +++ b/test/runner/main.js @@ -19,6 +19,7 @@ require(['../src/config/require-config.js', 'config/config.js'], function() { 'specs/commands/main.js', 'specs/style_manager/main.js', 'specs/storage_manager/main.js', + 'specs/parser/main.js', 'specs/utils/main.js' ], function(chai) { diff --git a/test/specs/parser/main.js b/test/specs/parser/main.js new file mode 100644 index 000000000..298e07cc0 --- /dev/null +++ b/test/specs/parser/main.js @@ -0,0 +1,17 @@ +var modulePath = './../../../test/specs/parser'; + +define([ + 'Parser', + modulePath + '/model/ParserHtml' + ], + function( + Parser, + ParserHtml + ) { + + describe('Parser', function() { + + ParserHtml.run(); + + }); +}); \ No newline at end of file diff --git a/test/specs/parser/model/ParserHtml.js b/test/specs/parser/model/ParserHtml.js new file mode 100644 index 000000000..229334d68 --- /dev/null +++ b/test/specs/parser/model/ParserHtml.js @@ -0,0 +1,102 @@ +var path = 'Parser/'; +define([path + 'model/ParserHtml',], + function(ParserHtml) { + + return { + run : function(){ + + describe('ParserHtml', function() { + var obj; + + beforeEach(function () { + obj = new ParserHtml(); + }); + + afterEach(function () { + delete obj; + }); + + it('Simple div node', function() { + var str = '
'; + var result = { tagName: 'div'}; + obj.parse(str).should.deep.equal(result); + }); + + it('Simple article node', function() { + var str = '
'; + var result = { tagName: 'article'}; + obj.parse(str).should.deep.equal(result); + }); + + it('Node with attributes', function() { + var str = '
'; + var result = { + tagName: 'div', + classes: ['test2', 'test3'], + attributes: { + 'data-one': 'test4', + id: 'test1', + 'strange': 'test5' + } + }; + obj.parse(str).should.deep.equal(result); + }); + + it('Parse style string to object', function() { + var str = 'color:black; width:100px; test:value;'; + var result = { + color: 'black', + width: '100px', + test: 'value', + }; + obj.parseStyle(str).should.deep.equal(result); + }); + + it('Parse class string to array', function() { + var str = 'test1 test2 test3 test-4'; + var result = ['test1', 'test2', 'test3', 'test-4']; + obj.parseClass(str).should.deep.equal(result); + }); + + it('Style attribute is isolated', function() { + var str = '
'; + var result = { + tagName: 'div', + attributes: { id: 'test1'}, + style: { + color: 'black', + width: '100px', + test: 'value', + } + }; + obj.parse(str).should.deep.equal(result); + }); + + it('Class attribute is isolated', function() { + var str = '
'; + var result = { + tagName: 'div', + attributes: { id: 'test1'}, + classes: ['test2', 'test3', 'test4'] + }; + obj.parse(str).should.deep.equal(result); + }); + + it.skip('Parse nested nodes', function() { + + }); + + it.skip('Parse images nodes', function() { + + }); + + it.skip('Parse text nodes', function() { + + }); + + }); + + } + }; + +}); \ No newline at end of file