<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,6 @@
-// Based on code from:
+// Originally based on code from:
 //   http://ajaxian.com/archives/handling-tabs-in-textareas
+
 var CodeAreaBehavior = Behavior.create({
   initialize: function() {
     new CodeArea(this.element);
@@ -15,52 +16,150 @@ var CodeArea = Class.create({
   onkeydown: function(event) {
     // Set desired tab - defaults to two space softtab
     var tab = &quot;  &quot;;
+    var tabStop = tab.length;
     
-    var t = event.target;
-    var ss = t.selectionStart;
-    var se = t.selectionEnd;
+    var t = this.element;
     
-    // Tab key - insert tab expansion
-    if (event.keyCode == 9) {
-      event.preventDefault();
-      if (ss != se &amp;&amp; t.value.slice(ss,se).indexOf(&quot;\n&quot;) != -1) {
-        // Special case of multi line selection
-        // In case selection was not of entire lines (e.g. selection begins in the middle of a line)
-        // we ought to tab at the beginning as well as at the start of every following line.
-        var pre = t.value.slice(0,ss);
-        var sel = t.value.slice(ss,se).replace(/\n/g,&quot;\n&quot;+tab);
-        var post = t.value.slice(se,t.value.length);
-        t.value = pre.concat(tab).concat(sel).concat(post);
-        t.selectionStart = ss + tab.length;
-        t.selectionEnd = se + tab.length;
-      } else {
-        // &quot;Normal&quot; case (no selection or selection on one line only)
-        t.value = t.value.slice(0,ss).concat(tab).concat(t.value.slice(ss,t.value.length));
-        if (ss == se) {
-          t.selectionStart = t.selectionEnd = ss + tab.length;
+    if (Prototype.Browser.IE) {
+      // Very limited support for IE
+      
+      if (event.keyCode == Event.KEY_TAB &amp;&amp; !event.shiftKey) {
+        event.preventDefault();
+        document.selection.createRange().text = tab;
+      }
+      
+    } else {
+      // Safari and Firefox
+      
+      // If this is the tab key, make the selection start at the begining and end of lines for
+      // multi-line selections
+      if (event.keyCode == Event.KEY_TAB) this.normalizeSelection(t);
+      
+      var ss = t.selectionStart;
+      var se = t.selectionEnd;
+      
+      if (event.keyCode == Event.KEY_TAB) {
+        // Tab key
+        
+        event.preventDefault();
+        
+        if (event.shiftKey) {
+          // Shift + Tab
+          
+          if (t.value.slice(ss,se).indexOf(&quot;\n&quot;) != -1) {
+            // Special case of multi line selection
+            
+            var pre = t.value.slice(0, ss)
+            var sel = t.value.slice(ss, se)
+            var post = t.value.slice(se, t.value.length);
+            
+            // Back off one tab
+            sel = sel.replace(new RegExp(&quot;^&quot; + tab, &quot;gm&quot;), '')
+            
+            // Put everything back together
+            t.value = pre.concat(sel).concat(post);
+            
+            // Readjust the selection
+            t.selectionStart = pre.length;
+            t.selectionEnd = pre.length + sel.length;
+            
+          } else {
+            // &quot;Normal&quot; case (no selection or selection on one line only)
+            
+            if (t.value.slice(ss - tabStop, ss) == tab) {
+              // Only unindent if there is a tab before the cursor
+              
+              t.value = t.value.slice(0, ss - tabStop).concat(t.value.slice(ss, t.value.length));
+              t.selectionStart = ss - tabStop;
+              t.selectionEnd = se - tabStop;
+            }
+          }
         } else {
-          t.selectionStart = ss + tab.length;
-          t.selectionEnd = se + tab.length;
+          // Tab
+          
+          if (ss != se &amp;&amp; t.value.slice(ss, se).indexOf(&quot;\n&quot;) != -1) {
+            // Special case of multi line selection
+            
+            // In case selection was not of entire lines (e.g. selection begins in the middle of a line)
+            // we ought to tab at the beginning as well as at the start of every following line.
+            var pre = t.value.slice(0, ss);
+            var sel = t.value.slice(ss, se);
+            var post = t.value.slice(se, t.value.length);
+            
+            // Indent one tab
+            sel = sel.replace(/^/gm, tab)
+            
+            // Put everything back together
+            t.value = pre.concat(sel).concat(post);
+            
+            // Readjust the selection
+            t.selectionStart = pre.length;
+            t.selectionEnd = pre.length + sel.length;
+            
+          } else {
+            // &quot;Normal&quot; case (no selection or selection on one line only)
+            
+            t.value = t.value.slice(0, ss).concat(tab).concat(t.value.slice(ss, t.value.length));
+            if (ss == se) {
+              t.selectionStart = t.selectionEnd = ss + tabStop;
+            } else {
+              t.selectionStart = ss + tabStop;
+              t.selectionEnd = se + tabStop;
+            }
+          }
         }
+      
+      } else if (event.keyCode == Event.KEY_BACKSPACE &amp;&amp; ss == se &amp;&amp; t.value.slice(ss - tabStop, ss) == tab) {
+        // Backspace - delete preceding tab expansion, if it exists and nothing is selected
+        
+        event.preventDefault();
+        t.value = t.value.slice(0, ss - tabStop).concat(t.value.slice(ss, t.value.length));
+        t.selectionStart = ss - tabStop;
+        t.selectionEnd = se - tabStop;
+        
+      } else if (event.keyCode == Event.KEY_DELETE &amp;&amp; t.value.slice(se, se + tabStop) == tab) {
+        // Delete key - delete following tab expansion, if exists
+        
+        event.preventDefault();
+        t.value = t.value.slice(0, ss).concat(t.value.slice(ss + tabStop ,t.value.length));
+        t.selectionStart = t.selectionEnd = ss;
+        
+      } else if (event.keyCode == Event.KEY_LEFT &amp;&amp; t.value.slice(ss - tabStop, ss) == tab) {
+        // Left arrow - move across the tab in one go
+        
+        event.preventDefault();
+        t.selectionStart = t.selectionEnd = ss - tabStop;
+      } else if (event.keyCode == Event.KEY_RIGHT &amp;&amp; t.value.slice(ss, ss + tabStop) == tab) {
+        // Left/right arrow - move across the tab in one go
+        
+        event.preventDefault();
+        t.selectionStart = t.selectionEnd = ss + tabStop;
+        
       }
-    } else if (event.keyCode == Event.KEY_BACKSPACE &amp;&amp; t.value.slice(ss - tab.length,ss) == tab) {
-      // Backspace key - delete preceding tab expansion, if exists
-      event.preventDefault();
-      t.value = t.value.slice(0,ss - tab.length).concat(t.value.slice(ss,t.value.length));
-      t.selectionStart = t.selectionEnd = ss - tab.length;
-    } else if (event.keyCode == Event.KEY_DELETE &amp;&amp; t.value.slice(se,se + tab.length) == tab) {
-      // Delete key - delete following tab expansion, if exists
-      event.preventDefault();
-      t.value = t.value.slice(0,ss).concat(t.value.slice(ss + tab.length,t.value.length));
-      t.selectionStart = t.selectionEnd = ss;
-    } else if (event.keyCode == Event.KEY_LEFT &amp;&amp; t.value.slice(ss - tab.length,ss) == tab) {
-      // Left arrow key - move across the tab in one go
-      event.preventDefault();
-      t.selectionStart = t.selectionEnd = ss - tab.length;
-    } else if (event.keyCode == Event.KEY_RIGHT &amp;&amp; t.value.slice(ss,ss + tab.length) == tab) {
-      // Left/right arrow keys - move across the tab in one go
-      event.preventDefault();
-      t.selectionStart = t.selectionEnd = ss + tab.length;
+    }
+  },
+  
+  normalizeSelection: function(textarea) {
+    var b = 0;
+    var value = textarea.value;
+    var e = textarea.length;
+    var ss = textarea.selectionStart;
+    var se = textarea.selectionEnd;
+    
+    if (ss != se &amp;&amp; textarea.value.slice(ss, se).indexOf(&quot;\n&quot;) != -1) {
+      // If multi-line adjust the selection
+      
+      // If the end of the line is selected back off one character
+      if (textarea.value.slice(se - 1, se) == &quot;\n&quot;) se = se - 1;
+      
+      // If the selection does not end with a new line or the end of the document increment until it does
+      while ((se &lt; e) &amp;&amp; (textarea.value.slice(se, se + 1) != &quot;\n&quot;)) se += 1;
+      
+      // If the selection does not begin at a new line or the begining of the document back off until it does
+      while ((ss &gt; b) &amp;&amp; (textarea.value.slice(ss - 1, ss) != &quot;\n&quot;)) ss -= 1;
+      
+      textarea.selectionStart = ss;
+      textarea.selectionEnd = se;
     }
   }
 });
\ No newline at end of file</diff>
      <filename>public/javascripts/admin/codearea.js</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>eeda76437bd2daca5101945743cd1b030d63d8f3</id>
    </parent>
  </parents>
  <author>
    <name>John W. Long</name>
    <email>me@johnwlong.com</email>
  </author>
  <url>http://github.com/radiant/radiant/commit/45a5c407fef9015d53531e4132433cb1154e7186</url>
  <id>45a5c407fef9015d53531e4132433cb1154e7186</id>
  <committed-date>2009-10-09T17:20:45-07:00</committed-date>
  <authored-date>2009-10-09T17:20:45-07:00</authored-date>
  <message>updated code for managing the tab character to handle block indent and unindent; fixed problem with deleting text closing #13</message>
  <tree>b4c2e123d7a9e25c211ffbebadee15fae06c6db2</tree>
  <committer>
    <name>John W. Long</name>
    <email>me@johnwlong.com</email>
  </committer>
</commit>
