From ca1c01b10805014a2f1c24c6f055cd77fc66af01 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Sun, 4 Aug 2013 22:18:49 +0200 Subject: [PATCH 1/1] Handling empty inline elements - Zero Width Space (ZWS) is stored in an empty document text element - Navigation with arrow keys handles ZWS characters - Content Observer makes sure node that stores content of a document text element is never actually set to empty node, but always contains at least ZWS --- modules/documentCanvas/canvas/canvas.js | 58 ++++++++++++++++++- .../documentCanvas/canvas/documentElement.js | 16 +++-- modules/documentCanvas/canvas/utils.js | 5 +- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/modules/documentCanvas/canvas/canvas.js b/modules/documentCanvas/canvas/canvas.js index 8ed9b58..f9f3ee0 100644 --- a/modules/documentCanvas/canvas/canvas.js +++ b/modules/documentCanvas/canvas/canvas.js @@ -1,8 +1,9 @@ define([ 'libs/jquery-1.9.1.min', 'libs/underscore-min', -'modules/documentCanvas/canvas/documentElement' -], function($, _, documentElement) { +'modules/documentCanvas/canvas/documentElement', +'modules/documentCanvas/canvas/utils' +], function($, _, documentElement, utils) { 'use strict'; @@ -142,23 +143,74 @@ $.extend(Canvas.prototype, { else if(position.offsetAtEnd) newEmpty = elements.second; if(newEmpty) { - goto = newEmpty.append(documentElement.DocumentTextElement.create({text: '\u200B'}, this)); + goto = newEmpty.append(documentElement.DocumentTextElement.create({text: ''}, this)); canvas.setCurrentElement(goto); } } } }); + + var KEYS = { + ARROW_LEFT: 37, + ARROW_UP: 38, + ARROW_RIGHT: 39, + ARROW_DOWN: 40 + } + this.wrapper.on('keyup', function(e) { if(e.which >= 37 && e.which <= 40) canvas.setCurrentElement(canvas.getCursor().getPosition().element, {caretTo: false}) }); + + this.wrapper.on('keydown', function(e) { + if(e.which >= 37 && e.which <= 40) { + var position = canvas.getCursor().getPosition(), + element = position.element; + if(element && (element instanceof documentElement.DocumentTextElement)) { + if(element.isEmpty()) { + var direction, caretTo; + if(e.which === KEYS.ARROW_LEFT || e.which === KEYS.ARROW_UP) { + direction = 'above'; + caretTo = 'end'; + } else { + direction = 'below'; + caretTo = 'start'; + } + var el = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]', direction, window.getSelection().focusNode)) + canvas.setCurrentElement(element, {caretTo: caretTo}); + } else { + var txt = element.dom().contents()[0].data; + if(e.which === KEYS.ARROW_LEFT && position.offset > 1 && txt.charAt(position.offset-2) === utils.unicode.ZWS) { + e.preventDefault(); + canvas._moveCaretToTextElement(element, position.offset-2); + } + if(e.which === KEYS.ARROW_RIGHT && position.offset < txt.length - 1 && txt.charAt(position.offset+1) === utils.unicode.ZWS) { + e.preventDefault(); + canvas._moveCaretToTextElement(element, position.offset+2); + } + } + } + + + } + }); this.wrapper.on('click', '[wlxml-tag], [wlxml-text]', function(e) { e.stopPropagation(); canvas.setCurrentElement(canvas.getDocumentElement(e.target), {caretTo: false}); }); + + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if(documentElement.DocumentTextElement.isContentContainer(mutation.target) && mutation.target.data === '') + mutation.target.data = utils.unicode.ZWS; + }); + }); + var config = { attributes: false, childList: false, characterData: true, subtree: true, characterDataOldValue: true}; + observer.observe(this.d[0], config); + } else { this.d = null; } diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js index c38008b..129d7b2 100644 --- a/modules/documentCanvas/canvas/documentElement.js +++ b/modules/documentCanvas/canvas/documentElement.js @@ -1,8 +1,9 @@ define([ 'libs/jquery-1.9.1.min', 'libs/underscore-min', -'modules/documentCanvas/classAttributes' -], function($, _, classAttributes) { +'modules/documentCanvas/classAttributes', +'modules/documentCanvas/canvas/utils' +], function($, _, classAttributes, utils) { 'use strict'; @@ -324,7 +325,7 @@ $.extend(DocumentTextElement, { createDOM: function(params) { return $('
') .attr('wlxml-text', '') - .text(params.text); + .text(params.text || utils.unicode.ZWS); }, create: function(params, canvas) { @@ -333,6 +334,9 @@ $.extend(DocumentTextElement, { fromHTMLElement: function(htmlElement, canvas) { return new this(htmlElement, canvas); + }, + isContentContainer: function(htmlElement) { + return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[wlxml-text]'); } }); @@ -353,7 +357,11 @@ $.extend(DocumentTextElement.prototype, { this.dom().contents()[0].data = text; }, getText: function() { - return this.dom().text(); + return this.dom().text().replace(utils.unicode.ZWS, ''); + }, + isEmpty: function() { + // Having at least Zero Width Space is guaranteed be Content Observer + return this.dom().contents()[0].data === utils.unicode.ZWS; }, after: function(params) { if(params instanceof DocumentTextElement || params.text) diff --git a/modules/documentCanvas/canvas/utils.js b/modules/documentCanvas/canvas/utils.js index 829b466..2462a29 100644 --- a/modules/documentCanvas/canvas/utils.js +++ b/modules/documentCanvas/canvas/utils.js @@ -20,7 +20,10 @@ var nearestInDocumentOrder = function(selector, direction, element) { } return { - nearestInDocumentOrder: nearestInDocumentOrder + nearestInDocumentOrder: nearestInDocumentOrder, + unicode: { + ZWS: '\u200B' + } }; }); -- 2.20.1