﻿// chat.js
//
// Chat Javascript Class
//
// ver. 0.01
// ver. 0.02 divide Chat Class into ChatUI class and ChatCtrl class
// ver. 0.03 update lingrcom.js, add autoLink function
// ver. 0.04 update lingrcom.js, change start sequence to async
// ver. 0.05 create Chat HTML by Javascript
//
// Copyright 2007 inutch   http://d.hatena.ne.jp/inutch/



/// const strings
var CHATUI_STR_OLDMSGS = "古いメッセージを見る";
var CHATUI_STR_ENTER = "さんが入室しました";
var CHATUI_STR_LEAVE = "さんが退室しました";
var CHATUI_STR_NAME_PREFIX = "";
var CHATUI_STR_NAME_SUFFIX = "";
var CHATUI_STR_TEXT_PREFIX = "&nbsp;";
var CHATUI_STR_TEXT_SUFFIX = "&nbsp;";
var CHATUI_STR_TIME_PREFIX = "&nbsp;";
var CHATUI_STR_TIME_SUFFIX = "";
var CHATUI_STR_CHATTERS = "参加者";

var CHATUI_STR_SAYBUTTON = "送信";
var CHATUI_STR_LEAVEBUTTON = "退室";
var CHATUI_STR_NICKLABEL = "&nbsp;nickname:";
var CHATUI_STR_JOINBUTTON = "参加";
var CHATUI_STR_CREDITS
   = "<a href='http://inutch.s18.xrea.com/soft/chatjs/' target='_blank'>chat.js</a>"
   + " Powered by <a href='http://www.lingr.com/' target='_blank'>Lingr</a>";

/// settings
var CHATUI_VAL_TIME_INTERVAL = 5*60*1000;
var CHATUI_VAL_TIME_HHMM_ONLY = 12*60*60*1000;
var CHATCTRL_BOOL_FOCUS_ONLOAD = false;
var CHATCTRL_VAL_GETMSGS_TIMEOUT = 15*1000;



/////// Common Function //////////////////////////////////////////////

if( typeof( window.escapeHtml ) == "undefined" ){
  function escapeHtml(str)
  {
    if(!str){ return ""; }
    var str2 = "";
    var tmp;
    for(var i=0; i<str.length; i++){
      if( str.charAt(i) == "<"){ tmp = "&lt;"; }
      else if( str.charAt(i) == ">"){ tmp = "&gt;"; }
      else if( str.charAt(i) == "\""){ tmp = "&quot;"; }
      else if( str.charAt(i) == "$"){ tmp = "&amp;"; }
      else { tmp = str.charAt(i); }
      str2 += tmp;
    }
    return str2;
  }
}


if( typeof( window.escapeRegExp ) == "undefined" ){
  function escapeRegExp(str)
  {
    if(!str){ return ""; }
    var str2 = "";
    var tmp;
    for(var i=0; i<str.length; i++){
      if( str.charAt(i) == "+"){ tmp = "\\+"; }
      else if( str.charAt(i) == "*"){ tmp = "\\*"; }
      else if( str.charAt(i) == "?"){ tmp = "\\?"; }
      else if( str.charAt(i) == "."){ tmp = "\\."; }
      else if( str.charAt(i) == "\\"){ tmp = "\\\\"; }
      else { tmp = str.charAt(i); }
      str2 += tmp;
    }
    return str2;
  }
}


if( typeof( window.autoLink ) == "undefined" ){
  function autoLink(str)
  {
    if(!str){ return ""; }
    var http = str.match(/(https?|ftp):\/\/[\a-zA-Z0-9~\/\._\?\&=\-%#\+:;,\@\'\\]+/g);
    if(http){
      for(var i=0; i<http.length; i++){
        var same = false;
        for(var j=0; j<i; j++){
          if(http[i] == http[j]){ same = true; }
        }
        if(!same){
          var atag = "<a href='" + http[i] + "' target='_blank'>" + http[i] + "</a>";
          re = new RegExp(escapeRegExp(http[i]), "g");
          str = str.replace(re, atag);
        }
      }
    }
    return str;
  }
}



/////// ChatUI Class //////////////////////////////////////////////

function ChatUI(myname, roomid)
{
  this.objname = myname;
  this.roomid = roomid;

  this.ts_interval = CHATUI_VAL_TIME_INTERVAL;
  this.time_only = CHATUI_VAL_TIME_HHMM_ONLY;
  this.c_ms = (new Date()).getTime();
  this.pre_time = 0;
  
  this.setNickname = function(){};
  this.say = function(){};
  this.leaveRoom = function(){};
//  this.getMessages = function(){};

}


//start prototype
ChatUI.prototype = {


  createUI:function()
  {
    var parent = document.getElementById("chatwrap");

      var chatlog = document.createElement("div");
      chatlog.id = "chatlog";

      var chatform = document.createElement("div");
      chatform.id = "chatform";
        var chattext = document.createElement("textarea");
        chattext.id = "chattext";
        chattext.onkeypress = this.onKeyPress.bind(this);
      chatform.appendChild(chattext);
        var saybutton = document.createElement("input");
        saybutton.id = "saybutton";
        saybutton.setAttribute("type", "button");
        saybutton.setAttribute("value", CHATUI_STR_SAYBUTTON);
        saybutton.onclick = this.onSay.bind(this);
      chatform.appendChild(saybutton);
        var leaveroom = document.createElement("a");
        leaveroom.id = "leaveroom";
        leaveroom.onclick = ( function(){this.onLeaveRoom();return false;} ).bind(this);
        leaveroom.setAttribute("href", "");
        leaveroom.innerHTML = CHATUI_STR_LEAVEBUTTON;
      chatform.appendChild(leaveroom);

      var nickwrap = document.createElement("div");
      nickwrap.id = "nickwrap";
      nickwrap.style.visibility = "visible";
        var nickform = document.createElement("form");
        nickform.onsubmit = ( function(){this.onSetNickname();return false;} ).bind(this);
          var nicklabel = document.createElement("span");
          nicklabel.innerHTML = CHATUI_STR_NICKLABEL;
        nickform.appendChild(nicklabel);
          var nickname = document.createElement("input");
          nickname.id = "nickname";
          nickname.setAttribute("type", "text");
        nickform.appendChild(nickname);
          var nickbutton = document.createElement("input");
          nickbutton.id = "nickbutton";
          nickbutton.setAttribute("type", "submit");
          nickbutton.setAttribute("value", CHATUI_STR_JOINBUTTON);
        nickform.appendChild(nickbutton);
      nickwrap.appendChild(nickform);

      var ocupants = document.createElement("div");
      ocupants.id = "ocupants";

      var credits = document.createElement("div");
      credits.id = "credits";
      credits.innerHTML = CHATUI_STR_CREDITS;

    parent.appendChild(chatlog);
    parent.appendChild(chatform);
    parent.appendChild(nickwrap);
    parent.appendChild(ocupants);
    parent.appendChild(credits);

  },


  focusOnNickname:function()
  {
    document.getElementById("nickname").focus();
  },

  addSysMsg:function(str)
  {
    if(!this.msgdiv){
      this.msgdiv = document.createElement("div");
      this.msgdiv.className = "chatlog_system";
      document.getElementById("chatlog").appendChild(this.msgdiv);
    }
    var log = document.createElement("span");
    log.innerHTML = escapeHtml(str).replace(/\n/, "<br>");
    this.msgdiv.appendChild(log);
  },


  delSysMsg:function()
  {
    if(this.msgdiv){
      document.getElementById("chatlog").removeChild(this.msgdiv);
      this.msgdiv = null;
    }
  },


  addArchiveLink:function()
  {
    var log = document.createElement("div");
    log.className = "chatlog_acv";
    log.innerHTML = "<a href='http://www.lingr.com/room/" + this.roomid
                           + "/archives' target='_blank'>" + CHATUI_STR_OLDMSGS + "</a>";
    document.getElementById("chatlog").appendChild(log);
  },


  dispMsgs:function(msgs, myid, first)
  {
    //displog(this.objname + ".dispMsgs: len=" + msgs.length);
    if(!msgs){
      displog(this.objname + ".dispMsgs: No Messages");
      return;
    }
    
    var parent = document.getElementById("chatlog");

    if(first){
      var logwrap = document.createElement("div");
      logwrap.className = "chatlog_innerwrap";
      for(var i=0; i<msgs.length; i++ ){
        logwrap.appendChild( this.createLogItem(msgs[i], myid, (first && i==msgs.length-1)) );
      }
      parent.appendChild(logwrap);
    } else {
      for(var i=0; i<msgs.length; i++ ){
        parent.appendChild( this.createLogItem(msgs[i], myid) );
      }
    }
    parent.scrollTop = parent.scrollHeight;
  },


  createLogItem:function(msg, myid, last)
  {
    //displog(this.objname + ".dispMsg: msg=" + msg.text);

    var log = document.createElement("div");
    log.className = "chatlog_item";
    var ts = this.makeTsStrFull(msg);
    log.title = ts;

    if(msg.type == "user"){
      if(msg.occupant_id == myid){
        log.className = "chatlog_item_myself";
      }
      
      var name = document.createElement("span");
      name.className = "chatlog_name";
      name.innerHTML = CHATUI_STR_NAME_PREFIX + escapeHtml(msg.nickname)
                     + CHATUI_STR_NAME_SUFFIX;
      log.appendChild(name);
      
      text = msg.text;
    }
    else if(msg.type == "system:enter"){
      log.className = "chatlog_item_enter";
      text = msg.nickname + CHATUI_STR_ENTER;
    }
    else if(msg.type == "system:leave"){
      log.className = "chatlog_item_leave";
      text = msg.nickname + CHATUI_STR_LEAVE;
    }
    
    var logtext = document.createElement("span");
    logtext.className = "chatlog_text";
    logtext.innerHTML = CHATUI_STR_TEXT_PREFIX
                      + autoLink(escapeHtml(text).replace(/\n/g, "<br>"))
                      + CHATUI_STR_TEXT_SUFFIX;
    log.appendChild(logtext);
    
    var msg_time = Date.parse(ts);
    if( msg_time - this.pre_time > this.ts_interval || last){
      log.appendChild(this.createTsObj(ts));
      this.pre_time = msg_time;
    }

    return log;
  },


  makeTsStrFull:function(msg)
  {
    var ts = msg.timestamp;
    ts = ts.replace("T", " ");
    ts = ts.replace(/-/g, "/");
    ts = ts.replace(/Z$/, "");
    ts = ts.replace(/\+.+$/, "");
    return ts;
  },


  makeTsStrTime:function(ts)
  {
    return ts.replace(/^....\/..\/..\s/, "");
  },


  remeveTsStrSecond:function(ts)
  {
    return ts.replace(/:..$/, "");
  },


  createTsObj:function(ts)
  {
    var logtime = null;
    var msg_time = Date.parse(ts);
    ts = this.remeveTsStrSecond(ts);
    if( this.c_ms - msg_time < this.time_only && this.time_only != 0 ){
      ts = this.makeTsStrTime(ts);
    }
    logtime = document.createElement("div");
    logtime.className = "chatlog_time";
    logtime.innerHTML = CHATUI_STR_TIME_PREFIX
                      + escapeHtml(ts)
                      + CHATUI_STR_TIME_SUFFIX;
    return logtime;
  },


  setOcupants:function(ocps, myid)
  {
    var i;
    var parent = document.getElementById("ocupants");
    for(i=parent.childNodes.length-1; i>=0; i--){
      parent.removeChild(parent.childNodes[i]);
    }

    var ocptop = document.createElement("div");
    ocptop.className = "ocp_item_top";
    ocptop.innerHTML = CHATUI_STR_CHATTERS;
    parent.appendChild(ocptop);

    if(!ocps){ return; }
    displog(this.objname + ".cb_ocupants_change: " + ocps.length);
    for(i=0; i<ocps.length; i++){
      if(ocps[i].nickname != undefined){
        var ocp = document.createElement("div");
        ocp.className = "ocp_item";
        ocp.innerHTML = escapeHtml(ocps[i].nickname);
        if(ocps[i].id == myid){
          ocp.className = "ocp_item_myself";
        }
        parent.appendChild(ocp);
      }
    }
  },


  onSetNickname:function()
  {
    var nick = document.getElementById("nickname");
    var nickwrap = document.getElementById("nickwrap");
    if( nick.value != "" && this.setNickname(nick.value)){
      nickwrap.style.visibility = "hidden";
      this.nick_toporg = nickwrap.style.top;
      nickwrap.style.top -= (nickwrap.style.height + 10);
      document.getElementById("chattext").focus();
    }
  },


  onSay:function()
  {
    var textobj = document.getElementById("chattext");
    if(textobj.value != "" ){
      this.say(textobj.value);
      textobj.value = "";
    }
  },


  onKeyPress:function(event)
  {
    var e = event;
    if(!e){ e = window.event; }
    //displog("onkeypress:" + e.keyCode);
    if(e.keyCode == 13){
      this.onSay();
      return false;
    }
    return true;
  },


  onLeaveRoom:function()
  {
    this.leaveRoom();
    var nickwrap = document.getElementById("nickwrap");
    nickwrap.style.visibility = "visible";
    nickwrap.style.top = this.nick_toporg;
    this.focusOnNickname();
  }


};
//end prototype


/////// ChatUI Class //////////////////////////////////////////////



/////// ChatCtrl Class //////////////////////////////////////////////

function ChatCtrl(myname, roomid)
{
  this.objname = myname;
  this.roomid = roomid;

  this.lingr = new LingrCom(this.objname + ".lingr");
  this.chat = new ChatUI(this.objname + ".chat", roomid);
  this.chat.createUI();
}


//start prototype
ChatCtrl.prototype = {


  start:function()
  {
    this.chat.setNickname = this.setNickname.bind(this);
    this.chat.say = this.say.bind(this);
    this.chat.leaveRoom = this.leaveRoom.bind(this);

    if(CHATCTRL_BOOL_FOCUS_ONLOAD){
      this.chat.focusOnNickname();
    }
    this.chat.delSysMsg();

    this.chat.addSysMsg("connecting ... ");
    if( !(this.lingr.createSessionA(this.start2.bind(this))) ){
      this.chat.addSysMsg("NG\n");
      this.chat.addArchiveLink();
    }
  },


  start2:function(ret)
  {
    if(ret){
      this.chat.addSysMsg("OK\n");
    } else {
      this.chat.addSysMsg("NG\n");
      this.chat.addArchiveLink();
      return;
    }

    this.chat.addSysMsg("entering room ... ");
    if( !(this.lingr.enterRoomA(this.roomid, null, this.start3.bind(this))) ){
      this.chat.addSysMsg("NG\n");
    }
  },


  start3:function(ret)
  {
    if(ret){
      this.chat.addSysMsg("OK\n");
    } else {
      this.chat.addSysMsg("NG\n");
      this.chat.addArchiveLink();
      return;
    }

    this.chat.addSysMsg("loading messages ... ");
    this.lingr.setOcupantsChangeCallback(this.cb_ocupants_change.bind(this));
    this.lingr.getMessages(-200, this.getMessages1st.bind(this));

    this.getmsg1st_timer = setTimeout(this.getMsgsTimeout.bind(this)
                                      , CHATCTRL_VAL_GETMSGS_TIMEOUT);
  },


  getMsgsTimeout:function()
  {
    this.chat.addSysMsg("NG\n");
    this.chat.addArchiveLink();
    //this.chat.addMsg("Observe Start.\n");
    this.lingr.observe(this.cb_observe.bind(this));
  },


  getMessages1st:function(msgs, myid)
  {
    //displog(this.objname + ".getMessages1st");
    if(this.getmsg1st_timer){
      clearTimeout(this.getmsg1st_timer);
    }
    this.chat.delSysMsg();
    this.chat.addArchiveLink();
    if(msgs){
      this.chat.dispMsgs(msgs, myid, true);
    }
    var parent = document.getElementById("chatlog");
    parent.scrollTop = parent.scrollHeight;

    this.lingr.observe(this.cb_observe.bind(this));
  },


  cb_observe:function(msgs, myid)
  {
    if(msgs){
      this.chat.dispMsgs(msgs, myid);
    } else {
      //this.chat.addMsg("obsserve failed.\n");
    }
  },


  setNickname:function(nickname)
  {
    return this.lingr.setNickname(nickname);
  },


  say:function(str)
  {
    this.lingr.say(str);
  },


  cb_ocupants_change:function(ocps, myid)
  {
    this.chat.setOcupants(ocps, myid);
  },


  leaveRoom:function()
  {
    if( this.lingr.leaveRoom() ){
      this.lingr.enterRoom(this.roomid, null);
      this.lingr.observe(this.cb_observe.bind(this));
    }
  },


  end:function()
  {
    this.lingr.destroySession();
  }

};
//end prototype


/////// ChatCtrl Class //////////////////////////////////////////////



