/*
 * JavaScriptによる簡易予定表
 *
 * このファイルの利用について
 * このファイルの内容は、個人利用・商用利用に関わらず、一部または全部を、
 * 自由に利用していただいてかまいません。
 * 但し、引用元の表示か、著作者が（有）ワイシステムクリエイトであることを
 * 示す、著作権表示を必ず行ってください。
 * また、このファイルの内容を利用した事により、何らかの不利益を被った場合
 * でも、（有）ワイシステムクリエイトは、一切保証できませんので、利用者ご
 * 自身の責任において、ご利用くださるようお願いします。
 *
 * Copyright (C) 2005 Y System Create Corporation. All Rights Reserved.
 *
 * $Date: 2005/05/28 09:03:21 $
 * $Revision: 1.5 $
 */
NoteApp.noteView;
NoteApp.calenderView;
function NoteApp() {

//    AppUtil.EXECUTION = AppUtil.DEBUGLOG;
    AppUtil.applcationInit();
    if (! AppUtil.isSupported.isActiveXObjectSupported &&
        ! AppUtil.isSupported.isXMLHttpRequestSupported) {

        var notSupportWarn = document.getElementById("warning_domversion");
        if (notSupportWarn) {
            notSupportWarn.style.display = "";
        }
        else {
            alert("お使いのWebブラウザが、必要な機能をサポートしていないため、当コンテンツを利用する事ができません。");
        }
        return;
    }
    var xmlLSImplement = new XmlLSImplement();
    /*
     * セーブを行う場合は、セーブしたファイルを読み込むように、
     * URIを修正してください。
     */
    NoteApp.xmlDoc = xmlLSImplement.perseFromURI("../cgi-bin/comment/save.xml");
//    NoteApp.xmlDoc = xmlLSImplement.perseFromURI("./save.xml");

//    AppUtil.printLog(XmlUtil.writeToString(NoteApp.xmlDoc));
    NoteApp.calenderView = CalenderView.init("tougetuCal", "tblctrl");
    NoteApp.noteView = NoteView.init("week_note", "notectrl", NoteApp.calenderView);
    var saveBtn = document.getElementById("save");
    saveBtn.onclick = function () {

        /*
         * データのセーブを実現するには、下の行からコメントを外してください。
         * XMLをセーブすることのできるcgiなどを設置し、
         * URIをそのcgiにアクセスするように修正してください。
         */
//        xmlLSImplement.writeToURI(NoteApp.xmlDoc, "../cgi-bin/save.cgi");
    }
}

/*
 * カレンダー
 * クラス変数の定義
 */
CalenderView.syukujitu = "syukujitu";
CalenderView.todayName = "today";
CalenderView.jpCalendar = new JPCalendar();
CalenderView.today = new Date();
CalenderView.currentYear = CalenderView.today.getFullYear();
CalenderView.currentMonth = CalenderView.today.getMonth() + 1;

/*
 * TableViewクラスのprototypeをコピーする事で、TableViewを継承する。
 * JavaScriptの継承は、あくまでも擬似的なものなので、いくつかの制限がある。
 * プロトタイプのコピーは、必ずインスタンスメソッドを定義する前で
 * 行う必要がある。そうしないと、折角定義したメソッドが、
 * 継承元のメソッドで上書きされてしまう。
 * また、インスタンス変数とインスタンスメソッドしか継承されない。
 * TableViewのクラス変数やクラスメソッドは、CalenderViewには存在しない。
 */
CalenderView.prototype = new TableView();

/*
 * カレンダー表示コントロールクラス
 * コンストラクタ
 */
function CalenderView(targetTable, controller) {

    /*
     * スーパークラスの初期化メソッドを呼び出す。
     */
    this.initTableView(targetTable, controller);

    this.noteCtrl = document.getElementById("notectrl");
}

/*
 * カレンダーアプリケーションの実行
 */
CalenderView.init = function (targetTableId, controllerId) {

    var targetTable = document.getElementById(targetTableId);
    var controller = document.getElementById(controllerId);
    var shell = document.getElementById("tougetuCalShell");
    controller.onfocus = function (e) {

        HtmlUtil.addClassValue(shell, "onfocus");
    }
    controller.onblur = function (e) {

        HtmlUtil.removeClassValue(shell, "onfocus");
    }
    var view = new CalenderView(targetTable, controller);

    var model = new CalendarModel(CalenderView.currentYear,
                                  CalenderView.currentMonth,
                                  CalendarModel.YOUBI_GETU,
                                  NoteApp.xmlDoc);
    view.setModel(model);
    controller.focus();
    return view;
}

/*
 * 左矢印キーが押下された。
 */
CalenderView.prototype.goLeft = function () {

    this.initializeView();
    CalenderView.currentMonth --;
    if (1 > CalenderView.currentMonth) {

        CalenderView.currentMonth = 12;
        CalenderView.currentYear --;
    }

    var model = new CalendarModel(CalenderView.currentYear,
                                  CalenderView.currentMonth,
                                  CalendarModel.YOUBI_GETU,
                                  NoteApp.xmlDoc);
    this.setModel(model);
}

/*
 * 右矢印キーが押下された。
 */
CalenderView.prototype.goRight = function () {

    this.initializeView();
    CalenderView.currentMonth ++;
    if (12 < CalenderView.currentMonth) {

        CalenderView.currentMonth = 1;
        CalenderView.currentYear ++;
    }

    var model = new CalendarModel(CalenderView.currentYear,
                                  CalenderView.currentMonth,
                                  CalendarModel.YOUBI_GETU,
                                  NoteApp.xmlDoc);
    this.setModel(model);
}

/*
 * Enterキーが押下された。
 */
TableView.prototype.enter = function () {

    setTimeout("NoteApp.noteView.controller.focus()", 0);
    //this.noteCtrl.focus();
}

/*
 * テーブルの内容が変更されたことを、通知します。
 * TableModelから呼び出される。
 */
CalenderView.prototype.tableChanged = function (tableModelEvent) {

    if (null == this.model) {

        return;
    }

    /*
     * テーブルのタイトルをセットする。
     */
    var tHead = this.tableElement.tHead;
    var row = tHead.rows.item(1);

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

        var cell = row.cells.item(i);
        var value = this.model.getColumnTitleAt(i);
        DomUtil.setTextValue(this.getDayDispElement(cell), value);
    }

    /*
     * 日付の情報をセットする。
     */
    var tbody = this.tableElement.tBodies.item(0);
    for (var rowIndex = 0; rowIndex < tbody.rows.length; rowIndex ++) {

        var row = tbody.rows.item(rowIndex);
        var cells = row.cells;

        for (var columnIndex = 0;
             columnIndex < cells.length;
             columnIndex ++) {

            var cell = cells.item(columnIndex);
            var date = this.model.getValueAt(rowIndex, columnIndex);
            if (date.getDate) {
                DomUtil.setTextValue(this.getDateDispElement(cell),
                                     date.getDate());
            }
            else {
                DomUtil.setTextValue(cell.firstChild, date);
                continue;
            }
            var syukujitu = date.syukujitu;

            if (null != syukujitu) {

                HtmlUtil.addClassValue(cell, CalenderView.syukujitu);
            }

            if (CalenderView.today.getFullYear() == date.getFullYear() &&
                CalenderView.today.getMonth()    == date.getMonth()    &&
                CalenderView.today.getDate()     == date.getDate()) {

                HtmlUtil.addClassValue(cell, CalenderView.todayName);

                if (this.selectedRow == -1) {

                    this.selectedRow = rowIndex;
                }
            }
        }
    }
    this.setMonth(this.model.year + "/" + this.model.month);
}

/*
 * テーブル表示の初期化を行う。
 * Modelからも呼び出される。
 */
CalenderView.prototype.initializeView = function () {

    var rows = this.tableElement.rows;

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

        var row = rows.item(i);
        HtmlUtil.removeClassValue(row, TableView.selectRowClass);
        HtmlUtil.addClassValue(row, TableView.nonSelectRowClass);

        for (var j = 0; j < row.cells.length; j ++) {

            var cell = row.cells.item(j);
//            DomUtil.setTextValue(cell.firstChild, "\u00a0");
            HtmlUtil.removeClassValue(cell, CalenderView.syukujitu);
            HtmlUtil.removeClassValue(cell, CalenderView.todayName);
        }
    }
}

/*
 * 年月の表示
 */
CalenderView.prototype.setMonth = function (value) {

    var tHead = this.tableElement.tHead;
    var row = tHead.rows.item(0);
    var cell = row.cells.item(0);
    DomUtil.setTextValue(document.getElementById("tougetuCal_month"), value);
}

/*
 * セルから、曜日をセットするエレメントを取得する。
 */
CalenderView.prototype.getDayDispElement = function (cell) {

    return cell.getElementsByTagName("div").item(0);
}


/*
 * セルから、日付をセットするエレメントを取得する。
 */
CalenderView.prototype.getDateDispElement = function (cell) {

    return cell.getElementsByTagName("div").item(0);
}

/**
 * コントローラにフォーカスを当てる。
 */
CalenderView.prototype.focusToController = function () {

    setTimeout("NoteApp.calenderView.controller.focus()", 0);
}


/*
 * カレンダーモデル
 * TableModelのカレンダー実装
 */
/*
 * カレンダーが使用する、IDの設定。
 */
CalendarModel.YOUBI_NITI = 0;
CalendarModel.YOUBI_GETU = 1;
CalendarModel.YOUBI_KA   = 2;
CalendarModel.YOUBI_SUI  = 3;
CalendarModel.YOUBI_MOKU = 4;
CalendarModel.YOUBI_KIN  = 5;
CalendarModel.YOUBI_DO   = 6;
CalendarModel.days = [ "日", "月", "火", "水", "木", "金", "土",
                       "日", "月", "火", "水", "木", "金" ];
CalendarModel.jpCalendar = new JPCalendar();

/*
 * カレンダーモデル
 * コンストラクタ
 * @param year     4桁の年
 * @param month    1月を1とする月
 * @param firstDay 0-6で指定する、先頭に表示する曜日、0が月曜日。
 * @param exTextDoc 
 */
function CalendarModel(year, month, firstDay, exTextDoc) {

    this.year = year;
    this.month = month;
    this.exTextDoc = exTextDoc;

    if (! firstDay) {

        firstDay = 0;
    }
    this.firstDay = firstDay;

    var days = this.getDaysOfMonth(this.year, this.month);
    var date = new Date(this.year, this.month - 1, 1);

    /*
     * 先頭曜日の調整
     */
    var day = date.getDay() - this.firstDay;
    if (0 > day) {
        day += 7;
    }

    this.dates = [
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"],
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"],
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"],
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"],
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"],
        ["\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0", "\u00a0"]
    ];
    var rowIndex = 0;
    var columnIndex = day;
    for (var i = 1; i <= days; i ++) {

        var date = new Date(this.year, this.month - 1, i)
        var syukujitu = CalendarModel.jpCalendar.getSyukuSaijitu(date);
        date.syukujitu = syukujitu;
        this.dates[rowIndex][columnIndex] = date;

        columnIndex ++;
        if (6 < columnIndex) {

            columnIndex = 0;
            rowIndex ++;
        }
    }
}

/*
 * リスナーの登録
 * TableModelとして、必須のメソッドです。
 */
CalendarModel.prototype.addTableModelListener = function (tableView) {

    this.tableView = tableView;
    this.tableView.tableChanged(null);
}

/*
 * 列のタイトルを取得する。
 * TableModelとして、必須のメソッドです。
 */
CalendarModel.prototype.getColumnTitleAt = function (columnIndex) {

    return CalendarModel.days[this.firstDay + columnIndex];
}

/*
 * 指定セルの値を取得する。
 * TableModelとして、必須のメソッドです。
 */
CalendarModel.prototype.getValueAt = function (rowIndex, columnIndex) {

    var rtnValue = this.dates[rowIndex][columnIndex];

    if (rtnValue.getFullYear) {

        var year = rtnValue.getFullYear();
        var month = rtnValue.getMonth() + 1;
        if (10 > month) {
            month = "0" + month;
        }
        var date = rtnValue.getDate();
        if (10 > date) {
            date = "0" + date;
        }
//        var xPath  = "descendant::y[attribute::year=\""+year+"\"]" +
//                     "/m[attribute::month=\""+month+"\"]" +
//                     "/d[attribute::date=\""+date+"\"]";
//        var targetNode = XmlUtil.singleNodeValue(this.exTextDoc, xPath);
        var id = "d" + year + month + date;
//        AppUtil.printLog("ID("+id+")");
        rtnValue.dateId = id;
        var targetNode;
        if (this.exTextDoc.getElementById) {
            targetNode = this.exTextDoc.getElementById(id);
        }
        else {
//            var xmlLSImplement = new XmlLSImplement();
            var pattern = "/body/extext[@id=\""+id+"\"]";
            targetNode = XmlUtil.singleNodeValue(this.exTextDoc, pattern);
        }
        if (targetNode) {
            rtnValue.exText = targetNode;
        }
    }
    return rtnValue;
}

/*
 * 指定セルの編集用の値を取得する。
 * TableModelとして、必須のメソッドです。
 * この実装では、なにもしない。
 */
CalendarModel.prototype.getEditableValueAt =
    function (rowIndex, columnIndex) {
}

/**
 * 月の日数を取得する。
 * 何年を指定されるか分からないので、
 * 閏年はまじめに計算しています。
 */
CalendarModel.prototype.getDaysOfMonth = function (year, month) {

    if (month == 4 || month == 6 || month == 9 || month == 11) {

        return 30;
    }
    else if (month == 2) {

        /*
         * 閏年の判定
         * 4で割れきれる年は、基本的には閏年となる。
         * 但し、100で割り切れる年は、閏年とならない。
         * 但し、400で割り切れる年は、閏年となる。
         */
        if (year % 400 == 0) {
            return 29;
        }
        else if (year % 100 == 0) {
            return 28;
        }
        else if (year % 4 == 0) {
            return 29;
        }
        return 28;
    }
    return 31;
}

/*
 * 編集可否判定
 * 指定セルが編集可能かどうかを返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 必ずfalse
 */
CalendarModel.prototype.isCellEditable = function (rowIndex, columnIndex) {

    return false;
}


/******************************************************************************
 * 予定表
 * TableViewクラスのprototypeを継承する。
 *****************************************************************************/
NoteView.prototype = new TableView();

/*
 * 予定表表示コントロールクラス
 * コンストラクタ
 */
function NoteView(targetTable, controller) {

    /*
     * スーパークラスのコンストラクタを呼び出す。
     */
    this.initTableView(targetTable, controller);
    this.calendarCtrl = document.getElementById("tblctrl");
    this.selectedColumn = 3;
}

/*
 * 予定表アプリケーションの実行
 */
NoteView.init = function (targetTableId, controllerId, calenderView) {

    var targetTable = document.getElementById(targetTableId);
    var controller = document.getElementById(controllerId);
    var shell = document.getElementById("week_noteShell");
    controller.onfocus = function (e) {

        HtmlUtil.addClassValue(shell, "onfocus");
    }
    controller.onblur = function (e) {

        HtmlUtil.removeClassValue(shell, "onfocus");
    }
    var view = new NoteView(targetTable, controller);

    var model = new NoteModel(calenderView, NoteApp.xmlDoc);
    view.setModel(model);
    calenderView.addSelectionListener(model);

    return view;
}

/*
 * テーブル表示の初期化を行う。
 * Modelからも呼び出される。
 */
NoteView.prototype.initializeView = function () {

    var rows = this.tableElement.rows;

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

        var row = rows.item(i);
        HtmlUtil.removeClassValue(row, TableView.selectRowClass);
        HtmlUtil.addClassValue(row, TableView.nonSelectRowClass);

        for (var j = 0; j < row.cells.length; j ++) {

            var cell = row.cells.item(j);
            HtmlUtil.removeClassValue(cell, TableView.selectColumnClass);
            HtmlUtil.addClassValue(cell, TableView.nonSelectColumnClass);
        }
    }

    if (this.selectedRow == -1) {

        this.selectedRow = 0;
    }
    this.selectRow();
    this.selectColumn();

    if (! this.model) {

        return;
    }

    /*
     * テーブルのタイトルをセットする。
     */
    var value = this.model.getColumnTitleAt(0);
    var titleCell = document.getElementById("note_month");
    DomUtil.setTextValue(titleCell, value);

}

/*
 * Enterキーが押下された。
 */
NoteView.prototype.enter = function (evnt) {

    if (evnt.nativeEvent.shiftKey) {

        setTimeout("NoteApp.calenderView.controller.focus()", 0);
        return;
    }
}

/**
 * 指定セルの入力要素の表示状態を決定する要素を返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセルの入力要素の表示状態を決定する要素。
 *         該当するセルや入力要素が存在しない場合、null。
 */
TableView.prototype.getCellEditorViewAt = function (rowIndex, columnIndex) {

    var cellEditorView = null;

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

        return null;
    }

    cellEditorView =
        DomUtil.getElementByAttributeValue(cell,
                                           "className",
                                           TableView.cellEditorViewClass);

    if (! cellEditorView) {

        return null;
    }

    return cellEditorView;
}

/**
 * コントローラにフォーカスを当てる。
 */
NoteView.prototype.focusToController = function () {

    setTimeout("NoteApp.noteView.controller.focus()", 0);
}

/*************************************************************************
 * 予定表モデル
 * カレンダーの選択週や表示月が変更された通知を、
 * このモデルが受けると、NoteViewに対して変更を通知します。
 * NoteViewは、変更の通知を受けると、表示中の全ての内容を、
 * モデルから取得し直して、表示します。
 ************************************************************************/
/*
 * コンストラクタ
 */
function NoteModel(calenderView, exTextDoc) {

    this.exTextDoc = exTextDoc;
    this.values = new Array();
}

NoteModel.prototype.addTableModelListener = function (view) {

    this.view = view;
}

NoteModel.prototype.getColumnTitleAt = function (columnIndex) {

    return this.year + "年 " + this.month + "月 第" + this.week +"週";
}

NoteModel.prototype.getValueAt = function (rowIndex, columnIndex) {

    var date = this.values[rowIndex];

    if (date.getDate) {

        switch (columnIndex) {
            case -1 :
                return date;
            case 0 :
                return date.getDate();
            case 1 :
                return date.day;
            case 2 :
                if (date.syukujitu) {

                    return date.syukujitu.jpName;
                }
                break;
            case 3 :
                if (date.exText) {

                    return date.exText.firstChild.nodeValue;
                }
            default :
        }
    }
    return "\u00a0";
}

NoteModel.prototype.getEditableValueAt = function (rowIndex, columnIndex) {

    return this.getValueAt(rowIndex, columnIndex);
}

/**
 * 指定の値を、モデルにセットする。
 * @param value : Object : セットする値。
 * @param rowIndex : 数値 : 値をセットする行のインデックス。
 * @param columnIndex : 数値 : 値をセットする列のインデックス。
 */
NoteModel.prototype.setValueAt = function (value, rowIndex, columnIndex) {

    var date = this.values[rowIndex];
    if (! date.exText) {

        date.exText = this.exTextDoc.createElement("extext");
        date.exText.setAttribute("id", date.dateId);
        this.exTextDoc.documentElement.appendChild(date.exText);
    }

    DomUtil.setTextValue(date.exText, value,
                         date.exText.ownerDocument);

    if (! this.view) {

        return;
    }

    modelEvent = new TableModelEvent(this,
                                     rowIndex,
                                     rowIndex,
                                     columnIndex,
                                     columnIndex);

    this.view.tableChanged(modelEvent);
}

/*
 * カレンダービューの選択行が変化した時に呼ばれる。
 * @param rowIndex 新しく選択された行のインデックス。
 * @param columnIndex 新しく選択された列のインデックス。
 * @param calenderView イベントを発行したカレンダービュー
 */
NoteModel.prototype.selectionChanged = function (rowIndex,
                                                 columnIndex,
                                                 calenderView) {

    var calenderModel = calenderView.model;
    this.rowIndex = rowIndex;
    this.year = calenderModel.year;
    this.month = calenderModel.month;
    this.week = rowIndex + 1;
    for (var i = 0; i < 7; i ++) {

        this.values[i] = calenderModel.getValueAt(rowIndex, i);
        this.values[i].day = calenderModel.getColumnTitleAt(i);
    }

    this.view.tableChanged(null);
}

/*
 * 編集可否判定
 * 指定セルが編集可能かどうかを返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 必ずtrue
 */
NoteModel.prototype.isCellEditable = function (rowIndex, columnIndex) {

    if (3 != columnIndex) {

        return false;
    }
    return true;
}
