<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -5,6 +5,7 @@ core:
 
 === Edge
 
+* Jason Garber
 * Chris Ricca
 * Jim Gay
 * John Muhl</diff>
      <filename>CONTRIBUTORS</filename>
    </modified>
    <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>
      <diff>@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
 
   s.required_rubygems_version = Gem::Requirement.new(&quot;&gt;= 0&quot;) if s.respond_to? :required_rubygems_version=
   s.authors = [&quot;Radiant CMS dev team&quot;]
-  s.date = %q{2009-09-30}
+  s.date = %q{2009-10-09}
   s.default_executable = %q{radiant}
   s.description = %q{Radiant is a simple and powerful publishing system designed for small teams.
 It is built with Rails and is similar to Textpattern or MovableType, but is</diff>
      <filename>radiant.gemspec</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>b68c281ac9f8b5448888f29304e04ea54ed24191</id>
    </parent>
    <parent>
      <id>45a5c407fef9015d53531e4132433cb1154e7186</id>
    </parent>
  </parents>
  <author>
    <name>Keith</name>
    <email>keith@keithbingman.com</email>
  </author>
  <url>http://github.com/radiant/radiant/commit/ff944b1e5f12c1c405fdbab49c3de04738162332</url>
  <id>ff944b1e5f12c1c405fdbab49c3de04738162332</id>
  <committed-date>2009-10-11T02:10:31-07:00</committed-date>
  <authored-date>2009-10-11T02:10:31-07:00</authored-date>
  <message>Merge branch 'master' of github.com:radiant/radiant into i18n</message>
  <tree>94e178baa5c3ea5bedd93dd1bd00871cb3cc80e4</tree>
  <committer>
    <name>Keith</name>
    <email>keith@keithbingman.com</email>
  </committer>
</commit>
