/*
 * Webアプリケーション用の、ユーティリティメソッド
 * Webアプリケーションで必要な、共通処理を行うユーティリティ群。
 *   ・サーバ通信用のバッファフレームの作成
 *   ・クライアントログ出力エリアの作成
 *   ・ログ出力用、ユーティリティメソッド
 *   等
 *
 * このファイルの利用について
 * このファイルの内容は、個人利用・商用利用に関わらず、一部または全部を、
 * 自由に利用していただいてかまいません。
 * 但し、引用元の表示か、著作者が（有）ワイシステムクリエイトであることを
 * 示す、著作権表示を必ず行ってください。
 * また、このファイルの内容を利用した事により、何らかの不利益を被った場合
 * でも、（有）ワイシステムクリエイトは、一切保証できませんので、利用者ご
 * 自身の責任において、ご利用くださるようお願いします。
 *
 * Copyright (C) 2004-2005 Y System Create Corporation. All Rights Reserved.
 * $Id: com_yscjp_AppUtil.js,v 1.5 2005/05/05 10:35:31 bison Exp $
 */
/*
 * コンストラクタ
 */
function AppUtil() {}

/*
 * ログ編集エリア
 */
AppUtil.logString = "";

/*
 * アプリケーション実行レベルの定義
 * NORMAL : 通常実行レベル
 * DEBUG : デバッグ実行レベル
 */
AppUtil.NORMAL = 1;
AppUtil.DEBUG  = 2;

/*
 * ログの出力レベル定義
 * ERRORLOG : エラーログ : 通常実行レベルでも出力
 * DEBUGLOG : デバッグログ : デバッグ実行レベルでのみ出力
 * INFOLOG : インフォメーションログ : 通常実行レベルでも出力
 */
AppUtil.ERRORLOG = 1;
AppUtil.DEBUGLOG = 2;
AppUtil.INFOLOG  = 3;

/*
 * アプリケーションの実行レベル
 */
AppUtil.EXECUTION = AppUtil.NORMAL;
//AppUtil.EXECUTION = AppUtil.DEBUGLOG;

/*
 * アプリケーション必須エリアのID定義
 */
AppUtil.APPID_APPAREA  = "applicationArea";
AppUtil.APPID_COMMUBUF = "communicationBuffer";
AppUtil.APPID_LOGAREA  = "logArea";

/*
 * 初期化処理終了フラグ
 */
AppUtil.init = false;

/*
 * アプリケーションの初期化処理
 * Webブラウザのスクリプト対応状況と、HTMLの内容をチェックし、
 * アプリケーションに必要な要素を作成する。
 */
AppUtil.applcationInit = function (level) {

    AppUtil.checkSupport();

    try {

        if (null != level && AppUtil.DEBUG == level) {

            AppUtil.EXECUTION = AppUtil.DEBUGLOG;
        }

        /*
         * ドキュメント上のアプリケーション固有エリアを取得する。
         * 存在しなければエラーとして、初期化失敗。
         */
        var appArea = document.getElementById(AppUtil.APPID_APPAREA);

        if (null == appArea) {

            /*
             * なければ作る。
             * 場所は、bodyエレメントの最後の子エレメント
             */
            appArea = document.createElement("div");
            appArea = document.body.appendChild(appArea);
            appArea.id = AppUtil.APPID_APPAREA;

        }

        if (AppUtil.EXECUTION == AppUtil.NORMAL) {

            /*
             * ノーマルモードの時は、アプリケーション固有エリアを表示しない。
             */
            appArea.style.display = "none";
        }

        /*
         * サーバとの通信バッファエリアと、ログ出力エリアを検索する。
         * 既に存在した場合エラーとして、初期化失敗。
         */
        var commuBuf = document.getElementById(AppUtil.APPID_COMMUBUF);
        var logArea = document.getElementById(AppUtil.APPID_LOGAREA);

        if (commuBuf) {

            alert("アプリケーションで作成するエリアが既に存在します。\n"+
                  "要素のid(" + AppUtil.APPID_COMMUBUF + ")");

            return false;
        }

        if (logArea) {

            alert("アプリケーションで作成するエリアが既に存在します。\n"+
                  "要素のid(" + AppUtil.APPID_LOGAREA + ")");

            return false;
        }

        /*
         * サーバとの通信バッファ作成。
         */
/*        commuBuf = document.createElement("iframe");
        commuBuf = appArea.appendChild(commuBuf);
        commuBuf.id = AppUtil.APPID_COMMUBUF;
        commuBuf.name = AppUtil.APPID_COMMUBUF;
        //commuBuf.src = "./dummy.html";
        commuBuf.width = "100%";
        commuBuf.height = "30px";
        commuBuf.scrolling = "yes";
        commuBuf.frameBorder = "2";
//        commuBuf.style.display = "none";*/

        /*
         * ログ出力エリアの作成。
         */
        logArea = document.createElement("div");
        logArea = appArea.appendChild(logArea);
        logArea.style.height = "30em";
        logArea.style.overflow = "scroll";

        /*
         * ログ出力テーブルの作成。
         */
        logTable = document.createElement("table");
        logTable = logArea.appendChild(logTable);
        logTable.style.fontFamily = "monospace";
        logTable.id = AppUtil.APPID_LOGAREA;

        /*
         * ログコントロールエリアの作成。
         */
        logCtrl = document.createElement("div");
        logCtrl = appArea.appendChild(logCtrl);
        cleanButton = document.createElement("button");
        cleanButton = logCtrl.appendChild(cleanButton);
        cleanText = document.createTextNode("ログ消去");
        cleanButton.appendChild(cleanText);
        cleanButton.onclick = AppUtil.cleanLog;
        toTextButton = document.createElement("button");
        toTextButton = logCtrl.appendChild(toTextButton);
        toTextText = document.createTextNode("テキスト表示...");
        toTextButton.appendChild(toTextText);
        toTextButton.onclick = AppUtil.toTextLog;


        AppUtil.init = true;

        AppUtil.printLog("アプリケーション初期化終了。", AppUtil.INFOLOG);

        var logDisp = AppUtil.getQueryValue("APPLOG");
        if ("DISP" == logDisp) {

            appArea.style.display = "";
        }
    }
    catch (e) {

        var errorText = "エラーが発生しました。\n";

        for (var parm in e) {

            errorText += parm + ":" + e[parm];
        }

        alert(errorText);
    }
}

/**
 * クエリ文字列から、指定の名前のパラメータを返す。
 * @param paramName : 文字列 : 取得するパラメータの名前
 * @return : 文字列 : paramNameに該当するパラメータの値。
 * paramNameがクエリ文字列に存在しない場合、nullを返す。
 */
AppUtil.getQueryValue = function (paramName) {

    var queryString = window.location.search;
    AppUtil.printLog("("+queryString+")");

    if (null == queryString) {

        AppUtil.printLog("queryString is null.");
        return null;
    }

    queryString = queryString.replace(/\?/, "");

    if (0 == queryString.length) {

        AppUtil.printLog("queryString length is 0.");
        return null;
    }

    var queries = queryString.split("&");

    for (var i = 0; i < queries.length; i ++) {

        var pair = queries[i].split("=");
        if (paramName == pair[0]) {
            return pair[1];
        }
    }
    return null;
}


/*
 * ログ出力
 * logString : 文字列 : ログに出力する内容
 * logLevel : 定数 : ログの出力レベル
 */
AppUtil.printLog = function (logString, logLevel) {

    var logTable = document.getElementById(AppUtil.APPID_LOGAREA);

    if (logTable == null) {

        alert("ログ出力エリアが設定されていません。。");
        return;
    }

    if (null == logLevel) {

        logLevel = AppUtil.DEBUGLOG;
    }

    switch (logLevel) {

        case AppUtil.DEBUGLOG :

            /*
             * 通常実行レベルの場合は、デバッグログは出力しない。
             */
            if (AppUtil.EXECUTION == AppUtil.NORMAL) {

                return;
            }

            break;

        case AppUtil.ERRORLOG :

            /*
             * エラーログの場合は、通常実行レベルの時ダイアログにも表示する。
             */
            if (AppUtil.EXECUTION == AppUtil.NORMAL) {

                alert("エラーが発生しました。\n" + logString);
            }

            break;

        case AppUtil.INFOLOG :

            break;

        default :   // 引数エラー

            alert("引数が不正です。(" + logLevel + ")");
            return;
    }

    var row = logTable.insertRow(-1);
    var dateCell = row.insertCell(-1);
    dateCell.style.fontFamily = "monospace";
    dateCell.style.backgroundColor = "#efefef";
    var msgCell = row.insertCell(-1);
    msgCell.style.fontFamily = "monospace";
    if (row.rowIndex % 2) {
        msgCell.style.backgroundColor = "#efefff";
    }
    else {
        msgCell.style.backgroundColor = "#efffef";
    }
    var timeValue = AppUtil.getCurrentTime();
    var dateText = document.createTextNode(timeValue);
    var dateDiv = document.createElement("div");
    dateDiv = dateCell.appendChild(dateDiv);
    dateDiv.style.whiteSpace = 'nowrap';
    dateDiv.appendChild(dateText);
    var msgText = document.createTextNode(logString);
    msgCell.appendChild(msgText);
}


/*
 * エラーログ出力
 * logString : 文字列 : ログに出力する内容
 */
AppUtil.printErrorLog = function (logString) {

    AppUtil.printLog(logString, AppUtil.ERRORLOG);
}


/**
 * 例外ログ出力
 * @param exception 例外
 */
AppUtil.printExceptionLog = function (exception) {

    for (var parm in exception) {

        var textValue = exception[parm];

        if (textValue.split) {

            var msgList = textValue.split("\n");

            for (var j = 0; j > msgList.length; j ++) {

                AppUtil.printErrorLog(parm + ":" + msgList[j]);
            }
        }
        else {
            AppUtil.printErrorLog(parm + ":" + exception[parm]);
        }
    }
}


/**
 * ログエリアのクリア
 */
AppUtil.cleanLog = function () {

    var logTable = document.getElementById(AppUtil.APPID_LOGAREA);
    var rowCount = logTable.rows.length;

    for (var i = 0; i < rowCount; i ++) {

        logTable.deleteRow(0);
    }
}


/**
 * ログのテキスト表示
 */
AppUtil.toTextLog = function () {

    var logWindow = window.open();
    var logTextArea = logWindow.document.createElement("div");
    logTextArea = logWindow.document.body.appendChild(logTextArea);
    logTextArea.style.whiteSpace = "pre";

    var logTable = document.getElementById(AppUtil.APPID_LOGAREA);
    var rowCount = logTable.rows.length;
    var logText = "";

    for (var i = 0; i < rowCount; i ++) {

        var row = logTable.rows.item(i);
        var dateCell = row.cells.item(0);
        var msgCell = row.cells.item(1);
        logText += dateCell.firstChild.nodeValue +
                   "\u00A0" + msgCell.firstChild.nodeValue + "\n";
    }

    logText = logWindow.document.createTextNode(logText);
    logTextArea.appendChild(logText);
}


/*
 * 日付をミリ秒までフォーマット付きで返す
 */
AppUtil.getCurrentTime = function () {

    var current = new Date();
    var fullYear = current.getFullYear();
    var month = current.getMonth() + 1;
    var date = current.getDate();
    var hours = current.getHours();
    var minutes = current.getMinutes();
    var seconds = current.getSeconds();
    var milliseconds = current.getMilliseconds();

    if (10 > month) { month = "0" + month; }
    if (10 > date) { date = "0" + date; }
    if (10 > hours) { hours = "0" + hours; }
    if (10 > minutes) { minutes = "0" + minutes; }
    if (10 > seconds) { seconds = "0" + seconds; }
    if (100 > milliseconds) {

        if (10 > milliseconds) {

            milliseconds = "00" + milliseconds;
        }
        else {

            milliseconds = "0" + milliseconds;
        }
    }

    return fullYear + "/" + month + "/" + date + " " +
           hours + ":" + minutes + ":" + seconds + " " +
           milliseconds;
}

AppUtil.isSupported = new Object();
// DOM 使用の可否
AppUtil.isSupported.isDomSupported = false;
// DOM Level2 使用の可否
AppUtil.isSupported.isDom2Supported = false;
// XPath 使用の可否
AppUtil.isSupported.isXPathSupported = false;
// ActiveXObject の使用可否
// これがtrueの場合は、間違いなくIE
AppUtil.isSupported.isActiveXObjectSupported = false;
// XMLDOM３点セット の使用可否
// これらがtrueの場合は、間違いなく(たぶん)Mozilla
AppUtil.isSupported.isXMLHttpRequestSupported = false;
AppUtil.isSupported.isDOMParserSupported      = false;
AppUtil.isSupported.isXMLSerializerSupported  = false;
// FORMからSUBMITされた結果を受け取る、iframeがあるか。
AppUtil.isSupported.isBufferIframe  = false;

/*
 * Webブラウザの各機能サポートチェック
 */
AppUtil.checkSupport = function () {

    AppUtil.isSupported.userAgent = navigator.userAgent;

    if (document.getElementById) {
        AppUtil.isSupported.isDomSupported = true;
    }
    if (document.getElementsByTagNameNS) {
        AppUtil.isSupported.isDom2Supported = true;
    }
    if (window.ActiveXObject) {
        AppUtil.isSupported.isActiveXObjectSupported = true;
        try {
            var newDoc = new ActiveXObject("Microsoft.XMLDOM");
            newDoc.setProperty("SelectionLanguage", "XPath");
            AppUtil.isSupported.isXPathSupported = true;
        }
        catch (e) {}
    }
    if (window.XMLHttpRequest) {
        AppUtil.isSupported.isXMLHttpRequestSupported = true;
    }
    if (window.DOMParser) {
        AppUtil.isSupported.isDOMParserSupported = true;
    }
    if (window.XMLSerializer) {
        AppUtil.isSupported.isXMLSerializerSupported  = true;
    }
    if (document.createNSResolver &&
        document.createExpression) {
        AppUtil.isSupported.isXPathSupported = true;
    }

    var iframes = document.getElementsByTagName("iframe");

    if (iframes && 0 != iframes.length) {

        for (var i = 0; i < iframes.length; i ++) {

            if (AppUtil.APPID_COMMUBUF == iframes.item(i).name) {

                AppUtil.isSupported.isBufferIframe = true;
            }
        }
    }

    var supportText = "";

    for (var parm in AppUtil.isSupported) {

        supportText += parm + ":" + AppUtil.isSupported[parm] + "\n";
    }
//    alert(supportText);
}


/*
 * Eventオブジェクト
 */
function PrivateEvent(e) {

    if (e) {

        this.data = e.which;
        this.currentTarget = e.currentTarget;
    }
    else if (window.event) {

        e = event;
        this.data = e.keyCode;
        this.currentTarget = e.srcElement;
    }
    else {
        return;
        // throw new MyException();
    }

    this.nativeEvent = e;
//    AppUtil.printLog("イベントの表示開始");
//    for (var prop in e) {
//        AppUtil.printLog(prop + " : " + e[prop]);
//    }
//    AppUtil.printLog("イベントの表示終了");
    this.type = e.type;
}

/*
 * イベントファクトリ
 */
PrivateEvent.createEvent = function (evnt) {

    return new PrivateEvent(evnt);
}

function EventUtil() {}

/*
 * イベントファクトリ
 * あくまでも、106や109キーボードしか、考慮していない。
 */
EventUtil.createEvent = function (e) {

    var ctrlKey;
    var shiftKey;
    var altKey;
    var metaKey;
    var keyIdentifier;
    var currentTarget;

    if (e) {
    }
    else if (window.event) {

        e = event;
    }

    if ("undfined" != typeof e.ctrlKey) { ctrlKey = e.ctrlKey; }
    if ("undfined" != typeof e.shiftKey) { shiftKey = e.shiftKey; }
    if ("undfined" != typeof e.altKey) { altKey = e.altKey; }
    if ("undfined" != typeof e.metaKey) {
        metaKey = e.metaKey;
    }
    else {
        metaKey = e.altKey;
    }
    if ("undfined" != typeof e.currentTarget) {
        currentTarget = e.currentTarget;
    }
    else if ("undfined" != typeof e.srcElement) {
        currentTarget = e.srcElement;
    }
    if ("undfined" != typeof e.which) {
        keyIdentifier = e.which;
    }
    else if ("undfined" != typeof e.keyCode) {
        keyIdentifier = e.keyCode;
    }

    return { type : e.type, ctrlKey : ctrlKey, shiftKey : shiftKey,
             altKey : altKey, metaKey : metaKey, keyIdentifier : keyIdentifier,
             currentTarget : currentTarget, nativeEvent : e };
}

function Timer(delay, listener, isRepeats) {

    if (! listener.action) {
        AppUtil.printErrorLog("Not implement action method.");
        throw new error();
    }

    this.delay = delay;
    this.listener = listener;
    this.isRepeats = true;
    if (null != isRepeats && false == isRepeats) {

        this.isRepeats = false;
    }
    this.listenerId = Timer.listenerId;
    Timer.listenerId ++;
}

Timer.listenerList = new Array();
Timer.timerList = new Array();
Timer.listenerId = 1000;
Timer.timerListener = function (listenerId) {

//    AppUtil.printErrorLog("listenerId("+listenerId+")");

    var listener = Timer.listenerList[listenerId];
    var timer = Timer.timerList[listenerId];
    if (! listener || ! timer) {
        AppUtil.printErrorLog("listenerId("+listenerId+")");
        return;
    }
    listener.action(timer);
    if (timer.isRepeats) {
    }
    else {
        Timer.listenerList.splice(listenerId, 1);
        Timer.timerList.splice(listenerId, 1);
    }
}

Timer.prototype.start = function () {

    Timer.listenerList[this.listenerId] = this.listener;
    Timer.timerList[this.listenerId] = this;

    if (true == this.isRepeats) {
//    AppUtil.printErrorLog("Timer.prototype.start !!");
    }
    else {
//    AppUtil.printErrorLog("Timer.prototype.start("+this.delay+")("+this.listenerId+")");

        this.timerId =
            setTimeout("Timer.timerListener("+this.listenerId+")", this.delay);
    }
}

Timer.prototype.stop = function () {

    if (this.isRepeats) {
    }
    else {

        clearTimeout(this.timerId);
    }
    Timer.listenerList.splice(this.listenerId, 1);
    Timer.timerList.splice(this.listenerId, 1);
}

/**
 * メソッドトレースの実装
 * メソッドの先頭に、以下の１行を追加する。
 * try{FTrace.start(fname, arguments);
 * メソッドの最期に、以下の１行を追加する。
 * }catch(e){FTrace.stop();throw e;}finally{FTrace.end();}
 */
function FTrace() {
}
var maxCount = 100;
FTrace.index = -1;
FTrace.fnameList = new Array(maxCount);
FTrace.argList = new Array(maxCount);
FTrace.startTime = new Array(maxCount);
FTrace.fullTrace = new Array();
//FTrace.fnameList = new Array();
//FTrace.argList = new Array();
//FTrace.startTime = new Array();
FTrace.isStop = true;
FTrace.isDeep = false;
FTrace.isFullTrace = false;
FTrace.maxDeep = 0;
//var cnt = 0;
FTrace.start = function (fname, args) {
//    FTrace.fnameList.push(fname);
//    FTrace.argList.push(args);
    FTrace.index ++;
    FTrace.fnameList[FTrace.index] = fname;
    FTrace.argList[FTrace.index] = args;
    if (FTrace.isFullTrace) {
        FTrace.startTime[FTrace.index] = new Date();
        FTrace.fullTrace.push(fname+":start");
    }
    FTrace.isStop = false;
    FTrace.isDeep = false;
//    cnt ++;
}
FTrace.end = function () {
    if (! FTrace.isStop) {
//        FTrace.fnameList.pop();
//        FTrace.argList.pop();
        if (FTrace.isFullTrace) {

            var endTime = new Date().getTime();
            var startDate = FTrace.startTime[FTrace.index];
            var startTime;
            if (null == startDate) {
                startTime = 0;
            }
            else {
                startTime = startDate.getTime();
            }
            FTrace.fullTrace.push(FTrace.fnameList[FTrace.index] +
                ":end:time("+(endTime - startTime)+")");
        }
        FTrace.fnameList[FTrace.index] = null;
        FTrace.argList[FTrace.index] = null;
//        FTrace.startTime[FTrace.index] = null;
//        if (! FTrace.isDeep) {

//            FTrace.maxDeep =
//                FTrace.maxDeep < FTrace.index ? FTrace.index : FTrace.maxDeep;
//            FTrace.isDeep = true;
//        }
        FTrace.index --;
//        if (0 == FTrace.index) {
//            AppUtil.printLog("FTrace.maxDeep("+FTrace.maxDeep+")("+cnt+")");
//        }
    }
}
FTrace.stop = function () {
    FTrace.isStop = true;
}
FTrace.showTrace = function () {

    //for (var i = (FTrace.fnameList.length - 1); i >= 0; i --) {
    for (var i = FTrace.index; i >= 0; i --) {

        var value = FTrace.fnameList[i] + "(";
        var args = FTrace.argList[i];
        if (null == args) {
            continue;
        }
        for (var j = 0; j < args.length; j ++) {

            value += args[j];
            if (j != (args.length - 1)) {

                value += ", ";
            }
        }
        value += ")";
        AppUtil.printLog(value);
    }
}
FTrace.showFullTrace = function () {

//    var value = "";
    for (var i = 0; i < FTrace.fullTrace.length; i ++) {

        AppUtil.printLog(FTrace.fullTrace[i]);
//        value = value + FTrace.fullTrace[i] + "---";
    }
//    AppUtil.printLog(value);
}
var test;
FTrace.getFunctionName = function (func) {
    var ftext = func.toString();
    var ptn = /\([^(]*\)/;
    var result = ftext.match(ptn);
    return result[0];
}
FTrace.test = function() {
    AppUtil.EXECUTION = AppUtil.DEBUGLOG;
    AppUtil.initializeApply();
    try {
        FTrace.test1(1, 2, 3);
    }
    catch(e) {
        AppUtil.printLog(e);
        FTrace.showTrace();
    }
}
FTrace.test1 = function (a1, a2, a3) {
    try{FTrace.start("FTrace.test1", arguments);
    FTrace.test2(1, 2);
    FTrace.test3(1, 2, "abcd");
    }catch(e){FTrace.stop();throw e;}finally{FTrace.end();}
}
FTrace.test2 = function (a1, a2) {
    try{FTrace.start("FTrace.test2", arguments);
    var xxxxxxx = 0;
    }catch(e){FTrace.stop();throw e;}finally{FTrace.end();}
}
FTrace.test3 = function (a1, a2, a3) {
    try{FTrace.start("FTrace.test3", arguments);
    FTrace.test4();
    }catch(e){FTrace.stop();throw e;}finally{FTrace.end();}
}
FTrace.test4 = function () {
    try{FTrace.start("FTrace.test4", arguments);
    test.toString();
    }catch(e){FTrace.stop();throw e;}finally{FTrace.end();}
}
