From 0f406b246f9261651b5982be71a49189349c8e3e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 20 Jun 2013 10:01:21 +0200 Subject: [PATCH] Experimenting with higher level canvas api --- karma.conf.js | 1 + modules/documentCanvas/canvas.js | 135 ++++++++++++++++++++ modules/documentCanvas/tests/canvas.test.js | 103 +++++++++++++++ modules/documentCanvas/tests/utils.js | 29 +++++ modules/documentCanvas/tests/utils.test.js | 29 +++++ tests/main.js | 17 ++- 6 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 modules/documentCanvas/canvas.js create mode 100644 modules/documentCanvas/tests/canvas.test.js create mode 100644 modules/documentCanvas/tests/utils.js create mode 100644 modules/documentCanvas/tests/utils.test.js diff --git a/karma.conf.js b/karma.conf.js index b6085cc..9f3fa53 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -6,6 +6,7 @@ files = [ REQUIRE, REQUIRE_ADAPTER, + 'vkbeautify.js', {pattern: 'libs/*.js', included: false}, {pattern: 'fnpjs/**/*.js', included: false}, {pattern: 'modules/**/*.js', included: false}, diff --git a/modules/documentCanvas/canvas.js b/modules/documentCanvas/canvas.js new file mode 100644 index 0000000..2d364ac --- /dev/null +++ b/modules/documentCanvas/canvas.js @@ -0,0 +1,135 @@ +define([ +'libs/jquery-1.9.1.min', +'libs/underscore-min', +'modules/documentCanvas/transformations', +'modules/documentCanvas/wlxmlNode', +'libs/text!./template.html' +], function($, _, transformations, wlxmlNode, template) { + +'use strict'; + +var Canvas = function(xml) { + this.xml = xml; + this.dom = $(template); + + this.content = this.dom.find('#rng-module-documentCanvas-content') + + + this.content.html(transformations.fromXML.getHTMLTree(xml)); +} + +Canvas.prototype.toXML = function() { + return transformations.toXML.getXML(this.content.html()); +} + +Canvas.prototype.getNode = function(desc) { + var selector = ''; + if(desc.klass) + selector += '[wlxml-class=' + desc.klass + ']'; + if(desc.tag) + selector += '[wlxml-tag=' + desc.tag + ']'; + var toret = []; + this.content.find(selector).each(function() { + toret.push(new wlxmlNode.Node($(this))); + }); + return toret; +} + +Canvas.prototype._createNode = function(wlxmlTag, wlxmlClass) { + var toBlock = ['div', 'document', 'section', 'header']; + var htmlTag = _.contains(toBlock, wlxmlTag) ? 'div' : 'span'; + var toret = $('<' + htmlTag + '>'); + toret.attr('wlxml-tag', wlxmlTag); + if(wlxmlClass) + toret.attr('wlxml-class', wlxmlClass); + toret.attr('id', 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random()*16|0,v=c=='x'?r:r&0x3|0x8;return v.toString(16);})); + return toret; + }; + +Canvas.prototype.insertNode = function(options) { + var element = $(this.content.find('#' + options.context.id).get(0)); + if(options.place == 'after') + element[options.place](this._createNode(options.tag, options.klass)); + else if(options.place == 'wrapText') { + var elementContents = element.contents(); + if(elementContents.length !== 1 || elementContents.get(0).nodeType != 3) + return false; + var textElement = elementContents.get(0); + + var prefix = textElement.data.substr(0, options.offsetStart); + var suffix = textElement.data.substr(options.offsetEnd); + var core = textElement.data.substr(options.offsetStart, options.offsetEnd - options.offsetStart); + var newNode = this._createNode(options.tag, options.klass); + newNode.text(core); + $(textElement).replaceWith(newNode); + newNode.before(prefix); + newNode.after(suffix); + } +} + +Canvas.prototype.splitNode = function(options) { + var element = $(this.content.find('#' + options.node.id).get(0)); + + var elementContents = element.contents(); + if(elementContents.length !== 1 || elementContents.get(0).nodeType != 3) + return false; + var textElement = elementContents.get(0); + + var prefix = textElement.data.substr(0, options.offset); + var suffix = textElement.data.substr(options.offset); + var prefixNode = this._createNode(element.attr('wlxml-tag'), element.attr('wlxml-class')); + var suffixNode = this._createNode(element.attr('wlxml-tag'), element.attr('wlxml-class')); + prefixNode.text(prefix); + suffixNode.text(suffix); + element.before(prefixNode); + element.after(suffixNode); + element.remove(); +} + +Canvas.prototype.createList = function(options) { + var element1 = $(this.content.find('#' + options.start.id).get(0)); + var element2 = $(this.content.find('#' + options.end.id).get(0)); + if(!element1.parent().get(0).isSameNode(element2.parent().get(0))) + return false; + + var parent = element1.parent(); + var nodesToWrap = []; + + var place = 'before'; + var canvas = this; + parent.contents().each(function() { + var node = this; + if(node.isSameNode(element1.get(0))) + place = 'inside'; + if(place === 'inside') { + var $node; + if(node.nodeType === 3) { + $node = canvas._createNode('div').text(node.data); + $(node).remove(); + } + else { + $node = $(node); + } + $node.attr('wlxml-class', 'list.item'); + nodesToWrap.push($node); + } + if(node.isSameNode(element2.get(0))) + return; + }); + + var list = this._createNode('div', 'list'); + element1.before(list); + + nodesToWrap.forEach(function(node) { + node.remove(); + list.append(node); + }); + + + +} + + +return {Canvas: Canvas, Node: Node}; + +}); \ No newline at end of file diff --git a/modules/documentCanvas/tests/canvas.test.js b/modules/documentCanvas/tests/canvas.test.js new file mode 100644 index 0000000..386e375 --- /dev/null +++ b/modules/documentCanvas/tests/canvas.test.js @@ -0,0 +1,103 @@ +define([ +'libs/jquery-1.9.1.min', +'libs/chai', +'./utils.js', +'modules/documentCanvas/canvas' +], function($, chai, utils, canvas) { + + 'use strict'; + + var assert = chai.assert; + + assert.xmlEqual = function(lhsText, rhsText) { + var cleanLhs = utils.cleanUp(lhsText); + var cleanRhs = utils.cleanUp(rhsText); + + var lhs = $(cleanLhs); + var rhs = $(cleanRhs); + + this.equal(lhs.length, 1); + this.equal(rhs.length, 1); + + lhs = lhs.get(0); + rhs = rhs.get(0); + + var test = lhs.isEqualNode(rhs); + if(!test) { + console.log(cleanLhs); + console.log(cleanRhs); + } + return this.ok(test, 'xmls are equal'); + }; + + var retrievingTest = function(title, xml) { + test(title, function() { + var c = new canvas.Canvas(xml); + assert.xmlEqual(c.toXML(), xml); + }); + }; + + suite('Basic document retrieving', function() { + test('empty document', function() { + var c = new canvas.Canvas(''); + assert.equal(c.toXML(), ''); + }); + retrievingTest('empty tag', '
'); + retrievingTest('tag with content', '
Some text
'); + retrievingTest('tag with class', '
'); + }); + + suite('Nodes', function() { + test('getting nodes via selector', function() { + var c = new canvas.Canvas('
Header 1
'); + var header = c.getNode({tag: 'header'})[0]; + assert.equal(header.tag, 'header'); + assert.equal(header.klass, 'some-class'); + }); + + test('inserting after', function() { + var c = new canvas.Canvas('
Header 1
'); + var header = c.getNode({tag: 'header'})[0]; + c.insertNode({place: 'after', context: header, tag: 'div', klass: 'some.class'}); + assert.xmlEqual(c.toXML(), '
Header 1
'); + }); + + test('wrap text in node', function() { + var c = new canvas.Canvas('
Header 1
'); + var header = c.getNode({tag: 'header'})[0]; + c.insertNode({place: 'wrapText', context: header, tag: 'span', klass: 'url', offsetStart: 1, offsetEnd: 6}); + assert.xmlEqual(c.toXML(), '
Header 1
'); + }); + + test('split node', function() { + var c = new canvas.Canvas('
Header 1
'); + var header = c.getNode({tag: 'header'})[0]; + c.splitNode({node: header, offset: 4}); + assert.xmlEqual(c.toXML(), '\ +
\ +
Head
\ +
er 1
\ +
' + ); + }); + + test('list', function() { + var c = new canvas.Canvas('
Alice
has
a cat
'); + var div1 = c.getNode({tag:'div'})[0]; + var div2 = c.getNode({tag:'div'})[1]; + + c.createList({start: div1, end: div2}); + + assert.xmlEqual(c.toXML(), '\ +
\ +
\ +
Alice
\ +
has
\ +
a cat
\ +
\ +
'); + + }); + }); + +}); \ No newline at end of file diff --git a/modules/documentCanvas/tests/utils.js b/modules/documentCanvas/tests/utils.js new file mode 100644 index 0000000..e23808c --- /dev/null +++ b/modules/documentCanvas/tests/utils.js @@ -0,0 +1,29 @@ +define(['libs/jquery-1.9.1.min'], function($) { + return { + cleanUp: function(xml) { + + var rmws = function(node) { + if(node.nodeType === 3) { + node.data = $.trim(node.data); + } + else { + $(node).contents().each(function() { + rmws(this); + }); + } + } + + xml = $($.trim(xml)); + xml.each(function() { + rmws(this); + }); + + /*var toret = xml + .replace(/(<.*>)\s*(<.*>)/gm, '$1$2') + .replace(/(<\/.*>)\s*(<\/.*>)/gm, '$1$2') + .replace(/(<\/.*>)\s*(<.*>)/gm, '$1$2'); + return $.trim(toret);*/ + return $('
').append(xml).html(); + } + } +}); \ No newline at end of file diff --git a/modules/documentCanvas/tests/utils.test.js b/modules/documentCanvas/tests/utils.test.js new file mode 100644 index 0000000..d25b3d3 --- /dev/null +++ b/modules/documentCanvas/tests/utils.test.js @@ -0,0 +1,29 @@ +define(['libs/chai', './utils.js'], function(chai, utils) { + +'use strict'; +var assert = chai.assert; + +test('open+open', function() { + assert.equal(utils.cleanUp('
\n
'), '
'); +}) + +test('close+close', function() { + assert.equal(utils.cleanUp('
\n
'), '
'); +}) + +test('close+open', function() { + assert.equal(utils.cleanUp('
\n
'), '
'); +}) + +test('bug', function() { + var txt = '\ +
\ +
Head
\ +
er 1
\ +
' + var txt2 = '
Head
er 1
'; + assert.equal(utils.cleanUp(txt), txt2); +}); + + +}); \ No newline at end of file diff --git a/tests/main.js b/tests/main.js index 8a093d9..9614d72 100644 --- a/tests/main.js +++ b/tests/main.js @@ -9,7 +9,22 @@ require({ baseUrl: '/base/', deps: tests, - callback: window.__karma__.start + callback: window.__karma__.start, + shim: { + 'libs/jquery-1.9.1.min': { + exports: '$', + }, + 'libs/underscore-min': { + exports: '_' + }, + 'libs/bootstrap/js/bootstrap.min': { + deps: ['libs/jquery-1.9.1.min'] + }, + 'libs/backbone-min': { + exports: 'Backbone', + deps: ['libs/jquery-1.9.1.min', 'libs/underscore-min'] + } + } }); })(); \ No newline at end of file -- 2.20.1