Handling empty inline elements
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Sun, 4 Aug 2013 20:18:49 +0000 (22:18 +0200)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Sun, 4 Aug 2013 20:18:49 +0000 (22:18 +0200)
- 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
modules/documentCanvas/canvas/documentElement.js
modules/documentCanvas/canvas/utils.js

index 8ed9b58..f9f3ee0 100644 (file)
@@ -1,8 +1,9 @@
 define([
 'libs/jquery-1.9.1.min',
 'libs/underscore-min',
 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';
 
     
 'use strict';
 
@@ -142,23 +143,74 @@ $.extend(Canvas.prototype, {
                         else if(position.offsetAtEnd)
                             newEmpty = elements.second;
                         if(newEmpty) {
                         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);
                         }
                     }
                 }
             });
 
                             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('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});
             });
 
 
             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;
         }
         } else {
             this.d = null;
         }
index c38008b..129d7b2 100644 (file)
@@ -1,8 +1,9 @@
 define([
 'libs/jquery-1.9.1.min',
 'libs/underscore-min',
 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';
 
     
 'use strict';
 
@@ -324,7 +325,7 @@ $.extend(DocumentTextElement, {
     createDOM: function(params) {
         return $('<div>')
             .attr('wlxml-text', '')
     createDOM: function(params) {
         return $('<div>')
             .attr('wlxml-text', '')
-            .text(params.text);
+            .text(params.text || utils.unicode.ZWS);
     },
 
     create: function(params, canvas) {
     },
 
     create: function(params, canvas) {
@@ -333,6 +334,9 @@ $.extend(DocumentTextElement, {
 
     fromHTMLElement: function(htmlElement, canvas) {
         return new this(htmlElement, canvas);
 
     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() {
         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)
     },
     after: function(params) {
         if(params instanceof DocumentTextElement || params.text)
index 829b466..2462a29 100644 (file)
@@ -20,7 +20,10 @@ var nearestInDocumentOrder = function(selector, direction, element) {
 }
 
 return {
 }
 
 return {
-    nearestInDocumentOrder: nearestInDocumentOrder
+    nearestInDocumentOrder: nearestInDocumentOrder,
+    unicode: {
+        ZWS: '\u200B'
+    }
 };
 
 });
 };
 
 });