

var _dtime;
function _setDateTime(dateTime){
    // cellEditor で記憶する年月日：NOA の年月日とは必ずしも一致しない
    _dtime = dateTime;
}
function _dateTime(){
    return _dtime;
}

var _menus;
var _menuLimit = 5; // メニュー表示文字数制限
function setMenus(tag, array){
    // tag 毎のメニューを記憶：init が true なら menus を初期化
    if (!_menus) _menus = new Object();
   
    //console.log("== setMenus", tag); //##
/*
    var dictionary = new Object();
    for (num in array){
        var st = array[num]; // "短縮名:フルネーム"
        var ary = st.split(':');
        var alias = ary[0];
        var fullname = st;
        //console.log("alias", alias, fullname); //##
        if ((alias != "...その他") && (alias.length > _menuLimit)){
            alias = alias.substr(0, _menuLimit) + " ...";
            fullname = ary[1];
        }
        //console.log("->", alias, fullname); //##
        dictionary[alias] = fullname;
    }
    
    _menus[tag] = dictionary; // "{"短縮名":"フルネーム",,,}
*/
    _menus[tag] = array;
}
function menus(tag){
    /*
    if (!_menus) return null;
    
    var dictionary = _menus[tag];
    return (dictionary) ? allKeys(dictionary) : null;
     */

    return (_menus) ? _menus[tag] : null;
}
function menuWithAlias(tag, alias){
    // 短縮名に対応するフルネームを返す
    /*
    var dictionary = _menus[tag];

    return dictionary[alias];
     */
    return alias;
}

function removeMenusForTag(tag){
    // tag に相当するメニュー項目を削除：getMenu() で再読込されるようにする
    delete _menus[tag];
}

var _groupMenuItems;
function setGroupMenuItems(obj){
    // グループ・メニューの親子構造を記憶
    /*
    _groupMenuItems = new Object();
    for (st in obj){
        var ary = st.split(':');
        var alias = ary[0];
        console.log("alias", alias); //##
        if ((alias != "...その他") && (alias.length > _menuLimit)){
            alias = alias.substr(0, _menuLimit) + " ...";
      //      fullname = ary[1];
        }
        console.log("->", alias);//##
        _groupMenuItems[alias] = obj[st];
    }
    */
    _groupMenuItems = obj;
}
function groupMenuItems(){
    return _groupMenuItems;
}

var _hasGroupDate;
function setHasGroupDate(val){
    _hasGroupDate = val;
    //console.log("setHasGroupDate", val); //##
}
function hasGroupDate(){
    // 選択されたグループ・メニュー先頭に日付を挿入するかどうかのフラッグ
    return _hasGroupDate * 1;
}

var _isNewPage;
function setIsNewPage(status){
    _isNewPage = status;
}
function isNewPage(){
    // ページが遷移した
    return _isNewPage;
}

function fieldId(dateTime, tag){
    // フィールド値の記入されたエレメントID を返す
    return dateTime * "_" + tag;
}

///////////////////////////////////////////
///// Ajax ////////////////////////////////

var _menuObj
function gotDisesesMenu(answer){
    // サーバから病名リストを取得
    var obj = JSON.parse(answer);
    //console.log("gotDisesesMenu", obj); //##
    
    var menus = [""];
    _menuObj = new Object();
    for (num in obj){
        var rec = obj[num];
        if (rec.menu.length){
            //console.log(rec.menu, rec.value); //##
            
            menus.push(rec.menu);
            _menuObj[rec.menu] = rec.value;
        }
    }
    setMenus(currentTag(), menus); // メニューを記憶
    
    showCellEditor();
}
function valueForMenu(menu){
    // menu に対応する value（ローマ字）を返す
    return _menuObj[menu];
}

function gotMenu(answer){
    // メニュー属性をサーバから受け取り CELL に設定
    // obj=[{"menu":"下腹痛","value":"","public":"","freq":"55","menu":"下腹痛"},,,]
    // obj は freq で降順ソートされている
    // メニューデータ obj は記憶せず使い捨て
    var obj = JSON.parse(answer);
    //_debug("=== gotMenu ->"+encodeObject(obj)); //##
    
    var cell = cellForTag(currentTag());
    if (cell.menuType * 1 == _group_)
        setGroupMenuItems(obj); // グループ・メニューの親子構造を記憶
    
    var menus = [""];
    setHasGroupDate('0'); // 選択されたグループ・メニュー先頭に日付を挿入しない
    for (num in obj){
        var rec = obj[num];
        _debug(rec.menu+"->"+rec.owner+"->"+rec.public); //##
        
        // public メニューの非表示には 2 が設定されている：すでに 1 は使われていることあり
        if ((rec.owner != 'public') && rec.public && (rec.public*1 > 1)) continue; // 共有メニュー打ち消し
        
        var subject = rec['ProgressSection.subject'];
        if (subject == "_date_check_"){
            // グループ・メニュー行頭に日付を入れるモードか否かを取得
            var note = rec['ProgressSection.note']
            if (note * 1 > 0){
                // 選択されたグループ・メニュー先頭に日付を挿入しない}
                setHasGroupDate('1'); 
                continue;
            }
        }
        
        if (rec.menu.length){
            menus.push(rec.menu);
        }
    }
    menus.push("...その他");
    
    setMenus(currentTag(), menus); // メニューを記憶
    showCellEditor();
}
function kickCellEditor(tag){
    // menu を取得し CellEditor を起動
    _log(["== kickCellEditor", tag]); //##
    
    var cell = cellForTag(tag);
    setCurrentTag(tag); // showCellEditor() で使われる

    if (menus(tag) || (cell.menuType == _none_)){
        // すでに tag に相当するメニューが記憶されているなら
        showCellEditor();
    } else {
        // 以下はいずれも gotMenu で showCellEditor() を起動
        if (cell.menuType * 1 == _group_){
            var args = new Object();
            args["owner"] = owner();
            NRCall("GET_GROUP_MENU", args, gotMenu);
        } else if (tag == "AddressSection.address"){
            // address では頻度学習を強制的に行う
            get_address(owner(), "都道府県", gotMenu);
        } else if (tag == "ProgressSection.disease"){
            // 病名欄では検索キーも含めて読み込む
            get_menu(owner(), "ProgressSection.disease", gotDisesesMenu);
        } else {
            /* --- MenuTable の内容 ---
             tag: ProgressSection.examination
             menu: 尿一般
             template: null
             value: 昔のエラーで伝票構造が入っている（本来は空）
             public: public を非表示にするには 1 が入る
             */
            get_menu(owner(), tag, gotMenu);
        }
    }
}

///// Ajax ////////////////////////////////
///////////////////////////////////////////

///////////////////////////////////////////
///// 別スレッド ////////////////////////////

function kickEditorWorker(noaString){
    // Worker を使いマルチスレッドで CELL内容を更新
    // DB とのやりとりは cellEditorWorker.js で設定
    //console.log("== kickEditorWorker", noaString); //##
    
    var worker = new Worker('./cellEditorWorker.js');
    worker.postMessage(noaString); // ### worker に命令書 noaString を送る ###

    worker.onmessage = function (event) {
        // ### worker の処理したデータを受け取る ###
        var answer = event.data; // ### worker からの返答 ###
        //console.log("== kickEditorWorker", answer); //##
        
		var array = answer.split("<SEPARATOR>");
		if (array.length > 1){
            //console.log("== worker.onmessage->"+array[1]); //##
            
            // サーバから返されたデータから tag, dateTime, value を取り出す
            var obj = JSON.parse(array[1]);
            //console.log("kickEditorWorker obj", encodeObject(obj)); //##

            var dateTime = currentDate();
            if (obj.entryDate)
                dateTime = obj.entryDate;
            else
                alert("kickEditorWorker: obj.entryDate->"+obj.entryDate); //##

            var value = "";
            for (key in obj){
                // closeCellEditor() でサーバへ送った key を基に処理
                if (key == "owner") continue;
                else if (key == "patientId") continue;
                else if (key == "timeLimit") continue;
                else if (key == "entryDate") continue; // dateTime = obj[key];
                else {
                    tag = key;
                    value = obj[key];
                    var elm = valueElementForTag(dateTime, tag);
                    elm.innerHTML = value; // 編集中の VALUE 基板エリアに書込み
                                        
                    // 記憶したデータも更新しておく
                    // ## VALUE の値再表示より前に実行しておかないと、
                    // ## NOA のメモリーに反映していないデータを編集してしまうことがある
                    setValueForTag(tag, dateTime, value);
                    
                    if (document.getElementById("alertArea"))
                        document.getElementById("alertArea").innerHTML = "";
                    
                    // VALUE の値を再表示
                    if (elm){
                        var cell = cellForTag(tag);
                        if (!cell) alert(tag+" 相当の CELL がありません"); //##
                        showValue(elm, cell, value, false); // 背景色を白にする
                        
                        if (cell) cell.editorIsOpen = false; // エディターが閉じた
                    } else {
                        alert(dateTime + " " + tag +" の CELL がみつかりません");
                    }
                    // ### ここで setCurrentTag(null) を実行してはならない ###
                }
            }
		}
        _debug("--- worker.onmessage terminated."); //##
    }
}

///// 別スレッド ////////////////////////////
///////////////////////////////////////////


///////////////////////////////////////////
///// GROUP MENU //////////////////////////

function doNothing(answer){
    // ### 頻度情報が変更になっただけなので何もしない ###
    //alert("doNothing->"+answer); //##
}
function groupPopupChanged(elm){
    // グループ・メニューの選択内容が変更された
    var alias = elm.value;
	if (isSame(alias, "...その他")){
        window.open("groupMenuEditor.php", "tools"); // CELL PREFERENCE
        return;
	}
    
    var cell = cellForTag(currentTag());
    if (cell.study * 1 > 0){ // 頻度学習が on なら頻度学習を行う
        // freq = "" なら、使用されたメニュー項目の頻度を increment
        // ### doNothing の代わりに null や "" を指定すると正常終了しない
        var item = menuWithAlias(currentTag(), alias);
        put_group_menu(owner(), item, "", [], doNothing);
    }
}

function closedGroupSubEditor(answer){
    // 全てのサブ・エディターを閉じサーバの保存内容を表示
    var dateTime = currentDate();
    var obj = JSON.parse(answer);
    _debug("== closedGroupSubEditor->"+dateTime); //##
    //_debug("obj->"+encodeObject(obj)); //##
    
    setGroupEditorOpen(false);
    var array = layoutForMode(_progress_);
    for (num in array){
        var layoutObj = array[num];
        if (layoutObj.hitAndRun * 1) continue; // 非表示セルはスキップ
        
        var tag = layoutObj.tag;
        
        // 戻り値がなかった CELL の value は undefined となる：変更がなかった
        var value = obj[tag];
        if (value){ // 戻り値があったので画面ならびにメモリーを更新
            //_debug(tag+"->"+encodeObject(cell)); //##
            
            // VALUE の値を再表示
            var elm = valueElementForTag(dateTime, tag);
            var cell = cellForTag(tag);
            showValue(elm, cell, value, false); // 背景色を白にする
            
            // 記憶したデータも更新しておく
            setValueForTag(tag, dateTime, value);
        } else { // 戻り値がなかったのでメモリー上の値を再表示
            var rec = valueForTag(tag);
            var value = (rec) ? rec.value : "";
            var isPast = (rec) ? rec.isPast : false;
            
            _debug("-- old: "+tag+"-> "+value); //##
            
            var elm = valueElementForTag(dateTime, tag);
            var cell = cellForTag(tag);
            showValue(elm, cell, value, isPast);
            
            // 過去データなら背景を過去色にする：淡青色
            if (isPast) elm.setAttribute("class", "pastCellValue");
        }
        
        var cell = cellForTag(tag);
        if (cell) cell.editorIsOpen = false; // CELL のエディターが閉じたことを記憶
    }
}
function closeGroupSubEditor(needSave){
    // subEditor を閉じる： needSave があれば変更された内容をサーバへ保存
    //console.log("== closeGroupSubEditor", needSave); //##
    //console.log("groupEditorOpen", groupEditorOpen()); //##
    
    var dateTime = currentDate();
    if (groupEditorOpen()){
        // サブ・エディターが開いている場合
        var array = layoutForMode(_progress_);
        if (needSave){
            // Agent データ・オブジェクトからタグ情報を削除
            var agt = agent();
            agt.tag = null;
            agt.cell = null;
            agt.cellValue = null;
            setAgent(agt);
            
            var container = new Object();
            for (num in array){
                var layoutObj = array[num];
                if (layoutObj.hitAndRun * 1) continue; // 非表示セルはスキップ
                
                // 内容に変更あれば Editor 内容をサーバへ保存
                var tag = layoutObj.tag;
                _debug("tag->"+tag+" currentTag->"+currentTag()); //##
                
                var cell = cellForTag(tag);
                if (! cell) continue; // ProgressSection.pageHeader などをスキップ
                if (cell.disabled * 1 > 0) continue; // read-only なのでスキップ

                var node = editorElementForTag(tag);
                if (!node) continue;

                if (tag == currentTag()){ // group menu を起動した CELL
                    // ### innerText では HTML タグが削除される
                    // ### 編集前のオリジナルとの比較には innerText を使う
                    if (node.innerHTML != beforEdit()){ // データが編集された
                        // CELL 値の編集領域を削除する前に VALUE 値を container へ記憶
                        cell.originalValue = null; // 用済みなので消去
                        var node = editorElementForTag(tag);
                        if (!node){
                            var msg = "closeGroupSubEditor: node not exist->"+tag;
                            showFadeoutInfo("alertArea", msg, 1000);
                            return;
                        }
                        var newVal = node.innerText; // 編集されたデータ
                        //console.log("newVal", newVal); //##
                        
                        newVal = convertNewLine(newVal); // HTML 改行を全て "\n" に変換
                        //console.log("->", newVal); //##
                        
                        container[tag] = newVal; // 編集されたデータ
                        
                        // CELL の VALUE 編集領域を削除しメッセージ表示
                        var elm = valueElementForTag(currentDate(), tag);
                        elm.innerHTML = "";
                        var sp = newSPAN(elm, "");
                        sp.innerHTML = "サーバへ保存中...";
                        sp.style.color = "#faa";
                    }
                } else { // subEditor の内容
                    if (node.innerHTML != beforEdit()){ // データが編集された
                        cell.originalValue = null; // 用済みなので消去
                        var cid = dateTime + "." + tag + ".val";
                        var elm = document.getElementById(cid);
                        var newVal = elm.innerText; // innerHTML では駄目
                        newVal = convertNewLine(newVal); // HTML 改行を全て "\n" に変換
                        container[tag] = newVal; // 編集されたデータ
                    }
                }
            }
            
            put_page(owner(),patientId(),dateTime,timeLimit(),container,closedGroupSubEditor);
        } else {
            // cellEditor を閉じ元の表示にする
            _setDateTime(null);
            setCurrentTag(null); // EDITOR が閉じたことを記憶しスタンバイ状態にする
            setGroupEditorOpen(false);
            openPage(dateTime, true); // そのページをアクティブにする
        }
    } else {
        // グループ・メニューのあるエディターだけが開いているなら
        // ### needSave を活かす ###
        closeCellEditor(needSave);
        setCurrentTag(null); // closeCellEditor() でやるとタイミングによっては不整合発生
    }
}

function insertGroupItems(){
	// グループメニューのクリックによりテンプレート内容を各フィールドに挿入
	var alias = document.getElementById("menuPop").value;
    var item = menuWithAlias(currentTag(), alias);
    //console.log("== insertGroupItems", currentTag(), alias, item); //##
    var obj = groupMenuItems()[item];
    
    // 各編集フィールドにメニュー内容を追加し、それを save する
    var array = layoutForMode(_progress_);
    for (num in array){
        var layoutObj = array[num];
        //_debug("layoutObj->"+encodeObject(layoutObj)); //##
        
        // 非表示セルはスキップ
        if (layoutObj.hitAndRun * 1) continue;
        if (layoutObj.tag == "ProgressSection.pageHeader") continue;
        
        var tag = layoutObj.tag;
        //console.log("insertGroupItems", tag, obj[tag]); //##
        
        var menuItem = (obj[tag]) ? obj[tag] : ""; // null だとエラーになる
        var cell = cellForTag(tag);
        
        if (tag == currentTag()){
            // tag の編集フィールド内データに menuItem を挿入
            var elm = document.getElementById(tag + ".value");
            elm.innerHTML = addGroupItem(cell, elm.innerHTML, menuItem);
        } else {
            // tag に相当する cell.value に menuItem を挿入
            showSubEditor(cell, menuItem);
        }
    }
    setGroupEditorOpen(true);
    
    function addGroupItem(cell, cVal, mVal){
        // 元々の値にグループ・メニューの値を加える
        //alert("addGroupItem: cval("+cVal+") mVal("+mVal+")"); //##
        
        // cVal は CELL の元値
        cVal = trimCR(cVal); // 文字列前後の改行を取り去る
        cVal = (cVal.length > 0) ? convertSTRING(cVal, "\n", "<br>") : "";
        // mVal はグループ・メニューからの追加値
        // いずれも文字列前後の改行を取り去り、中間にある改行は <br> に変換して扱う
        mVal = trimCR(mVal); // 文字列前後の改行を取り去る
        mVal = (mVal.length > 0) ? convertSTRING(mVal, "\n", "<br>") : "";

        // CELL の値である cVal にメニュー・アイテムの文字列 mVal を追加
        if (cell.tag == currentTag()){ // GroupMenu が起動されたフィールド：主訴など
			// 入力欄に存在する #problemNumber の最大値のひとつ上の値
			// を付した #problemNumber で template を文頭に挿入
            // ### maxNum() には cVal が '\n' で区切られていることが必要 ###
            var numString = "#" + maxNum(cVal);
            if (hasGroupDate() > 0){
                // "#番号 140705 メニュー項目" 型式で日付を挿入
                mVal = numString + " " + shortDate(currentDate()) + " " + mVal;
            } else {
                // "#番号 メニュー項目" 型式
                mVal = numString + " " + mVal;
            }
            
           return addedValue(cVal, mVal, cell.menuAction * 1); // メニュー項目を挿入
        } else if (cell.tag == "ProgressSection.disease"){
            var date = currentDate();
            var array = new Array();
            var ary = mVal.split('<br>');
            for (num in ary)
                array.push(modifyItem(date, cell.tag, ary[num]));
            mVal = array.join('<br>');
            
            return addedValue(cVal, mVal, cell.menuAction * 1); // メニュー項目を挿入
        } else {
            return addedValue(cVal, mVal, cell.menuAction * 1); // メニュー項目を挿入
        }
  
        function maxNum(doc){
            // 各行をチェックして最大番号を取り出す
            var array = doc.split("#"), max=0;
            for (i in array){
                var ln = array[i];
                var ary = ln.split(" ");
                num = ary[0] * 1;
                if (num > max) max = num;
            }
            return max + 1;
        }
        
        function addedValue(originalValue, menuItem, menuAction){
            // originalValue へ menuAction に従った位置へ menuItem を挿入して返す
            if (trim(originalValue).length == 0) return menuItem;
            if (menuItem.length == 0) return originalValue;
            
            switch (menuAction * 1){ // insert position
                case _menu_to_tail_: // 0: 文末へ追加
                    return originalValue + " " + menuItem;
                case _menu_to_last_row_: // 1: 最終行へ追加
                    if (trim(originalValue).length == 0)
                        return menuItem;
                    else
                        return originalValue + "<br>" + menuItem;
                case _menu_to_head_: // 3: 文頭へ挿入
                    if (trim(menuItem).length == 0)
                        return originalValue;
                    else
                        return menuItem + "<br>" + originalValue;
                default: // 2 _menu_replace_: 置換
                    return menuItem;
            }
        }
    }
    
    function showSubEditor(cell, val){
        // GROUP MENU により従属するフィールドの CELL EDITOR を開く
        var elm = valueElementForTag(currentDate(), cell.tag);
        elm.innerHTML = "";
        
        // TEXT
        var obj = valueForTag(cell.tag, currentDate());
        var value = (obj && obj.value) ? obj.value : "";
        var cid = cell.tag + ".value"; // cellEditor 入力欄の cell-id
        var dv = newDIV(elm, cid + "/subEditorArea");
        dv.innerHTML = addGroupItem(cell, value, val);
        dv.contentEditable = true; // TEXT を編集可能状態にする
    }
}

///// GROUP MENU //////////////////////////
///////////////////////////////////////////



/////////////////////////////////////////////////
///// MENU //////////////////////////////////////

function compareFreq(a, b){
    // a:"freq^$^item"
    // separator を ":" にすると、”lmp:" など : を含むタイトルが正確に処理されない
    var ary1 = a.split("^$^");
    var ary2 = b.split("^$^");
    
    // freq で比較
    return ary2[0] - ary1[0];
}

function showFrequency(answer){
	// メニューの頻度がインクリメントされたレスポンスを得る：ポップアップメニューを再描画
	var obj = JSON.parse(answer);
    //alert("== showFrequency: obj->"+encodeObject(obj)); //##

    var cell = cellForTag(currentTag());
    if (cell.menuType * 1 == _group_){
        // グループ・メニューの親子構造を記憶
        setGroupMenuItems(obj);
    } else {
        // メニューを再描画
        var menus = [""];
        for (num in obj){
            var rec = obj[num];
            if (rec.menu.length){
                _debug(num+"->"+rec.menu); //##
                
                menus.push(rec.menu);
            }
        }
        menus.push("...その他");
        setMenus(currentTag(), menus); // メニューを更新
    }
    showMenu(cell.menuType * 1);
}
function incrementFrequency(tag, menuItem, value, addStatus){
	// 選択されたメニューの頻度を１つ増やす
	// ### menuItem が MenuTable に存在しない場合 menuItem の新規登録も行う
    var cell = cellForTag(tag);
    
    if (addStatus || (cell.study * 1 > 0)){
        // addStatus が true なら強制的にメニュー項目追加
        // 頻度学習が on なら頻度学習を行う
        put_menu(owner(), tag, menuItem , "", value, showFrequency);
    }
}

function modifyItem(dateTime, tag, item){
    // フィールドの種類により menuItem をモディファイ
    // tag は "ProgressSection.disease" や "gm-disease" など
    if (tag.indexOf("disease") >= 0){
        // 挿入アイテムの行頭に「タイムスタンプ」をつける
        var yy = dateTime.substr(2, 2);
        var mm = dateTime.substr(5, 2);
        var dd = dateTime.substr(8, 2);
        
        // item が複数行になっていても、各行頭に dateStamp を付加
        var array = item.split("\n");
        var ary = new Array();
        for (num in array){
            if (array[num].length) ary.push(yy+mm+dd+" "+array[num]);
        }
        return ary.join("\n");
    }
    else
        return item;
}

function addMenu(){
    // menuItem を新規追加
    var item = prompt("新規に追加するメニュー項目");
    if (item.length > 0){
        // item をサーバへ登録するために incrementFrequency() を使う
        incrementFrequency(currentTag(), item, "", true);
    }
}

function focusPressed(){
    // 絞り込みフィールドでキーが押される度に実行
    var key = document.getElementById("focusF").value;
    if (trim(key).length == 0) return;
    
    var elm = document.getElementById("menuPopArea");
    elm.innerHTML = "";
    var items = menus(currentTag());
    var pu = newPopupMenu(elm, "menuPop", menuItems(items, key), "");
    pu.setAttribute("onchange", "popupChanged(this)");
    
    function menuItems(menuArray, key){
        // メニュー・データを表示用にカスタマイズして返す
        var array = [""];
        for (num in menuArray){
            var item = menuArray[num];
            
            // 絞り込みキーにマッチしないものはスキップ
            if (item.indexOf(key) < 0){
                if (currentTag() == "ProgressSection.disease"){
                    // 病名欄では検索キーが使える
                    // key に対応する item がなかった：key がアルファベットかも知れない
                    // メニューに対応するローマ字は value フィールドに存在
                    var romaji = valueForMenu(item);
                    if (romaji && (romaji.length > 0)){
                        // ローマ字が絞り込みキーにマッチしないものはスキップ
                        if (romaji.indexOf(key) < 0) continue;
                    }
                } else {
                    continue;
                }
            }
            
            array.push(item);
        }
        array.push("...その他");
        return array;
    }
}
function insertMenuItem(item){
    // メニューの選択内容が変更された
    if (item == "...その他"){
        addMenu();
        return;
    }
    
    // フィールドの種類により item をモディファイ
    // ProgressSection.treatment などの場合は年月日をつける
	// ## item 自体を変更すると最後の incrementFrequency() で
	// ## 新規登録されてしまうので menuItem に名称を変え使用する
    var dateTime = currentDate();
    var tag = currentTag();
    var menuItem = modifyItem(dateTime, tag, item);
    
    //_initDebug(true); //##
    _debug("menuItem->"+menuItem); //##
    
	// 選択されたアイテムを文中に挿入
    var elm = editorElementForTag(tag); // 文章
    var html = elm.innerHTML;
    var cell = cellForTag(tag);
    _debug("pureSize(html)->"+pureSize(html)); //##
    
	if (pureSize(html) == 0){ // 入力先が空欄の場合
		elm.innerHTML = menuItem;
	} else {
        _debug("cell.menuAction->"+cell.menuAction); //##
        
		switch (cell.menuAction * 1){ // insert position
			case _menu_to_tail_: // 0: 文末へ追加
				elm.innerHTML = html + " " + menuItem;
				break;
			case _menu_to_last_row_: // 1: 最終行へ追加
				elm.innerHTML = html + "<br>" + menuItem;
				break;
			case _menu_to_head_: // 3: 文頭へ挿入
				elm.innerHTML = menuItem + "<br>" + html;
				break;
			default: // 2 _menu_replace_: 置換
				elm.innerHTML = menuItem;
				break;
		}
	}
    
    // 使用されたメニュー項目の頻度を increment
    incrementFrequency(tag, item, "");
    
    if (cell.buttonType * 1 > 0){
        // 「メニュー１回のみ選択」型式なら save し編集モードを抜ける
        jumpToNext(cell.tag);
    }

    
    function pureSize(html){
        // HTML をプレーンテキストにした trim サイズ を返す
        var doc = htmlForValue(html); // "<br>" を "\n" へ変換
        doc = convertSTRING(doc, "&nbsp;", "");
        
        return trim(doc).length;
    }
}
function popupChanged(elm){
    // ポップアップメニューが選択された
    insertMenuItem(elm.value);
}

///// MENU //////////////////////////////////////
/////////////////////////////////////////////////

/////////////////////////////////////////////
///// address ///////////////////////////////

function putAddress(){
    // 住所メニューの町村が選択された時、メニューの都道府県を入力欄へ転記
    var tag = "AddressSection.address";
    var prefecture = document.getElementById("prefPop").value;
    var city = document.getElementById("cityPop").value;
    var village = document.getElementById("villagePop").value;
    
    if (village == "...その他"){
		village = window.prompt("町村名(のみ)を入力してください", "");
		if (village.length == 0) return;
        
        // 町村を登録
        // 選択アイテムが存在しなければ登録、存在すれば頻度をインクリメント
        put_menu(owner(), city, village, 1, "", gotVILLAGE);
	} else {
        // 選択されたアイテムを文中に挿入
        var elm = editorElementForTag(tag); // 文章
        elm.innerHTML = prefecture + " " + city + " " + village + "&nbsp;";
        moveCursorAtEndOf(elm); // elm 中のカーソルを文末に置く
    }
}

var _selectedAddress;
function gotVILLAGE(answer){
	var obj = JSON.parse(answer);
    var shiku = document.getElementById("cityPop").value;
    if (shiku.length == 0){
        return; // 町村のポップアップメニューを表示しない
    } else {
        var menuItems = [""]; // 最初に空メニューを入れておく
        for (num in obj){
            var rec = obj[num];
            menuItems.push(rec.menu); // メニューアイテム
        }
        menuItems.push("...その他");
    }
    
	// 町村ポップアップを更新
    var elm = document.getElementById("_chouson");
    elm.innerHTML = "";
    var obj = valueForTag(currentTag(), currentDate());
    var value = (obj && obj.value) ? obj.value : "";
    var val = "";
    if (value){
        var addressArray = value.split(" ");
        val = addressArray[2];
    }
    
    var pu = newPopupMenu(elm, "villagePop", menuItems, val);
    // 町村 が変更されたら 都道府県 を入力欄に転記
    pu.setAttribute("onchange", "putAddress()");
}
function getVILLAGE(elm){
    // 町村のポップアップを設定
    var shiku = elm.value;
	if (shiku == "...その他"){
		shiku = window.prompt("市区名(のみ)を入力してください", "");
		if (shiku.length == 0) return;
        var tag = document.getElementById("prefPop").value;
        _selectedAddress = shiku;
        // todoufuken を登録
        // 選択アイテムが存在しなければ登録、存在すれば頻度をインクリメント
        put_menu(owner(), tag, shiku, 1, "", gotCITY);
	} else {
        // address では頻度学習を強制的に行う
        get_address(owner(), shiku, gotVILLAGE);
    }
}

function gotCITY(answer){
	var obj = JSON.parse(answer);
    var todoufuken = document.getElementById("prefPop").value;
    
    if (todoufuken.length == 0){
        return; // 市区のポップアップメニューを表示しない
    } else {
        var menuItems = [""]; // 最初に空メニューを入れておく
        for (num in obj){
            var rec = obj[num];
            menuItems.push(rec.menu); // メニューアイテム
        }
        menuItems.push("...その他");
    }
    
	// 町村ポップアップを更新
    var elm = document.getElementById("_shiku");
    elm.innerHTML = "";
    var obj = valueForTag(currentTag(), currentDate());
    var value = (obj && obj.value) ? obj.value : "";
    
    var val = "";
    if (value){
        var addressArray = value.split(" ");
        val = addressArray[1];
    }
    
    var pu = newPopupMenu(elm, "cityPop", menuItems, val);
    // 市区 が変更されたら 町村 のメニューを取り寄せる
    pu.setAttribute("onchange", "getVILLAGE(this)");
    getVILLAGE(pu);
}
function getCITY(elm){
    // 市区のポップアップを設定
    var todoufuken = elm.value;
	if (todoufuken == "...その他"){
		todoufuken = window.prompt("都道府県名(のみ)を入力してください", "");
		if (todoufuken.length == 0) return;
        _selectedAddress = todoufuken;
        // todoufuken を登録
        // 選択アイテムが存在しなければ登録、存在すれば頻度をインクリメント
        put_menu(owner(), "都道府県", todoufuken, 1, "", gotCITY);
	} else {
        // address では頻度学習を強制的に行う
        get_address(owner(), todoufuken, gotCITY);
    }
}

///// address ///////////////////////////////
/////////////////////////////////////////////

function cellEditorHelp(type){
    // HELP を開く
    var url = "cellEditorHelp.html";
    switch (type * 1){
        case _group_: url = "groupCellEditorHelp.html";
            break;
    }
    openHelp(url);
}

function cleardValue(answer){
    //alert("== cleardValue->"+answer); //##
    var obj = JSON.parse(answer);
    _setDateTime(null);
    setCurrentTag(null); // EDITOR が閉じたことを記憶しスタンバイ状態にする
    
    // カルテを再表示
    openChart(patientId());
}
function clearValue(tag){
    // 編集内容を削除し前回受診時の記述が透けて見えるようにする
    var elm = document.getElementById(tag + ".editorArea");
    elm.innerHTML = "";
    
    var sp = newSPAN(elm, "alertArea");
    sp.innerHTML = "... 本日のデータを空にしつつあります";
    
    var container = new Object();
    container[tag] = "";
    
    put_page(owner(),patientId(),currentDate(),timeLimit(),container,cleardValue);
}

function dateChanged(answer){
    // newDatePopUp() の操作で返される '2012-07-15' のような answer
    var elm = editorElementForTag(currentTag()); // 文章
    
    // まだ elm が生成されたいないうちに動作することがある
    if (elm) elm.innerHTML = answer;
}

function checkKeyUp(tag){
    // CTRL-j が入力されたら saveJumpEditor() を実行
    if (window.event.ctrlKey){
        if (! arrowKeyJump()) return;
        
        if (window.event.keyCode == 74){ // CTRL-J
            jumpToNext(tag);
        }
    }
}

function setNow(cid, type){
    // 年月日ポップアップに現在の日時をセット
    var elm = document.getElementById(cid+".datePopArea");
    elm.innerHTML = "";
    
    var dateTIme = todayAndTime();
    var ary = arrayWithDateTime(dateTIme);
    var elm2 = editorElementForTag(currentTag()); // 文章
    if (type == "yearMonth"){
        newDatePopUp(elm, cid, "平成", ary[0], ary[1], ary[2], true, dateChanged);
        elm2.innerHTML = dateTIme.substr(0, 7) + "-01";
    } else {
        newDatePopUp(elm, cid, "平成", ary[0], ary[1], ary[2], false, dateChanged);
        elm2.innerHTML = dateTIme.substr(0, 10);
    }
}

////////////////////////////////////////////////////////
///// DRAG and DROP ////////////////////////////////////

function cell_dragover(event){
    // ドラッグ要素がドロップ要素に重なっている間の処理
    //　dragoverイベントをキャンセルして、ドロップ先の要素がドロップを受け付けるようにする
    event.preventDefault();
}

function cell_drop(event){
    // ドロップ時の処理
    // ドラッグされたデータをDataTransferオブジェクトから取得
    var value = event.dataTransfer.getData("text");
    
    value = decodeSTRING(value);
    
    // ドラッグされた要素を CELL 値に追加
    var elm = editorElementForTag(currentTag());
    if (typeof elm.textContent != "undefined") {
        elm.textContent += value; // innerHTML では value が改変されてしまう
    } else {
        elm.innerText += value; // innerHTML では value が改変されてしまう
    }

    // エラー回避のため、ドロップ処理の最後にdropイベントをキャンセルしておく
    event.preventDefault();
}

///// DRAG and DROP ////////////////////////////////////
////////////////////////////////////////////////////////


////////////////////////////////////////////////////////
///// HTML editor //////////////////////////////////////

function fixHTMLText(){
    // HTML エディターの編集内容を CellEditor へ転記
    var tag = currentTag();
    var value = elmFor("htmlText").value;
    
    //console.log("fixHTMLText", value); //##
    //console.log("->", htmlForValue(value)); //##
    
    // 編集内容を cellEditor へ上書き
    var dv = elmFor(tag + ".value");
    dv.innerHTML = htmlForValue(value);
    
    // 編集内容を強制的に save させるようセット
    var cell = cellForTag(tag);
    cell.originalValue = null; // 編集前の表現形

    closeFloatPanel();
}
function openHTMLEditor(elm, status){
    // CELL 内容を HTML として表示し編集
    var w = 330;
    var pos = getPosition(elm); // dom.js
	var x = (status > 0) ? 10 : pos.x - 80; // 表示するx座標
	var y = pos.y; // 表示するy座標
    var title = "HTML 形式で編集";
    var elm = openSeeThroughPanel("_floatPanel", x, y, w, title);
    if (!elm) return;
    elm.style.padding = "8px";

    // テキストエリア
    var div = newDIV(elm, "");
    div.style.marginBottom = "2px";
    div.style.fontSize = "10pt";
    if (_defaultValue){
        var val = _defaultValue; // DocMaker などから与えられた値
    } else {
        var node = elmFor(currentTag() + ".value");
        // "openImage()" を "<IMG:url>" に変換
        var val = valueForHtml(node.innerHTML);
    }
    var ta = newTEXTAREA(div, "htmlText", 40, 10, val);
    
    // 保存ボタン
    var div = newDIV(elm, "");
    div.style.textAlign = "right";
    var bt = newDIV(div, "/fixButton");
    bt.setAttribute("onclick", "fixHTMLText()");
    bt.innerHTML = "確定";
}

//////////////////////////////////////////////////////////
///// 選択部分の文字列属性を設定 /////////////////////////////

function palletHelp(){
    // カラー・パレットのヘルプを開く
    openHelp('./palleteHelp.html'); 
}

function openColorPallet(elm){
    // カラー・パレットを開く
    var w = 330;
    var pos = getPosition(elm); // dom.js
	var x = pos.x - 50; // 表示するx座標
	var y = pos.y; // 表示するy座標
    var title = "文字属性パレット";
    var elm = openSeeThroughPanel("_floatPanel", x, y, w, title, "palletHelp()");
    if (!elm) return;
    elm.style.padding = "8px";
    var div = newDIV(elm, "/clerfix");
    
    // NULL COLOR
    var bt = newDIV(div, "/color-chip");
    bt.innerHTML = "普通文字";
    bt.setAttribute("onMouseOver", "setAttributesToText(this)");
    bt.setAttribute("onclick", "closeColorPallet()");
    // BOLD
    var bt = newDIV(div, "/color-chip");
    bt.innerHTML = "bold";
    bt.style.border = "thin solid #aaa";
    bt.setAttribute("onMouseOver", "setAttributesToText(this, 'bold')");
    bt.setAttribute("onclick", "closeColorPallet()");
    // RED
    var bt = newDIV(div, "/color-chip");
    bt.innerHTML = "　";
    bt.style.backgroundColor = "#f00";
    bt.setAttribute("onMouseOver", "setAttributesToText(this, '#f00')");
    bt.setAttribute("onclick", "closeColorPallet()");
    // BLUE COLOR
    var bt = newDIV(div, "/color-chip");
    bt.innerHTML = "　";
    bt.style.backgroundColor = "#00f";
    bt.setAttribute("onMouseOver", "setAttributesToText(this,'#00f')");
    bt.setAttribute("onclick", "closeColorPallet()");
}

var _selection;
function textSelected(){
    // 文字列の選択部分を _selection に記憶
    // 文字選択の参考：http://uhyohyo.net/javascript/8_3.html
    var sel = document.getSelection(); // 選択された文字列オブジェクト
    if (sel.type == "Range"){ // 単なるクリックならスキップ
        _selection = sel;
    }
}

function setAttributesToText(elm, attribute){
    // テキストの選択部分に カラーなどの文字属性 をつける
    // range 参考：https://developer.mozilla.org/ja/docs/Web/API/range
    if (!_selection) return;
    if (_selection.rangeCount == 0) return;

    var st =  _selection.toString();
    var range = _selection.getRangeAt(0);
    var df = range.extractContents(); // 選択範囲のエレメント：document fragment
    
    //console.log("range.startContainer", range.startOffset); //##
    //console.log("range.endContainer", typeof range.endContainer); //##

    // 親エレメントを取得するため、仮 span を df へ挿入：df から親エレメント取得できないため
    var sp = newSPAN(df, "");
    range.insertNode(sp);
    var pnode = sp.parentNode;
    pnode.removeChild(sp); // 用済みの sp を削除
  
    if (pnode.tagName.toLowerCase() == "span"){ // 親エレメント が <span> なら
        // 新エレメントを生成
        var sp = document.createElement("span");
        setAttribute(attribute, sp);
        sp.id = "xxx"; // 後でキャッチできるよう ID を付けておく
        sp.innerHTML = st;

        //console.log("pnode", pnode); //##

        // 親エレメントを新エレメントで置換
        if (range.startOffset == 0) // <span>target</span> の場合
            pnode.parentNode.replaceChild(sp, pnode);
        else // <span>...target..</span> の場合
            range.insertNode(sp);

        // range が変わってしまっているので再指定が必要
        window.getSelection().selectAllChildren(elmFor("xxx"));
        sp.removeAttribute("id"); // 用済みの ID は後で重複する可能性あるので消去
    } else { // 新エレメントを選択位置に挿入
        var sp = newSPAN(df, "");
        sp.innerHTML = st; // 選択された文字列
        setAttribute(attribute, sp);
        range.insertNode(sp);
        
        // focus が文字属性パレットへ移動しているので、元の文字列選択部分へ戻す
        window.getSelection().addRange(range);
    }

    function setAttribute(attribute, elm){
        // elm に attribute に相当する属性をつける
        if (attribute == null)
            return;
        else if (attribute == 'bold')
            elm.style.fontWeight = "bold";
        else if (attribute.charAt(0) == '#')
            elm.style.color = attribute;
    }
}
function closeColorPallet(){
    // 文字列属性パネルを閉じる
    _selection = null; 
    
    // 変更が保存される status に設定
    var cell = cellForTag(currentTag());
    cell.originalValue = null; // CELL 内容に変更があった

    closeFloatPanel();
}

///// 選択部分の文字列属性を設定 /////////////////////////////
//////////////////////////////////////////////////////////

function showMenu(menuType){
    // メニューを生成
    var elm = document.getElementById("menuArea");
    elm.innerHTML = "";
    var items = menus(currentTag());
    //alert("showMenu->"+encodeObject(items)); //##
    
    var rec = valueForTag(currentTag(), currentDate());
    var val = rec.value;
    switch (menuType){
        case _group_:
            // グループ・メニュー
            var sp = newSPAN(elm, "menuPopArea");
            sp.innerHTML = "";
            var pu = newPopupMenu(sp, "menuPop", items, "");
            pu.setAttribute("onchange", "groupPopupChanged(this)");
            var bt = newDIV(elm, "/whiteButton");
            bt.innerHTML = "挿入";
            bt.setAttribute("onclick", "insertGroupItems()");
            break;
        case _address_:
            // ### menus() に挿入すると menus() が変化してしまうのでコピー作成
            var newItems = new Array();
            for (num in items) newItems.push(items[num]);
            
            var prefecture = "";
            if (val && (val.length > 0)){ // CELL 値に既存住所があれば
                var addressArray = val.split(" ");
                prefecture = addressArray[0];
            }
            
            // 都道府県ポップメニュー・エリア
            var span = newSPAN(elm, "addressPopArea");
            span.innerHTML = "";
            var sp = newSPAN(span, "_prefecture");
            var popup = newPopupMenu(sp, "prefPop", newItems, prefecture);
            // 都道府県 が変更されたら 市区 のメニューを取り寄せる
            popup.setAttribute("onchange", "getCITY(this)");
            
            // 市区・町村ポップアップメニュー・エリア
            var sp = newSPAN(span, "_shiku");
            var sp = newSPAN(span, "_chouson");
            getCITY(popup);
            break;
        case _panel_:
            var elm = document.getElementById("panelMenuArea");
            elm.innerHTML = "";
            elm.style.fontSize = "9pt";
            var array = items;
            for (num in array){
                var item = array[num];
                if (item.length == 0) continue;
                
                var sp = newDIV(elm, "/blueButton");
                sp.innerHTML = item;
                sp.setAttribute("onclick", "insertMenuItem('"+item+"')");
            }
            break;
        case _date_:
            var yy = "";
            var mm = "";
            var dd = "";
            val = trim(val);
            if (val && val.length){
                var dateArray = val.split("-");
                yy = dateArray[0];
                mm = (dateArray.length > 1) ? dateArray[1] : "";
                dd = (dateArray.length > 2) ? dateArray[2] : "";
            }
            // 年月日ボタン表示エリア
            var cid = currentDate() + "."+currentTag() + ".hidden"; // 隠しフィールドの ID
            var sp = newSPAN(elm, "");
            sp.innerHTML = "";
            sp.style.paddingRight = "5px";
            var img = newIMAGE(sp, "", "./timer-set.png", "time");
            img.style.height = "16px";
            img.setAttribute("onclick", "setNow('" + cid +"','dateType')");
            img.style.position = "relative";
            img.style.top = "3px";
            img.setAttribute("class", "expandIcon");
            var sp = newSPAN(elm, cid+".datePopArea");
            sp.innerHTML = "";
            newDatePopUp(sp, cid, "平成", yy, mm, dd, false, dateChanged);
            break;
        case _yearMonth_:
            var yy = "";
            var mm = "";
            
            val = trim(val);
            if (val && val.length){
                var dateArray = val.split("-");
                yy = dateArray[0];
                mm = (dateArray.length > 1) ? dateArray[1] : "";
            }
            // 年月日ボタン表示エリア
            var cid = currentDate() + "."+currentTag() + ".hidden"; // 隠しフィールドの ID
            var sp = newSPAN(elm, "");
            sp.innerHTML = "";
            sp.style.paddingRight = "3px";
            var img = newIMAGE(sp, "", "./timer-set.png", "time");
            img.style.height = "16px";
            img.setAttribute("onclick", "setNow('" + cid +"','yearMonth')");
            img.style.position = "relative";
            img.style.top = "5px";
            img.setAttribute("class", "expandIcon");
            var sp = newSPAN(elm, cid+".datePopArea");
            sp.innerHTML = "";
            newDatePopUp(sp, cid, "平成", yy, mm, "1", true, dateChanged);
            break;
        case _rotary_:
            // ポップアップ・メニュー
            var sp = newSPAN(elm, "menuPopArea");
            sp.innerHTML = "";
            sp.style.paddingRight = "3px";
            var pu = newPopupMenu(sp, "menuPop", items, val); //##
            pu.setAttribute("onchange", "popupChanged(this)");
            // 絞り込み
            var fd = newFIELD(elm, "focusF", "", 10, "");
            fd.setAttribute("placeholder", "絞込み"); // 入力ヒントを表示
            fd.setAttribute("onkeyup", "focusPressed()");
            fd.focus();
            break;
        case _none_:
        case _calendar_:
        default:
            break;
    }
}
function showCellEditor(){
    // CELL 編集欄を生成
    var tag = currentTag();
    var cell = cellForTag(tag);
    if (cell) cell.editorIsOpen = true; // CELL のエディターが開いていることを記憶
    //console.log("== showCellEditor", tag); //##
    
    var elm = valueElementForTag(currentDate(), tag);
    elm.innerHTML = "";
    
    var div = newDIV(elm, tag + ".editorArea/editorArea");

    if (_defaultValue){
        var val = _defaultValue; // DocMaker などから与えられた値
    } else {
        var obj = valueForTag(tag, currentDate());
        var originalValue = (obj && obj.value) ? obj.value : "";
        var val = originalValue;
        val = convertSTRING(val, "\'", "'"); // DocMaker ICON で発生の可能性
    }

    // Agent データ・オブジェクトに値を記憶
    var agt = agent();
    agt.tag = tag;
    agt.cellValue = val;
    agt.cell = encodeObject(cell);
    setAgent(agt);

    // HEADER ====================================
    var dv = newDIV(div, "/editorBar");
    var isDeseaseTool = ((cell.editor == "openDisease") && (currentTag() == "ProgressSection.disease"));
    if (isDeseaseTool){ 
        openDisease(dv);
    } else {
        // メニュー -------------------------------
        // LEFT ----------------
        var left = newDIV(dv, "/left-side");
        left.style.width = "250px";
        left.style.paddingLeft = "0";
        // POPUP MENU
        var sp = newSPAN(left, "menuArea");
        
        // RIGHT ----------------
        var right = newDIV(dv, "/right-side");
        right.style.paddingRight = "0";
        if (tag == "ProgressSection.subject"){
            // 「妊娠歴」アンカー
            if (val.length > 0){
                // 文中に "lmp:" 文字列があれば「妊娠暦アンカー」を表示
                var array = val.split("lmp:");
                if (array.length > 1){
                    var sp = newSPAN(right, " ");
                    sp.style.fontSize = "9pt";
                    sp.style.paddingLeft = "5px";
                    sp.style.position = "relative";
                    sp.style.bottom = "2px";
                    var a = newA(sp, "妊娠暦", "#", "");
                    a.setAttribute("onclick", "openGestateCalendar('"+cell.id+"')");
                }
            }
        } else if (tag == "ProgressSection.object"){
            // 「画像読込ツール」アイコン
            var sp = newSPAN(right, "pictTip");
            sp.style.paddingLeft = "10px";
            setInfoTip("pictTip", "画像読込"); // HELP
            var img = newIMAGE(sp, "", "./camera.png", "pict");
            img.style.height = "20px";
            img.setAttribute("onclick", "openPictureTool()");
            img.style.position = "relative";
            img.style.top = "5px";
            img.setAttribute("class", "expandIcon");
        }
        // HELP ICON
        var sp = newSPAN(right, "helpTip");
        setInfoTip("helpTip", "HELP"); // HELP
        sp.style.paddingLeft = "8px";
        var img = newIMAGE(sp, "", "./help.png", "?");
        img.style.height = "17px";
        img.setAttribute("onclick", "cellEditorHelp('" + cell.menuType * 1 + "')");
        img.setAttribute("class", "expandIcon");
        
        // パネル・メニュー・エリア
        var dv = newDIV(div, "panelMenuArea");
    }
        
    // 入力欄 ==============================
    var dv = newDIV(div, tag + ".value");
    dv.setAttribute("class", "editorValue");
    dv.innerHTML = htmlForValue(val);
    if (_defaultValue)
        setBeforEdit(null); // 編集前のエディター内容を記憶
    else
        setBeforEdit(dv.innerHTML); // 編集前のエディター内容を記憶
    dv.setAttribute("onMouseUp", "textSelected()");
    // google chrome では画像に添付したデータより画像を優先して受け取るようになったので
    // picture.js から画像をドロップするために以下のインプリメントが必要になった
    dv.setAttribute("ondragover", "cell_dragover(event)");
    dv.setAttribute("ondrop", "cell_drop(event)");
    
    if (_defaultValue) // DocMaker などから値を与えられた
        cell.originalValue = originalValue;
    else
        cell.originalValue = dv.innerText; // 編集前の表現形を CELL に記憶
    
    dv.contentEditable = true; // TEXT を編集可能状態にする
    dv.setAttribute("onkeyup", "checkKeyUp('" + tag + "')");
    if (isDeseaseTool){ 
        // 病名ツールを使う場合、入力欄は非表示にして運用する
        dv.style.display = "none";
    } else {
        moveCursorAtEndOf(dv); // dv 中のカーソルを文末に置く
    }
    
    // FOOTER ===========================
    var dv = newDIV(div, "/editorBar");
    dv.style.height = "20px";
    // LEFT ----------------
    var left = newDIV(dv, "/left-side");
    //left.style.border = "thin solid #aaa"; //########
    left.style.paddingLeft = "5px";
    left.style.fontSize = "9pt";
    // basicPage の場合は CELL 巾が小さいのでサイズを調整
    var isBasicPage = false;
    if (div.clientWidth < 400){
        dv.style.height = "40px";
        left.style.width = "150px";
        isBasicPage = true;
    } else {
        dv.style.height = "20px";
        left.style.width = "250px";
    }
    // REMOVE ICON
    var sp = newSPAN(left, "removeFieldTip");
    sp.style.paddingRight = "10px";
    setInfoTip("removeFieldTip", "空欄にする"); // HELP
    var img = newIMAGE(sp, "", "./remove-field.png", "X");
    img.style.height = "12px";
    img.setAttribute("onclick", "removeValue('" + tag + "')");
    img.setAttribute("class", "expandIcon");
    var sp = newSPAN(left, "clearFieldTip");
    sp.style.paddingRight = "6px";
    setInfoTip("clearFieldTip", "前回記述が透けて見えるようにする"); // HELP
    var img = newIMAGE(sp, "", "./selection.png", "X");
    img.style.height = "12px";
    img.setAttribute("onclick", "clearValue('" + tag + "')");
    img.setAttribute("class", "expandIcon");
    if (cell.editor == ""){ // 専用編集ツールがある場合は文字列の属性設定を行わない
        // COLOR PALLET
        var sp = newSPAN(left, "colorPalletArea");
        sp.setAttribute("onclick", "openColorPallet(this)");
        sp.style.marginRight = "5px";
        var img = newIMAGE(sp, "", "./pen.png", "color");
        img.style.height = "13px";
        img.setAttribute("class", "expandIcon");
        setInfoTip("colorPalletArea", "選択された文字列に色をつける"); // HELP
    }
    // HTML ツール ICON
    if (HTMLEditor()){
        var sp = newSPAN(left, "/listMember");
        sp.innerHTML = "HTML";
        var status = (isBasicPage) ? 1 : 0;
        sp.setAttribute("onclick", "openHTMLEditor(this,'" + status + "')");
    }
    // PREFERENCE ICON
    var sp = newSPAN(left, "fieldPrefTip");
    setInfoTip("fieldPrefTip", "初期設定"); // HELP
    var img = newIMAGE(sp, "", "./hammer.png", "X");
    img.style.height = "15px";
    img.setAttribute("onclick", "openCellPreference()");
    img.setAttribute("class", "expandIcon");
    // RIGHT ----------------
    var right = newDIV(dv, "/right-side");
    right.style.width = "150px";
    right.style.paddingRight = "0";
    // 保存ボタン
    if (cell.menuType == _group_){
        var bt = newDIV(right, "/whiteButton");
        bt.innerHTML = "とりやめ";
        bt.setAttribute("onclick", "closeGroupSubEditor()");
        var bt = newDIV(right, "/fixButton");
        bt.innerHTML = "保存";
        bt.setAttribute("onclick", "closeGroupSubEditor(true)");
    } else {
        var bt = newDIV(right, "/whiteButton");
        bt.innerHTML = "とりやめ";
        var action = "closeEditorAndShowValue('" + cell.tag + "','close')";
        bt.setAttribute("onclick", action);
        var bt = newDIV(right, "/fixButton");
        bt.innerHTML = "保存";
        bt.setAttribute("onclick", "jumpToNext('" + cell.tag + "')");
    }
    
    if (! isDeseaseTool){ 
        // ポップアップメニューなどを生成
        showMenu(cell.menuType * 1);
        
        if (cell.editor){
            // cell.editor が設定されていれば tools エリアに開く
            eval( cell.editor + '()');
        }
    }
    
    if (currentTag() == null)
        setCurrentTag(tag); // 編集中の CELL を記憶
}

////////////////////////////////////////////////////
///// CELL 表示 /////////////////////////////////////

var _blackOut = '_blackOutArea';
function closeWarm(){
    // blackOut を解除し警告パネルを閉じる
    var tarElement = document.getElementById(_blackOut);
    tarElement.parentNode.removeChild(tarElement);
}
function openWarm(action, y){
    // このデータが過去のものであれば、警告パネルを出す
    // 画面いっぱいに表示する要素を作成する
    var ds = document.createElement('div');
    ds.setAttribute('id', _blackOut);
    ds.style.display = 'none';
    // 要素の透明度を設定：opacity を使うと背景色がその上の要素にも継承されてしまう
    ds.style.backgroundColor = 'rgba(0,0,0,0.4)';
    ds.style.position='absolute';
    ds.style.width='100%';
    ds.style.left='0px';
    ds.style.top='0px';
    ds.style.height= document.documentElement.clientHeight +'px';
    document.body.appendChild(ds);
    ds.style.display = 'block';
    
    // 警告を表示する要素を作成する
    var elm = document.createElement('div');
    ds.appendChild(elm);
    elm.setAttribute('class', 'warm');
    elm.style.position = "relative";
    elm.style.marginLeft = 'auto';
    elm.style.marginRight = 'auto';
    // タイトル
    var dv = newDIV(elm, "");
    var msg = yyyymmdd(currentDate()) + " は過去の記録で原則として修正できません。";
    msg += "修正した場合は、誰が何時修正したか記録されます。";
    dv.innerHTML = msg;
    // ボタン・エリア
    var dv = newDIV(elm, "");
    dv.style.margin = "10px 0";
    dv.style.textAlign = "right";
    // 修正ボタン
    var sp = newDIV(dv, "/fixButton");
    sp.innerHTML = "強制的に修正";
    sp.setAttribute("onclick", action);
    sp.style.marginRight = "10px";
    sp.style.backgroundColor = "#f00";
    // とりやめボタン
    var sp = newSPAN(dv, "/whiteButton");
    sp.innerHTML = "とりやめ";
    sp.setAttribute("onclick", "closeWarm()");
    
    if (y == null)
        elm.style.top = (window.innerHeight - elm.offsetHeight) / 2;
    else
        elm.style.top = y; // 指定位置に警告表示
}
function closeWarmAndOpenCell(entryDate, tag){
    // 警告パネルを消し CELL エディターを開く
    closeWarm();
    removeCheck(); // 編集不可のチェックをはずす
    
    setCurrentTag(null);
    setCurrentDate(entryDate);
    
    // cell editor を開く：すでに開いているなら save して閉じる
    openCellEditor(entryDate, tag);
}
function open_cellEditor(dateTime, tag, isActive){
    // 本文をクリックして CELL を編集状態にする
    var cell = cellForTag(tag);
    if (cell.editorIsOpen || groupEditorOpen()) return; // 自分自身のエディターがすでに開いている

    //console.log("-- open_cellEditor text clicked", tag); //##

    if (cell.disabled * 1 > 0){
        alert(cell.label + " は参照のみで編集できません");
        return;
    }
    
    if (groupEditorOpen()){
        alert("「とりやめ」か「保存」ボタンを使ってください");
    } else if (isActive > 0){
        if (cell.tag == "ProgressSection.pageHeader"){
            // 修正履歴ツールを開く
            openCorrectionHistory();
        } else if (isReadOnly()){
            // readOnly モードなら警告パネルを開くだけで終了
            var elm = document.getElementById(dateTime+"."+tag + ".cell");
            var pos = getPosition(elm); // dom.js
            var y = pos.y + 20;
            openWarm("closeWarmAndOpenCell('" + dateTime+"','"+tag + "')", y);
        } else {
            // cell editor を開く：すでに開いているなら save して閉じる
            _openCellEditor(dateTime, tag);
        }
    } else {
        // そのページをアクティブにする
        openPage(dateTime, true); // basicPage でも使うなら そちらでも宣言
    }
}
function labelClicked(dateTime, tag, isActive){
    // cell label がクリックされた
    //_log(["== labelClicked", dateTime, tag, isActive]); //##
    
    if (_kickScript) return; // ラベル領域のタイトルがクリックされた

    var cell = cellForTag(tag);
    if (cell.disabled * 1 > 0){
        alert(cell.label + " は参照のみで編集できません");
        return;
    }

    if (groupEditorOpen()){
        alert("「とりやめ」か「保存」ボタンを使ってください");
    } else if (isActive > 0){
        if (cell.tag == "ProgressSection.pageHeader"){
            // 修正履歴ツールを開く
            openCorrectionHistory();
        } else if (isReadOnly()){
            // readOnly モードなら警告パネルを開くだけで終了
            var elm = document.getElementById(dateTime+"."+tag + ".cell");
            var pos = getPosition(elm); // dom.js
            var y = pos.y + 20;
            openWarm("closeWarmAndOpenCell('" + dateTime+"','"+tag + "')", y);
        } else {
            // cell editor を開く：すでに開いているなら save して閉じる
            openCellEditor(dateTime, tag);
        }
    } else {
        // そのページをアクティブにする
        openPage(dateTime, true); // basicPage でも使うなら そちらでも宣言
    }
}

var _kickScript;
function kickScript(tag){
    // スクリプトを実行
    _kickScript = true; // ラベル領域が反応しないようにする
    
    var script = callForTag(tag); // dataCenter.js
    //alert("kickScript->"+script); //##
    script = decodeHTML(script);
    call(script); // dataCenter.js
    
    // 一定時間後にラベルが正常動作するよう戻す
    setTimeout("kickOff()", 100);
}
function kickOff(){
    // ラベルのクリックを正常動作に戻す
    _kickScript = false;
}

function openCell(elm, cell, dateTime, isActive){
    // cell を表示
    var obj = valueForTag(cell.tag, dateTime);
    var val = (obj && obj.value) ? obj.value : "";
    var isPast = (obj && obj.isPast) ? true : false;
    
    // インアクティブなページでは空行を表示しない
    if (!isActive && (trim(val).length == 0)) return;
    
    // dateTime を指定しなければ curentDate() が使われる
    var cid = dateTime + "." + cell.tag;
    var div = newDIV(elm, cid + ".cell");
    var tbl = newTABLE(div, "/base-table");
    var tr = newTR(tbl, "", "");
    
    // LABEL ===========================
    var td = newTD(tr, "/cellLabel", "");    
    var sp = newSPAN(td, "");
    sp.innerHTML = cell.label;
    // script がある場合はラベルは機能せず script を作動
    _kickScript = false;
    var script = callForTag(cell.tag); // dataCenter.js
    if (script){
        sp.setAttribute("class", "listMember");
        sp.style.padding = "0";
        sp.setAttribute("onclick", "kickScript('" + cell.tag + "')");
    }
    // フォント・サイズを設定
    var size = cellFontSize(); // localStorage
    sp.style.fontSize = size * 1 + "pt";
    // ラベル・クリックによる動作
    var status = (isActive) ? 1 : 0;
    var action = "labelClicked('" + dateTime + "','" + cell.tag + "','" + status + "')";
    td.setAttribute("onclick", action);

    // VALUE =============================
    var td = newTD(tr, cid + ".val", "");
    if (isActive == false) isPast = true; // インアクティブなページはすべてグレイアウト
    showValue(td, cell, val, isPast, dateTime);
    // カルテ本文クリックによる動作
    if (textClick()){
        // 住所欄には googleMaps を開くよう仕掛けてあるので、ここはスキップ
        if (cell.tag == "AddressSection.address") return;
        
        var status = (isActive) ? 1 : 0;
        var action = "open_cellEditor('" + dateTime + "','" + cell.tag + "','" + status + "')";
        td.setAttribute("onMouseDown", action); // onClick だと focus() で起動してしまう
    }
}

function showValue(elm, cell, value, isPast, dateTime){
    // VALUE 欄に値を表示
    //console.log("== showValue", cell.label, value); //##
    
    // 過去データなら背景を過去色にする：淡青色
    elm.innerHTML = "";
    if (isPast)
        elm.setAttribute("class", "pastCellValue");
    else
        elm.setAttribute("class", "cellValue");
    
    // CELL 値を HTML 型式で表示
    if (!dateTime) dateTime = currentDate();
    var fid = fieldId(dateTime, cell.tag); // CELL 値を記入したエレメントの ID
    var sp = newSPAN(elm, fid);
    sp.innerHTML = htmlForValue(value);
    
    // フォント・サイズを設定
    var size = cellFontSize(); // localStorage
    sp.style.fontSize = size * 1 + "pt";
    if (cell.fontSize * 1 > 0) // CELL 属性設定で上書き
        sp.style.fontSize = cell.fontSize * 1 + "pt";
   
    // cell 属性に文字色・背景色があるなら着色
    if (cell.bgcolor) sp.style.backgroundColor = cell.bgcolor;
    if (cell.color) sp.style.color = cell.color;
    
    // 住所欄ならクリックで Google map を開く
    if (cell.tag == "AddressSection.address"){
        sp.setAttribute("class", "showMapElement");
        sp.setAttribute("onclick", "showMap(this,'" + trim(value) + "')");
    }
}

function showMap(elm, address){
	// eid のフィールドに表示された住所で Google map を開く
    
    // elm.setAttribute("class", "showMapElement");
    if (elm.childNodes[0].childNodes.length == 0){
        // 編集モードなら map 表示しない
        var href = 'http://maps.google.co.jp/?hl=ja&q='+encodeURI(address);
        var win = window.open(href,"maps","scrollbars=yes");
        win.focus();
    }
}

///// CELL 表示 /////////////////////////////////////
////////////////////////////////////////////////////

function editorElementForTag(tag){
    // tag に相当する 入力欄 エレメントを返す
    var elm = window.top.noa.document.getElementById(tag + ".value");
    if (elm)
        return elm;
    else
        return window.top.tools.document.getElementById(tag + ".value");
}

function valueElementForTag(dateTime, tag){
    // dateTime, tag に相当する 値表示欄 エレメントを返す
    var elm = window.top.noa.document.getElementById(dateTime+"."+tag+".val");
    if (elm)
        return elm;
    else
        return window.top.tools.document.getElementById(dateTime+"."+tag+".val");
}


function closeCellEditor(needSave){
    // 額たままのエディターがあれば save して閉じる
    //console.log("closeCellEditor", currentTag()); //##
    
    // Agent データ・オブジェクトからタグ情報を削除
    var agt = agent();
    agt.tag = null;
    agt.cell = null;
    agt.cellValue = null;
    setAgent(agt);

    var tag = currentTag(); // エディターが開いたままの CELL の tag
    if (tag){ // エディターが開いたままの CELL の後片付け
        var cell = cellForTag(tag);
        if (cell) cell.editorIsOpen = false; // エディターが閉じた

        if (needSave){ // 入力欄を閉じ worker がサーバ値を表示するのを待つ
            if (saveEditor(tag) == false)
                closeEditorAndShowValue(tag);
        } else { // サーバへ保存の必要なし
            closeEditorAndShowValue(tag);
        }
    }

    function saveEditor(tag){
        // tag に属するエディターの内容を保存
        // 入力欄を閉じ worker がサーバ値を表示するのを待つ
        // 編集 CELL 上の tag と value をオブジェクトに入れて返す

        // CELL の VALUE 編集領域を削除する前に VALUE 値を val へ記憶
        var node = editorElementForTag(tag);
        if (!node) return false;
        
        //console.log("beforEdit()", beforEdit()); //##
        //console.log("node.innerHTML", node.innerHTML); //##
        
        if (node.innerHTML == beforEdit()){ // データが編集されなかった
            // オリジナル・データと差がなかったので再表示のみで終了
            // DocMaker などから _defaultValue を与えられた場合はオリジナルを "" とする
            var cell = cellForTag(tag);

            cell.originalValue = null; // 用済みなので消去
            closeEditorAndShowValue(tag);
            return false;
        }

        // CELL の VALUE 編集領域を削除しメッセージ表示
        var elm = valueElementForTag(currentDate(), tag);
        elm.innerHTML = "";
        var sp = newSPAN(elm, "");
        sp.innerHTML = "サーバへ保存中...";
        sp.style.color = "#faa";
        
        // ### もし編集中の CELL でない CELL がクリックされた場合、この後で実行される
        // ### getMenu() で Ajax がカブるので、以下の処理は別スレッドで実行する必要あり
        
        // 値が変更されたので編集中の CELL と認識：これが結構キモ
        setCurrentTag(tag); // kickEditorWorker() で必要
        
        // ### CELL 値を編集前のオリジナルと比較するには innerText を使うが
        // ### innerText では HTML タグが削除されるので以下では innerHTML を使う
        // ### cellEditor で加えた改行は内部的に <div></div> になるが innerText では
        // ### これが削除されるので改行が消えてしまう。
        var val = node.innerHTML; // 編集されたデータ
        
        // val にイメージデータがペーストされた場合 < > を実データに変換
        val = replaceAll(val, "&lt;", "<");
        val = replaceAll(val, "&gt;", ">"); // "/>" にすると / がダブル可能性あり
        val = replaceAll(val, "&nbsp;", " "); // 空白を通常の空白に変換
        //console.log("saveEditor", val); //##
        val = convertNewLine(val); // HTML 改行を全て "\n" に変換
        val = valueForHtml(val); // "<a>..openImage(url)..</a>" を "<IMG:url>" へ変換
        
        //console.log("saveEditor->", val); //##
        //console.log("-> ", encodeSTRING(val)); //##
        
        var container = new Object();
        container[tag] = encodeSTRING(val);
        container["owner"] = owner();
        container["patientId"] = patientId();
        container["entryDate"] = currentDate();
        container["timeLimit"] = timeLimit();
        var noaString = encodeObject(container);

        // マルチスレッドで CELL 内容をサーバへ保存
        kickEditorWorker(noaString);
        
        return true;
    }
}
function closeEditorAndShowValue(tag, close){
    // エディターを閉じて CELL の値を表示
    var dateTime = currentDate();
    var obj = valueForTag(tag, dateTime);
    var value = (obj && obj.value) ? obj.value : "";
    var isPast = (obj && obj.isPast) ? true : false;
    
    // CELL 値を表示
    var elm = valueElementForTag(dateTime, tag);
    var cell = cellForTag(tag);
    showValue(elm, cell, value, isPast);
    
    // Agent データ・オブジェクトからタグ情報を削除
    var agt = agent();
    agt.tag = null;
    agt.cell = null;
    agt.cellValue = null;
    setAgent(agt);

    // EDITOR が閉じたことを記憶しスタンバイ状態にする
    _setDateTime(null);
    cell.editorIsOpen = false;
    if (close) setCurrentTag(null);
}


function jumpToNext(tag){
    // 開いている CELL を閉じ、次の CELL を開く
    // 今まで開いていた cellEditor の内容を save し閉じる
    //console.log("--　jumpToNext", tag); //##
    
    _defaultValue = null; // DocMaker などから与えられた値を捨てる
    closeCellEditor('needSave');
    
    // 次の CELL の cellEditor を開く
    var nxTag = nextTag(tag); // 最終 tag では null が返される
    //console.log("nxTag", nxTag); //##
    
    if (nxTag){
        // openCellEditor() を使わない
        _setDateTime(currentDate());
        
        // サーバからメニューを取得しエディタを生成
        kickCellEditor(nxTag);
    }
}


/////////////////////////////////////////////////////////////////////////////
///// SHELL から使えるメソッド /////////////////////////////////////////////////

function removeValue(tag){
    // 編集内容を空にする
    var elm = document.getElementById(tag + ".value");
    
    // ### 上書き文字列 は倍角スペースでないと innerHTML や innerText で "" になってしまう
    elm.innerHTML = "　";
    
    if (tag == "ProgressSection.disease"){ 
        // 病名リストの表示を削除
        setDiseases("");
        makeDiseseRow("removed");
    } else {
        elm.focus();
    }
}

var _defaultValue;
function _openCellEditor(dateTime, tag, value){
    // CELL 編集欄を開く
    //_log(["== openCellEditor", dateTime, tag, value]); //##
    
    if (dateTime == null) dateTime = currentDate();
    
    var cell = cellForTag(tag);
    if (cell) cell.editorIsOpen = true; // CELL のエディターが開いていることを記憶
    
    // DocMaker などから value を与えられた場合はそれを表示
    _defaultValue = (value) ? value : null;
    
    closeCellEditor('needSave');
    _setDateTime(dateTime);
    
    // サーバからメニューを取得しエディタを生成
    kickCellEditor(tag);
}
function openCellEditor(dateTime, tag, value){
    // CELL 編集欄を開く
    //_log(["== openCellEditor", dateTime, tag, value]); //##

    if (dateTime == null) dateTime = currentDate();
    
    var cell = cellForTag(tag);
    //_debug("cell->"+encodeObject(cell)); //##
    
    // DocMaker などから value を与えられた場合はそれを表示
    _defaultValue = (value) ? value : null;
    
    if (cell.editorIsOpen){ // 自分自身のエディターが開いている：閉じて終了
        closeCellEditor('needSave'); // 自分自身を閉じ終了
        setCurrentTag(null); // closeCellEditor() でやるとタイミングによっては不整合発生
    } else { // 他に開いているエディターがあれば閉じ、続いて tag 相当エディターを開く
        closeCellEditor('needSave');
        _setDateTime(dateTime);
       
        // サーバからメニューを取得しエディタを生成
        kickCellEditor(tag);
    }
}

///// SHELL から使えるメソッド /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


