/*
 * Webアプリケーションで必要な、共通処理を行うクラス群。
 *   ・ログの出力
 *   ・Webブラウザ間で共通に利用できるイベントクラス
 *   ・オブジェクトをリスナーとした、タイマークラス
 *   等
 *
 * このファイルの利用について
 * このファイルの内容は、個人利用・商用利用に関わらず、一部または全部を、
 * 自由に利用していただいてかまいません。
 * 但し、引用元の表示か、著作者が（有）ワイシステムクリエイトであることを
 * 示す、著作権表示を必ず行ってください。
 * また、このファイルの内容を利用した事により、何らかの不利益を被った場合
 * でも、（有）ワイシステムクリエイトは、一切保証できませんので、利用者ご
 * 自身の責任において、ご利用くださるようお願いします。
 *
 * Copyright (C) 2005 Y System Create Corporation. All Rights Reserved.
 *
 * $Date: 2005/05/28 09:12:26 $
 * $Revision: 1.5 $
 */
/*
 * コンストラクタ
 */
function AppUtil() {}

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

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

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

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

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

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

/*
 * アプリケーションログ
 */
AppUtil.appLog;

/**
 * アプリケーションの初期化処理
 * このメソッドは、将来削除される。
 * AppUtil.initializeApply を使用すること。
 */
AppUtil.applcationInit = function (level) {

    AppUtil.initializeApply(level);
    AppUtil.printLog("AppUtil.initializeApply を使用すること。",
                     AppUtil.WARNINGLOG);
}

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

    try {

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

            AppUtil.EXECUTION = AppUtil.DEBUGLOG;
        }
        var logDisp = AppUtil.getQueryValue("EXECUTION");
        if ("DEBUG" == logDisp) {

//            appArea.style.display = "";
            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";
        }

        /*
         * アプリケーションログ
         */
        AppUtil.appLog = new AppLog(appArea, "30em");

        /*
         * ログコントロールエリアの作成。
         */
        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.checkSupport();

        AppUtil.init = true;
        AppUtil.printLog("アプリケーション初期化終了。", AppUtil.INFOLOG);
    }
    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;
}


/**
 * コンソール
 * JavaScriptによる、コンソール出力をサポートする。
 */
function JSConsoleBase() {

//    this.initializeJSConsoleBase();
}

/**
 * コンソールイニシャライザ
 * JSConsoleBaseクラスを継承する場合は、コンストラクタ内で、
 * 必ずこのメソッドをコールすること。
 * @param columnCount : 数値 : 列数、デフォルトは１。
 * @param consoleView : 要素 : このエレメントの最後の子供として、
 *                    コンソールを作成する。
 *                    指定されなかった場合や、nullが指定された場合、
 *                    指定された内容が、エレメントノード以外の場合は、
 *                    document.bodyの最後の子供として、コンソールを
 *                    作成する。
 * @param height : 文字列 : コンソールの表示高さ
 *               カスケードスタイルシートの、height属性に設定できる
 *               内容であること。
 *               指定されなかった場合や、nullが指定された場合は、
 *               デフォルトで"10em"が設定される。
 */
JSConsoleBase.prototype.initializeJSConsoleBase =
function (columnCount, consoleView, height) {

    if (! columnCount) {

        columnCount = 1;
    }

    if (! consoleView || ! consoleView.nodeType ||
        consoleView.nodeType != 1) {
//        consoleView.nodeType != document.ELEMENT_NODE) {

        consoleView = document.body;
    }

    if (! height) {

        height = "10em";
    }

    this.columnCount = columnCount;

    /*
     * ログ出力エリアの作成。
     */
    var consoleArea = document.createElement("div");
    consoleArea = consoleView.appendChild(consoleArea);
    consoleArea.style.height = height;
    consoleArea.style.overflow = "scroll";

    /*
     * ログ出力テーブルの作成。
     */
    this.consoleTable = document.createElement("table");
    this.consoleTable = consoleArea.appendChild(this.consoleTable);
    this.consoleTable.style.fontFamily = "monospace";
}

/**
 * コンソール出力
 * メッセージをコンソールへ出力する、デフォルトの実装。
 * @param message : 文字列 : 出力するメッセージ
 */
JSConsoleBase.prototype.print = function (message) {

    var rowIndex = this.append();
    this.setTextValueAt(message, rowIndex, 0)
}

/**
 * 指定のセルにテキストを表示する。
 */
JSConsoleBase.prototype.setTextValueAt =
function (textValue, rowIndex, columnIndex) {

    var textContainer = this.getTextContainerAt(rowIndex, columnIndex);
    textValue = document.createTextNode(textValue);
    textContainer.appendChild(textValue);
}

/**
 * 行追加
 * コンソールに表示用の行を１行追加します。
 * @param columnCount : 数値 : 追加する行の列数
 * @return : 数値 : 追加した行の行インデックス
 */
JSConsoleBase.prototype.append = function () {

    var row = this.consoleTable.insertRow(-1);

    for (var i = 0; i < this.columnCount; i ++) {

        var cell = row.insertCell(-1);
        this.cellModify(cell, i);
    }

    /*
     * Opera9で、エラーが発生する対策
     */
    if ("undefined" != typeof row.rowIndex) {

        return row.rowIndex;
    }
    else {

        return this.consoleTable.rows.length - 1;
    }
}

/**
 * セルの更新
 * この実装では、なにもしない。
 * セルに対し、何らかの更新を行うような場合、このメソッドをオーバーライドする。
 * @param cell : 要素 : セル
 * @param columnIndex : 数値 : 列インデックス
 */
JSConsoleBase.prototype.cellModify = function (cell, columnIndex) {
}

/**
 * 指定のセルから、実際にテキストを格納する要素を返す。
 * @param rowIndex : 数値 : 行インデックス
 * @param columnIndex : 数値 : 列インデックス
 * @return : 要素 : 指定された行・列のセルで実際にテキストを格納する要素。
 *         この実装では、指定のセルをそのまま返す。
 */
JSConsoleBase.prototype.getTextContainerAt = function (rowIndex, columnIndex) {

    var cell = this.getCellAt(rowIndex, columnIndex);

    return cell;
}

/**
 * 指定のセルを返す。
 * @param rowIndex : 数値 : 行インデックス
 * @param columnIndex : 数値 : 列インデックス
 * @return : 要素 : 指定された行・列のセル
 */
JSConsoleBase.prototype.getCellAt = function (rowIndex, columnIndex) {

    var rows = this.consoleTable.rows;

    if (rowIndex >= rows.length) {

        throw WebApplyUtil.createException(new Error(),
                "引数が不正です。rowIndex("+rowIndex+")");
    }

    var row = this.consoleTable.rows.item(rowIndex);
    var cell = row.cells.item(columnIndex);

    return cell;
}

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

    var rowCount = this.consoleTable.rows.length;

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

        this.consoleTable.deleteRow(0);
    }
}

/**
 * コンソールの内容を、テキストで返す。
 */
JSConsoleBase.prototype.toString = function () {

    var rows = this.consoleTable.rows;
    var rowCount = rows.length;
    var returnText = "";

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

        for (var j = 0; j < this.columnCount; j ++) {

            var textContainer = this.getTextContainerAt(i, j);

            if (textContainer.hasChildNodes()) {

                var textValue = textContainer.firstChild.nodeValue;
                returnText += textValue + "\u00A0";
            }
        }
        returnText += "\n";
    }

    return returnText;
}

/*
 * JSConsoleBaseクラスを継承して、AppLogクラスを作成。
 */
AppLog.prototype = new JSConsoleBase();

function AppLog(consoleView, height) {

    this.initializeJSConsoleBase(3, consoleView, height);
}

/**
 * セルの更新
 * @param cell : 要素 : セル
 * @param columnIndex : 数値 : 列インデックス
 */
AppLog.prototype.cellModify = function (cell, columnIndex) {

    switch (columnIndex) {

        case 0 :    // 時間表示セル

            var dateDiv = document.createElement("div");
            dateDiv = cell.appendChild(dateDiv);
            dateDiv.style.whiteSpace = "nowrap";
            dateDiv.style.width = "16em";

            break;

        case 1 :    // エラーレベル表示セル

            var levelDiv = document.createElement("div");
            levelDiv = cell.appendChild(levelDiv);
            levelDiv.style.width = "2em";
            levelDiv.style.textAlign = "center";

            break;

        default :
    }
}

/**
 * 指定のセルから、実際にテキストを格納する要素を返す。
 * @param rowIndex : 数値 : 行インデックス
 * @param columnIndex : 数値 : 列インデックス
 * @return : 要素 : 指定された行・列のセルで実際にテキストを格納する要素。
 */
AppLog.prototype.getTextContainerAt = function (rowIndex, columnIndex) {

    var cell = this.getCellAt(rowIndex, columnIndex);
    var returnVale = cell;

    switch (columnIndex) {

        case 0 :    // 時間表示セル
        case 1 :    // エラーレベル表示セル

            if (cell.hasChildNodes()) {

                var child = cell.firstChild;

                //if (child.nodeType == document.ELEMENT_NODE &&
                if (child.nodeType == 1 &&
                    "div" == child.nodeName.toLowerCase()) {

                    returnVale = child;
                }
            }

            break;

        default :
    }

    return returnVale;
}

/**
 * コンソール出力
 * メッセージをコンソールへ出力する。
 * @param level : 文字列 : 出力レベル
 * @param message : 文字列 : 出力するメッセージ
 */
AppLog.prototype.print = function (level, message) {

    var rowIndex = this.append();

    var timeValue = this.getCurrentTime();
    this.setTextValueAt(timeValue, rowIndex, 0)

    this.setTextValueAt(level, rowIndex, 1)

    this.setTextValueAt(message, rowIndex, 2)

    if (level == AppUtil.ERRORLOG) {

        this.getCellAt(rowIndex, 0).style.backgroundColor = "#ffafaf";
        this.getCellAt(rowIndex, 1).style.backgroundColor = "#ffafaf";
        this.getCellAt(rowIndex, 2).style.backgroundColor = "#ffafaf";
    }

    if (level == AppUtil.WARNINGLOG) {

        this.getCellAt(rowIndex, 0).style.backgroundColor = "#ffffaf";
        this.getCellAt(rowIndex, 1).style.backgroundColor = "#ffffaf";
        this.getCellAt(rowIndex, 2).style.backgroundColor = "#ffffaf";
    }
}

/**
 * 現在時間の取得
 * 日付をミリ秒までフォーマット付きで返す
 * @return : 文字列 : フォーマットされた日付時間
 */
AppLog.prototype.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.cleanLog = function () {

    AppUtil.appLog.cleanLog();
}

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

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

    var logText = AppUtil.appLog.toString();

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

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

    AppUtil.appLog

    if (! AppUtil.appLog) {

        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 :
        case AppUtil.WARNINGLOG :
            break;

        default :   // 引数エラー

            throw WebApplyUtil.createException(new Error(),
                "引数が不正です。:logLevel(" + logLevel + ")");
//            alert("引数が不正です。(" + logLevel + ")");
            return;
    }

    AppUtil.appLog.print(logLevel, logString);
}


/*
 * エラーログ出力
 * 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.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;
            }
        }
    }

    for (var parm in AppUtil.isSupported) {

        AppUtil.printLog(parm + ":" + AppUtil.isSupported[parm],
                         AppUtil.INFOLOG);
    }
}


function EventUtil() {}

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

    var ctrlKey;        // コントロールキーが押されていたか
    var shiftKey;       // シフトキーが押されていたか
    var altKey;         // ALTキーが押されていたか
    var metaKey;        // METAキーが押されていたか
    var keyIdentifier;  // イベント発行の元となったキーのユニコード値
    var currentTarget;  // イベントハンドラが設定されている要素
    var target;         // 実際にイベント発行の元となった要素

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

        e = event;
    }

    if ("undefined" != typeof e.ctrlKey) { ctrlKey = e.ctrlKey; }
    if ("undefined" != typeof e.shiftKey) { shiftKey = e.shiftKey; }
    if ("undefined" != typeof e.altKey) { altKey = e.altKey; }
    if ("undefined" != typeof e.metaKey) {
        metaKey = e.metaKey;
    }
    else {
        metaKey = e.altKey;
    }
    if ("undefined" != typeof e.currentTarget) {

        target = e.target;
        currentTarget = e.currentTarget;
    }
    else if ("undefined" != typeof e.srcElement) {

        target = e.srcElement;

        /*
         * currentTargetが定義されていないので、srcElementから、
         * 実際にイベントハンドラが設定されている要素を検索する。
         * イベントハンドラが設定されている要素は、必ずsrcElementの
         * 親要素なので、これを検索して、当該イベントと同じタイプの
         * イベントハンドラが設定されている(nullではない)要素を
         * currentTargetに設定する。
         */
        var parent = e.srcElement;
        while (parent.parentNode) {
            if (parent["on"+e.type]) {

                currentTarget = parent;
                break;
            }
            parent = parent.parentNode;
        }
    }
    else {
    }

    if ("undefined" != typeof e.which) {
        keyIdentifier = e.which;
    }
    else if ("undefined" != typeof e.keyCode) {
        keyIdentifier = e.keyCode;
    }

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


/**
 * タイマークラスコンストラクタ
 * オブジェクトをリスナとして登録できるタイマ。
 * @param 数値 : 遅延時間(ミリ秒)
 * @param オブジェクト : リスナオブジェクト。
 *                       必ず、actionメソッドを実装している必要がある。
 * @param boolean : 繰り返しリスナを呼び出すかの指定。
 *                  繰り返し呼び出す場合、trueそれ以外はfalseを指定する。
 */
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.printLog("Timer.timerListener("+listenerId+")");

    var listener = Timer.listenerList[listenerId];
    var timer = Timer.timerList[listenerId];
    if (! listener || ! timer) {
        AppUtil.printLog("Timer.timerListener error("+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);
    }
//AppUtil.printLog("Timer.prototype.start("+this.listenerId+")");
}

/**
 * タイマ終了
 * 繰り返さない指定の場合は、このメソッドをコールした後、
 * 再度startメソッドをコールすることで、このインスタンスに、
 * タイマを実行させることができる。
 */
Timer.prototype.stop = function () {

    if (this.isRepeats) {
    }
    else {

        clearTimeout(this.timerId);
    }
    Timer.listenerList.splice(this.listenerId, 1);
    Timer.timerList.splice(this.listenerId, 1);
//AppUtil.printLog("Timer.prototype.stop("+this.listenerId+")");
}

function WebApplyUtil() {}

WebApplyUtil.createException = function (exception, msg) {

    if (AppUtil.init) {
        AppUtil.printErrorLog(msg);
    }
    exception.message = msg;
    return exception;
}


/*
 * イベントテーブル
 */
EventController.eventTable = new Object();

/*
 * テーブルイベントコントロール
 * ダミーコンストラクタ
 */
function EventController() {}

/*
 * イベントコントローラに、リスナオブジェクトを登録します。
 * @param eventType : 文字列 : HTML4.01で定義された組み込みイベントから、
 *        先頭のonを削除したもの。
 * @param ctrlInput : 要素 : リスナを設定する要素
 * @param listener : Object : リスナオブジェクト
 *        必ずactionPerformed(event) メソッドを実装する必要がある。
 */
EventController.addEventListener = function (eventType, ctrlInput, listener) {

    if (! listener.actionPerformed) {

        throw WebApplyUtil.createException(new Error(),
                                           "actionPerformed");
    }

    var listeners = EventController.eventTable[ctrlInput.id];

    if (null == listeners) {

        listeners = new Array();
    }
    var listenersValue = new Object();
    listenersValue.listener = listener;
    listenersValue.eventType = eventType;
    listeners.push(listenersValue);
    EventController.eventTable[ctrlInput.id] = listeners;

    ctrlInput["on"+eventType] = EventController.eventListener;
}

/*
 * イベントリスナ
 * 登録された全てのイベントを処理します。
 */
EventController.eventListener = function (evnt) {

    evnt = EventUtil.createEvent(evnt);
    var listeners = EventController.eventTable[evnt.currentTarget.id];

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

        if (evnt.type == listeners[i].eventType) {

            try {
                listeners[i].listener.actionPerformed(evnt);
            }
            catch (e) {
                AppUtil.printExceptionLog(e);
            }
        }
    }
}

/**
 * メソッドトレースの実装
 * メソッドの先頭に、以下の１行を追加する。
 * 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();}
}
