Source: CodeInput.js

/**
 * A code editor
 * @constructor
 */
hui.ui.CodeInput = function(options) {
  this.options = hui.override({},options);
  this.name = options.name;
  var e = this.element = hui.get(options.element);
  this.textarea = hui.get.firstByTag(e,'textarea');
  if (options.value) {
    this.textarea.value = options.value;
  }
  this.value = this.textarea.value;
  hui.ui.extend(this);
  this._addBehavior();
};

hui.ui.CodeInput.create = function(options) {
  options = options || {};
  options.element = hui.build('div',{
    'class' : 'hui_codeinput',
    html : '<textarea class="hui_codeinput_input" spellcheck="false"></textarea>'
  });
  if (options.height) {
    options.element.style.height = hui.style.length(options.height);
  }
  return new hui.ui.CodeInput(options);
};

hui.ui.CodeInput.prototype = {
  _addBehavior : function() {
    hui.listen(this.textarea, 'keydown', this._onKeyDown.bind(this));
    hui.listen(this.textarea, 'keyup', this._onKeyUp.bind(this));
  },

  getValue : function() {
    return this.textarea.value;
  },
  setValue : function(value) {
    this.textarea.value = value;
    this.value = value;
  },
  addLine : function(line) {
    if (this.value==='') {
      this.setValue(line);
    } else {
      this.setValue(this.value+"\n"+line);
    }
  },
  reset : function() {
    this.setValue('');
  },
  _onKeyUp : function() {
    if (this.textarea.value !== this.value) {
      this.value = this.textarea.value;
      this.fireValueChange();
    }
  },

  _onKeyDown: function(evt) {
    var tab = String.fromCharCode(9);
    var e = window.event || evt;
    var t = e.target ? e.target : e.srcElement ? e.srcElement : e.which;
    var scrollTop = t.scrollTop;
    var k = e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which;
    var ch, a, i, rt, nr;
    if (k == 9 && !e.ctrlKey && !e.altKey) {
      if (t.setSelectionRange) {
        e.preventDefault();
        var ss = t.selectionStart;
        var se = t.selectionEnd;
        // Multi line selection
        if (ss != se && t.value.slice(ss, se).indexOf("\n") != -1) {
          if (ss > 0) {
            ss = t.value.slice(0, ss).lastIndexOf("\n") + 1;
          }
          var pre = t.value.slice(0, ss);
          var sel = t.value.slice(ss, se);
          var post = t.value.slice(se, t.value.length);
          if (e.shiftKey) {
            a = sel.split("\n");
            for (i = 0; i < a.length; i++) {
              if (a[i].slice(0, 1) == tab || a[i].slice(0, 1) == ' ') {
                a[i] = a[i].slice(1, a[i].length);
              }
            }
            sel = a.join("\n");
            t.value = pre.concat(sel, post);
            t.selectionStart = ss;
            t.selectionEnd = pre.length + sel.length;
          } else {
            sel = sel.replace(/\n/g, "\n" + tab);
            pre = pre.concat(tab);
            t.value = pre.concat(sel, post);
            t.selectionStart = ss;
            t.selectionEnd = se + (tab.length * sel.split("\n").length);
          }
        }
        // Single line selection
        else {
          if (e.shiftKey) {
            var brt = t.value.slice(0, ss);
            ch = brt.slice(brt.length - 1, brt.length);
            if (ch == tab || ch == ' ') {
              t.value = brt.slice(0, brt.length - 1).concat(t.value.slice(ss, t.value.length));
              t.selectionStart = ss - 1;
              t.selectionEnd = se - 1;
            }
          } else {
            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;
            } else {
              t.selectionStart = ss + tab.length;
              t.selectionEnd = se + tab.length;
            }
          }
        }
      } else {
        e.returnValue = false;
        var r = document.selection.createRange();
        var br = document.body.createTextRange();
        br.moveToElementText(t);
        br.setEndPoint("EndToStart", r);
        //Single line selection
        if (r.text.length === 0 || r.text.indexOf("\n") === -1) {
          if (e.shiftKey) {
            ch = br.text.slice(br.text.length - 1, br.text.length);
            if (ch == tab || ch == ' ') {
              br.text = br.text.slice(0, br.text.length - 1);
              r.setEndPoint("StartToEnd", br);
            }
          } else {
            var rtn = t.value.slice(br.text.length, br.text.length + 1);
            if (rtn != r.text.slice(0, 1)) {
              br.text = br.text.concat(rtn);
            }
            br.text = br.text.concat(tab);
          }
          nr = document.body.createTextRange();
          nr.setEndPoint("StartToEnd", br);
          nr.setEndPoint("EndToEnd", r);
          nr.select();
        }
        //Multi line selection
        else {
          var p;
          if (e.shiftKey) {
            a = r.text.split("\r\n");
            rt = t.value.slice(br.text.length, br.text.length + 2);
            if (rt == r.text.slice(0, 2)) {
              p = br.text.lastIndexOf("\r\n".concat(tab));
              if (p != -1) {
                br.text = br.text.slice(0, p + 2).concat(br.text.slice(p + 3, br.text.length));
              }
            }
            for (i = 0; i < a.length; i++) {
              ch = a[i].length > 0 && a[i].slice(0, 1);
              if (ch == tab || ch == ' ') {
                a[i] = a[i].slice(1, a[i].length);
              }
            }
            r.text = a.join("\r\n");
          } else {
            if (br.text.length > 0) {
              rt = t.value.slice(br.text.length, br.text.length + 2);
              if (rt != r.text.slice(0, 2)) {
                r.text = tab.concat(r.text.split("\r\n").join("\r\n".concat(tab)));
              } else {
                var selectionStart; // TODO: what is this about?
                p = br.text.slice(0, selectionStart).lastIndexOf("\r\n") + 2;
                br.text = br.text.slice(0, p).concat(tab, br.text.slice(p, br.text.length));
                r.text = r.text.split("\r\n").join("\r\n".concat(tab));
              }
            } else {
              r.text = tab.concat(r.text).split("\r\n").join("\r\n".concat(tab));
            }
          }
          nr = document.body.createTextRange();
          nr.setEndPoint("StartToEnd", br);
          nr.setEndPoint("EndToEnd", r);
          nr.select();
        }
      }
    }
    t.scrollTop = scrollTop;
  }

};