
var _PATH = "/NOA/";

var _isPrivate = false; //####### 開発者用 #######

// cell.type に与える定数
var _mainPane = 0; // NOA メイン・エリア
var _toolPane = 1; // TOOL エリア

// 診療情報・基本情報・保険情報の各ページを区別するフラッグ
var _progress_ = 0;
var _basic_ = 1;
var _ins_ = 2;

// 以下は cell.id へ設定されるが、第７世代 NOA の cell.id とカブらないよう数値以外とする
var _progress = 'P';
var _basic = 'B';
var _insurance = 'I';

function modeForTag(tag){
    // tag から mode を判定して返す
    var array = tag.split(".");
    var table = array[0];
    if (table == "ProgressSection"){
        return _progress;
    } else if (tag == "FrontTable.plan"){
        return _progress;
    } else if (table.indexOf("Insurance") >= 0){
        return _insurance;
    } else {
        return _basic;
    }
}

var _beforEdit;
function setBeforEdit(value){
    // 編集前のエディター内容を記憶
    _beforEdit = value;
}
function beforEdit(){
    return _beforEdit;
}

function tools(){
    if (window.name == "tools")
        return window;
    else if (window.top.name == "tools")
        return window.top;
    else if (window.top.tools)
        return window.top.tools;

    console.log("=== tools", "が見つかりません");
    //console.log("window", window);
    //console.log("window.top", window.top);
    //console.log("window.top.tools", window.top.tools);
    return null;
}

//////////////////////////////////////////////////////
///// NOA からの継承値 /////////////////////////////////

function parent(){
    if (window.opener){
        if (window.top.noa){
            return window.top.noa;
        } else if (window.opener.name == "noa"){
            return window.opener;
        } else if (window.opener.name == "tools"){
            return window.opener.top.noa;
        } else if (window.opener.name == "iframe"){ 
            //### iframe は必ず window.top.noa と同じ window 上にある必要あり ###
            return window.opener.top.noa;
        } else if (window.opener.owner()){ // owner() を返す機能があれば
            return window.opener;
        }
    } 
    if (window.top.noa){
        return window.top.noa;
    }
    console.log("parent(): 親ウインドウが見つかりません");
    return null;
}

///////////////////////////////////////////////////////
///// MENU STRUCTURE //////////////////////////////////

// menuType
var _main = 0;
var _page = 1;
var _tool = 2;

function menuTemplate(){
    // メニュー構造のテンプレート
    var array = new Array();
    
    // メイン・メニュー =========================
    array.push({"menuType":_main,"label":"履歴","url":"showAccessLog()","editor":"カルテの参照履歴","fix":"1"});
    array.push({"menuType":_main,"label":"待受","url":"showBookingList()","editor":"受診待ちリスト"});
    array.push({"menuType":_main,"label":"検索欄","url":"search()","editor":"カルテ内容を検索","fix":"1"});
    array.push({"menuType":_main,"label":"search","url":"search.php","editor":"検索ツールを開く","icon":"search.png"});
    
    if (_isPrivate){
        array.push({"menuType":_main,"label":"一括処理","editor":"一括処理カスタマイズ","url":"openCalls()","editor":"一括処理","icon":"plugIn.png"}); // ### PRIVATE
        array.push({"menuType":_main,"label":"周産期管理","url":"../PerinatalManager","editor":"周産期管理","icon":"human.png","window":"1"}); // ### PRIVATE
    }
    
    
    // ページ・ヘッダー =========================
    array.push({"menuType":_page,"label":"ページ追加","url":"addTodayPage()","editor":"ページ追加", "icon":"add-field.png","fix":"1"});
    array.push({"menuType":_page,"label":"テンプレート・メニュー","url":"openTemplateMenu()","editor":"テンプレート・メニュー", "icon":"menu.png"});
    array.push({"menuType":_page,"label":"文書作成","url":"../DocMaker","editor":"文書作成","window":"1","icon":"docSmall.png"});
    if (_isPrivate){
        array.push({"menuType":_main,"label":"一括処理","url":"openCalls()","editor":"一括処理","icon":"plugIn.png"}); // ### PRIVATE
    }
    array.push({"menuType":_page,"label":"予約","url":"../Booking","editor":"診療予約","icon":"list.png"});
    array.push({"menuType":_page,"label":"付箋","url":"showPostItForce()","editor":"付箋","fix":"1","icon":"flag.png"});
    array.push({"menuType":_page,"label":"Print","url":"printPDF()","editor":"印刷", "fix":"1","icon":"printer.png","fix":"1"});
    array.push({"menuType":_page,"label":"ページ削除","url":"removeTodayPage()","editor":"ページ削除", "icon":"remove-field.png","fix":"1"});
    
    // ツール・メニュー ==========================
    // 検索 ---------------------
    array.push({"menuType":_tool,"label":"検索","subTitle":"1"});
    array.push({"menuType":_tool,"label":"薬剤添付文書","url":"../Prescription"});
    
    if (_isPrivate){
        array.push({"menuType":_tool,"label":"薬剤検索","url":"/Medicine"});
        array.push({"menuType":_tool,"label":"妊婦検索","url":"../PregnantFinder","window":"1"});
    }
    
    // 道具 ----------------------
    array.push({"menuType":_tool,"label":"道具","subTitle":"1"});
    array.push({"menuType":_tool,"label":"Pickup","url":"../PickUp"});
    array.push({"menuType":_tool,"label":"リマインダー","url":"../Reminder"});
    array.push({"menuType":_tool,"label":"注意事項","url":"openCautionEditor()","fix":"1"});
    array.push({"menuType":_tool,"label":"画像読込","url":"../Picture"});
    
    if (_isPrivate){
        array.push({"menuType":_tool,"label":"HL7","url":"../HL7","window":"1"}); // ### PRIVATE
    }
    
    array.push({"menuType":_tool,"label":"年齢計算","url":"../AGE"});
    array.push({"menuType":_tool,"label":"妊娠暦","url":"../ReproductiveHistory"});
    array.push({"menuType":_tool,"label":"診療統計","url":"../Statistics","window":"1"});
    array.push({"menuType":_tool,"label":"ORCA","url":"../OrcaAgent"});
    
    // 印刷 -----------------------
    array.push({"menuType":_tool,"label":"印刷","subTitle":"1"});
    array.push({"menuType":_tool,"label":"カルテの全ページ","url":"printAllPages()"});
    array.push({"menuType":_tool,"label":"本日受診の全カルテ","url":"printAllPDF()"});
    array.push({"menuType":_tool,"label":"検査伝票ラベル","url":"preferenceOfPrintLabel()"});
    
    // 受付 -----------------------
    array.push({"menuType":_tool,"label":"受付","subTitle":"1"});
    array.push({"menuType":_tool,"label":"FRONT","url":"openFRONT()","window":"1"});
    array.push({"menuType":_tool,"label":"FRONTへ伝達","url":"openMessenger()"});
    array.push({"menuType":_tool,"label":"新患登録","url":"./patientRegister.php"});
    array.push({"menuType":_tool,"label":"診療予約","url":"../Booking","window":"1"});
    array.push({"menuType":_tool,"label":"保険証画像","url":"../InsViewer","window":"1"});
    array.push({"menuType":_tool,"label":"月計表","url":"../FRONT/monthlyTable.php"});
    array.push({"menuType":_tool,"label":"待受パネル","url":"showWaitingListPanel()"});
    
    // 管理 ------------------------
    array.push({"menuType":_tool,"label":"管理","subTitle":"1"});
    array.push({"menuType":_tool,"label":"バックアップ","url":"./archiver.php", "fix":"1"});
    array.push({"menuType":_tool,"label":"NOAManager","url":"../NOAManager", "window":"1"});
    if (_isPrivate){
        array.push({"menuType":_tool,"label":"phpMyAdmin","url":"http://202.17.213.209/phpMyAdmin", "window":"1"});
    }
    
    // 設定 ------------------------
    array.push({"menuType":_tool,"label":"設定","subTitle":"1"});
    array.push({"menuType":_tool,"label":"初期設定","url":"./preference.php", "fix":"1"});
    array.push({"menuType":_tool,"label":"レイアウト設定","url":"./layout.php", "fix":"1"});
    array.push({"menuType":_tool,"label":"PriceList","url":"../NOAManager/priceList.php","window":"1"});
    
    // HELP -----------------------
    array.push({"menuType":_tool,"label":"HELP","subTitle":"1"});
    array.push({"menuType":_tool,"label":"InfoSack","url":"../InfoSack"});
    array.push({"menuType":_tool,"label":"SOURCEFORGE","url":"http://sourceforge.jp/projects/noa-project/", "window":"1"});
    array.push({"menuType":_tool,"label":"NOA Project","url":"https://sites.google.com/site/noawebspace", "window":"1"});
    if (_isPrivate){
        array.push({"menuType":_tool,"label":"TEST","url":"test()"});
    }
    
    return array;
}

var _outputTarget;
function setOutputTarget(status){
    _outputTarget = status;
}
function outputTarget(){
    // ウンど～を外部へ開くか toolArea へ開くかを返す
    return (_outputTarget * 1 > 0) ? "_blank" : "tools";
}

var _clickedElement;
function setClickedElement(elm){
    // ICON ギャラリーの中のクリックされたアイコンを記憶
    _clickedElement = elm;
}
function clickedElement(){
    return _clickedElement;
}
function kickMenuCell(url, inWindow, cliickedElement){
    // ToolMenu のクリックによりアクションを起動
    //console.log("kickMenuCell", url, inWindow, cliickedElement); //##
    
    setOutputTarget(inWindow);
    setClickedElement(cliickedElement);
    
    // 以前の Agent 情報とは別のカルテから呼ばれることがあるので Aget 情報を更新
    var agt = agent();
    agt.patientId = patientId();
    agt.patientKanjiName = patientKanjiName();
    agt.entryDate = currentDate();
    agt.hospitalId = hospitalId();
    agt.hospitalName = hospitalName();
    agt.owner = owner();
    setAgent(agt);
    //console.log("agt->", encodeObject(agt)); //##
    
    if (url && ((url.indexOf("()") > 0))){
        // 組込み関数を実行
        eval(url);
    } else {
        // URL を実行
        openTool(url);
    }
}

///// MENU STRUCTURE //////////////////////////////////
///////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////
///// CELL STRUCTURE //////////////////////////////////////////

function cellTemplate(){
    // CELL 構造のテンプレート：サーバにデータがない場合使われる default 値
    var obj = new Object();
    
    var num = 0;
    makeCell(obj, num++, _progress, "ProgressSection.pageHeader", "記録", 0, 6);
    //makeCell(obj, num++, _progress, "ProgressSection.note", "重要", 0, 6);
    makeCell(obj, num++, _progress, "ProgressSection.subject", "主訴", 1, 3, "");
    makeCell(obj, num++, _progress, "ProgressSection.object", "所見", 1);
    makeCell(obj, num++, _progress, "ProgressSection.assessment", "考察", 1);
    makeCell(obj, num++, _progress, "ProgressSection.plan", "予定", 1);
    makeCell(obj, num++, _progress, "FrontTable.plan", "計画", 1);
    makeCell(obj, num++, _progress, "ProgressSection.disease", "病名", 1, 6, "openDisease");
    makeCell(obj, num++, _progress, "ProgressSection.examination", "検査", 1, 6, "openForm");
    makeCell(obj, num++, _progress, "ProgressSection.prescription", "処方", 1, 6, "openPrescription");
    makeCell(obj, num++, _progress, "ProgressSection.treatment", "診療", 1, 6, "openVin");
    
    var num = 0;
    makeCell(obj, num++, _basic, "UniversalSection.link", "リンク", 0, 6);
    makeCell(obj, num++, _basic, "NameSection.patientKanjiName", "氏名", 0, 6);
    makeCell(obj, num++, _basic, "NameSection.patientKanaName", "ふりがな", 0, 6);
    makeCell(obj, num++, _basic, "NameSection.patientRomajiName", "ローマ字", 0, 6);
    makeCell(obj, num++, _basic, "PatientTable.anotherId", "anotherId", 0, 6);
    makeCell(obj, num++, _basic, "PatientTable.receiptId", "receiptId", 0, 6);
    makeCell(obj, num++, _basic, "PatientTable.birthDate", "生年月日", 0, 5);
    makeCell(obj, num++, _basic, "PatientTable.sex", "性別", 0, 4);
    makeCell(obj, num++, _basic, "UniversalSection.introduction", "紹介", 0);
    makeCell(obj, num++, _basic, "AddressSection.email", "E-mail", 0, 6);
    makeCell(obj, num++, _basic, "AddressSection.zip", "郵便番号", 0, 6);
    makeCell(obj, num++, _basic, "AddressSection.address", "住所", 0, 1);
    makeCell(obj, num++, _basic, "AddressSection.phone", "電話", 0, 6);
    makeCell(obj, num++, _basic, "BasicSection.job", "本人職業", 0);
    makeCell(obj, num++, _basic, "MalitalSection.type", "婚姻", 0, 4);
    makeCell(obj, num++, _basic, "MalitalSection.startDate", "結婚日", 0, 5);
    makeCell(obj, num++, _basic, "MalitalSection.partner", "配偶者", 0, 6);
    makeCell(obj, num++, _basic, "MalitalSection.partnersJob", "配偶職業", 0);
    makeCell(obj, num++, _basic, "MalitalSection.partnersOffice", "勤務先", 0);
    makeCell(obj, num++, _basic, "BasicSection.allergy", "アレルギー", 1, 4);
    
    if (_isPrivate){
        makeCell(obj, num++, _basic, "UniversalSection.menstrualCycle", "月経周期", 0); // local
        makeCell(obj, num++, _basic, "UniversalSection.menstruationBlood", "月経血", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.periodPains", "月経痛", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.firstMenstrualPeriod", "初潮", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.KA", "KA", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.SA", "SA", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.FG", "FG", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.NG", "NG", 0, 4); // local
        makeCell(obj, num++, _basic, "UniversalSection.pregnancyRecord", "妊娠履歴", 0); // local
        makeCell(obj, num++, _basic, "UniversalSection.menopause", "閉経", 0, 6); // local
    }
    
    makeCell(obj, num++, _basic, "BasicSection.bloodtype", "血液型", 0, 4);
    makeCell(obj, num++, _basic, "BasicSection.drinking", "飲酒", 0);
    makeCell(obj, num++, _basic, "BasicSection.smoking", "喫煙", 0);
    makeCell(obj, num++, _basic, "BasicSection.history", "既往歴", 0);
    makeCell(obj, num++, _basic, "AddressSection.memo", "メモ", 0);
    
    var num = 0;
    makeCell(obj, num++, _insurance, "HealthInsurance.shubetsu", "種別", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.kubun", "区分", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.paymentRatio", "負担割", 0);
    makeCell(obj, num++, _insurance,
             "HealthInsurance.kigou", "記号", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.bangou", "番号", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.ownerNumber", "保険番号", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.ownerName", "保険者名", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.ownerPhone", "電話", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.continuedDiseases", "継続療養", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.discountRatio", "割引％", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.startDate", "資格取得", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.expiredDate", "有効期限", 0);
    makeCell(obj, num++, _insurance, "HealthInsurance.confirmation", "証書確認", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.shubetsu", "公費種別", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.ownerName", "公費名称", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.ownerNumber", "負担番号", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.recipientNumber", "受給番号", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.paymentRatio", "負担率", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.payment", "負担額", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.startDate", "交付日", 0);
    makeCell(obj, num++, _insurance, "PublicInsurance.expiredDate", "有効期限", 0);
    
    return obj;
    
    function makeCell(obj, num, mode, tag, label, fieldType, menuType, editor){
        // CELL 属性を生成
        var cell = new Object();
        // owner
        cell.tag = tag;
        cell.buttonType = 0; // 0:メニュー複数選択 1:単一選択
        // menuType 0:rotary 1:address 2:calendar 3:group 4:panel 5:date 6:none
        cell.menuType = (menuType) ? menuType : 0;
        cell.menuAction = 1; //  0:文末へ追加 1:最終行へ追加 2:置換
        cell.hitAndRun = ""; // 0:表示 1:非表示
        cell.fieldType = fieldType; // 0:field 1:text
        cell.direction = "CELL"; // old NOA 型式との識別用
        cell.disabled = (tag == "PatientTable.birthDate") ? 1 : 0;
        cell.editor = (editor) ? editor : "";
        cell.id = mode;
        cell.script = "";
        cell.bgcolor = "";
        cell.label = label;
        cell.numbering = num; // groupMenu の場合、プロブレム番号自動付加を示す
        cell.study = 0;
        cell.fontSize = "";
        cell.fontFamily = "";
        cell.color = "";
        
        obj[tag] = cell;
    }
}

///// CELL STRUCTURE //////////////////////////////////////////
///////////////////////////////////////////////////////////////

function openNewNOA(pid){
    // 新しいカルテを開く
    var url = "/NOA/top.php";
    if (pid) url += "?patientId=" + pid;
    
    window.open(url, "_blank");
}

var _geOpen; // _groupEditorOpen 名称は cellEditor とカブる
function setGroupEditorOpen(status){
    //showToolMessage("setGroupEditorOpen-> "+status);
    _geOpen = status;
}
function groupEditorOpen(){
    // グループ・メニューのサブ・エディターが開いていれば true を返す
    return _geOpen;
}

var _cellArray;
var _layoutObj;
function setLayoutObj(records){
    // NOA のレイアウトを記憶
    //_debug("=== setLayoutObj->"+records+"->"+records.length); //##
    
    var parray = new Array();
    var barray = new Array();
    var harray = new Array();
    _cellArray = new Array();
    
    if (records.length > 0){
        for (num in records){
            var cell = records[num];
            //_debug(cell.tag+"->"+cell.label+"->"+cell.id); //##
            
            switch(cell.id){
                case 'P': parray.push(cell); break;
                case 'B': barray.push(cell); break;
                case 'I': harray.push(cell); break;
            }
            _cellArray.push(cell);
        }
    } else { // DB にレイアウトが記憶されていない場合はテンプレートを使う
        var obj = cellTemplate();
        for (tag in obj){
            var cell = obj[tag];
            //_debug(cell.tag+"->"+cell.label+"->"+cell.id); //##
            
            switch(cell.id){
                case 'P': parray.push(cell); break;
                case 'B': barray.push(cell); break;
                case 'I': harray.push(cell); break;
            }
            _cellArray.push(cell);
        }
    }
    
    _layoutObj = new Object();
    _layoutObj[_progress_] = parray;
    _layoutObj[_basic_] = barray;
    _layoutObj[_ins_] = harray;
}
function layoutForMode(mode){
    // _progress_ _basic_ _ins_ 毎の CELL レイアウトの配列
    if (mode > _ins_){
        return null;
    } else {
        var lo = _layoutObj;
        //alert("layoutForMode->"+mode+"->"+lo); //##
        //alert("lo->"+lo[mode]); //##
        
        if (lo && lo[mode] && Object.keys(lo[mode]).length){
            return _layoutObj[mode];
        } else { // DB にレイアウトが記憶されていない場合はテンプレートを使う
            var parray = new Array();
            var barray = new Array();
            var harray = new Array();
            _cellArray = new Array();
            var obj = cellTemplate();
            for (tag in obj){
                var cell = obj[tag];
                switch(cell.id){
                    case 'P': parray.push(cell); break;
                    case 'B': barray.push(cell); break;
                    case 'I': harray.push(cell); break;
                }
                _cellArray.push(cell);
            }
            //alert("layoutForMode ->"+encodeObject(_layoutObj)); //##
            
            // CELL 属性を保存するとレイアウトの一部だけが DB に保存されるため、
            // レイアウト・テンプレートが読み込まれずページ表示が不完全になる
            // これを防ぐためレイアウト・テンプレートを使ったらそれを DB に記憶
            // ### それ以前のレイアウトやCELL属性の編集結果は上書きされてしまうが
            // ### NOA インストール初期だけに発生する現象なので問題は少ないはず
            if (confirm("レイアウトがサーバに保存されていません。default レイアウトをサーバに保存しますか？")){
                var args = new Object();
                args["owner"] = owner();
                args["value"] = encodeObject(_cellArray);
                args["installMode"] = "1"; // 既存データがあれば上書きしない
                
                NRPost("PUT_LAYOUT", args); // NRNoAction
            }

            _layoutObj = new Object();
            _layoutObj[_progress_] = parray;
            _layoutObj[_basic_] = barray;
            _layoutObj[_ins_] = harray;
            
            return _layoutObj[mode];
        }
    }
}
function cellArray(){
    // CELL の配列を返す
    /* CELL 構造
     tag(ProgressSection.subject)buttonType(1)menuType(3)menuAction(1)hitAndRun(0)fieldType(0)disabled(0)editor()id(P)script()bgcolor()label(主訴)numbering(0)study(1)fontSize(0)fontFamily()color()
     */
    return _cellArray;
}
function cellForTag(tag){
    // tag に対応する CELL を返す
    for (num in _cellArray){
        var cell = _cellArray[num];
        if (cell.tag == tag) return cell;
    }
    return null;
}
function progressTagArray(){
    // ProgressSection の TAG の配列を返す
    var array = new Array();
    for (num in _cellArray){
        var cell = _cellArray[num];
        if (cell.tag.indexOf("ProgressSection") < 0) continue;
        
        array.push(cell.tag);
    }
    return array;
}
function labelForTag(tag){
    // tag に対応する label を返す
    for (num in _cellArray){
        var rec = _cellArray[num];
        if (rec.tag == tag) return rec.label;
    }
    return "";
}
function tagForLabel(label){
    // label に対応する tag を返す
    for (num in _cellArray){
        var rec = _cellArray[num];
        if (rec.label == label) return rec.tag;
    }
    return "";
}

function labelTagValue(){
    // NOA の全 CELL の tag を返す
    
    // NOA の全 CELL のレイアウトを得る：全ラベル名を得るため
    var array = layoutForMode(_progress_);
    array = array.concat(layoutForMode(_basic_));
    array = array.concat(layoutForMode(_ins_));

    var items = new Array();
    for (num in array){
        var rec = array[num];
        
        if (rec) items[rec.label] = rec.tag;
    }
    
    return items;
}

function jumpLabel(script){
    // ジャンプ先のラベル名を返す
    if (!script) return "";
    
    var array = script.split(" ");        
    return labelForTag(array[0]);
}
function jumpTag(script){
    // ジャンプ先のタグを返す
    if (!script) return "";
    
    var array = script.split(" ");        
    return array[0];
}
function jumpCondition(script){
    // ジャンプ条件を返す
    if (!script) return "";
    
    var pos = script.indexOf(" ");
    return (pos >= 0) ? script.substr(pos + 1) : "";
}

function nextTag(tag){
    // NOA 本文内における tag の次の tag を返す：basic ページでは basicPage.js で定義
    //console.log("--　nextTag", tag); //##
    var cell = cellForTag(tag);
    if (cell.script){
        // script が設定されている場合は script に従いジャンプ
        var condition = parent().jumpCondition(cell.script);
        var jumpTag = parent().jumpTag(cell.script);
        
        if (condition){
            // 条件に一致したら jumpTag を返す
            var status = eval('(' + condition + ')');
            
            if (status) return jumpTag;
        } else {
            // 無条件で jumpTag を返す
            return jumpTag;
        }
    }
    
    var found = false;
    var array = layoutForMode(_progress_);
    for (num in array){
        var layoutObj = array[num];
        if (layoutObj.hitAndRun * 1) continue; // 非表示セルをスキップ
        
        if (found)
            return layoutObj.tag;
        if (layoutObj.tag == tag) found = true;
    }
    return null;
}

var _toolMenus;
var _isTemplateMenu;
function isTemplateMenu(){
    // TOOL MENU が menuTemplate() から生成されている
    return _isTemplateMenu;
}
function setToolMenus(array){
    // array = 0(window(0)menuType(0)fix(0)subTitle(0)disabled(0)editor(カルテの参照履歴)id(0)url(showAccessLog^[^^]^)label(履歴)icon())1(window(0)menuType(0)fix(0)subTitle(0)disabled(0)editor(受診待ちリスト)id(1)url(showWaitingList^[^^]^)label(待受)icon())
    //alert("setToolMenus->"+encodeObject(array)); //##
    if (array.length){
        _toolMenus = array;
        _isTemplateMenu = false;
    } else {
        _toolMenus = menuTemplate();
        _isTemplateMenu = true;
    }
}
function toolMenus(){
    // [{"id":"1","label":"検索",,,},,,] 形式のメニュー構造を保持
    for (num in _toolMenus){
        var cell = _toolMenus[num];
        
        // menuTemplate() には id がないので num を id として cell に記憶
        // 新たに menuCell が追加される場合は最大 id が振られる
        cell.id = num;
    }
    
    return _toolMenus;
}

//////////////////////////////////////////
///// TEXT SEARCH ////////////////////////

var _keyArray;
var _keyPos;

function jumpToKey(keyPos){
    // テキスト中の次のキーへカーソル・ジャンプ
    if (_keyPos * 1 > (keyPos * 1 + 1)){
        var fieldId = _keyArray[(keyPos * 1 + 1)]; // 次のフィールド
        var elm = document.getElementById(fieldId);
        
        // 次のヒット・ページの先頭にジャンプ
        var pos = getPosition(elm.parentNode.parentNode);
        window.scroll(0, pos.y);
    } else {
        window.scroll(0, 0);
    }
}
function searchRecord(key){
    // 全レコードの ProgressSection 中を検索
    _debug("== searchRecord->"+key); //##
    
    eraseMarks(); // 以前の検索でつけたマークをすべて削除
    
    _keyPos = 0;
    _keyArray = new Array();
    var count = 0;
    var obj = progressObj();
    for (entryDate in obj){
        // rec は ProgressSection と NameSection のデータを保有
        _debug("entryDate->"+entryDate); //##
        
        var rec = obj[entryDate];
        var array = layoutForMode(_progress_);
        for (num in array){
            var layoutObj = array[num];
            //_debug("layoutObj->"+encodeObject(layoutObj)); //##
            if (layoutObj.hitAndRun * 1) continue; // 非表示セルをスキップ
            
            // CELL の VALUE を表示するエリア elm を取得
            var tag = layoutObj.tag;
            var cid = entryDate + "." + tag + ".val";
            var elm = document.getElementById(cid);
            if (!elm) continue; // 値がないため生成されていないセルをスキップ
            
            // vALUE
            var val = elm.innerText;
            
            _debug("-- "+cid+"->"+val); //##
            var ary = val.split(key);
            if (ary.length > 1){
                // 検索キーと一致する文言があれば VALUE 中に action を埋め込む
                var st = '<span style="background-color:#ff0">';
                st += key + '</span>';
                elm.innerHTML = ary.join(st);
                elm.setAttribute("onclick", "jumpToKey('" + _keyPos + "')");
                
                // key にマッチした element の id を記憶
                _keyArray[_keyPos++] = cid;
                
                // key に一致した回数をカウント
                count += (ary.length - 1);
            }
        }
    }
    return count;
    
    
    function eraseMarks(){
        // 以前の検索でつけたマークをすべて削除
        for (num in _keyArray){
            var cid = _keyArray[num];
            var elm = document.getElementById(cid);
            if (elm){
                var ary = cid.split(".");
                var dateTime = ary[0];
                var tag = ary[1] + "." + ary[2]; // "talbe.field" 形式にする
                var obj = valueForTag(tag, dateTime);
                if (!obj) continue;
                
                elm.innerHTML = htmlForValue(obj.val);
                elm.setAttribute("class","cellValue");
                // 過去データなら背景を過去色にする：淡青色
                if (obj && obj.isPast) elm.setAttribute("class", "pastCellValue");
            }
        }
    }
}

/////////////////////////////////////////////////////////////////
///// neuron の wrapper /////////////////////////////////////////

function get_address(owner, tag, receiver, debug){
	// 住所メニューを読み込む：FRONT の viewer.js でも使う
    var args = new Object();
    args["owner"] = owner;
    args["tag"] = tag;
    args["isAddressMenu"] = 1;
    NRCall("GET_MENU", args, receiver, debug);
}

function get_patients(searchKey, receiver, debug){
	// serchKey に一致する患者リストを返す
    //alert("get_patients->"+searchKey); //##
    
	// 検索キーにマッチしたカルテのリストを表示
    // ### FRONT からも利用される
    var ch = searchKey.charAt(0);
    if (('0' <= ch) && (ch <='9'))
        var mode = "id";
    else if (('a' <= ch) && (ch <='z'))
        var mode = "roma";
    else if ('.' == ch){
        var mode = "anotherId";
        searchKey = searchKey.substr(1);
    } else
        var mode = "kanji";

    var args = new Object();
    args["key"] = mode;
    args["val"] = searchKey;
    
    NRCall("GET_PATIENTS", args, receiver, debug);
}

function get_pages(owner, patientId, receiver, debug){
    // patientId のカルテの全情報をサーバへリクエスト
    var args = new Object();
    args["owner"] = owner;
    args["hospitalId"] = hospitalId();
    args["patientId"] = patientId;
    NRCall("GET_PAGES", args, receiver, debug);
}
function get_page(owner, patientId, dateTime, receiver, debug){
    // patientId, dateTime のカルテ情報をサーバへリクエスト
    var args = new Object();
    args["owner"] = owner;
    args["patientId"] = patientId;
    args["dateTime"] = dateTime;
    NRCall("GET_PAGE", args, receiver, debug);
}

function put_page(owner,hid,patientId,currentDate,timeLimit,container,receiver, debug){
    var args = new Object();
    args["owner"] = owner;
    args["hospitalId"] = hid;
    args["patientId"] = patientId;
    args["entryDate"] = currentDate;
    args["timeLimit"] = timeLimit;
    for (tag in container){
        // '#' が入るので encodeSTRING() 処理が必要
        args[tag] = encodeSTRING(container[tag]);
    }
    
    //console.log("put_page: hospitalId", hospitalId()); //##
    //console.log("put_page", encodeObject(args)); //##
    
    NRCall("PUT_PAGE", args, receiver, debug);
}

function get_menu(owner, tag, receiver, debug){
	// ### groupMenu type の場合はサーバで判断し専用メニューを読み込み
    var args = new Object();
    args["owner"] = owner;
    args["tag"] = tag;
    NRCall("GET_MENU", args, receiver, debug);
}

function put_menu(owner, tag, item, freq, value, receiver, debug){
	if (!owner || owner.length == 0)
		alert("put_menu: owner *** is null");
	
    var args = new Object();
    args["owner"] = owner;
    args["tag"] = tag;
    args["menuItem"] = encodeSTRING(item);
    args["freq"] = freq;
    args["val"] = encodeSTRING(value);
    
    NRCall("PUT_MENU", args, receiver, debug);
}
function remove_menu(owner, tag, item, template, receiver, debug){
    var args = new Object();
    args["owner"] = owner;
    args["tag"] = tag;
    args["menuItem"] = encodeSTRING(item);
    args["template"] = template;
    
    NRCall("REMOVE_MENU", args, receiver, debug);
}

function put_group_menu(owner, item, freq, subItems, receiver, debug){
    var args = subItems;
    args["owner"] = owner;
    args["menuItem"] = encodeSTRING(item);
    args["freq"] = freq;
    console.log("put_group_menu", encodeObject(args)); //##
    
    NRCall("PUT_GROUP_MENU", args, receiver, debug);
}

function put_archive(owner, patientId, receiver, debug){
    // owner, patientId のルテをアーカイブし保存
    var args = new Object();
    args["owner"] = owner;
    args["patientId"] = patientId;
    
    NRCall("PUT_ARCHIVE", args, receiver, debug);
}

function merge_archive(owner, pid, tag, entryDate, updateTime, val, receiver){
    _debug("== merge_archive"); //##
    
    var args = new Object();
    args["owner"] = owner;
    args["patientId"] = pid;
    args["tag"] = tag;
    args["entryDate"] = entryDate;
    args["updateTime"] = updateTime;
    args["val"] = val;
    
    _debug("pid->"+pid+" tag->"+tag+" date->"+entryDate+"update->"+updateTime); //##
    _debug("owner->"+owner+" val->"+val); //##

    NRCall("MERGE_ARCHIVE", args, receiver);
}

function kickedWindow(answer){
    // kick_window() のレシーバー
    //console.log("kickedWindow", answer); //##
    
    var obj = JSON.parse(answer);
    
    //console.log("obj", obj); //##
    
    var win = window.open(obj.url, obj.arguments);
    win.focus();
}
function kick_window(url, parameter, receiver, debug){
    // 外部ウインドウを表示
    var args = new Object();
    args["url"] = encodeSTRING(url);
    args["arguments"] = parameter;

    //console.log("kick_window", args, debug); //##

    NRCall("KICK_WINDOW", args, receiver, debug);
}

function remove_archive_file(owner, folder, filename, receiver, isDebugMode){
    var args = new Object();
    args["owner"] = owner;
    args["folder"] = folder;
    args["filename"] = filename;
    
    NRCall("REMOVE_ARCHIVE_FILE", args, receiver, isDebugMode);
}

/////////////////////////////////////////////////////////////////
///// 施設情報を記憶するオブジェクト /////////////////////////////////

function putUserInfo(owner, obj, receiver, isDebugMode){
    // ユーザ情報をサーバに記憶させる
    // obj = userCode(all)sectionCode(23)groupCode(doctor)loginName(ohashi)kanjiName(大橋 克洋)identifier(c44f9b64052f12c4e9229fd899c7113b)hospitalId(1304000000077)memo()entryDate(2010-04-29 15:49:06)updateTime(2012-10-10 15:53:22)owner(ohashi)rowid(38)hospitalTable(大橋医院(1304000000077)SEA SIDE CLINIC(03234200))
    
    //console.log("putUserInfo", encodeObject(obj)); //##

    NRCall("PUT_USER_INFO", obj, receiver, isDebugMode);
}
function getUserInfo(userName, receiver, isDebugMode){
    // ユーザ情報をサーバへリクエスト
    //console.log("getUserInfo", userName); //##

    var args = new Object();
    args['owner'] = userName;
    NRCall("GET_USER_INFO", args, receiver, isDebugMode);
}

var _hospitalObj;
function setHospitalObj(obj){
    //console.log("setHospitalObj", encodeObject(obj)); //##
    _hospitalObj = obj;
}
function hospitalObj(){
    /*
     userCode(all)sectionCode(23)groupCode(doctor)loginName(ohashi)kanjiName(大橋 克洋)identifier(c44f9b64052f12c4e9229fd899c7113b)hospitalId(1304000000077)memo()entryDate(2010-04-29 15:49:06)updateTime(2013-06-23 19:04:20)owner(ohashi)rowid(38)hospitalTable(大橋医院(1304000000077)SEA SIDE CLINIC(03234200))hospitalName(大橋医院)
     */
    return _hospitalObj;
}
function hospitalName(){
    return (hospitalObj()) ? hospitalObj().hospitalName : null;
}
function hospitalId(){
    return (hospitalObj()) ? hospitalObj().hospitalId : null;
}
function userCode(){
    return (hospitalObj()) ? hospitalObj().userCode : null;
}
function isSuperUser(){
    // 管理者権限か否か
    var hospo = parent().hospitalObj(); // toolArea から呼ばれることもある
    if (!hospo) return false;
    
    return (hospo.userCode == "all") ? true : false;
}
function isReadable(){
    // カルテの参照が可能か否か
    var hospo = parent().hospitalObj(); // toolArea から呼ばれることもある
    
    if (!hospo) return 0; // 参照不可
    if (hospo.userCode == "all") return 2; // すべて参照可
    if (hospo.userCode == "write") return 2; 
    if (hospo.userCode == "read") return 2;
    if (hospo.userCode == "write 2") return 2;
    if (hospo.userCode == "read 1") return 1; // 自分のもののみ参照可
    if (hospo.userCode == "write 1") return 1; 
    return 0;
}
function isWritable(){
    // カルテの編集が可能か否か
    var hospo = parent().hospitalObj(); // toolArea から呼ばれることもある
    
    if (!hospo) return 0;
    if (hospo.userCode == "all") return 1;
    if (hospo.userCode == "write") return 1;
    if (hospo.userCode == "write 1") return 1;
    if (hospo.userCode == "write 2") return 2;
    return 0;
}
function isLimited(){
    // 参照や編集が自分のデータに限定されているか否か
    var hospo = parent().hospitalObj(); // toolArea から呼ばれることもある
    
    if (!hospo) return 0;
    if (hospo.userCode == "read 1") return 1;
    if (hospo.userCode == "write 1") return 1;
    if (hospo.userCode == "write 2") return 2;
    return 0;
}
function owner(){
    return (hospitalObj()) ? hospitalObj().loginName : null;
}
function userName(){
    // 旧バージョンの userKanjiName に相当：漢字氏名を返す
    return (hospitalObj()) ? hospitalObj().kanjiName : null;
}

var _coworkers;
function setCoworkers(array){
    //console.log("setCoworkers", encodeObject(array)); //##

    _coworkers = array;
}
function coworkers(){
    // 同じ施設に所属するユーザ名の配列
    return _coworkers;
}

var _hospitalInfo;
function setHospitalInfo(obj){
    // obj = 施設名(大橋医院)まるめ係数(5)診療曜日(,月,火,水,木,金,土)保険医療機関コード(0926253)自費係数(20)住所(142-0063 東京都品川区荏原4-4-2)訂正可能時間(24)施設(診療所)診療時間(,,0000,0600,深夜^,,2200,2400,深夜^,日,0000,2400,休日^,,0845,1215,時間内^,,1445,1715,時間内^,,0900,1200,時間内^)電話番号(03-3784-3101)明細書発行体制等加算(yes)都道府県番号(13)点数表番号(1)FAX(03-3784-5215)
    //alert("setHospitalInfo->"+encodeObject(obj)); //##
    
    _hospitalInfo = new Object();
    _hospitalInfo.round = obj['まるめ係数'] * 1;
    _hospitalInfo.ownRate = obj['自費係数'] * 1;
    _hospitalInfo.hospitalAddress = obj['住所'];
    _hospitalInfo.hospitalName = obj['施設名'];
    _hospitalInfo.hospitalPhone = obj['電話番号'];
    _hospitalInfo.hospitalCode = obj['保険医療機関コード'];
    _hospitalInfo.consultationHour = obj['診療時間'];
    _hospitalInfo.divisionNumber = obj['都道府県番号'];
    _hospitalInfo.tableNumber = obj['点数表番号'];
    
    if (obj && obj['訂正可能時間'])
        setTimeLimit(obj['訂正可能時間']);
    else
        setTimeLimit("24 * 60 * 60"); // 単位は秒数
    
    function setTimeLimit(val){
        // 編集可能時間を記憶: "3" or ("3:00" or "3:00:00") 形式
        var array = val.split(":");
        switch (array.length){
            case 1: var sec = array[0] * 3600; break;
            case 2: var sec = array[0] * 3600 + array[1] * 60; break;
            default: var sec = array[0] * 3600 + array[1] * 60 + array[2] * 1; break;
        }
        _hospitalInfo.timeLimit = "" + sec;
    }
}
function hospitalInfo(){
    return _hospitalInfo;
}
function timeLimit(){
    return _hospitalInfo.timeLimit;
}
function hospitalInfoForKey(key){
    // prescription から利用される
    alert("hospitalInfoForKey->"+key+"->"+_hospitalInfo[key]); //##
    
    return _hospitalInfo[key];
}

///// 施設情報を記憶するオブジェクト /////////////////////////////////
/////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
///// noaObj 現在開いているカルテ属性を記憶するオブジェクト /////////////

var _noaObj; 
function noaObj(){
    // 現在開かれているカルテの諸属性を記憶するオブジェクト
    if (!_noaObj) _noaObj = new Object(); // noaObj を生成
    return _noaObj;
}
function setPatientId(id){
    noaObj().patientId = id;
}
function patientId(){
    return noaObj().patientId;
}
function setPatientName(name){
    noaObj().patientName = name;
}
function patientName(){
    return noaObj().patientName;
}

function setCurrentDate(dateTime){
    noaObj().date = dateTime;
}
function currentDate(){
   return noaObj().date;
}

var _patientTable;
function setPatientTable(obj){
    // ProgressSection のデータを記憶
    // _patientTable = date(2013-06-24 11:19:31)anotherId()receiptId()sex(女)unpaid(2013-06-24 11:20:24,0)birthDate(1941-06-11)lastVisitDate(2013-06-24 11:30:56)
    //console.log("setPatientTable->", encodeObject(obj)); //##
    _patientTable = obj;
    if (_patientTable.receiptId && (_patientTable.receiptId.length > 0))
        noaObj().receiptId = _patientTable.receiptId;
    else
        noaObj().receiptId = patientId();
    noaObj().anotherId = _patientTable.anotherId;
    noaObj().sex = _patientTable.sex;
    noaObj().birthDate = _patientTable.birthDate;
    noaObj().lastVisitDate = _patientTable.lastVisitDate;
    noaObj().unpaid = _patientTable.unpaid;
}
function receiptId(){
    // レセプトID が設定されていなければ patientId() を返す
    return noaObj().receiptId;
}
function anotherId(){
    return noaObj().anotherId;
}
function sex(){
    return noaObj().sex;
}
function birthDate(){
    return noaObj().birthDate;
}
function lastVisitDate(){
    return noaObj().lastVisitDate;
}
function unpaid(){
    return noaObj().unpaid;
}

function setCurrentTag(tag){
    noaObj().currentTag = tag;
    noaObj().currentLabel = labelForTag(tag);
}
function currentTag(){
    // 編集状態になっている CELL の tag を返す
    return noaObj().currentTag;
}
function currentLabel(){
    // 編集状態になっている CELL の ラベル名 を返す
    return noaObj().currentLabel;
}

///// noaObj 現在開いているカルテ属性を記憶するオブジェクト /////////////
/////////////////////////////////////////////////////////////////


var _frontObj;
function setFrontObj(obj){
    // FrontTable のデータを記憶
    // obj = "plan(flag(true)val(治療)update(2013-08-12 17:56:20))"
    //alert("setFrontObj->"+encodeObject(obj)); //##
    _frontObj = obj;
}
function frontTableObj(){
    return _frontObj;
}

var _progressObj;
function setProgressObj(obj){
    // ProgressSection のデータを記憶
    //_progressObj = {entryDate:{"entryDate:"",suject:"",object:"",,,,}}
    //console.log("setProgressObj->", encodeObject(obj)); //##
    
    _progressObj = obj;
}
function progressObj(){
    // calendar.js で使われる
    return _progressObj;
}
var _hasAnotherUser;
function history(){
    // 受診日の配列を返す
    _hasAnotherUser = new Object();
    var array = new Array();
    for (dateTime in _progressObj){
        if (dateTime == "0000-00-00 00:00:00") continue;

        // レコードの owner により history に追加するかどうか判断
        var rec = _progressObj[dateTime];
        var own = rec.owner.val;
        if (isLimited() == 1){ // 自分の書いたデータのみ読み書き可
            if (own != owner()){
                _hasAnotherUser[own] = dateTime;
                continue; // 権限が自分に限定され かつ自分のデータではなかった
            }
            /*
        } else if (hasMember(coworkers(), own) == 0){
            _hasAnotherUser[own] = dateTime;
            continue; // 同じ施設のユーザのデータではなかった
             */
        }
        
        //console.log("history->", dateTime); //##
        
        array.push(dateTime);
    }
    return array;
}
function hasAnotherUser(){
    // カルテ内の参照権限外のページのユーザ・ログイン名と受診日のオブジェクト
    // {"ohashi":"2015-05-04 10:36:00","akitsuki":"2000-02-05 09:38:00"}
    return _hasAnotherUser;
}

var _nameObj;
function setNameObj(obj){
    // currentDate における NameSection のデータを記憶
    // _nameObj = 1989-03-20 00:00:00(patientKanjiName(里野 春)patientKanaName(さとの はる)patientRomajiName(satono haru))
    //console.log("setNameObj->"+encodeObject(obj)); //##
    _nameObj = obj;
}
function patientKanjiName(){
    var obj = valueForTag("ProgressSection.patientKanjiName");
    return (obj && obj.value) ? obj.value : "";
/*
    if (_nameObj){
        var obj = _nameObj[currentDate()];
        return (obj) ? obj.patientKanjiName : "";
    } else {
        return "";
    }
 */
}

function patientAge(dateTime){
    // dateTime の年令を返す
    if (!dateTime) dateTime = currentDate();
    
    var obj = valueForTag("PatientTable.birthDate"); // 1942-03-23 型式
    var birthDate = obj.value;
    return　ageAtDate(birthDate, dateTime);
}

var _addressObj;
function setAddressObj(obj){
    //_debug("setAddressObj ->"+encodeObject(obj)); //##
    _addressObj = obj;
}

var _basicObj;
function setBasicObj(obj){
    _basicObj = obj;
}

var _maritalObj;
function setMaritalObj(obj){
    _maritalObj = obj;
}

var _healthInsObj;
function setHealthInsuranceObj(obj){
    // date(2011-12-03 10:37:29)ownerName(日立物流健保組合)ownerNumber(06135024)ownerPhone(03-5634-0326)shubetsu(組合)kubun(本人)kigou(335)bangou(788421)continuedDiseases()paymentRatio(3)discountRatio(0)confirmation(2011-12-03 10:49:31 ohashi)startDate(2007-04-01)expiredDate(0000-00-00)
    _healthInsObj = obj;
}

var _publicInsObj;
function setPublicInsuranceObj(obj){
    //_debug("setPublicInsuranceObj ->"+encodeObject(obj)); //##
    _publicInsObj = obj;
}

var _univObj;
function setUniversalObj(obj){
    // menstrualCycle(date(28-30)value(28-30)type(0))menstruationBlood(date(中)value(中)type(0))periodPains(date(中)value(中)type(0))firstMenstrualPeriod(date(小学校)value(小学校)type(0))KA(date(0)value(0)type(0))SA(date(1)value(1)type(0))FG(date(0)value(0)type(0))NG(date(2)value(2)type(0))pregnancyRecord(date(h 16.9 NG 神奈川県立足柄病院 3335 gr female h 20.12 NG 神奈川県立足柄病院 3300 gr male)value(h 16.9 NG 神奈川県立足柄病院 3335 gr female h 20.12 NG 神奈川県立足柄病院 3300 gr male)type(0))
    //_debug("setUniversalObj ->"+encodeObject(obj)); //##
    _univObj = obj;
}

var _postItObjects;
function setPostItObjects(array){
    // PostIt オブジェクトの配列を記憶
    // array = '(2013-08-06 07:06:00(value(test)type(0)updateTime(2013-08-07 15:32:05))date(value(..)type(..)updateTime(..)))' 形式
    //alert("setPostItObjects->"+encodeObject(array)); //##
    
    _postItObjects = array;
}
function postItForDate(dateTime){
    // dateTime に相当する postIt オブジェクトを返す
    //alert("postItForDate->"+dateTime); //##
    for (dtime in _postItObjects){
        //alert("dtime->"+dtime); //##
        if (dtime <= dateTime) return _postItObjects[dtime];
    }
    return null;
}

function setValueForTag(tag, dateTime, value){
    // tag, dateTime に対応する value を記憶
    if (!dateTime) dateTime = currentDate();
    //alert("setValueForTag->"+tag+"->"+dateTime+"->"+value); //##
    
    var ary = tag.split(".");
    var table = ary[0];
    var field = (ary.length > 1) ? ary[1] : "";
    if (table == "PatientTable"){
        _patientTable[field] = value;
        _patientTable.date = dateTime;
    } else if (table == "NameSection"){
        // _nameObj は存在しても _nameObj[field] が存在しないことがある
        if (!_nameObj[field]) _nameObj[field] = new Array();
        _nameObj[field]['val'] = value;
        _nameObj.update = dateTime;
    } else if (table == "AddressSection"){
        if (!_addressObj[field]) _addressObj[field] = new Array();
        _addressObj[field]['val'] = value;
        _addressObj.update = dateTime;
    } else if (table == "BasicSection"){
        if (!_basicObj[field]) _basicObj[field] = new Array();
        _basicObj[field]['val'] = value;
    } else if (table == "MalitalSection"){
        if (!_maritalObj[field]) _maritalObj[field] = new Array();
        _maritalObj[field]['val'] = value;
        _maritalObj.update = dateTime;
    } else if (table == "HealthInsurance"){
        if (!_healthInsObj[field]) _healthInsObj[field] = new Array();
        _healthInsObj[field]['val'] = value;
        _healthInsObj.update = dateTime;
    } else if (table == "PublicInsurance"){
        if (!_publicInsObj[field]) _publicInsObj[field] = new Array();
        _publicInsObj[field]['val'] = value;
        _publicInsObj.update = dateTime;
    } else if (table == "UniversalSection"){
        if (!_univObj[field]) _univObj[field] = new Array();
        _univObj[field]['val'] = value;
        _univObj.update = dateTime;
    } else if (table == "FrontTable"){
        if (! _frontObj[dateTime])
            _frontObj[dateTime] = new Array(); // FrontTable では必要
        if (! _frontObj[dateTime][field])
            _frontObj[dateTime][field] = new Array();
        _frontObj[dateTime][field]['val'] = value;
        _frontObj[dateTime][field]['update'] = dateTime;
    } else if (table == "ProgressSection"){
        // obj[dateTime] は存在しても obj[dateTime][field] が存在しないことがある
        if (! _progressObj[dateTime][field])
            _progressObj[dateTime][field] = new Array();
        _progressObj[dateTime][field]['val'] = value;
        _progressObj[dateTime][field]['update'] = dateTime;
    }
}
function valueForTag(tag, dateTime){
    // tag に対応する当日の値の入ったオブジェクトを返す
    if (!dateTime) dateTime = currentDate();
    
    var ary = tag.split(".");
    var table = ary[0];
    var field = (ary.length > 1) ? ary[1] : "";
    if (table == "PatientTable"){
        // PatientTable のみは updateTime を持つ必要がないので構造が他と異なる
        var obj = new Object();
        obj.value = _patientTable[field];
        obj.isPast = false;
        return obj;
    } else if (table == "NameSection"){
        // NameSection データは変わること（改姓）があるので、日付ごとに持つ必要があり
        // サーバから ProgressSection データに埋め込んで返される
        // ProgressSection の場合は空データを過去へ遡る動作をこちら：クライアント側で行う
        var obj = new Object();
        obj.value = "";
        obj.isPast = true;
        for (date in _progressObj){
            if (date > dateTime) continue;
            
            var rec = _progressObj[date];
            for (fld in rec){
                if (fld == field){
                    var record = rec[fld];
                    var val = record['val'];
                    if (val && (val.length > 0)){
                        obj.value = val;
                        var update = record['update'];
                        obj.isPast = (isSameDate(update, dateTime)) ? false : true;
                        return obj;
                    }
                }
            }
        }
        return obj;
        /*
        var obj = new Object();
        if (_nameObj[field]){
            obj.value = _nameObj[field]['val'];
            var udate = _nameObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
        */
    } else if (table == "AddressSection"){
        var obj = new Object();
        if (_addressObj[field]){
            obj.value = _addressObj[field]['val'];
            var udate = _addressObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
    } else if (table == "BasicSection"){
        var obj = new Object();
        if (_basicObj[field]){
            obj.value = _basicObj[field]['val'];
            var udate = _basicObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
    } else if (table == "MalitalSection"){
        var obj = new Object();
        if (_maritalObj[field]){
            obj.value = _maritalObj[field]['val'];
            var udate = _maritalObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
    } else if (table == "HealthInsurance"){
        var obj = new Object();
        if (_healthInsObj[field]){
            obj.value = _healthInsObj[field]['val'];
            var udate = _healthInsObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
    } else if (table == "PublicInsurance"){
        var obj = new Object();
        if (_publicInsObj[field]){
            obj.value = _publicInsObj[field]['val'];
            var udate = _publicInsObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        }
        return obj;
    } else if (table == "UniversalSection"){
        //console.log("valueForTag: _univObj", _univObj.menstrualCycle); //##
        
        var obj = new Object();
        if (_univObj[field]){
            obj.value = _univObj[field]['val'];
            var udate = _univObj[field]['update'];
            obj.isPast = (isSameDate(udate, dateTime)) ? false : true;
        } else {
            obj.value = "";
            obj.isPast = true;
        }
        return obj;
    } else if (table == "FrontTable"){
        var obj = new Object();
        var hasCurrentDate = false;
        obj.value = "";
        obj.isPast = true;
        var pickuppedDate;
        for (date in _frontObj){
            if (date > dateTime) continue;
            
            var record = _frontObj[date][field];
            if (!record) continue;
            
            var val = record['val'];
            if (val && (val.length > 0)){
                // ピックアップ済みレコードの日付より前にアップデートされたレコードはスキップ
                if (record['update'] < pickuppedDate) continue;
                
                // 当日データが取得済みなら他日データはスキップ
                // 　この後で当日データが出てくる可能性あるため return しない
                // 　setValueForTag() で追加されたデータは sort されていない
                if (! isSameDate(date, dateTime) && hasCurrentDate)
                    continue;
                
                //alert(date+" / "+dateTime+" val("+val+")->"+val.length); //##
                
                pickuppedDate = date; // ここでの比較に利用
                obj.value = val;
                var update = record['update'];
                obj.isPast = (isSameDate(update, dateTime)) ? false : true;
                
                // 当日のデータを取得済みであることを記憶
                if (isSameDate(date, dateTime)) hasCurrentDate = true;
            }
        }
        return obj;
    } else if (table == "ProgressSection"){
        // ProgressSection の場合は空データを過去へ遡る動作をこちら：クライアント側で行う
        var obj = new Object();
        obj.value = "";
        obj.isPast = true;
        for (date in _progressObj){
            if (date > dateTime) continue;
            
            var rec = _progressObj[date];
            for (fld in rec){
                if (fld == field){
                    var record = rec[fld];
                    var val = record['val'];
                    if (val && (val.length > 0)){
                        obj.value = val;
                        var update = record['update'];
                        obj.isPast = (isSameDate(update, dateTime)) ? false : true;
                        return obj;
                    }
                }
            }
        }
        return obj;
    }
    
    return null;
}
function valueOfTag(tag){
    // tag に対応した CELL の value を返す
    var obj = valueForTag(tag);
    return obj.value;
}
function valueOfCell(){
    // 現在アクティブな CELL の value を返す
    return valueOfTag(currentTag());
}

function tagStrings(){
    // "tag:label,,," 型式のオブジェクトを返す
    var results = new Array();
    
    var array = layoutForMode(_basic_);
    for (num in array){
        var cell = array[num];
        results.push(cell.tag + ":" + cell.label);
    }
    
    var array = layoutForMode(_ins_);
    for (num in array){
        var cell = array[num];
        results.push(cell.tag + ":" + cell.label);
    }
    
    var array = layoutForMode(_progress_);
    for (num in array){
        var cell = array[num];
        results.push(cell.tag + ":" + cell.label);
    }
    
    //alert("tagStrings これで良いか？->"+results.join(",")); //##
    
    return results.join(",");
}

///////////////////////////////////////////////////////
///// 祝日リスト ////////////////////////////////////////

var _holidays;
function hasHolidays(year){
    // year のデータを有する _holidays があれば true を返す
    if (_holidays){
        for (num in _holidays){
            var rec = _holidays[num];
            return (rec.date.substr(0, 4) == year) ? true : false;
        }
    }
    return false;
}
function isHoliday(date){
    // "2014-05-01" 型式の date が祝日なら「祝日の名称」を返す
    var year = date.substr(0, 4);
    date = date.substr(0, 10); // 時刻は削除
    if (hasHolidays(year)){
        for (num in _holidays){
            var rec = _holidays[num];
            if (rec.date == date) return rec.title;
        }
    } else {
        getHolidays(year);
        // alert(year + " の祝日を読み込んでいます。しばらくしてから再行してください");
    }
    return null;
}
function todayIsHoliday(){
    // 本日が祝日なら true を返す：すでにページが開かれ当年の祝日リストが読み込まれていること
    var val = isHoliday(currentDate());
    
    return (val) ? true : false;
}
function holidays(){
    return _holidays;
}
function setHolidays(array){
    // クリスマスが祝日に入っているので１２月２５日を抜く
    _holidays = new Array();
    for (num in array){
        var rec = array[num];
        if (rec.date.indexOf("-12-25") > 0) continue;
        _holidays.push(rec);
    }
}
function getHolidays(year, receiver){
    // 祝日リストを取得：[{"date":"2014-03-21","title":"春分の日"},,] 型式
    // DB とのやりとりは noaWorker.js で設定
    var obj = new Object();
    obj['year'] = year;
    var noaString = encodeObject(obj);
    
    // 他のツールから呼ばれてもよいよう full path で呼ぶ
    var worker = new Worker(_PATH + 'holidayWorker.js');
    worker.postMessage(noaString); // ### worker に命令書 noaString を送る ###
    
    worker.onmessage = function (event) {
        // ### worker の処理したデータを受け取る ###
        var answer = event.data; // ### worker からの返答 ###

        var array = answer.split("<SEPARATOR>");
        if (array.length > 1){
            var array = JSON.parse(array[1]);
            
            // レスポンスが欲しい場合は receiver に alert などを設定
            if (receiver) receiver("祝日リストを読み込みました");
            
            setHolidays(array); // dataCenter.js
        }
    }
}

///////////////////////////////////////////////////////
///// 印刷 /////////////////////////////////////////////

function archived(answer){
    // カルテデータがアーカイブされた返事を受け取る
    hideMessage("_message");
    var msg = patientId()+" "+patientKanjiName()+" のカルテをバックアップしました";
    showFadeoutInfo("alertArea", msg, 1000);
    
    var printImage = 0;
    /*
    if (hasImage()){
        if (confirm("添付されている画像も印刷しますか？ （ Alpha channel を持たない画像は印刷できません。その場合はキャンセルを押せば画像なしで印刷されます）。")){
            printImage = 1;
        }
    }*/
    
    var safariStatus = (isSafari()) ? "1" : "0";
	var url = "./printLog.php?patientId="+patientId()
	+ "&entryDate="+currentDate()
	+ "&age="+patientAge(currentDate())
	+ "&owner="+owner()
	+ "&printImage="+printImage
	+ "&isSmallFont="+pagePrintFontSize()
	+ "&isHalfPage="+pagePrintWidth()
    + "&isSafari="+ safariStatus
	+ "&tags="+tagStrings();
    
    //console.log("url", url); //##
    
    if (slipWithChart() > 0){
        // カルテとともに印刷モードで、検査が実施されている場合slipカルテとともに伝票ラベル印刷
        var obj = valueForTag("ProgressSection.treatment");
        var ary = obj.value.split("検査[");
        var count = ary.length - 1; // 診療欄の検査行の数
        var countLimit = slipCount(); // 印刷枚数の上限
        console.log(countLimit, count); //##
        
        if (count > countLimit) count = countLimit; // 上限以内の枚数を印刷
        
        if (count > 0){
            // 診療欄に検査があれば、検査の数だけ検査伝票ラベル印刷を追加
            var slip = _slipForm(slipTemplate());
            slip = encodeSTRING(slip);
            url += "&slip="+slip;
            url += "&slipCount="+count;
        }
    }
    //console.log("url", url); //##
    
	var win = window.open(url, "NOA_PDF", "scrollbars=yes");
	win.document.title = "PRINT"; // window title に "PRINT" 表示
	win.focus();

    /*    
    function hasImage(){
        // このページが image 貼付されているかどうかを返す
        var obj = valueForTag("ProgressSection.object");
        var val = obj.value;
        
        return (val && (val.indexOf("<IMG:") >= 0)) ? true : false;
    }*/
}
function printPDF(answer){
	// カルテを印刷する
    if (answer){
        var obj = JSON.parse(answer);
        setSlipTemplate(obj);
    } else {
        // Ajax による値の取得をリカーシブルに実行
        NRCall("GET_SLIP_FORM", [], printPDF);
        return;
    }

    closeInfoTip(); // 印刷の場合消えないことがるので
	if (isSameDate(currentDate(), today()) == 0){
		if (!confirm(currentDate() + " は本日のページではありませんが印刷しますか"))
			return;
	}
    
    // このカルテの全データをアーカイブし保存するようサーバへ依頼
	put_archive(owner(), patientId(), archived);
}

/////////////////////////////////////////////////////////////////////////////
///// 伝票ラベル印刷 //////////////////////////////////////////////////////////

var _slipPrefernce;
function setSlipTemplate(obj){
    _slipPrefernce = obj;
}
function slipTemplate(){
    // 伝票フォーマットを返す
    var template = _slipPrefernce.value;
    template = convertSTRING(template, "<br>", "\n");
    return template;
}
function slipWithChart(){
    // 伝票ラベルをカルテとともに印刷するなら true
    return (_slipPrefernce.withChart && (_slipPrefernce.withChart * 1)) ? 1 : 0;
}
function slipCount(){
    // 伝票ラベルを印刷する枚数
    return (_slipPrefernce.count) ? _slipPrefernce.count * 1 : 0;
}

function _printSlip(){
    // 伝票を印刷
    var count = elmFor("labelCountPop").value * 1;
    if (count == 0){
        alert("印刷枚数が指定されていません");
        return;
    }
    
    var dateTime = parent().currentDate();
    if (isSameDate(dateTime, today()) == 0){
        if (!confirm(dateTime + " は本日のページではありませんが印刷しますか"))
            return;
    }
    
    // 伝票を印刷
    var buff = elmFor("previewText").innerHTML;
    buff = encodeSTRING(buff);
    var url = "../NOA/printSlip.php?count=" + count + "&slip=" + buff;
    var win = window.open(url, "NOA_SLIP", "scrollbars=yes");
    win.document.title = "SLIP"; // window title に "PRINT" 表示
    win.focus();
}
function _slipForm(template){
    // 伝票フォーマットを返す
    var buff = new String(template);
    var obj = decodeObject(template);
    for (label in obj){
        var tag = obj[label];
        if (tag == "patientId"){
            var value = parent().patientId();
        } else if (tag == "currentDate"){
            var value = parent().currentDate();
        } else if (tag == "age"){
            var value = age(parent().birthDate());
        } else {
            var rec = parent().valueForTag(tag); 
            var value = rec.value;
        }
        
        var key = "(" + tag + ")";
        buff = convertSTRING(buff, key, value);
    }
    buff = convertSTRING(buff, '\n', '<br>');
    
    return buff;
}
function _addLabelName(){
    // ラベル名を伝票フォーマットに追加
    var name = elmFor("labelNamePop").value;
    if (name == "カルテID")
        var tag = "patientId";
    else if (name == "受診年月日")
        var tag = "currentDate";
    else if (name == "年齢")
        var tag = "age";
    else
        var tag = labelTagValue()[name];
    var ta = elmFor("labelText");
    ta.value = ta.value + name + "：(" + tag + ")　";
}
function _previewSlipForm(){
    // 伝票イメージをプレビュー表示
    var div = elmFor("previewArea");
    div.innerHTML = "";

    var template = elmFor("labelText").value;
    template = convertSTRING(template, "<br>", "\n");
    var buff = _slipForm(template);
    if (trim(buff).length > 0){
        var dv = newDIV(div, "");
        dv.style.fontSize = "9pt";
        dv.style.backgroundColor = "#ffe";
        dv.style.padding = "10px";
        dv.style.borderRadius = "5px";
        dv.style.marginBottom = "10px";
        // プレビューを表示
        var pdv = newDIV(dv, "previewText");
        pdv.innerHTML = buff;
        // 印刷ボタン
        var pdv = newDIV(dv, "");
        pdv.style.textAlign = "right";
        var bt = newDIV(pdv, "/whiteButton");
        bt.innerHTML = "ラベルを印刷";
        bt.setAttribute("onclick", "_printSlip()");
    }
}
function _emptySlipForm(){
    // 入力欄を空にする
    elmFor("labelText").value = "";
    elmFor("previewArea").innerHTML = "";
}
function _savedSlipPrintForm(answer){
    closeFloatPanel();
}
function _saveSlipPrintForm(){
    // 伝票フォーマットをサーバへ保存
    var args = new Object();
    args["owner"] = owner();
    args["val"] = elmFor("labelText").value;
    args["withChart"] = (elmFor("printWithChartBox").checked) ? "1" : "";
    args["count"] = elmFor("labelCountPop").value;
    NRCall("PUT_SLIP_FORM", args, _savedSlipPrintForm);
}
function preferenceOfPrintLabel(answer){
    // 伝票ラベル印刷の初期設定
    if (answer){
        var obj = JSON.parse(answer);
        setSlipTemplate(obj);
    } else {
        // Ajax による値の取得をリカーシブルに実行
        NRCall("GET_SLIP_FORM", [], preferenceOfPrintLabel);
        return;
    }
    
    var w = 400;
    var x = 10; // 表示するx座標
    var y = 10; // 表示するy座標
    var title = "伝票印刷フォーマット設定パネル";
    var action = "openHelp('./printSlipHelp.html')";
    var panel = new FloatPanel("_floatPanel", x, y, w, title);
    if (!panel) return;
    var elm = panel.contents;
    elm.style.padding = "8px";
    
    // ラベル名を選択
    var menuItems = ["カルテID","受診年月日","年齢"];
    var obj = parent().labelTagValue(); // NOA の全 CELL の tag 
    for (label in obj){
        if (label.length > 0){
            menuItems.push(label);
        }
    }
    
    var div = newDIV(elm, "");
    div.style.fontSize = "9pt";
    var pu = newPopupMenu(div, "labelNamePop", menuItems, "");
    var sp = newSPAN(div, "");
    var bt = newDIV(sp, "/flatButton");
    bt.innerHTML = "をラベル項目として追加";
    bt.setAttribute("onclick", "_addLabelName()");
    var sp = newSPAN(div, "");
    sp.style.paddingLeft = "20px";
    var bt = newDIV(sp, "/flatButton");
    bt.innerHTML = "空欄";
    bt.setAttribute("onclick", "_emptySlipForm()");
    
    // ラベル・フォーマット設定フィールド
    var div = newDIV(elm, "");
    div.style.fontSize = "9pt";
    div.style.margin = "5px 0";
    var ta = newTEXTAREA(div, "labelText", 50, 10, slipTemplate());
    
    // プレビュー・エリア
    var div = newDIV(elm, "previewArea");
    
    // ラベル枚数ポップアップ・メニュー
    var div = newDIV(elm, "");
    div.style.fontSize = "9pt";
    var dv = newDIV(div, "/left-side");
    dv.style.paddingLeft = "0";
    dv.style.width = "220px";
    // 印刷枚数ポップアップ
    var menus = ["","1","2","3","4","5","6","7","8","9","10"]
    var pu = newPopupMenu(dv, "labelCountPop", menus, slipCount());
    var sp = newSPAN(dv, "");
    sp.innerHTML = " 枚印刷";
    sp.style.paddingRight = "5px";
    // カルテとともに印刷チェックボックス
    var sp = newSPAN(dv, "");
    var cb = newCHECKBOX(sp, "printWithChartBox", "カルテと同時印刷", slipWithChart());
    // 設定ボタン
    var dv = newDIV(div, "/right-side");
    dv.style.paddingRight= "0";
    var bt = newDIV(dv, "/flatButton");
    bt.innerHTML = "プレビュー";
    bt.setAttribute("onclick", "_previewSlipForm()");
    var div = newDIV(dv, "/fixButton");
    div.innerHTML = "設定";
    div.setAttribute("onclick", "_saveSlipPrintForm()");
}

///// 伝票ラベル印刷 //////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

var _dumpPath = "../Users/_noadump.txt";
function do_print_allPages(answer){
    if (answer.length > 0){
        // このカルテの全ページを印刷する
        var url = "printAllPages.php?owner=" + parent().owner();
        url += "&patientId=" + parent().patientId();
        url += "&patientKanjiName=" + parent().patientKanjiName();
        url += "&path=" + _dumpPath;
        
        var win = window.open(url, "ALL PAGES", "scrollbars=yes");
        win.document.title = "ALL PAGES"; // window title に表示
        win.focus();
    } else {
        alert("do_print_allPages: "+answer); //##
    }
}
function printAllPages(){
    // カルテの全ページを _dumpPath へ JSON 形式で書き出す
    // patientId のカルテの全情報をテンポラリーファイルへ書き出す
    var args = new Object();
    args["owner"] = parent().owner();
    if (parent().isReadable() == 2){
        args["coworkers"] = parent().coworkers();
    } else {
        var array = [];
        array.push(parent().owner());
        args["coworkers"] = array;
    }
    //args["hoapitalId"] = parent().hospitalId();
    args["patientId"] = parent().patientId();
    args["currentDate"] = parent().currentDate();
    args["path"] = _dumpPath;
    console.log("coworkers", encodeObject(args["coworkers"])); //##
    
    NRCall("DUMP_PAGES", args, do_print_allPages);
}

function archivedAllItems(answer){
    showFadeoutInfo("_debug", answer, 1000);
}
function printAllPDF(answer){
	// 本日の全カルテを印刷する
    if (answer){
        var obj = JSON.parse(answer);
        var template = obj.value;
        template = convertSTRING(template, "<br>", "\n");
    } else {
        // Ajax による値の取得をリカーシブルに実行
        NRCall("GET_SLIP_FORM", [], printAllPDF);
        return;
    }

    var dateTime = parent().currentDate();
	if (isSameDate(dateTime, today()) == 0){
		if (!confirm(dateTime + " は本日のページではありませんが印刷しますか"))
			return;
	}
	
	var url = "./printLog.php?patientId="
	+ "&entryDate="+dateTime
	+ "&owner="+parent().owner()
	+ "&isSmallFont="+parent().pagePrintFontSize()
	+ "&isHalfPage="+parent().pagePrintWidth()
	+ "&tags="+parent().tagStrings();
    
	var win = window.open(url, "NOA_PDF", "scrollbars=yes");
	win.document.title = "PRINT"; // window title に "PRINT" 表示
	win.focus();
	
	// 当日の全患者のデータをアーカイブし保存するようサーバへ依頼
	showMessage("_message", "本日受診の全カルテをアーカイブします...[ 未完成 ]");
	NRPutAllArchive(parent().owner(), currentDate, archivedAllItems);
}

function openMessenger(){
    // メッセンジャーを起動
    // ### tools.js から起動しないと patientId() などがうまく取得できない
    // ### NOA FRONT 両者に同様のものをインプリメントする
    //_initDebug(true); //##
    _debug("== openMessenger"); //##
    
    var pid = parent().patientId();
    var name = parent().patientKanjiName();
    _debug(pid+"->"+name); //##

    var obj = new Object();
    obj.sender = "";
    obj.sender = "@FRONT";
    obj.receiver = "@NOA";
    var st = "--- " + pid + " " + name + "さん ---\n";
    obj.message = (pid) ? st : "";
    obj.docId = "";
    obj.updateTime = todayAndTime();
    
    _debug("obj->"+encodeObject(obj)); //##
    
    showMessenger(obj);
}

///////////////////////////////////////////////////////
///// ページ・スクロール /////////////////////////////////

function pageScrollTo(dateTime){
    // entryDate のページを画面上端にスクロール: calendar.js から呼ばれる
    var elm = document.getElementById(dateTime + ".page");
    var pos = getPosition(elm);
    
    // ページ・ヘッダーがきっかり画面の上端だと心理的にその上を見たい気持ちになるので
    // 80px ほどその上の部分も見えるようにする
    window.scroll(0, pos.y - 220);
    
    openPage(dateTime, true);
}

//////////////////////////////////////////////////////
///// call() に関するもの //////////////////////////////

var _calls;
function setCalls(obj){
    _calls = obj;
}
function calls(){
    // 一括処理オブジェクトを返す
    return _calls;
}

function callForTag(tag){
    // tag に対応する call script があれば返す
    for (menu in _calls){
        var rec = _calls[menu];
        if ((rec.type == 1) && (menu == tag)){
            return rec.value;
        }
    }
    return null;
}

function call(script){
    // ユーザ操作を記憶するエージェント
    //console.log("call", script); //##
    
    eval(script);
}

function showCellValue(value, dateTime, tag){
    // CELL の値を value で表示しなおす
    if (!dateTime) dateTime = currentDate();
    if (!tag) tag = currentTag();
    
    setValueForTag(tag, dateTime, value);
    
    var elm = valueElementForTag(dateTime, tag);
    if (elm){
        elm.innerHTML = value; // 編集中の VALUE 基板エリアに書込み
    } else {
        var msg = dateTime + " " + tag + " の CELL が見つかりません";
        var elm = elmFor("_alert");
        if (elm) elm.value = msg;
    }
}


///////////////////////////////////////////////////////
///// 周辺ツールから外部参照されるオブジェクト //////////////

var _isReadOnly;
var _readOnlyDate;
function setIsReadOnly(status, date){
    // 書込不可の status を条件により設定
    var pageChanged = (isSameDate(date, _readOnlyDate) == false) ? true : false;
    if (date){
        // 日付が添えてあれば、日付を基に _isReadOnly を設定
        if (isSameDate(today(), date)){
            // date が本日なら無条件に readOnly を否定
            _isReadOnly = false;
        } else if (pageChanged){
            // 今まで開いていたエディターと異なる日付なら
            // 初心に戻り本日以外の日付はすべて readOnly とする
            _isReadOnly = (isSameDate(date, today())) ? false : true;
        } else {
            // すでに開いていたエディターと同じ日付なので readOnly の設定変更せず
            if (!_isReadOnly) _isReadOnly = status;
        }
        _readOnlyDate = date;
    } else {
        // 日付が添えてなければ status を基に _isReadOnly　を設定
        _isReadOnly = status;
    }
}
function isReadOnly(){
    // 編集可能かどうかを返す
    return _isReadOnly;
}
function getOnOff(answer){
    // onOffButton の status を受け取る
    var status = (answer * 1 > 0) ? false : true;
    setIsReadOnly(status);
}

function gotValueFromTool(val, touchAndGo, doPrint){
    // 周辺ツールから値 val を受け取るための API
    //alert("== gotValueFromTool == "+val+"->"+touchAndGo+"->"+doPrint); //##
    
    // 編集欄の内容を置換
    var elm = editorElementForTag(currentTag()); // tag に相当する 入力欄 エレメント
    if (!elm){
        alert("データ転記対象となる編集欄が開いていません");
        return;
    }
    
    // ### innerHTML では画像データが期待されぬ処理をされる ###
    if (val.indexOf('<IMG:') >= 0){
        if (typeof elm.textContent != "undefined") {
            elm.textContent = val; 
        } else {
            elm.innerText = val; 
        }
    } else {
        elm.innerHTML = val;
    }
    
    if (touchAndGo){
        // 必要なら内容をサーバへ保存し Editor を閉じる
        closeCellEditor('needSave');
        setCurrentTag(null); // closeCellEditor() でやるとタイミングによっては不整合発生
    }
    if (doPrint){
        // ページを印刷
        printPDF();
    }
}

var _cellTag;
function addedValueFromTool(answer){
    // _cellTag 相当の CELL にデータが追加されたので CELL を再表示
    var obj = JSON.parse(answer);
    setValueForTag(_cellTag, currentDate(), obj[_cellTag]);
    
    var cell = cellForTag(_cellTag);
    cell.value = obj[_cellTag]; // これは必要
    var elm = elmFor(currentDate() + "." + _cellTag + ".val");
    showValue(elm, cell, cell.value, false, currentDate());
}
function addValueFromTool(val, cellTag){
    // 周辺ツールからの val を tag 相当の CELL に追加し再表示
    //console.log("addValueFromTool", val, cellTag); //##
    
    _cellTag = cellTag;
    var hid = parent().hospitalId();
    var container = new Object();
    container[cellTag] = val;
    put_page(owner(),hid,patientId(),currentDate(),timeLimit(),container,addedValueFromTool);
}

function editor_ElementForTag(tag){
    // tag に相当する 入力欄 エレメントを返す
    // ReproductiveHistory から使われる
    var elm = document.getElementById(tag + ".value");
    return (elm) ? elm : null;
}

/////////////////////////////////////////
///// 妊娠暦 /////////////////////////////

function lmp(){
    // 当日の主訴欄から最終月経を取得し返す
    var obj = valueForTag("ProgressSection.subject"); 
    if (obj){
        var array = obj.value.split("lmp:");
        if (array.length > 1){
            var lmpSt = trim(array[1]);
            if (lmpSt.length >= 6){
                var year = "20" + lmpSt.substr(0, 2);
                var month = lmpSt.substr(2, 2);
                var day = lmpSt.substr(4, 2);
                
                return year + "-" + month + "-" + day;
            }
        }
    } else {
        alert("主訴欄に lmp：最終月経 の記述がありません");
        return null;
    }
}
function gestateWeekDay(){
    // 妊娠週数・日数の配列を返す
    if (lmp() == null) return null; // 最終月経の記述がなかった
    
    // 最終月経を取得
    var array = lmp().split("-");
	var date = new Date(array[0], array[1] - 1, array[2]);
	var lmpTime = date.getTime();
    
    // 当日を取得
    var array = currentDate().substr(0,10).split("-");
	var date = new Date(array[0], array[1] - 1, array[2]);
	var nowTime = date.getTime();
    
	var gw = parseInt((nowTime - lmpTime) / (7 * 24 * 3600 * 1000));
	var gd = parseInt((nowTime - lmpTime) % (7 * 24 * 3600 * 1000));
	gd /= parseInt((24 * 3600 * 1000));
    
    var array = new Array();
    array.push(gw);
    array.push(gd);
    
    return array;
}
function termDate(){
    // 当日の主訴欄から計算した分娩予定日を返す
    var array = gestateWeekDay();
    if (!array) return null; // 最終月経の記述がなかった
    
    // 週数・日数
    var gw = array[0];
    var gd = array[1];
    
    // 予定日を計算
	var tm = new Date(lmp());
	tm.setTime(tm.getTime() + (40 * 7 * 24 * 3600 * 1000));
	var yy = tm.getYear();
	if (yy < 2000) { yy += 1900; }
	var mm = tm.getMonth() + 1;
	if (mm * 1 < 10) mm = "0" + mm;
	var dd = tm.getDate();
	if (dd * 1 < 10) dd = "0" + dd;
    
	return yy + "-" + mm + "-" + dd;
}
function termDescriptions(){
    // 紹介状などに記入する「妊娠週数・分娩予定日」を返す
    var array = lmp().split('-');
    var st = "最終月経 " + array[0] + "年" + array[1] + "月" + array[2] + "日\n";
    
    var array = termDate().split('-');
    st += "推定分娩予定日 " + array[0] + "年" + array[1] + "月" + array[2] + "日";
    
    var array = gestateWeekDay();
    st += "（妊娠" + array[0] + "週" + array[1] + "日）";
    
    return st;
}

function showObject(obj){
    // オブジェクトを箇条書き文字列にして返す
    var st = encodeObject(obj);
    st = convertSTRING(st, ')', ')\n');
    
    alert(st);
}

///// 周辺ツールから外部参照されるオブジェクト //////////////
///////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
///// kickMenuCell(url, inWindow, cliickedElement) から呼ばれる

function putValueForTag(val, tag){
    // currentDate(), tag の CELL のエディターを開いて val を書き込む
    // DocMaker から呼ばれる
    //console.log("== putValueForTag", tag, val); //##
    
    // DocMaker から使われる
    if (currentTag()){
        var label = labelForTag(currentTag());
        alert("「" + label + "」欄を閉じて やり直してください");
    } else if (isReadOnly()){
        alert("カルテ本体が「編集不可」で上書きできません");
    } else {
        openCellEditor(currentDate(), tag, val);
    }
}

//////////////////////////////////////////////////////////////////////
///// listMaker //////////////////////////////////////////////////////

function openTool(src){
	// tool area の iFrame に src の内容を開く
    var tools = window.top.tools;
    var isWindow = (outputTarget() == "_blank") ? true : false;
    
    tools.openApp(src, isWindow);
}

function openToolPane(){
    // tools パネルを開く
    if (floatToolMenu())
        openFloatTools();
    else
        window.open("./tools.php", "tools");
}

function openGestateCalendar(){
    // 妊娠歴を開く
    openTool("../ReproductiveHistory");
}

function openPictureTool(){
    // 画像読込ツールを外部ウインドウとして開く
    setOutputTarget("_blank");
    openTool("../Picture");
}

function openPictureWindow(){
    // 画像読込ツールを開く：script から呼ばれる
    // (例) script で以下のように設定 
    // label: ProgressSection.object
    // openPictureWindow();
    // openCellEditor(null, "ProgressSection.object");

    // この後に実行されるであろう openCellEditor() で Ajax が輻輳するので multiThread で処理
    var obj = new Object();
    obj["url"] = "../Picture";
    var noaString = encodeObject(obj);

    var worker = new Worker('./multiThread.js');
    worker.postMessage(noaString); // ### worker に命令書 noaString を送る ###

    worker.onmessage = function (event) {
        // ### worker の処理したデータを受け取る ###
        var answer = event.data; // ### worker からの返答 ###
        
        var array = answer.split("<SEPARATOR>");
        if (array.length > 1){
            var obj = JSON.parse(array[1]);
            
            // 外部ウインドウとして開く: ウインドウ・サイズ指定で外部ウインドウになる
            var win = window.open(obj.url, obj.arguments, "width=200");
            win.focus();
        }
    }
}

function openCellPreference(){
    // tag の CELL 属性編集パネルを開く
    openTool("./cellPreference.php?tag=" + currentTag());
}

function openCorrectionHistory(){
    // 修正履歴を開く：Cell の editor として呼ばれる
	// var win = window.open("../CorrectionHistory","tools");
    openTool("../CorrectionHistory");
}

function openListMaker(){
    // listMaker を立ち上げる
	window.open("./listMaker.php","listMaker"
				,"width=1000,height=700,scrollbars=yes,resizable=yes");
}

function openFRONT(){
    // FRONT を開く
	var array = new Array();
	array['owner'] = owner();
	array['hospitalId'] = hospitalId();
	array['hospitalName'] = hospitalName();
	var args = encodeObject(array);
	var href = "../FRONT/index.php?value="+encodeSTRING(args);
    
	var win = window.open(href, "FRONT", "_blank");
	win.focus();
}

function openCautionEditor(){
    // 注意事項入力パネルを開く：TOOL MENU などから開く
    parent().showCautionEditor();
}

function openTemplateMenu(){
    // テンプレート・メニューを開く
    var elm = clickedElement(); // クリックされたアイコン
    showGroupTemplatePanel(elm, null, "openHelp('templateHelp.html')");
}

function openInsViewerPanel(answer){
    // InsViewer を開く
    var obj = JSON.parse(answer);
    
    var win = window.open(obj.url, "", "width=500");
    win.focus();
}
function openInsViewer(pid, date){
    // InsViewer: 保険証画像ツール を開く
    var args = new Object();
    var url = "../InsViewer?patientId=" + pid + "&currentDate=" + date;  
    
    kick_window(url, "_blank", openInsViewerPanel);
}

function openImage(url){
    // 本文中の Image をクリックした時に呼ばれる
	var win = window.open("imageViewer.php?value=" + url, "ImageViewer"
                          ,"width=1700,height=1700,scrollbars=yes,resizable=yes");
	win.focus();
}

function openPreferenceMini(){
    // read-only 専用初期設定を開く
    openTool("./preferenceMini.php"); 
}

function getPrescription(value, touchAndGo){
	// 処方箋からデータを受け取る標準 API
    //_initDebug(true); //##
    //alert("== getPrescription == "+value+"->"+touchAndGo); //##
    
	var type = medicalEncodeType(value); // lib.js
	if (type == "NOA"){
		// NOA 形式で受け取る場合 (prescription.js 側も対応の必要あり)
		var obj = decodeObject(value); // value は処方情報のみ
	} else if (type == "HL7"){
		// HL7 形式で受け取る場合 (prescription.js 側も対応の必要あり)
		var obj = HL7ToObject(value); // value は患者基本情報・保険情報・処方情報
	}
	
    var val = objectToShohouText(obj.prescription, obj.isOwn);
    
    _debug("--- val->"+val); //##
    
    // 編集エリアの内容を val へ置換
    gotValueFromTool(val, touchAndGo);
    
	function objectToShohouText(obj, isOwn){
		// object をこの電子カルテの処方表示フォーマットへ変換
        //_debug("-- objectToShohouText->"+encodeObject(obj)); //##
		var buff = "";
        if (isOwn) buff = "=== 自費処方 ===<br>";
		for (key in obj){
			var val = obj[key];
			if (typeof(val) == "object"){
				// グループの中を処理
				buff +=  objectToShohouText(val);
			}
			else {
                //_debug("-- obj->"+encodeObject(obj)); //##
                if (obj.generic && (obj.generic.length > 0))
                    _debug(obj.name+" ->"+obj.generic); //##
                
				// レコードの処理
				if (obj.code > 100) buff += "----- "; // 用法行の場合
				buff += obj.name + "(" + obj.dose + " " + obj.unit + ")";
                // generic 薬剤の場合は行末に以下のマークがつく
                // Vin.js はこれを基に generic 有無を判断する
                if (obj.generic && (obj.generic.length > 0)) buff += "　[般]";
                buff += "<br>";
				return buff;
			}
		}
		return buff;
	}
}
function openPrescriptionEditor(){
    // 旧バージョンとの互換性のため：2014-07-23
    alert("処方欄のハンマーアイコンで属性編集パネルを開き、専用エディターの設定を「処方」に設定し直し、再度「処方」欄を開いてください");
}

function openVin(){
	// 診療費計算ツールを iFrame の中に開く
    tools().initVin();
}
function openVinEditor(){
    // 旧バージョンとの互換性のため：2014-07-23
    alert("診療欄のハンマーアイコンで属性編集パネルを開き、専用エディターの設定を「医事計算」に設定し直し、再度「診療」欄を開いてください");
}

///// new tools ///////////////

function openForm(){
    // 伝票ツールを開く
    tools().initSlip();
}
function _openForm(){
    // FORM 編集ペーンを開く
    // ### 処方箋ツールからデータ受信 API として gotValueFromTool() をインプリしておくこと
    setOutputTarget("");
    openTool("form.php");
}

function openPrescription(){
    // 処方箋を開く
    tools().initPrescription();
}


///// kickMenuCell(url, inWindow, cliickedElement) から呼ばれる
/////////////////////////////////////////////////////////////////


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

function getPatients(searchKey){
    // searchKey に相当するカルテをリストアップ
    if (searchKey.length){
        // listMaker へ渡す情報を localStorage に記憶
        var st = "カルテ検索（ " + searchKey + " を含む検索結果 ）";
        setListMakerHeader(st); // localStorage に記憶
        
        get_patients(searchKey, gotPatientList); // サーバでアクセス・ログに記憶
    } else {
        alert("検索キーが指定されていません");
    }
}

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

