From 7dc3d4d5e031615e4dd5393fe126daa7248ce184 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 15:51:29 +0200 Subject: [PATCH 01/16] Fixing wrapping part of text element - handling start/end equal to zero, - handling start > end --- modules/documentCanvas/canvas/canvas.test3.js | 52 ++++++++++++------- .../documentCanvas/canvas/documentElement.js | 6 +-- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index 34c372d..0d8e87a 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -375,26 +375,42 @@ describe('Canvas', function() { expect(returned.getWlxmlClass()).to.equal('some.class'); }); - it('wraps part of DocumentTextElement', function() { - var c = canvas.fromXML('
Alice has a cat
'), - text = c.doc().children()[0]; - - var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 5, end: 12}), - children = c.doc().children(); - - expect(children.length).to.equal(3); - - expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement); - expect(children[0].getText()).to.equal('Alice'); + describe('wrapping part of DocumentTextElement', function() { + [{start: 5, end: 12}, {start: 12, end: 5}].forEach(function(offsets) { + it('wraps in the middle ' + offsets.start + '/' + offsets.end, function() { + var c = canvas.fromXML('
Alice has a cat
'), + text = c.doc().children()[0]; + + var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: offsets.start, end: offsets.end}), + children = c.doc().children(); + + expect(children.length).to.equal(3); + + expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement); + expect(children[0].getText()).to.equal('Alice'); + + expect(children[1].sameNode(returned)).to.be.true; + expect(returned.getWlxmlTag()).to.equal('header'); + expect(returned.getWlxmlClass()).to.equal('some.class'); + expect(children[1].children().length).to.equal(1); + expect(children[1].children()[0].getText()).to.equal(' has a '); + + expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement); + expect(children[2].getText()).to.equal('cat'); + }); + }); - expect(children[1].sameNode(returned)).to.be.true; - expect(returned.getWlxmlTag()).to.equal('header'); - expect(returned.getWlxmlClass()).to.equal('some.class'); - expect(children[1].children().length).to.equal(1); - expect(children[1].children()[0].getText()).to.equal(' has a '); + it('wraps whole text inside DocumentTextElement if offsets span entire content', function() { + var c = canvas.fromXML('
Alice has a cat
'), + text = c.doc().children()[0]; + + var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 0, end: 15}), + children = c.doc().children(); - expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement); - expect(children[2].getText()).to.equal('cat'); + expect(children.length).to.equal(1); + expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement); + expect(children[0].children()[0].getText()).to.equal('Alice has a cat'); + }); }); it('wraps text spanning multiple sibling DocumentTextNodes', function() { diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js index 89e98a2..b82b471 100644 --- a/modules/documentCanvas/canvas/documentElement.js +++ b/modules/documentCanvas/canvas/documentElement.js @@ -186,12 +186,12 @@ $.extend(DocumentTextElement.prototype, { return documentElementFromHTMLElement(dom[0]); }, wrapWithNodeElement: function(wlxmlNode) { - if(wlxmlNode.start && wlxmlNode.end) { + if(typeof wlxmlNode.start === 'number' && typeof wlxmlNode.end === 'number') { return this.canvas.wrapText({ inside: this.parent(), textNodeIdx: this.parent().childIndex(this), - offsetStart: wlxmlNode.start, - offsetEnd: wlxmlNode.end, + offsetStart: Math.min(wlxmlNode.start, wlxmlNode.end), + offsetEnd: Math.max(wlxmlNode.start, wlxmlNode.end), _with: {tag: wlxmlNode.tag, klass: wlxmlNode.klass} }); } else { -- 2.20.1 From 1d25d2c941f5ffdc6e0824724db9a34960dc5450 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:01:38 +0200 Subject: [PATCH 02/16] Fixing Canvas.getDocumentElement Handling text nodes properly --- modules/documentCanvas/canvas/canvas.js | 2 +- modules/documentCanvas/canvas/canvas.test3.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/documentCanvas/canvas/canvas.js b/modules/documentCanvas/canvas/canvas.js index ad294b8..c4bfc09 100644 --- a/modules/documentCanvas/canvas/canvas.js +++ b/modules/documentCanvas/canvas/canvas.js @@ -116,7 +116,7 @@ $.extend(Canvas.prototype, { return wrapperElement; }, getDocumentElement: function(from) { - if(from instanceof HTMLElement) { + if(from instanceof HTMLElement || from instanceof Text) { return documentElement.wrap(from, this); } }, diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index 0d8e87a..9aab247 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -107,6 +107,22 @@ describe('Canvas', function() { expect(section.getWlxmlClass()).to.be.undefined; }); }); + + it('returns DocumentNodeElement instance from HTMLElement', function() { + var c = canvas.fromXML('
'), + htmlElement = c.doc().dom().get(0), + element = c.getDocumentElement(htmlElement); + expect(element).to.be.instanceOf(documentElement.DocumentNodeElement); + expect(element.sameNode(c.doc())); + }); + + it('returns DocumentTextElement instance from Text Node', function() { + var c = canvas.fromXML('
Alice
'), + textNode = c.doc().children(0)[0].dom().get(0), + element = c.getDocumentElement(textNode); + expect(element).to.be.instanceOf(documentElement.DocumentTextElement); + expect(element.sameNode(c.doc().children()[0])); + }); }); -- 2.20.1 From 0f2c47c0ac58d2ee6b7ce7981a8f277a6b237051 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:05:25 +0200 Subject: [PATCH 03/16] integration wip: togglegrid, newnode --- modules/documentCanvas/canvasManager.js | 105 +++++++++------------ modules/documentCanvas/documentCanvas.js | 13 +-- modules/documentToolbar/documentToolbar.js | 45 +++++---- modules/documentToolbar/template.html | 22 ++--- modules/rng/rng.js | 13 +-- 5 files changed, 82 insertions(+), 116 deletions(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index 235c97b..3618e11 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -96,47 +96,6 @@ Manager.prototype.selectNode = function(cnode, options) { this.sandbox.publish('nodeSelected', cnode); }; -Manager.prototype.insertNewNode = function(wlxmlTag, wlxmlClass) { - var selection = window.getSelection(), - $anchorNode = $(selection.anchorNode), - $focusNode = $(selection.focusNode); - - - if(!selection.isCollapsed && $anchorNode.parent()[0] === $focusNode.parent()[0]) { - var textNodeIdx, - parent = $anchorNode.parent(), - parentContents = parent.contents(), - offsetStart = selection.anchorOffset, - offsetEnd = selection.focusOffset; - - if(selection.anchorNode === selection.focusNode) { - if(offsetStart > offsetEnd) { - var tmp = offsetStart; - offsetStart = offsetEnd; - offsetEnd = tmp; - } - textNodeIdx = parentContents.index($anchorNode); - } else { - if(parentContents.index($anchorNode) > parentContents.index($focusNode)) { - offsetStart = selection.focusOffset; - offsetEnd = selection.anchorOffset; - } - textNodeIdx = [parentContents.index($anchorNode), parentContents.index($focusNode)]; - } - - var wrapper = canvasNode.create({tag: wlxmlTag, klass: wlxmlClass}); - this.canvas.nodeWrap({inside: canvasNode.create(parent), - _with: wrapper, - offsetStart: offsetStart, - offsetEnd: offsetEnd, - textNodeIdx: textNodeIdx - }); - this.selectNode(wrapper, {movecaret: 'end'}); - } - - -}; - Manager.prototype.getNodeElement = function(cnode) { return this.canvas.doc().dom().find('#'+cnode.getId()); }; @@ -189,11 +148,6 @@ Manager.prototype.movecaretToNode = function(nodeElement, where) { selection.addRange(range); }; -Manager.prototype.toggleGrid = function(toggle) { - this.canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', toggle); - this.gridToggled = toggle; -}; - Manager.prototype.onEnterKey = function(e) { e.preventDefault(); var pos = getCursorPosition(); @@ -234,33 +188,66 @@ Manager.prototype.toggleList = function(toggle) { node2 = $(selection.focusNode).parent()[0], element1 = this.canvas.getDocumentElement(node1), element2 = this.canvas.getDocumentElement(node2); - if(toggle) { - this.canvas.list.create({element1: element1, element2: element2}); - } else { - if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { - this.canvas.list.extractItems({element1: element1, element2: element2, merge: false}); - } - } + }; -Manager.prototype.command = function(command, meta) { +Manager.prototype.command = function(command, params) { var selection = window.getSelection(), - node1 = $(selection.anchorNode).parent()[0], - node2 = $(selection.focusNode).parent()[0], - element1 = this.canvas.getDocumentElement(node1), - element2 = this.canvas.getDocumentElement(node2); + element1 = this.canvas.getDocumentElement(selection.anchorNode), + element2 = this.canvas.getDocumentElement(selection.focusNode); + if(command === 'unwrap-node') { // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)}); // this.sandbox.publish('contentChanged'); + element1 = element1.parent(); + element2 = element2.parent(); if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { this.canvas.list.extractItems({element1: element1, element2: element2}); } } else if(command === 'wrap-node') { + element1 = element1.parent(); + element2 = element2.parent(); if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { this.canvas.list.create({element1: element1, element2: element2}); } - } + } else if(command === 'toggle-list') { + element1 = element1.parent(); + element2 = element2.parent(); + if(params.toggle) { + this.canvas.list.create({element1: element1, element2: element2}); + } else { + if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { + this.canvas.list.extractItems({element1: element1, element2: element2, merge: false}); + } + } + } else if(command == 'toggle-grid') { + this.canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', params.toggle); + this.gridToggled = params.toggle; + } else if(command == 'newNodeRequested') { + if(!selection.isCollapsed && element1.parent().sameNode(element2.parent())) { + var parent = element1.parent(), + offsetStart = selection.anchorOffset, + offsetEnd = selection.focusOffset; + if(element1.sameNode(element2)) { + element1.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: offsetStart, end: offsetEnd}); + } + else { + if(parent.childIndex(element1) > parent.childIndex(element2)) { + var tmp = offsetStart; + offsetStart = offsetEnd; + offsetEnd = tmp; + } + this.canvas.wrapText({ + inside: parent, + _with: {tag: params.wlxmlTag, klass: params.wlxmlClass}, + offsetStart: offsetStart, + offsetEnd: offsetEnd, + textNodeIdx: [parent.childIndex(element1), parent.childIndex(element2)] + }); + } + } + } }; diff --git a/modules/documentCanvas/documentCanvas.js b/modules/documentCanvas/documentCanvas.js index 2085155..39b2610 100644 --- a/modules/documentCanvas/documentCanvas.js +++ b/modules/documentCanvas/documentCanvas.js @@ -56,17 +56,8 @@ return function(sandbox) { if(!canvasNode.isSame(manager.currentNode)) manager.selectNode(canvasNode, {movecaret: true}); }, - toggleGrid: function(toggle) { - manager.toggleGrid(toggle); - }, - toggleList: function(toggle) { - manager.toggleList(toggle); - }, - insertNewNode: function(wlxmlTag, wlxmlClass) { - manager.insertNewNode(wlxmlTag, wlxmlClass); - }, - command: function(command, meta) { - manager.command(command, meta); + command: function(command, params) { + manager.command(command, params); } }; diff --git a/modules/documentToolbar/documentToolbar.js b/modules/documentToolbar/documentToolbar.js index 0e17fa3..e4d5039 100644 --- a/modules/documentToolbar/documentToolbar.js +++ b/modules/documentToolbar/documentToolbar.js @@ -11,35 +11,32 @@ return function(sandbox) { this.node.find('button').click(function(e) { e.stopPropagation(); - var btn = $(e.currentTarget); + var btn = $(e.currentTarget), + btnName = btn.attr('data-name'), + meta = btn.attr('data-meta'), + params = {}, + command = btnName; + if(btn.attr('data-btn-type') === 'toggle') { + command = 'toggle-' + command; btn.toggleClass('active'); - var event; - var btnId = btn.attr('data-btn'); - if(btnId === 'grid') - event = 'toggleGrid'; - if(btnId === 'tags') - event = 'toggleTags'; - if(btnId === 'list') - event = 'toggleList' - sandbox.publish(event, btn.hasClass('active')); + params.toggle = btn.hasClass('active'); } - if(btn.attr('data-btn-type') === 'cmd') { - var command = btn.attr('data-btn'); - var meta = btn.attr('data-meta'); - if(command === 'new-node') { - var wlxmlTag = view.getOption('newTag-tag'); - var wlxmlClass = view.getOption('newTag-class'); - if(meta) { - var split = meta.split('/'); - wlxmlTag = split[0]; - wlxmlClass = split[1]; - } - sandbox.publish('newNodeRequested', wlxmlTag, wlxmlClass); - } else { - sandbox.publish('command', btn.attr('data-btn'), btn.attr('data-meta')); + + if(btnName === 'new-node') { + command = 'newNodeRequested'; + params.wlxmlTag = view.getOption('newTag-tag'); + params.wlxmlClass = view.getOption('newTag-class'); + if(meta) { + var split = meta.split('/'); + params.wlxmlTag = split[0]; + params.wlxmlClass = split[1]; } + } else { + params.meta = meta; } + + sandbox.publish('command', command, params); }); }, getOption: function(option) { diff --git a/modules/documentToolbar/template.html b/modules/documentToolbar/template.html index f639c43..96d645a 100644 --- a/modules/documentToolbar/template.html +++ b/modules/documentToolbar/template.html @@ -1,9 +1,9 @@
- - - - + + + + - +
- - + +
- - + +
- - + + <
\ No newline at end of file diff --git a/modules/rng/rng.js b/modules/rng/rng.js index 42c71ec..d773c30 100644 --- a/modules/rng/rng.js +++ b/modules/rng/rng.js @@ -256,17 +256,8 @@ return function(sandbox) { ready: function() { views.visualEditing.setView('toolbar', sandbox.getModule('documentToolbar').getView()); }, - toggleGrid: function(toggle) { - sandbox.getModule('documentCanvas').toggleGrid(toggle); - }, - toggleList: function(toggle) { - sandbox.getModule('documentCanvas').toggleList(toggle); - }, - newNodeRequested: function(wlxmlTag, wlxmlClass) { - sandbox.getModule('documentCanvas').insertNewNode(wlxmlTag, wlxmlClass); - }, - command: function(cmd, meta) { - sandbox.getModule('documentCanvas').command(cmd, meta); + command: function(cmd, params) { + sandbox.getModule('documentCanvas').command(cmd, params); } }; -- 2.20.1 From 3d09a2c29184336c6cd3c17d00e145e123e05293 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:14:14 +0200 Subject: [PATCH 04/16] intergration wip: unwrapping DocumentTextNode from its parent if it's its parent only child --- modules/documentCanvas/canvasManager.js | 26 ++++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index 3618e11..b483937 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -194,30 +194,28 @@ Manager.prototype.toggleList = function(toggle) { Manager.prototype.command = function(command, params) { var selection = window.getSelection(), element1 = this.canvas.getDocumentElement(selection.anchorNode), - element2 = this.canvas.getDocumentElement(selection.focusNode); + element2 = this.canvas.getDocumentElement(selection.focusNode), + parent1 = element1.parent(), + parent2 = element2.parent(); if(command === 'unwrap-node') { // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)}); // this.sandbox.publish('contentChanged'); - element1 = element1.parent(); - element2 = element2.parent(); - if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { - this.canvas.list.extractItems({element1: element1, element2: element2}); + if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { + this.canvas.list.extractItems({element1: parent1, element2: parent2}); + } else if (!selection.collapsed) { + element1.unwrap(); } } else if(command === 'wrap-node') { - element1 = element1.parent(); - element2 = element2.parent(); - if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { - this.canvas.list.create({element1: element1, element2: element2}); + if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { + this.canvas.list.create({element1: parent1, element2: parent2}); } } else if(command === 'toggle-list') { - element1 = element1.parent(); - element2 = element2.parent(); if(params.toggle) { - this.canvas.list.create({element1: element1, element2: element2}); + this.canvas.list.create({element1: parent1, element2: parent2}); } else { - if(this.canvas.list.areItemsOfTheSameList({element1: element1, element2: element2})) { - this.canvas.list.extractItems({element1: element1, element2: element2, merge: false}); + if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { + this.canvas.list.extractItems({element1: parent1, element2: parent2, merge: false}); } } } else if(command == 'toggle-grid') { -- 2.20.1 From 4310156c7c3713b31e159c76d8ba2657a7421ead Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:15:26 +0200 Subject: [PATCH 05/16] fixing markup in a template --- modules/documentToolbar/template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/documentToolbar/template.html b/modules/documentToolbar/template.html index 96d645a..d1e6fdc 100644 --- a/modules/documentToolbar/template.html +++ b/modules/documentToolbar/template.html @@ -28,7 +28,7 @@
- < +
\ No newline at end of file -- 2.20.1 From 12255fb81ee2d3b63e238a595e4813520650cd01 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:16:44 +0200 Subject: [PATCH 06/16] Rearranging document toolbar icons --- modules/documentToolbar/template.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/documentToolbar/template.html b/modules/documentToolbar/template.html index d1e6fdc..389405c 100644 --- a/modules/documentToolbar/template.html +++ b/modules/documentToolbar/template.html @@ -22,13 +22,14 @@ -
- - -
+
+ + +
+
\ No newline at end of file -- 2.20.1 From 75a87d41029b315b107ca7ab859e4e92fc923a3f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:17:15 +0200 Subject: [PATCH 07/16] Removing unused icon from document toolbar --- modules/documentToolbar/template.html | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/documentToolbar/template.html b/modules/documentToolbar/template.html index 389405c..01c0682 100644 --- a/modules/documentToolbar/template.html +++ b/modules/documentToolbar/template.html @@ -28,7 +28,6 @@
-
-- 2.20.1 From 4442dc983215c50244bfc43bec26b6d7d9806dbe Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:19:04 +0200 Subject: [PATCH 08/16] Allowing grid toggle event if no node selected --- modules/documentCanvas/canvasManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index b483937..dbf414b 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -195,8 +195,8 @@ Manager.prototype.command = function(command, params) { var selection = window.getSelection(), element1 = this.canvas.getDocumentElement(selection.anchorNode), element2 = this.canvas.getDocumentElement(selection.focusNode), - parent1 = element1.parent(), - parent2 = element2.parent(); + parent1 = element1 ? element1.parent() : undefined, + parent2 = element2 ? element2.parent() : undefined; if(command === 'unwrap-node') { // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)}); -- 2.20.1 From 20020035edf7edeb436a010dc55555c7a7846027 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Thu, 11 Jul 2013 16:22:08 +0200 Subject: [PATCH 09/16] fixing f16638a725925fce791fe40d85f5aca28b514dad --- modules/documentCanvas/canvasManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index dbf414b..afc3363 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -203,7 +203,7 @@ Manager.prototype.command = function(command, params) { // this.sandbox.publish('contentChanged'); if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { this.canvas.list.extractItems({element1: parent1, element2: parent2}); - } else if (!selection.collapsed) { + } else if (selection.isCollapsed) { element1.unwrap(); } } else if(command === 'wrap-node') { -- 2.20.1 From 1c409979eddd9cd58c1efe2705328c91c95c592b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 13:22:11 +0200 Subject: [PATCH 10/16] Canvas.getCursor() --- modules/documentCanvas/canvas/canvas.js | 90 ++++++++++++++- modules/documentCanvas/canvas/canvas.test3.js | 104 +++++++++++++++++- 2 files changed, 192 insertions(+), 2 deletions(-) diff --git a/modules/documentCanvas/canvas/canvas.js b/modules/documentCanvas/canvas/canvas.js index c4bfc09..e6b8135 100644 --- a/modules/documentCanvas/canvas/canvas.js +++ b/modules/documentCanvas/canvas/canvas.js @@ -120,7 +120,12 @@ $.extend(Canvas.prototype, { return documentElement.wrap(from, this); } }, - list: {} + getCursor: function() { + return new Cursor(this); + }, + + list: {}, + }); $.extend(Canvas.prototype.list, { @@ -253,6 +258,89 @@ $.extend(Canvas.prototype.list, { } }); + +var Cursor = function(canvas) { + this.canvas = canvas; +}; + +$.extend(Cursor.prototype, { + isSelecting: function() { + var selection = window.getSelection(); + return !selection.isCollapsed; + }, + isSelectingWithinElement: function() { + return this.isSelecting() && this.getSelectionStart().element.sameNode(this.getSelectionEnd().element); + }, + isSelectingSiblings: function() { + return this.isSelecting() && this.getSelectionStart().element.parent().sameNode(this.getSelectionEnd().element.parent()); + }, + getPosition: function() { + return this.getSelectionAnchor(); + }, + getSelectionStart: function() { + return this.getSelectionBoundry('start'); + }, + getSelectionEnd: function() { + return this.getSelectionBoundry('end'); + }, + getSelectionAnchor: function() { + return this.getSelectionBoundry('anchor'); + }, + getSelectionBoundry: function(which) { + var selection = window.getSelection(), + anchorElement = this.canvas.getDocumentElement(selection.anchorNode), + focusElement = this.canvas.getDocumentElement(selection.focusNode); + + if(which === 'anchor') { + return { + element: anchorElement, + offset: selection.anchorOffset + }; + } + + var element, + offset; + + if(anchorElement.parent().sameNode(focusElement.parent())) { + var parent = anchorElement.parent(), + anchorFirst = parent.childIndex(anchorElement) < parent.childIndex(focusElement); + if(anchorFirst) { + if(which === 'start') { + element = anchorElement; + offset = selection.anchorOffset; + } + else if(which === 'end') { + element = focusElement, + offset = selection.focusOffset; + } + } else { + if(which === 'start') { + element = focusElement, + offset = selection.focusOffset + } + else if(which === 'end') { + element = anchorElement; + offset = selection.anchorOffset; + } + } + } else { + // TODO: Handle order + if(which === 'start') { + element = anchorElement; + offset = selection.anchorOffset + } else { + element = focusElement; + offset = selection.focusOffset + } + } + + return { + element: element, + offset: offset + } + } +}) + return { fromXML: function(xml) { return new Canvas(xml); diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index 9aab247..56665d4 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -1,8 +1,9 @@ define([ 'libs/chai', +'libs/sinon', 'modules/documentCanvas/canvas/canvas', 'modules/documentCanvas/canvas/documentElement' -], function(chai, canvas, documentElement) { +], function(chai, sinon, canvas, documentElement) { 'use strict'; @@ -864,6 +865,107 @@ describe('Canvas', function() { }); }); + + describe('Cursor', function() { + + var getSelection; + + beforeEach(function() { + getSelection = sinon.stub(window, 'getSelection'); + }); + + afterEach(function() { + getSelection.restore(); + }); + + it('returns position when browser selection collapsed', function() { + var c = canvas.fromXML('
Alice has a cat
'), + dom = c.doc().dom(), + text = dom.contents(0); + + expect(text.text()).to.equal('Alice has a cat'); + + getSelection.returns({ + anchorNode: text[0], + focusNode: text[0], + anchorOffset: 5, + focusOffset: 5, + isCollapsed: true + }); + var cursor = c.getCursor(), + position = cursor.getPosition(); + + expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything'); + //expect(cursor.getElement().getText()) + expect(position.element.getText()).to.equal('Alice has a cat'); + expect(position.offset).to.equal(5); + }); + + it('returns boundries of selection when browser selection not collapsed', function() { + var c = canvas.fromXML('
Alice has a big cat
'), + dom = c.doc().dom(), + text = { + alice: dom.contents()[0], + has: $(dom.contents()[1]).contents()[0], + cat: dom.contents()[4] + }, + cursor = c.getCursor(), + aliceElement = c.getDocumentElement(text.alice), + catElement = c.getDocumentElement(text.cat); + + + [ + {focus: text.alice, focusOffset: 1, anchor: text.cat, anchorOffset: 2, selectionAnchor: catElement}, + {focus: text.cat, focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement} + ].forEach(function(s, idx) { + getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset}); + + var selectionStart = cursor.getSelectionStart(), + selectionEnd = cursor.getSelectionEnd(), + selectionAnchor = cursor.getSelectionAnchor(); + + expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting'); + expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx); + expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx); + expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx); + expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx); + expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok'); + expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok'); + }); + }); + + it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() { + var c = canvas.fromXML('
Alice has a big cat
'), + dom = c.doc().dom(), + text = { + alice: dom.contents()[0], + has: $(dom.contents()[1]).contents()[0], + a: dom.contents()[2], + big: $(dom.contents()[3]).contents()[0], + cat: dom.contents()[4] + }, + cursor = c.getCursor(); + + expect($(text.alice).text()).to.equal('Alice '); + expect($(text.has).text()).to.equal('has'); + expect($(text.a).text()).to.equal(' a '); + expect($(text.big).text()).to.equal('big'); + expect($(text.cat).text()).to.equal(' cat'); + + getSelection.returns({anchorNode: text.alice, focusNode: text.a}); + expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children'); + + getSelection.returns({anchorNode: text.alice, focusNode: text.cat}); + expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children'); + + getSelection.returns({anchorNode: text.alice, focusNode: text.has}); + expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children'); + + getSelection.returns({anchorNode: text.has, focusNode: text.big}); + expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children'); + + }) + }); }); -- 2.20.1 From 18bf38b34495f886dda3f42795602bed3ea95e6c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 13:22:40 +0200 Subject: [PATCH 11/16] Refactoring in CanvasManager - using cursor in command handler --- modules/documentCanvas/canvasManager.js | 37 ++++++++++--------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index afc3363..7875450 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -192,19 +192,20 @@ Manager.prototype.toggleList = function(toggle) { }; Manager.prototype.command = function(command, params) { - var selection = window.getSelection(), - element1 = this.canvas.getDocumentElement(selection.anchorNode), - element2 = this.canvas.getDocumentElement(selection.focusNode), - parent1 = element1 ? element1.parent() : undefined, - parent2 = element2 ? element2.parent() : undefined; + + var cursor = this.canvas.getCursor(), + selectionStart = cursor.getSelectionStart(), + selectionEnd = cursor.getSelectionEnd(), + parent1 = selectionStart.element.parent() || undefined, + parent2 = selectionEnd.element.parent() || undefined; if(command === 'unwrap-node') { // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)}); // this.sandbox.publish('contentChanged'); if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { this.canvas.list.extractItems({element1: parent1, element2: parent2}); - } else if (selection.isCollapsed) { - element1.unwrap(); + } else if(!cursor.isSelecting()) { + cursor.getPosition().element.unwrap(); } } else if(command === 'wrap-node') { if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { @@ -222,26 +223,18 @@ Manager.prototype.command = function(command, params) { this.canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', params.toggle); this.gridToggled = params.toggle; } else if(command == 'newNodeRequested') { - if(!selection.isCollapsed && element1.parent().sameNode(element2.parent())) { - var parent = element1.parent(), - offsetStart = selection.anchorOffset, - offsetEnd = selection.focusOffset; - - if(element1.sameNode(element2)) { - element1.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: offsetStart, end: offsetEnd}); + if(cursor.isSelecting() && cursor.isSelectingSiblings()) { + if(cursor.isSelectingWithinElement()) { + selectionStart.element.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: selectionStart.offset, end: selectionEnd.offset}); } else { - if(parent.childIndex(element1) > parent.childIndex(element2)) { - var tmp = offsetStart; - offsetStart = offsetEnd; - offsetEnd = tmp; - } + var parent = selectionStart.element.parent(); this.canvas.wrapText({ inside: parent, _with: {tag: params.wlxmlTag, klass: params.wlxmlClass}, - offsetStart: offsetStart, - offsetEnd: offsetEnd, - textNodeIdx: [parent.childIndex(element1), parent.childIndex(element2)] + offsetStart: selectionStart.offset, + offsetEnd: selectionEnd.offset, + textNodeIdx: [parent.childIndex(selectionStart.element), parent.childIndex(selectionEnd.element)] }); } } -- 2.20.1 From 3c7a0345c83f8377152afc99ea773ccdf573e051 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 13:23:24 +0200 Subject: [PATCH 12/16] clean up --- modules/documentCanvas/canvas/canvas.test3.js | 1 - modules/documentCanvas/canvasManager.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index 56665d4..147da86 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -896,7 +896,6 @@ describe('Canvas', function() { position = cursor.getPosition(); expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything'); - //expect(cursor.getElement().getText()) expect(position.element.getText()).to.equal('Alice has a cat'); expect(position.offset).to.equal(5); }); diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index 7875450..81eb3c2 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -200,8 +200,6 @@ Manager.prototype.command = function(command, params) { parent2 = selectionEnd.element.parent() || undefined; if(command === 'unwrap-node') { - // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)}); - // this.sandbox.publish('contentChanged'); if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) { this.canvas.list.extractItems({element1: parent1, element2: parent2}); } else if(!cursor.isSelecting()) { -- 2.20.1 From 3b3bcea4776b283d1a29d81bdd190778428d41e2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 13:31:57 +0200 Subject: [PATCH 13/16] cleanup --- modules/documentCanvas/canvasManager.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/documentCanvas/canvasManager.js b/modules/documentCanvas/canvasManager.js index 81eb3c2..4b26135 100644 --- a/modules/documentCanvas/canvasManager.js +++ b/modules/documentCanvas/canvasManager.js @@ -182,15 +182,6 @@ Manager.prototype.onBackspaceKey = function(e) { } }; -Manager.prototype.toggleList = function(toggle) { - var selection = window.getSelection(), - node1 = $(selection.anchorNode).parent()[0], - node2 = $(selection.focusNode).parent()[0], - element1 = this.canvas.getDocumentElement(node1), - element2 = this.canvas.getDocumentElement(node2); - -}; - Manager.prototype.command = function(command, params) { var cursor = this.canvas.getCursor(), -- 2.20.1 From 13e2b1378fc439299f211939abeb07c732c91566 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 16:09:57 +0200 Subject: [PATCH 14/16] Fixing splitting at the very beginning of a text element --- modules/documentCanvas/canvas/canvas.test3.js | 28 +++++++++++++++++++ .../documentCanvas/canvas/documentElement.js | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index 147da86..a4e0c2e 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -332,6 +332,34 @@ describe('Canvas', function() { expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content'); }); + it('leaves empty copy of DocumentNodeElement if splitting at the very beginning', function() { + var c = canvas.fromXML('
Some header
'), + section = c.doc(), + text = section.children()[0].children()[0]; + + text.split({offset: 0}); + + var header1 = section.children()[0]; + var header2 = section.children()[1]; + + expect(header1.children().length).to.equal(0); + expect(header2.children()[0].getText()).to.equal('Some header'); + }); + + it('leaves empty copy of DocumentNodeElement if splitting at the very end', function() { + var c = canvas.fromXML('
Some header
'), + section = c.doc(), + text = section.children()[0].children()[0]; + + text.split({offset: 11}); + + var header1 = section.children()[0]; + var header2 = section.children()[1]; + + expect(header1.children()[0].getText()).to.equal('Some header'); + expect(header2.children().length).to.equal(0); + }); + it('keeps DocumentTextElement\'s parent\'s children elements intact', function() { var c = canvas.fromXML('\
\ diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js index b82b471..e805b35 100644 --- a/modules/documentCanvas/canvas/documentElement.js +++ b/modules/documentCanvas/canvas/documentElement.js @@ -225,7 +225,7 @@ $.extend(DocumentTextElement.prototype, { if(prefix.length > 0) this.setText(prefix); else - this.remove(); + this.detach(); var newElement = DocumentNodeElement.create({tag: parentElement.wlxmlTag, klass: parentElement.wlxmlClass}, myCanvas); parentElement.after(newElement); -- 2.20.1 From 723ac1f3c7cd74605ea7103d7738daad92772515 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 16:41:39 +0200 Subject: [PATCH 15/16] Getting rid of old wlxmlTag attribute of DocumentElement --- modules/documentCanvas/canvas/canvas.test3.js | 14 +++++++------- modules/documentCanvas/canvas/documentElement.js | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/documentCanvas/canvas/canvas.test3.js b/modules/documentCanvas/canvas/canvas.test3.js index a4e0c2e..cc2db49 100644 --- a/modules/documentCanvas/canvas/canvas.test3.js +++ b/modules/documentCanvas/canvas/canvas.test3.js @@ -68,7 +68,7 @@ describe('Canvas', function() { it('gives access to its document root node', function() { var c = canvas.fromXML('
'); - expect(c.doc().wlxmlTag).to.equal('section'); + expect(c.doc().getWlxmlTag()).to.equal('section'); }); describe('DocumentTextElement', function() { @@ -324,10 +324,10 @@ describe('Canvas', function() { var header1 = section.children()[0]; var header2 = section.children()[1]; - expect(header1.wlxmlTag).to.equal('header', 'first section child represents wlxml header'); + expect(header1.getWlxmlTag()).to.equal('header', 'first section child represents wlxml header'); expect(header1.children().length).to.equal(1, 'first header has one text child'); expect(header1.children()[0].getText()).to.equal('Some ', 'first header has correct content'); - expect(header2.wlxmlTag).to.equal('header', 'second section child represents wlxml header'); + expect(header2.getWlxmlTag()).to.equal('header', 'second section child represents wlxml header'); expect(header2.children().length).to.equal(1, 'second header has one text child'); expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content'); }); @@ -375,19 +375,19 @@ describe('Canvas', function() { var sectionChildren = section.children(); expect(sectionChildren.length).to.equal(2, 'Section has two children'); - expect(sectionChildren[0].wlxmlTag).to.equal('header', 'First section element is a wlxml header'); - expect(sectionChildren[1].wlxmlTag).to.equal('header', 'Second section element is a wlxml header'); + expect(sectionChildren[0].getWlxmlTag()).to.equal('header', 'First section element is a wlxml header'); + expect(sectionChildren[1].getWlxmlTag()).to.equal('header', 'Second section element is a wlxml header'); var firstHeaderChildren = sectionChildren[0].children(); expect(firstHeaderChildren.length).to.equal(3, 'First header has three children'); expect(firstHeaderChildren[0].getText()).to.equal('A ', 'First header starts with a text'); - expect(firstHeaderChildren[1].wlxmlTag).to.equal('span', 'First header has span in the middle'); + expect(firstHeaderChildren[1].getWlxmlTag()).to.equal('span', 'First header has span in the middle'); expect(firstHeaderChildren[2].getText()).to.equal(' a', 'First header ends with text'); var secondHeaderChildren = sectionChildren[1].children(); expect(secondHeaderChildren.length).to.equal(3, 'Second header has three children'); expect(secondHeaderChildren[0].getText()).to.equal('nd ', 'Second header starts with text'); - expect(secondHeaderChildren[1].wlxmlTag).to.equal('span', 'Second header has span in the middle'); + expect(secondHeaderChildren[1].getWlxmlTag()).to.equal('span', 'Second header has span in the middle'); expect(secondHeaderChildren[2].getText()).to.equal(' header', 'Second header ends with text'); }); }); diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js index e805b35..d0adbc0 100644 --- a/modules/documentCanvas/canvas/documentElement.js +++ b/modules/documentCanvas/canvas/documentElement.js @@ -12,8 +12,6 @@ var DocumentElement = function(htmlElement, canvas) { return; this.canvas = canvas; this.$element = $(htmlElement); - - this.wlxmlTag = this.$element.attr('wlxml-tag'); } $.extend(DocumentElement.prototype, { @@ -227,7 +225,7 @@ $.extend(DocumentTextElement.prototype, { else this.detach(); - var newElement = DocumentNodeElement.create({tag: parentElement.wlxmlTag, klass: parentElement.wlxmlClass}, myCanvas); + var newElement = DocumentNodeElement.create({tag: parentElement.getWlxmlTag(), klass: parentElement.getWlxmlClass()}, myCanvas); parentElement.after(newElement); if(suffix.length > 0) -- 2.20.1 From 029c09d7b013195c31125fd7c56347e08963df6d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Fri, 12 Jul 2013 16:45:16 +0200 Subject: [PATCH 16/16] Moving DocumentNodeElement methods where they belong --- .../documentCanvas/canvas/documentElement.js | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js index d0adbc0..84610b8 100644 --- a/modules/documentCanvas/canvas/documentElement.js +++ b/modules/documentCanvas/canvas/documentElement.js @@ -18,26 +18,6 @@ $.extend(DocumentElement.prototype, { dom: function() { return this.$element; }, - children: function() { - var toret = []; - if(this instanceof DocumentTextElement) - return toret; - - - var elementContent = this.$element.contents(); - var element = this; - elementContent.each(function(idx) { - var childElement = documentElementFromHTMLElement(this, element.canvas); - if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '') - return true; - if(idx > 0 && childElement instanceof DocumentTextElement) { - if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '') - return true; - } - toret.push(childElement); - }); - return toret; - }, parent: function() { return documentElementFromHTMLElement(this.$element.parent()[0], this.canvas); }, @@ -53,18 +33,6 @@ $.extend(DocumentElement.prototype, { return wrapper; }, - childIndex: function(child) { - var children = this.children(), - toret = null; - children.forEach(function(c, idx) { - if(c.sameNode(child)) { - toret = idx; - return false; - } - }); - return toret; - }, - detach: function() { this.$element.detach(); this.canvas = null; @@ -105,6 +73,37 @@ $.extend(DocumentNodeElement.prototype, { after: function(params) { manipulate(this, params, 'after'); }, + children: function() { + var toret = []; + if(this instanceof DocumentTextElement) + return toret; + + + var elementContent = this.$element.contents(); + var element = this; + elementContent.each(function(idx) { + var childElement = documentElementFromHTMLElement(this, element.canvas); + if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '') + return true; + if(idx > 0 && childElement instanceof DocumentTextElement) { + if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '') + return true; + } + toret.push(childElement); + }); + return toret; + }, + childIndex: function(child) { + var children = this.children(), + toret = null; + children.forEach(function(c, idx) { + if(c.sameNode(child)) { + toret = idx; + return false; + } + }); + return toret; + }, getWlxmlTag: function() { return this.$element.attr('wlxml-tag'); }, -- 2.20.1