///////////////////////////////////////////////////////////////////////////////
// comet.js
// Copyright (c) 2007 masahito suzuki, Inc. All Rights Reserved.
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Ajaxオブジェクト.
///////////////////////////////////////////////////////////////////////////////
var MAjax = function() {} ;
// Ajaxメソッド.
MAjax.prototype = {
    // timeout.
    timeout: 0,
    // コネクションオブジェクト.
    xmlhttp: null,
    // 戻りシーケンス.
    resultSequence: 0,
    // 前回接続時の状態.
    beforeError: false,
    // url.
    url: null,
    // 非同期処理終了時間.
    asyncExitTime: -1,
    // 正常終了時のコールバックオブジェクト.
    callbackSuccess: function( o ) {
        // 実装してください.
    },
    // エラー終了時のコールバックオブジェクト.
    callbackError: function( method,url,params,state ) {
        // 実装してください.
    },
    // オブジェクト破棄.
    destroy: function() {
        try {
            if( isNull( this.xmlhttp ) == false ) {
                this.xmlhttp.abort() ;
            }
        } catch( e ) {
        }
    },
    // POSTでAjax実行.
    postAjax: function(url,header,params,sync) {
        if( useString( params ) == false ) {
            return ;
        }
        params = encodeParam(params) ;
        return this.accessAjax( this,"POST",url,header,params,sync ) ;
    },
    // GETでAjax実行.
    getAjax: function(url,header,params,sync) {
        var tm = new Date().getTime() ;
        if( isNull( params ) == false ) {
            params = encodeParam(params) ;
            return this.accessAjax( this,"GET",url+"?time="+tm+"&"+params,header,null,sync ) ;
        }
        else {
            return this.accessAjax( this,"GET",url+"?time="+tm,header,null,sync ) ;
        }
    },
    // AccessAjax.
    accessAjax: function(obj,method,url,header,params,sync) {
        //dbg( ">>method:" + method + " url:" + url + " params:" + params + " sync:" + sync ) ;
        this.beforeError = false ;
        if( isNull( sync ) ) {
            sync = "true" ;
        }
        else if( typeof( sync ) != "string" ) {
            if( sync == true ) {
                sync = "true" ;
            }
            else if( sync == false ) {
                sync = "false" ;
            }
        }
        // Ajax接続処理.
        if(window.ActiveXObject) {
            try {
                obj.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                obj.xmlhttp = null ;
            }
            if( isNull( obj.xmlhttp ) ) {
                try {
                    obj.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {
                    obj.xmlhttp = null;
                }
            }
        }
        else if(window.XMLHttpRequest) {
            obj.xmlhttp = new XMLHttpRequest();
        }
        if( isNull( obj.xmlhttp ) ) {
            obj.xmlhttp = new XMLHttpRequest();
            if( isNull( obj.xmlhttp ) ) {
                this.beforeError = true ;
                return ;
            }
        }
        // タイムアウト値設定.
        if( obj.timeout > 0 ) {
            if( isNull( header ) == false ) {
                header['CometTimeout'] = obj.timeout ;
            }
            else {
                header = {CometTimeout: obj.timeout} ;
            }
        }
        // ヘッダを設定.
        if( isNull( header ) == false ) {
            // IEや、Safariは、特殊ヘッダは送信できないらしいので、
            // 内容は、パラメータとして、設定する.
            if( method == "GET" ) {
                for( var k in header ) {
                    url += "&" + k + "=" + header[k] ;
                }
            }
            else {
                if( isNull( params ) == false ) {
                    for( var k in header ) {
                        params += "&" + k + "=" + header[k] ;
                    }
                }
                else {
                    var cnt = 0 ;
                    params = "" ;
                    for( var k in header ) {
                        if( cnt != 0 ) {
                            params += "&" ;
                        }
                        params += k + "=" + header[k] ;
                        cnt ++ ;
                    }
                }
            }
        }
        // コネクションオープン.
        obj.xmlhttp.open(method,url,sync);
        
        // リクエストヘッダが利用可能.
        if( typeof( obj.xmlhttp.setRequestHeader ) == 'function' ) {
            if( method == "POST" ) {
                obj.xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
            }
            // ajaxではKeepAliveは無しにする.
            //obj.xmlhttp.setRequestHeader("Keep-Alive","");
            //obj.xmlhttp.setRequestHeader("Connection","close");
        }
        // 非同期の場合.
        if( sync == "true" ) {
            obj.xmlhttp.onreadystatechange = function() {
                var errorFlag = false ;
                // HTTP処理完了.
                if( obj.xmlhttp.readyState == 4 ) {
                    try {
                        if( obj.xmlhttp.status == 200 ) {
                            var res = obj.xmlhttp.getResponseHeader("CometSequenceId") ;
                            if( isNull( res ) == false && res != "" && isNumeric( res ) == true ) {
                                try {
                                    res = parseInt( res ) ;
                                    obj.resultSequence = res ;
                                } catch( e ) {
                                }
                            }
                            var resValue = obj.xmlhttp.responseText ;
                            obj.callbackSuccess( resValue ) ;
                        }
                        else {
                            errorFlag = true ;
                        }
                    } catch( e ) {
                        errorFlag = false ;
                    }
                    if( errorFlag == true ) {
                        var resState = obj.xmlhttp.status ;
                        obj.callbackError( method,url,params,resState ) ;
                        this.beforeError = true ;
                    }
                    try {
                        obj.xmlhttp.abort() ;
                    } catch( e ) {
                    }
                    obj.xmlhttp = null ;
                    obj.asyncExitTime = new Date().getTime() ;
                }
            }
        }
        // 送信処理.
        this.url = url ;
        if( isNull( params ) ) {
            params = "" ;
        }
        obj.xmlhttp.send(params);
        // 同期の場合.
        if( sync == "false" ) {
            var res = obj.xmlhttp.getResponseHeader("CometSequenceId") ;
            if( isNull( res ) == false && res != "" && isNumeric( res ) == true ) {
                try {
                    res = parseInt( res ) ;
                    obj.resultSequence = res ;
                } catch( e ) {
                }
            }
            var resValue = obj.xmlhttp.responseText ;
            obj.callbackSuccess( resValue ) ;
        }
        else {
            obj.asyncExitTime = -1 ;
        }
    }
} ;

///////////////////////////////////////////////////////////////////////////////
// コメットオブジェクト.
///////////////////////////////////////////////////////////////////////////////
var MComet = function() {} ;
// コメットメソッド.
MComet.prototype = {
    // タイムアウト.
    timeout: 0,
    // url.
    url: null,
    // グループID.
    groupId: null,
    // リトライカウント.
    reconnect: 0,
    // オブジェクト生成チェック.
    createFlag: false,
    // 送信用パラメータ.
    sendParams: null,
    // コネクションAjax.
    ajax: new MAjax(),
    // 問い合わせ用Ajax.
    sndajax: new MAjax(),
    // 問い合わせ前回アクセス時間.
    resultAccessTime: new Date().getTime(),
    // タイマーコネクション.
    sendMcTime: null,
    // オブジェクト生成.
    create: function( ajax,sendAjax ) {
        this.ajax = ajax ;
        this.sndajax = sendAjax ;
    },
    // オブジェクト破棄.
    destroy: function() {
        if( isNull( this.sendMcTime ) == false ) {
            try {
                this.sendMcTime.shutdown() ;
            } catch( e ) {
            }
        }
        try {
            if( isNull( this.ajax ) == false ) {
                this.ajax.destroy() ;
            }
        } catch( e ) {
        }
        try {
            if( isNull( this.sndajax ) == false ) {
                this.sndajax.destroy() ;
            }
        } catch( e ) {
        }
    },
    // コネクションアクセス.
    connect: function( url,groupId,param ) {
        if( this.createFlag == true ) {
            return ;
        }
        if( isNull( this.sendMcTime ) ) {
            this.sendMcTime = new McTime() ;
            this.sendMcTime.startup( this,this.ajax ) ;
        }
        var x = url ;
        var p = url.indexOf( "?" ) ;
        if( p != -1 ) {
            x = x.substring( 0,p ) ;
        }
        if( endsWith( x,".ms" ) == false ) {
            x += ".ms" ;
        }
        if( p != -1 ) {
            var pm = url.substring( p+1,url.length ) ;
            if( isNull( param ) == false ) {
                param += "&"+pm ;
            }
            else {
                param = pm ;
            }
        }
        url = x ;
        x = null ;
        p = null ;
        if( isNull( groupId ) || ( groupId = trim( groupId ) ).length <= 0 ) {
            groupId = "D~"+ MD5_hexhash( url ) + "~" + url.length ;
        }
        var pparam = this.pv_trim_by_comet_param( groupId,param ) ;
        this.url = url ;
        this.groupId = groupId ;
        this.sendParams = pparam ;
        this.pv_connection_comet( this ) ;
    },
    // コメット先に送信(ajax).
    send: function( url,params ) {
        // グループIDが存在しない場合は、処理しない.
        if( isNull( this.ajax ) == false && useString( this.groupId ) == true ) {
            // 以前にコメットコネクションが接続されていたが、
            // 現在接続されていない場合は、再接続する.
            if( this.ajax.beforeError == true ) {
                if( useString( this.url ) == true && useString( this.groupId ) == true ) {
                    this.connect( this.url,this.groupId,this.sendParams ) ;
                }
            }
            this.sndajax.timeout = 0 ;
            this.resultAccessTime = new Date().getTime() ;
            return this.sndajax.postAjax( url,{CometSequenceId:this.ajax.resultSequence},
                this.pv_trim_by_comet_param( this.groupId,params ),false ) ;
        }
    },
    // コメット先に送信(ajax[非同期]).
    sendASync: function( url,params ) {
        // グループIDが存在しない場合は、処理しない.
        if( isNull( this.ajax ) == false && useString( this.groupId ) == true ) {
            // 以前にコメットコネクションが接続されていたが、
            // 現在接続されていない場合は、再接続する.
            if( this.ajax.beforeError == true ) {
                if( useString( this.url ) == true && isNull( this.groupId ) == true ) {
                    this.connect( this.url,this.groupId,this.sendParams ) ;
                }
            }
            this.sndajax.timeout = 0 ;
            this.resultAccessTime = new Date().getTime() ;
            return this.sndajax.postAjax( url,{CometSequenceId:this.ajax.resultSequence},
                this.pv_trim_by_comet_param( this.groupId,params ) ) ;
        }
    },
    // コネクション接続確認.
    isConnect: function() {
        return this.createFlag ;
    },
    // コールバック.
    callback: function( o ) {
        // 実装してください.
    },
    // エラーコールバック.
    callbackError: function( state ) {
        // 実装してください.
    },
    // コネクション用内部メソッド.
    pv_connection_comet: function( obj ) {
        var ccmt = obj.ajax  ;
        // 通信成功.
        ccmt.callbackSuccess = function( o ) {
            var con = obj.ajax ;
            if( isNull( o ) == false && ( o = trim( o ) ).length > 0 ) {
                obj.callback( o ) ;
            }
            con.timeout = obj.timeout ;
            con.createFlag = false ;
            obj.sendMcTime.connection( obj.url,{CometSequenceId:obj.ajax.resultSequence},obj.sendParams ) ;
            obj.reconnect ++ ;
        } ;
        // 通信失敗.
        ccmt.callbackError = function( method,url,params,state ) {
            obj.createFlag = false ;
            obj.callbackError( state ) ;
        } ;
        ccmt.timeout = obj.timeout ;
        obj.sendMcTime.connection( obj.url,{CometSequenceId:obj.ajax.resultSequence},obj.sendParams ) ;
        obj.createFlag = true ;
    },
    // パラメータを整形.
    pv_trim_by_comet_param: function( groupId,params ) {
        var ret = "" ;
        if( isNull( params ) || ( params = trim( params ) ).length <= 0 ) {
            ret = "" ;
        }
        else if( typeof( params ) == "string" ) {
            ret = params ;
        }
        if( isNull( groupId ) == false && ( groupId = trim( groupId ) ).length > 0 ) {
            if( ret.length <= 0 ) {
                ret = "groupId="+groupId ;
            }
            else {
                ret += "&groupId="+groupId ;
            }
        }
        return ret ;
    }
} ;
// コメットコネクションタイマー接続管理.
var McTime = function() {} ;
McTime.prototype = {
    comet: null,
    conn: null,
    url: null,
    head: null,
    params: null,
    sendOn: false,
    sendTime: 0,
    timerId: null,
    // コメットコネクションタイマー開始.
    startup: function( comet,conn ) {
        this.comet = comet ;
        this.conn = conn ;
        this.conn.asyncExitTime = 0 ;
        this.timerId = setInterval( this.sendInterval.bind( this ),100 ) ;
    },
    // コメットコネクションタイマー終了.
    shutdown: function() {
        if( isNull( this.timerId ) == false ) {
            this.comet = null ;
            this.conn = null ;
            this.conn.asyncExitTime = -1 ;
            clearInterval( this.timerId ) ;
            this.timerId = null ;
        }
    },
    // コメットコネクション処理.
    connection: function( url,head,params ) {
        this.url = url ;
        this.head = head ;
        this.params = params ;
        this.sendOn = true ;
        this.sendTime = new Date().getTime() ;
    },
    // 送信インターバル.
    sendInterval: function() {
        if( this.sendOn == true ) {
            if( ( ( this.sendTime + 100 ) - new Date().getTime() ) <= 0 ) {
                if( this.conn.asyncExitTime != -1 &&
                    ( ( this.conn.asyncExitTime + 100 ) - new Date().getTime() ) <= 0 ) {
                    this.sendOn = false ;
                    //this.conn.destroy() ;
                    this.conn.postAjax( this.url,this.head,this.params ) ;
                    this.comet.resultAccessTime = new Date().getTime() ;
                    this.url = null ;
                    this.head = null ;
                    this.params = null ;
                }
                else {
                    this.sendTime = new Date().getTime() ;
                }
            }
        }
    }
} ;

