Commits

Luke Plant committed 1ffcc7d

Intercepted 'delete' and 'backspace' so that you cannot delete command blocks

Tested in Firefox 3.5 (Linux) and Safari 3.2 (Windows)

  • Participants
  • Parent commits 58b005c

Comments (0)

Files changed (1)

File semanticeditor/media/semanticeditor/javascript/wymeditor/plugins/semantic/wymeditor.semantic.js

     // command_dict: a dictionary mapping command name to PresentationInfo object.
     this.command_dict = {};
 
+    // a selector which matches any command block.  Filled in later.
+    this.all_command_selectors = "";
+
     // the node of the current block level element being edited.
     this.current_node = null;
 
                                });
     jQuery(this.wym._doc)
         .bind("keyup", function(evt) {
-                  // this style of binding give doc_keyup
+                  // this style of binding gives doc_keyup
                   // access to 'this' PresentationControl
                   self.doc_keyup(evt);
               })
+        .bind("keydown", function(evt) {
+                  self.doc_keydown(evt);
+              })
         .bind("mouseup", function(evt) {
                   self.update_classlist_item_all();
               });
     this.wym.listen();
 };
 
+PresentationControls.prototype.nextElementSibling = function(node) {
+    var next = node;
+    while (true) {
+        if (next == null) {
+            return null;
+        }
+        if (next.nextSibling == null) {
+            next = next.parentNode;
+        } else {
+            next = next.nextSibling;
+            if (next.nodeName != "#text") {
+                return next;
+            }
+        }
+    }
+}
+
+PresentationControls.prototype.previousElementSibling = function(node) {
+    var prev = node;
+    while (true) {
+        if (prev == null) {
+            return null;
+        }
+        if (prev.previousSibling == null) {
+            prev = prev.parentNode;
+        } else {
+            prev = prev.previousSibling;
+            if (prev.nodeName != "#text") {
+                return prev;
+            }
+        }
+    }
+}
+
 PresentationControls.prototype.doc_keyup = function(evt) {
     // Some browsers (Firefox at least) will insert a new paragraph with the
     // same ID as the old one when the user presses 'Enter'. This needs fixing
     this.update_classlist_item_all(container);
 };
 
+PresentationControls.prototype.doc_keydown = function(evt) {
+    // Need to intercept backspace and delete, to stop command blocks
+    // being deleted. (The system copes fairly well with them being
+    // deleted, but it can be very confusing for the user.)
+    var is_backspace = (evt.keyCode == 8);
+    var is_delete = (evt.keyCode == 46);
+
+    if (is_backspace || is_delete) {
+
+        s = this.wym._iframe.contentWindow.getSelection()
+        if (s.anchorNode != s.focusNode) {
+            // an extended selection.  It doesn't matter if this
+            // contains command blocks - it doesn't confuse the user
+            // if these are deleted wholesale.
+            return;
+        }
+        if (s.focusNode == null) {
+            return;
+        }
+
+        node = s.focusNode;
+        if (node.nodeName == "#text") {
+            // always true?
+            node = node.parentNode;
+        }
+        if (is_backspace) {
+            if (s.focusOffset != 0) {
+                return; // not at first character within text node
+            }
+            // need to check *previous* node
+            target = this.previousElementSibling(node);
+        }
+        if (is_delete) {
+            if (s.focusOffset != s.focusNode.textContent.length) {
+                return; // not at last character within text node
+            }
+            // need to check *next* node
+            target = this.nextElementSibling(node);
+        }
+        if (target == null) {
+            return;
+        }
+
+        if (jQuery(target).is(this.all_command_selectors)) {
+            // stop backspace or delete from working.
+            evt.preventDefault();
+        }
+    }
+};
+
 PresentationControls.prototype.update_classlist_item_all = function(cur_container) {
     var self = this;
     if (cur_container == undefined) {
 PresentationControls.prototype.insert_command_block = function(sect_id, command) {
     // There is some custom logic about the different commands here i.e.
     // that newrow should appear before newcol.
-    var newelem = jQuery("<p class=\"" + command.name + "\">*</p>");
+    var newelem = jQuery("<p class=\"" + command.name + "\">&nbsp;</p>");
     var elem = jQuery(this.wym._doc).find("#" + sect_id);
     // New row should appear before new col
     if (elem.prev().is("p.newcol") && command.name == 'newrow') {
                                 var c = self.commands[i];
                                 self.command_dict[c.name] = c;
                             }
+                            self.all_command_selectors = jQuery.map(self.commands,
+                                                                    function(c, i) { return self.tagname_to_selector(c.name); }
+                                                                    ).join(",");
                             self.build_commandlist();
                         });
 };