// Cross Browser Emacs Key Bind for TextArea
// Version 0.1
// Copyright (c) 2007-2009 Nakamura Narihiro
// Author  id:authorNari
// blog    http://d.hatena.ne.jp/authorNari/
// 
// This script is distributed under the MIT licence.
// http://www.opensource.org/licenses/mit-license.php


var emacs_area;

(function() {
    var DEBUG = {
	is_debug_mode : true,
	put : function(str) {
	    if(this.is_debug_mode) {
		var stderr = document.getElementById('stderr');
		var now = new Date();
		if(stderr) {
		    stderr.value += "[" + now.toString() + "] DEBUG -- : " + str + "\n";
		    stderr.scrollTop = 1000000000;
		} else {
		    var err_contena = document.createElement('div');
		    err_contena.id = 'err_contena';
		    var err = document.createElement('textarea');
		    err.id = 'stderr';
		    err.style.cssText = "width: 1000px; height: 300px; font-size:11px;";
		    err.value += "[" + now.toString() + "] DEBUG -- : " + str + "\n";
		    title = document.createElement('h4');
		    title.innerHTML = "DEBUG CONSOLE";
		    err_contena.appendChild(title);
		    err_contena.appendChild(err);
		    document.body.appendChild(err_contena);
		}
	    } else {
                if(document.getElementById('err_contena')) {
		  document.body.removeChild(document.getElementById('err_contena'));
		}		
	    }
	},

	popup : function(str) {
	    if(this.is_debug_mode) alert(str);
	}
    };

    Object.extend = function(destination, source) {
	for (var property in source) {
	    destination[property] = source[property];
	}
	return destination;
    }

    String.prototype.reject = function(start, end) {
	return this.substring(0, start) + this.substring(end, this.length);
    }

    String.prototype.insert = function(position, str) {
	return this.substring(0, position) + str + this.substring(position, this.length);
    }

    String.prototype.reverse = function() {
	var res = ""
	for(var i=this.length-1,len=0;i>=len;i--) {
	    res += this.charAt(i);
	}
	return res;
    }

    Array.prototype.last = function() {
	if(this.length == 0) return null;
	return this[this.length -1];
    }

    var Editor = {
	ctrl_pressed : function(letter) {
	    switch(letter){
		case "P":
		  emacs_area.core.up_cursor();
		  emacs_area.core.reset_delete_time();
 		  return true;
		case "N":
		  emacs_area.core.down_cursor();
		  emacs_area.core.reset_delete_time();
		  return true;
		case "A":
		  emacs_area.core.move_row_home();
		  emacs_area.core.info.is_move = true;
		  emacs_area.core.reset_delete_time();
		  return true;
		case "D":
		  if(emacs_area.core.info.is_select) { 
		      emacs_area.core.cut();
		      return true;
		  }
		  emacs_area.core.del(1);
		  return true;
		case "E":
		  emacs_area.core.move_row_end();
		  emacs_area.core.info.is_move = true;
		  emacs_area.core.reset_delete_time();
		  return true;
		case "F":
		  emacs_area.core.right();
		  emacs_area.core.info.is_move = true;
		  emacs_area.core.reset_delete_time();
		  return true;
		case "B":
		  emacs_area.core.left();
		  emacs_area.core.info.is_move = true;
		  emacs_area.core.reset_delete_time();
		  return true;
		case "BS":
		  return false;
		case "K":
		  emacs_area.core.kill_after();
		  return true;
		case "L":
		  return true;
		case "W":
		  if(emacs_area.core.info.is_select) { 
		      emacs_area.core.cut();
		  }
		  return true;
		case "Space":
		  emacs_area.core.switch_select();
		  return true;
		case "Y":
		  emacs_area.core.paste();
		  emacs_area.core.reset_delete_time();
		  return true;
		case "G":
		  emacs_area.core.clear_roll();
		  return true;
		case "/":
		  emacs_area.core.switch_buff();
		  return true;
		case "O":
		  emacs_area.core.return_after();
		  emacs_area.core.reset_delete_time();
		  return true;
		case "Q":
		  var area = emacs_area.core.area;
		  var keybind = emacs_area.latency_keybind;
		  emacs_area.clear();
		  area.onkeydown = keybind;
		  return true;
		case "V":
		  emacs_area.core.next_page();
		  return true;
	    }
	    return false;
	},

	alt_pressed : function(letter) {
	    switch(letter) {
		case "W":
		  if(emacs_area.core.info.is_select) {
		      emacs_area.core.copy();
		  }
		  return true;
		case "V":
		  emacs_area.core.prev_page();
		  return true;
	    }
	    return false;
	},

	shift_pressed : function(letter) {
	    switch(letter) {
		case "Home":
		    return true;
	    }
	    return false;
	},

	ctrl_shift_pressed : function(letter) {
	    switch(letter) {
		case "/":
		  emacs_area.core.show_help();
		  return true;
	    }
	    return false;
	},

	key_pressed : function(letter) {
	    switch(letter) {
		case "Jpmode":
		  var area = emacs_area.core.area;
		  var keybind = emacs_area.jpmode_keybind_nn;
		  if(emacs_area.core.is_ie()) keybind = emacs_area.jpmode_keybind_ie;
		  emacs_area.clear();
		  area.onkeydown = keybind;
		  return true;
	    }
	    return false;
	},

	is_alt : function(e) {
	    if (window.event) {
		return (window.event.altKey);
	    } else {
		if(e.modifiers)
		    return (e.altKey || (e.modifiers % 2));
		else
		    return e.altKey;
	    }
	},

	is_ctrl :  function(e) {
	    if (window.event) {
		return (window.event.ctrlKey);
	    } else {
		return (e.ctrlKey || (e.modifiers==2) || (e.modifiers==3) || (e.modifiers>5));
	    }
	},

	is_shift : function(e) {
	    if (window.event) {
		return (window.event.shiftKey);
	    } else {
		return (e.shiftKey || (e.modifiers>3));
	    }
	}
    };

    var Emacs = {

	init : function(id) {
	    this.core = Core;
	    this.core.init(id);
	    this.area_id = id;
	    this.core.area.onkeydown  = this.keybind;
	    this.mode = "emacs";
	},

	clear : function() {
	    this.core.area.onkeydown = null;
	    this.core.clear();
	    this.mode = "nomal";
	},
	
	editor : Editor,

	area_id : "",

	mode : "emacs",
	
	jpmode_keybind_nn : function(e) {
	    emacs_area.core.save_buffer();
	    if(!e) e=event;
	    var letter;
	    if (clavier_cds[e.keyCode]) {
		letter=clavier_cds[e.keyCode];
	    }else{
		letter=String.fromCharCode(e.keyCode);
	    }
	    DEBUG.put("jpmode!!  letter : " + letter + " keycode : " + e.keyCode);
	    var is_kill = false;
	    if(emacs_area.editor.is_ctrl(e)) {
		is_kill = true;
	    } else if(letter != "Jpmode") {
		emacs_area.init(emacs_area.area_id);
	    }
	    if(is_kill){
		DEBUG.put("command_kill!");
		return false;
	    }
	    return true;
	},

	jpmode_keybind_ie : function(e) {
	    emacs_area.core.save_buffer();
	    if(!e) e=event;
	    var letter;
	    if (clavier_cds[e.keyCode]) {
		letter=clavier_cds[e.keyCode];
	    }else{
		letter=String.fromCharCode(e.keyCode);
	    }
	    DEBUG.put("jpmode!!  letter : " + letter + " keycode : " + e.keyCode);
	    var is_kill = false;
	    if(emacs_area.editor.is_ctrl(e)) {
		is_kill = true;
	    } else if(emacs_area.editor.is_alt(e)) {
		if(letter != "Jpmode") {
		    emacs_area.init(emacs_area.area_id);
		}
		is_kill = true;
	    } else if(letter != "Jpmode") {
		emacs_area.init(emacs_area.area_id);
	    }
	    if(is_kill){
		DEBUG.put("command_kill!");
		return false;
	    }
	    return true;
	},

	latency_keybind : function(e) {
	    if(!e) e=event;
	    var letter;
	    if (clavier_cds[e.keyCode]) {
		letter=clavier_cds[e.keyCode];
	    }else{
		letter=String.fromCharCode(e.keyCode);
	    }
	    DEBUG.put("latency!!  letter : " + letter + " keycode : " + e.keyCode);
	    var is_kill = false;
	    if(emacs_area.editor.is_ctrl(e)) {
		if("Q" == letter) {
		    emacs_area.init(emacs_area.area_id);
		    is_kill = true;
		}
	    }
	    if(is_kill){
		DEBUG.put("command_kill!");
		return false;
	    }
	    return true;
	},

	keybind : function(e) {
	    emacs_area.core.save_buffer();
	    if(!e) e=event;
	    var is_kill = false;
	    var letter;
	    if (clavier_cds[e.keyCode]) {
		letter=clavier_cds[e.keyCode];
	    }else{
		letter=String.fromCharCode(e.keyCode);
	    }
	    k_buf = e.keyCode;
	    if(emacs_area.editor.is_ctrl(e) && emacs_area.editor.is_shift(e)) {
		is_kill = emacs_area.editor.ctrl_shift_pressed(letter);
	    } else if(emacs_area.editor.is_ctrl(e)) {
		is_kill = emacs_area.editor.ctrl_pressed(letter);
	    } else if(emacs_area.editor.is_shift(e)) {
		is_kill = emacs_area.editor.shift_pressed(letter);
	    } else if(emacs_area.editor.is_alt(e)) {
		is_kill = emacs_area.editor.alt_pressed(letter);
	    } else {
		is_kill = emacs_area.editor.key_pressed(letter);
		if(is_kill) return;
		if(!is_kill) emacs_area.core.clear_select_mode();
	    }
	    k = e.keyCode;
	    DEBUG.put( "keyCode_buf : "+ k_buf + " keyCode : " + k + " letter: " + letter);
	    
	    if(is_kill){
		DEBUG.put("command_kill!");
		if(emacs_area.core.is_ie()) {
		    if(k_buf != k) {
			return true;
		    }
		    e.keyCode = 0;
		}
		return false;
	    }
	    return true;
	}
    };

    var Core = {
	area : null,

	info : {
	    cursor : 0,
	    select_start : 0,
	    select_end : 0,
	    cut_buf : "",
	    row_cursor_absolute : 0,
	    row_start : 0,
	    row_end : 0,
	    scroll_top : 0,
	    scroll_left : 0,
	    select_range : 0,
	    first_line_number : 0,
	    cursor_line_number : 0,
	    all_lines : [],
	    delete_strings : [""],
	    delete_time : new Date(),
	    select_absolute : 0,
	    select_absolute_line_number : 0,
	    is_select : false,
	    is_move : true,
	    is_prev : true,
	    is_history_mode : false,
	    now_buff_num : 0,
	    buffs : [{text : "", cursor : 0}]
	},

	is_ie : function() {
	    return /*@cc_on!@*/false;
	},

	is_gecko : function() {
	    if(navigator){
		if(navigator.userAgent){
		    if(navigator.userAgent.indexOf("Gecko/") != -1){
			return true;
		    }
		}
	    }
	    return false;
	},

	is_hanaku : function(str) {
	    for (var i = 0; i < str.length; i++) {
		var c = str.charCodeAt(i);
		if ( (c >= 0x0 && c < 0x81) || (c == 0xf8f0) || (c >= 0xff61 && c < 0xffa0) || (c >= 0xf8f1 && c < 0xf8f4)) {
		    return true;
		}
	    }
	    return false;
	},

	init : function(textarea_id) {
	    this.area = document.getElementById(textarea_id);
	    if(this.is_ie()) {
		Object.extend(this,IECore);
	    } else {
		Object.extend(this,NNCore);
	    }
	    this.area.onkeyup = this.set_info;
	    this.area.onclick = function() { emacs_area.core.set_info(); emacs_area.core.clear_select_mode();};
	    this.save_buffer();
	    this.sub_init();
	},

	clear : function() {
	    this.area.onkeyup = null;
	    this.area.onclick = null;
	    this.area = null;
	},
	
	set_info_cursor : function() {
	    var self = emacs_area.core;
	    area_select = self.area_selection();
	    if(!self.info.is_select) self.info.cursor = area_select.end;
	    self.info.select_end = area_select.end;
	    self.info.select_start = area_select.start;
	    DEBUG.put('area_start : ' + area_select.start + "area_end : " + area_select.end + "cursor : " + self.info.cursor);
	},

	set_info : function() {
	    var self = emacs_area.core;
	    self.set_info_cursor();
	    var text = self.area;
	    self.sub_set_info();
	    self.info.scroll_top = text.scrollTop;
	    self.info.scroll_left = text.scrollLeft;
	    self.info.first_line_number = text.value.substring(0, self.info.select_start).split("\n").length -1;
	    var cursor_line_str = text.value.substring(0, self.info.cursor).split("\n");
	    self.info.cursor_line_number = text.value.substring(0, self.info.cursor).split("\n").length -1;
	    if("" == cursor_line_str.last() && self.info.cursor != self.info.row_start) {
		self.info.row_end = self.info.cursor;
		self.info.row_start = self.info.cursor;
	    }
	    DEBUG.put( "info cursor: "+ self.info.cursor + " row_start: " + self.info.row_start + " row_end: " + self.info.row_end + " scrollTop: " + self.info.scroll_top + " scroll_left" + self.info.scroll_left
		      + " first_line_number: " + self.info.first_line_number + " cursor_line_number: " + self.info.cursor_line_number + " select_absolute_line_number : " + self.info.select_absolute_line_number);
	},

	select : function(start, end) {
	    this.area.setSelectionRange(start, end);
	},

	left : function() {
	    if(this.info.is_select) {
		this.move_left_for_select_mode(-1);
	    }else{
		this.move(-1);
	    }
	},

	right : function() {
	    if(this.info.is_select) {
		this.move_right_for_select_mode(1);
	    }else{
		this.move(1);
	    }
	},

	clear_select_mode : function() {
	    this.info.select_range = 0;
	    this.info.select_absolute = 0;
	    this.info.is_select = false;
	},

	switch_select : function() {
	    DEBUG.put('switch_select : ' + this.info.is_select);
	    if(this.info.is_select) {
		move_num = this.create_clear_select_move_num();
		this.move_absolute(move_num);
		this.clear_select_mode();
	    } else {
		this.info.select_absolute = this.info.cursor;
		this.info.select_absolute_line_number = this.area.value.substring(0, this.info.cursor).split("\n").length-1;
		this.info.is_select = true;
	    }
	},

	clear_select : function() {
	    if(this.info.is_select) {
		move_num = this.create_clear_select_move_num();
		this.move_absolute(move_num);
		this.clear_select_mode();
	    }
	},

	clear_roll : function() {
	    if(this.info.is_select) {
		return this.clear_select();
	    }
	    if(this.info.is_prev) {
		this.info.is_prev = false;
	    } else {
		this.info.is_prev = true;
	    }
	},

	switch_buff : function() {
	    DEBUG.put("switch_buff-> is_prev : " + this.info.is_prev);
	    this.info.is_history_mode = true;
	    if(this.info.is_prev) {
		return this.prev_buf();
	    }
	    this.next_buf();
	},

	kill_after : function() {
	    var del_range = this.info.row_end - this.info.cursor;
	    DEBUG.put('kill_after : del_range = ' + del_range);
	    if(del_range == 0) {
		this.del(1);
		return;
	    }
	    this.del(del_range);
	},

	move_row_home : function() {
	    if(this.info.is_select) {
		DEBUG.put(this.info.row_start + " : " + this.info.cursor);
		this.move_left_for_select_mode(this.info.row_start - this.info.cursor);
	    } else {
		this.move_absolute(this.info.row_start);
	    }
	},

	move_row_end : function() {
	    if(this.info.is_select) {
		DEBUG.put(this.info.row_end + " : " + this.info.cursor);
		this.move_right_for_select_mode(this.info.row_end - this.info.cursor);
	    } else {
		this.move_absolute(this.info.row_end);
	    }
	},

	copy : function() {
	    var start = this.info.select_start;
	    var end = this.info.select_end;
	    if(end < start) {
		end = this.info.select_start;
		start = this.info.select_end;
	    }
	    var text = this.area.value;
	    var cursor = this.info.cursor;
	    this.set_delete_str(text.substring(start, end));
	    this.move_absolute(cursor);
	    this.clear_select_mode();
	},

	cut : function() {
	    var start = this.info.select_start;
	    var end = this.info.select_end;
	    if(end < start) {
		end = this.info.select_start;
		start = this.info.select_end;
	    }
	    var text = this.area.value;
	    this.set_delete_str(text.substring(start, end));
	    this.area.value = text.reject(start, end);
	    this.move_absolute(start);
	    this.clear_select_mode();
	},

	paste : function() {
	    var start = this.info.select_start;
	    var end = this.info.select_end;
	    if(start > end) {
		start = this.info.select_end;
		end = this.info.select_start;
	    }
	    var text = this.area.value;
	    var up_str = text.substring(0, start);
	    var under_str= text.substring(end, text.length);
	    var del_strs = this.info.delete_strings;
	    this.area.value = up_str + del_strs.last() + under_str;
	    var move_num = this.create_paste_move_num(del_strs.last());
	    this.move_absolute(move_num);
	    this.clear_select_mode();
	    DEBUG.put("paste : move_cusor = " + move_num + " paste_str = " + del_strs.last() + " end = " + end + "start = " + start);
	},

	up_cursor : function() {
	    if(0 == this.info.cursor_line_number){
		return;
	    }
	    if(this.info.is_move) {
		this.info.row_cursor_absolute =  this.info.cursor - this.info.row_start;
		this.info.is_move = false;
	    }
	    var before_line_length = this.info.all_lines[this.info.cursor_line_number - 1].length;
	    if(before_line_length <= this.info.row_cursor_absolute) {
		var move_num = this.info.row_start - 1;
		DEBUG.put('up : over_line_num!');
	    } else {
		var move_num = this.info.row_start + (this.info.row_cursor_absolute - before_line_length);
	    }
	    if(this.info.is_select) {
		this.move_up_for_select_mode(move_num);
	    } else { 
		this.move_absolute(move_num);
	    }
	    DEBUG.put('up : move_num = ' + move_num + ' row_cursor_absolute = ' + this.info.row_cursor_absolute + ' before_line_length = ' + before_line_length);
	},

	down_cursor : function() {
	    if(this.info.all_lines.length == (this.info.cursor_line_number + 1)){
		return;
	    }
	    if(this.info.is_move) {
		this.info.row_cursor_absolute =  this.info.cursor - this.info.row_start;
		this.info.is_move = false;
	    }
	    var after_line_str = this.info.all_lines[this.info.cursor_line_number + 1];
	    var after_line_length = after_line_str.length;
	    DEBUG.put('down : ' + after_line_str);
	    if(after_line_length <= this.info.row_cursor_absolute) {
		var move_num = this.info.row_end + after_line_length;
	    } else {
		var move_num = this.info.row_end + this.info.row_cursor_absolute + 1;
	    }
	    if(after_line_str == "\n") {
		var move_num = this.info.row_end + 1;
	    }
	    if(this.info.is_select) {
		this.move_down_for_select_mode(move_num);
	    } else {
		this.move_absolute(move_num);
	    }
	    DEBUG.put('down : move_num = ' + move_num + ' row_cursor_absolute = ' + this.info.row_cursor_absolute + ' after_line_length = ' + after_line_length);
	},

	move_up_for_select_mode : function(move_num) {
	    this.move_left_for_select_mode(move_num - this.info.cursor);
	},

	move_down_for_select_mode : function(move_num) {
	    this.move_right_for_select_mode(move_num - this.info.cursor);
	},

	save_buffer : function() {
	    if(!this.area) return;
	    var diff_buff_num = this.info.now_buff_num;
	    if(!this.info.is_history_mode) {
		var buffs = this.info.buffs;
		if(this.area && (buffs[this.info.now_buff_num -1] == null || buffs[this.info.now_buff_num -1].text != this.area.value)) {
		    buffs[this.info.now_buff_num] = { cursor : this.info.cursor, text : this.area.value};
		    this.info.now_buff_num++;
		    for(var i=this.info.now_buff_num,len=buffs.length;i<len;i++) {
			buffs[i] = null;
		    }
		    this.info.buffs = buffs;
		    DEBUG.put("save_buffer -> save? : true  buff_text : " + buffs[this.info.now_buff_num-1].text + " buff_cnt :" + buffs.length)
		    diff_buff_num = this.info.now_buff_num -1;
		}
	    }
	    if(!this.info.buffs[diff_buff_num]) {
		return this.info.is_history_mode = false;
	    }
	    if(this.area.value != this.info.buffs[diff_buff_num].text) {
		this.info.is_prev = true;
		this.info.is_history_mode = false;
	    }
	},

	prev_buf : function() {
	    if(this.info.buffs[this.info.now_buff_num-1]) {
		this.info.now_buff_num-=1;
		var buf = this.info.buffs[this.info.now_buff_num];
		if(buf.text == this.area.value) {
		    this.prev_buf();
		    return;
		}
		this.area.value = buf.text;
		DEBUG.put("prev_buf-> buf.text : " + buf.text + " buff_cnt : " + this.info.now_buff_num);
		this.move_absolute(buf.cursor);
	    }
	},

	next_buf : function() {
	    if(this.info.buffs[this.info.now_buff_num+1]) {
		this.info.now_buff_num+=1;
		var buf = this.info.buffs[this.info.now_buff_num];
		if(buf.text == this.area.value) {
		    this.next_buf();
		    return;
		}
		this.area.value = buf.text;
		DEBUG.put("next_buf-> buf.text : " + buf.text + " buff_cnt : " + this.info.now_buff_num);
		this.move_absolute(buf.cursor);
	    }
	},

	prev_page : function() {
	    this.area.scrollTop -= this.area.offsetHeight;
	},

	next_page : function() {
	    this.area.scrollTop += this.area.offsetHeight;
	},

	return_after : function() {
	    var cursor = this.info.cursor;
	    this.area.value = this.area.value.insert(cursor, "\n");
	    this.move_absolute(cursor);
	},

	reset_delete_time : function() {
	    this.info.delete_time = 0;
	},

	is_max : function(num) {
	    return this.area.value.length == num;
	},

	is_row_max : function(num) {
	    return this.info.row_end == num;
	},

	show_help : function() {
	    var help = "Key Help\n";
	    help += "Ctrl+q :  emacs mode/nomal mode\n"
	    help += "Ctrl+? :  show help\n"
	    help += "Ctrl+space :  select mode/nomal mode\n"
	    help += "Ctrl+f :  right\n"
	    help += "Ctrl+b :  left\n"
	    help += "Ctrl+p :  up\n"
	    help += "Ctrl+n :  down\n"
	    help += "Ctrl+g :  quite\n"
	    help += "Ctrl+d :  delete\n"
	    help += "Ctrl+k :  after line string kill\n"
	    help += "Ctrl+o :  line break\n"
	    help += "Ctrl+w :  select kill\n"
	    help += "Ctrl+y :  paste\n"
	    help += "Ctrl+a :  move line first\n"
	    help += "Ctrl+e :  move line end\n"
	    help += "Ctrl+/ :  history prev / next by Ctrl+g\n"
	    help += "Ctrl+v :  next page\n"
	    help += "Alt+w  :  copy select\n"
	    help += "Alt+v  :  prev page\n"
	    alert(help)
	}

    };

    var IECore = {
/*
	area_selection : function() {
	    var docRange = document.selection.createRange();
	    var textRange = document.body.createTextRange();
	    textRange.moveToElementText(this.area);

	    if(!this.info.is_select) {
		var range = textRange.duplicate();
		docRange = document.selection.createRange();
		range.setEndPoint('EndToEnd', docRange);
		var start = range.text.length;
		return { start: start, end : start };
	    }

	    var range = textRange.duplicate();
	    range.setEndPoint('EndToStart', docRange);
	    var start = range.text.length;

	    var range = textRange.duplicate();
	    range.setEndPoint('EndToEnd', docRange);
	    var end = range.text.length;
	    return { start: start, end : end };
	},
*/
	area_selection : function() {    
	    DEBUG.put(this.area.value);
	    this.area.focus();

	    var contents = this.area.value;
	    var originalContents = contents;
	    var range = document.selection.createRange();
	    var bookmark = range.getBookmark();

	    var marker = "##SELECTION_MARKER_" + Math.random() + "##";
	    while(contents.indexOf(marker) != -1) {
		marker = "##SELECTION_MARKER_" + Math.random() + "##";
	    }

	    var parent = range.parentElement();
	    if (parent == null || parent.type != "textarea") {
		return { start: 0, end: 0 };
	    }
	    range.text = marker + range.text + marker;
	    contents = this.area.value;

	    var result = {};
	    result.start = contents.indexOf(marker);
	    contents = contents.replace(marker, "");
	    result.end = contents.indexOf(marker);

	    this.area.value = originalContents;
	    DEBUG.put("area_selection -> start : " + result.start + " end : " + result.end + " value : " + this.area.value);
	    if(this.is_max(result.end) && result.end == result.start){
		range.move("character", result.start - (this.area.value.substring(0, result.start).split("\n").length - 1));
	    } else {
		DEBUG.put('hoge');
		range.moveToBookmark(bookmark);		
	    }
	    range.select();

	    return result;
	},

	move : function(move_num) {
	    var range = document.selection.createRange();
	    var move_fun = "";
	    if(move_num > 0) {
		move_fun = "moveStart";
	    } else {
		move_fun = "moveEnd";
	    }
	    if(this.area.value.length+1 != (this.info.cursor + move_num)) range[move_fun]('character', move_num);
	    range.select();
	    this.area.focus();
	},

	move_absolute : function(cursor) {
	    var range = document.selection.createRange();
	    this.area.value += "";
	    range.move('character', cursor - this.info.first_line_number);
	    range.select();
	    this.area.focus();
	    DEBUG.put("move_absolute : cursor = " + cursor + " line_number = " + this.info.first_line_number);
	},

	move_left_for_select_mode : function(move_num) {
	    var range = document.selection.createRange();
	    var move_range = this.info.select_range + move_num
	    if((this.info.cursor + move_num) < 0) {
		move_num = move_num - (this.info.cursor + move_num);
	    }
	    if(this.info.row_start > (this.info.cursor+move_num)) {
		this.info.cursor -= 1;
	    }
	    if(this.info.select_range <= 0) {
		this.info.cursor += move_num;
		range.moveStart('character', move_num);
	    } else {
		if(this.info.select_range < (-move_num)) {
		    range.moveEnd('character', -this.info.select_range);
		    range.moveStart('character', move_range);
		} else {
		    range.moveEnd('character', move_num);
		}
		this.info.cursor += move_num;
	    }
	    range.select();
	    this.area.focus();
	    this.info.select_range += move_num;
	    DEBUG.put("move_left_for_select_mode : select_range = " + this.info.select_range + " move_num = " + move_num);
	},

	move_right_for_select_mode : function(move_num) {
	    var move_range = this.info.select_range + move_num
	    if((this.info.cursor + move_num) > this.area.value.length) {
		move_num = move_num - ((this.info.cursor + move_num) - this.area.value.length);
	    }
	    var range = document.selection.createRange();
	    if(this.info.row_end < (this.info.cursor+move_num)) {
		this.info.cursor += 1;
	    }
	    if(this.info.select_range >= 0) {
		this.info.cursor += move_num;
		range.moveEnd('character', move_num);
	    } else {
		if(this.info.select_range > (-move_num)) {
		    range.moveStart('character', -this.info.select_range);
		    range.moveEnd('character', move_range);
		} else {
		    range.moveStart('character', move_num);
		}
		this.info.cursor += move_num;
	    }
	    range.select();
	    this.area.focus();
	    this.info.select_range += move_num;
	    DEBUG.put("move_right_for_select_mode : select_range = " + this.info.select_range + " move_num = " + move_num);
	},

	del : function(num) {
	    var start = this.info.select_start;
	    var text = this.area.value;
	    if(start == this.info.row_end && num == 1) {
		num += 1;
	    }
	    this.set_delete_str_time(text.substring(start, start+num));
	    this.area.value = text.reject(start, start+num);
	    this.move_absolute(start);
	    this.clear_select_mode();
	    DEBUG.put('delete : start = ' + start + ' del_range = ' + (start+1) + ' del_char = ' + text.substring(start, start+1) );
	},

	set_delete_str_time : function(str) {
	    var now = new Date();
	    var del_strs = this.info.delete_strings;
	    if((now - this.info.delete_time) > 10000) {
		this.set_delete_str(str);
		this.info.delete_time = now;
		return;
	    }	
	    this.info.delete_time = now;
	    del_strs[del_strs.length -1] += str;
	    clipboardData.setData("Text", del_strs.last());
	},

	set_delete_str : function(str) {
	    this.info.delete_strings.push(str);
	    clipboardData.setData("Text", str);
	},

	create_paste_move_num : function(str) {
	    var cursor = this.info.cursor;
	    cursor -= str.split("\n").length -1;
	    return (cursor + str.length);
	},

	create_clear_select_move_num : function() {
	    var row_diff = this.info.cursor_line_number - this.info.select_absolute_line_number;
	    if(row_diff > 0) return this.info.cursor - row_diff
	    return this.info.cursor;
	},

	sub_init : function() {
	},

	sub_set_info : function() {
	    var text = this.area;
	    this.info.all_lines = text.value.split("\n");
	    this.info.all_lines[this.info.all_lines.length-1] += "\n";
	    for(var i=0,len=this.info.all_lines.length,strs=0; i<len; i++) {
		if(strs <= this.info.cursor && this.info.cursor <= (strs + this.info.all_lines[i].length + 1)) {
		    this.info.row_start = strs;
		    this.info.row_end = strs + this.info.all_lines[i].length -1;
		    DEBUG.put('row_end : ' + this.info.row_end);
		}
		strs += this.info.all_lines[i].length + 1;
	    }
	}
    };

    var NNCore = {
	area_selection : function() {
	    return { start : this.area.selectionStart, end : this.area.selectionEnd};
	},

	area_selection_special : function() {
	    return this.area_selection();
	},

	move : function(move_num){
	    var start = this.info.cursor + move_num;
	    var end = this.info.cursor;
	    if(!this.info.is_select) end += move_num;
	    this.area.setSelectionRange(start, end);
	},

	move_absolute : function(cursor) {
	    this.area.setSelectionRange(cursor, cursor);
	},

	move_left_for_select_mode : function(move_num) {
	    var move_range = this.info.select_range + move_num
	    if((this.info.cursor + move_num) < 0) {
		move_num = move_num - (this.info.cursor + move_num);
	    }
	    if(this.info.select_range <= 0) {
		this.info.cursor += move_num;
		this.area.selectionStart += move_num;
	    } else {
		if(this.info.select_range < (-move_num)) {
		    this.area.selectionEnd += -this.info.select_range;
		    this.area.selectionStart += move_range
		} else {
		    this.area.selectionEnd += move_num;
		}
		this.info.cursor += move_num;
	    }
	    this.info.select_range += move_num;
	    DEBUG.put("move_left_for_select_mode : select_range = " + this.info.select_range + " move_num = " + move_num);
	},

	move_right_for_select_mode : function(move_num) {
	    var move_range = this.info.select_range + move_num
	    if((this.info.cursor + move_num) > this.area.value.length) {
		move_num = move_num - ((this.info.cursor + move_num) - this.area.value.length);
	    }
	    if(this.info.select_range >= 0) {
		this.info.cursor += move_num;
		this.area.selectionEnd += move_num;
	    } else {
		if(this.info.select_range > (-move_num)) {
		    this.area.selectionStart += -this.info.select_range;
		    this.area.selectionEnd += move_range
		} else {
		    this.area.selectionStart += move_num;
		}
		this.info.cursor += move_num;
	    }
	    this.info.select_range += move_num;
	    DEBUG.put("move_left_for_select_mode : select_range = " + this.info.select_range + " move_num = " + move_num);
	},

	del : function(num) {
	    var start = this.info.select_start;
	    var text = this.area.value;
	    this.set_delete_str_time(text.substring(start, start+num));
	    this.area.value = text.reject(start, start+num);
	    this.move_absolute(start);
	    this.clear_select_mode();
	    DEBUG.put('delete : start = ' + start + ' del_range = ' + (start+1) + ' del_char = ' + text.substring(start, start+1) );
	},

	set_delete_str : function(str) {
	    this.info.delete_strings.push(str);
	},

	set_delete_str_time : function(str) {
	    var now = new Date();
	    var del_strs = this.info.delete_strings;
	    if((now - this.info.delete_time) > 10000) {
		del_strs.push(str);
		this.info.delete_time = now;
		return;
	    }	
	    this.info.delete_time = now;
	    del_strs[del_strs.length -1] += str;
	},

	create_paste_move_num : function(str) {
	    var cursor = this.info.cursor;
	    return (cursor + str.length);
	},

	create_clear_select_move_num : function() {
	    return this.info.cursor;
	},

	sub_init : function() {
	},

	sub_set_info : function() {
	    var text = this.area;
	    this.info.all_lines = text.value.split("\n");
	    this.info.all_lines[this.info.all_lines.length-1] += "\n";
	    for(var i=0,len=this.info.all_lines.length,strs=0; i<len; i++) {
		if(strs <= this.info.cursor && this.info.cursor <= (strs + this.info.all_lines[i].length + 1)) {
		    this.info.row_start = strs;
		    this.info.row_end = strs + this.info.all_lines[i].length;
		    DEBUG.put('row_end : ' + this.info.row_end);
		}
		strs += this.info.all_lines[i].length + 1;
	    }
	    var texts = text.value.split("\n");
	    for(var i=0,len=texts.length; i<len; i++) {
		texts[i] += "\n";
	    }
	    this.info.all_lines = texts;
	}
    };

    emacs_area = Emacs;
    var clavier_cds={8:"BS", 9:"Tab", 13:"Enter", 16:"Shift", 17:"Ctrl", 18:"Alt", 19:"Pause", 25:"Jpmode",  27:"Esc", 32:"Space", 33:"Page up", 34:"Page down", 35:"End", 36:"Home", 37:"Left", 38:"Up", 39:"Right",
	40:"Down", 45:"Insert", 46:"Delete", 112:"F1", 113:"F2", 114:"F3", 115:"F4", 116:"F5", 117:"F6", 118:"F7", 119:"F8", 120:"F9", 121:"F10", 122:"F11", 123:"F12", 191:"/",
	53:"%", 229:"Jpmode" };

    DEBUG.is_debug_mode = false;
})();
