
var samples = ["MenuTable:./MENU.txt","FieldTable:./FIELD.txt","PriceList:./PRICE.txt","ProgressSection:./GROUP.txt"];
var _indispensables = ["施設名","施設","自費係数","まるめ係数","訂正可能時間"]; // 必須項目

var _hospitalId;
function setHospitalId(hid){
	_hospitalId = hid;
}
function hospitalId(){
	// 新規作成されつつある HospitalId
	return _hospitalId;
}

var _hospitalIdTable;
var _hospitalIdTableInDB;
function setHospitalIdTable(array){
	_hospitalIdTable = array;
	
	// _hospitalIdTableInDB = array では _hospitalIdTable が変化すると
	// _hospitalIdTableInDB も変化してしまうので、値をコピーして作成
	_hospitalIdTableInDB = new Array();
    for (num in array){
		_hospitalIdTableInDB.push(array[num]);
	}
}
function hospitalIdTable(){
	return _hospitalIdTable;
}
function hospitalIdTableInDB(){
	// DB に保存された 施設ID の配列を返す
	return _hospitalIdTableInDB;
}

var _modules = new Array();
function setModuleForId(obj, key){
	_modules[key] = obj;
}
function moduleForId(key){
	return _modules[key];
}

var _records;
function setRecords(obj){
    _records = (obj) ? obj : new Array();
}
function records(){
	return _records;
}
function owner(){
    // hospitalId() の一致したレコードの owner を返す
    for (num in _records){
        var rec = _records[num];
        if (rec.hospitalId != hospitalId()) continue;
        
        if (rec.owner) return rec.owner;
    }
	return null;
}
function recordForKey(key){
    // hospitalInfo から hospitalId(), key に一致するレコードを返す
    for (num in _records){
        var rec = _records[num];
        if (rec.key != key) continue;
        if (rec.hospitalId != hospitalId()) continue;
        
        return rec;
    }
	return null;
}
function setValueForKey(value, key){
    // hospitalId(), key に一致するレコードの値に value を与える
    if (_records.length){
        for (num in _records){
            var rec = _records[num];
            if (rec.hospitalId != hospitalId()) continue;
            if (rec.key != key) continue;
            
            _records[num].value = value;
            _records[num].changed = true;
            return;
        }
    } else {
        var num = 0;
    }
    
    // _records は DB の HospitalInfo から収集するので、あらかじめ DB に登録されていない
    // 空データの項目は _record に現れない。従って存在しない key は新規レコードを生成し追加
	_records[num + 1] = new Object();
	_records[num + 1].key = key;
	_records[num + 1].value = value;
	_records[num + 1].changed = true;
    
    _debug("setValueForKey _records->"+encodeObject(_records)); //##
}

function show_message(id, msg){
	// メッセージ・タグを開く
	// ## あらかじめ PHP ファイルなどに以下を埋め込んでおくこと ##
	// <SPAN id="_message" STYLE="visibility: hidden; position: absolute;"></SPAN>
	var elm = document.getElementById(id);
	elm.innerHTML = msg;
	elm.style.backgroundColor = "#f55";
	elm.style.color = "#fff";
	//elm.style.borderColor = "#900"; // "solid" の場合
	elm.style.borderWidth = 2;
	elm.style.border = "outset"; // "solid";
	elm.style.padding = "3px 10px";
	elm.style.left = 25; // 表示するx座標
	elm.style.top = 100; // 表示するy座標
	elm.style.visibility = "visible";
	var action = "hideMessage('"+id+"')";
	elm.setAttribute("onmouseover", action);
}
function hide_message(id){
	// メッセージ・タグを閉じる
	var elm = document.getElementById(id);
	elm.style.visibility = "hidden";
}

function encodeSCRIPT(buff){
    // "script": を検出し内容を JSON で解析できないよう encode
    // "script":"{"key":"value","key":"value",,}" を
    // "script":"@{@|@'key@|@':@|@'value@|@',,}@" のように encode
    var key1 = '"script":"{';
    var array = buff.split(key1);
    if (array.length > 1){
        var left = array[0]; // script より左側の文字列
        array.shift(); // array の最初の要素を削除
        var ln = array.join(key1); // key1 より右側の文字列
        var key2 = '}"';
        var array2 = ln.split(key2);
        if (array2.length > 1){
            var script = array2[0];
            array2.shift(); // array の最初の要素を削除
            var right = array2.join(key2); // script より右側の文字列
            // script 中の '\\' を '' にする
            var array3 = script.split('\\');
            if (array3.length > 1) script = array3.join('');
            
            // script 中の " を @|@' に encode
            script = convertSTRING(script, '"', '@|@');
            script = '"script":"@{' + script + '}@"';
            
            // key1 がみつかるうちはリカーシブルに処理を続ける
            var result = encodeSCRIPT(right);
            if (result)
                return left + script + result;
            else
                return left + script + right;
        }
    }
    return null;
}
function decodeScriptIn(array){
    // array 中の encode された script を decode する
    for (num in array){
        var obj = array[num];
        if (!obj) continue;
        
        var script = obj.script;
        if (script){
            // "script":"@{@|@key@|@:@|@value@|@,,}@" を
            // {"key":"value",,} に decode
            script = convertSTRING(script, '@|@', '"');
            script = convertSTRING(script, '@{', '{');
            script = convertSTRING(script, '}@', '}');
            //   script = '{' + script + '}';
            obj.script = script;
        }
    }
    return array;
}

function mergeTable(table, obj){
    // テーブルの内容をテンプレートとマージ
    _debug("mergeTable->"+table); //##
    //alert("mergeTable->"+table+"->"+encodeObject(obj)); //##
    
    // obj は mergeFieldTable() でオブジェクトとして認識されてしまうので配列のはずだが配列に変換
    var array = new Array();
    for (key in obj){
        array.push(obj[key]);
    }
    
    if (table == "MenuTable"){
        // obj: [{"owner":"ohashi","tag":"..","menu":"あり",,}{,,}]
        _source = array;
        mergeMenuTable();
    } else if (table == "FieldTable"){
        // obj:[{"owner":"ohashi","tag":"..",,},{,,},,]
        _source = array;
        mergeFieldTable();
    } else if (table == "PriceList"){
        // obj: [{"code":"","alias":"",},{"code":"",},]
        _source = array;
        mergePriceList();
    } else { // GROUP データをサーバへ送るループに入る
        _source = array;
        mergeGroup();
    }
}


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

var _sampleNum;

function sampleTableName(){
    // 現在選択されているサンプルのテーブル名を返す
    var st = samples[_sampleNum]; // "MenuTable:./MENU.txt"
    var ary = st.split(":");
    return ary[0]; // "MenuTable"
}

function loadedSampleData(answer){
	// サンプルデータを DB に読み込んだ結果を返す
    //alert("loadedSampleData->"+answer); //##

    if (answer && (answer.length > 0)){
        _num = 0;
        var obj = decodeObject(answer);// NOA 型式の文字列をオブジェクトに展開
        mergeTable(sampleTableName(), obj);
    } else {
        alert("ERROR *** メニュー・ソースがありません\n\n"+ value);
    }
}
function loadSampleData(){
	// サンプルデータを DB に読み込む
    if (_sampleNum >= samples.length){
        // サンプルデータの読み込みが終了
        hide_message("_progress");
        alert("サンプルデータの読込が終了しました。左メニュー「施設・ユーザ登録」を再度クリックしてください。");
        // debug メッセージを残すため initRegister() や showHospitalInfo() へ進まない
    } else {
        var args = new Object();
        var st = samples[_sampleNum]; // "MenuTable:./MENU.txt"
        var ary = st.split(":");
        args['tableType'] = ary[0]; // "MenuTable"
        args['filename'] = ary[1]; // "./MENU.txt"
        
        _callServer("GET_ARCHIVE", "sysServer.php", args, loadedSampleData);
    }
}

var _source;
var _num;
function mergedMenuTable(answer){
	// MenuTable がマージされた
    show_message("_progress", answer);
    mergeMenuTable();
}
function mergeMenuTable(){
	// メニュー・ソースを DB とマージ
	if (_num < _source.length){
        var obj = _source[_num++];
        obj.owner = "public"; // すべて public 権限にしておく
                
        _callServer("MERGE_MENU", "sysServer.php", obj, mergedMenuTable);
	} else {
		// 振り出しへ戻る
        _sampleNum++;
        loadSampleData();
	}
}

function mergedFieldTable(answer){
	// MenuTable がマージされた
    show_message("_progress", answer);
    
    mergeFieldTable();
}
function mergeFieldTable(){
	// メニュー・ソースを DB とマージ
	if (_num < _source.length){
        var obj = _source[_num++];
        obj.owner = "public"; // すべて public 権限にしておく
                
        _callServer("MERGE_FIELD", "sysServer.php", obj, mergedFieldTable);
	} else {
		// 振り出しへ戻る
        _sampleNum++;
        loadSampleData();
	}
}

function mergedGroup(answer){
	// GROUP がマージされた
    show_message("_progress", answer);
    mergeGroup();
}
function mergeGroup(){
	// GROUP を DB とマージ
	if (_num < _source.length){
        var args = new Object();
        var obj = _source[_num++];
        //alert("mergeGroup->"+encodeObject(obj)); //return; //##
        var args = new Array();
        args['patientId'] = "GroupMenu";
        args['pageHeader'] = obj.pageHeader;
        args['subject'] = obj.subject;
        args['object'] = obj.object;
        args['examination'] = obj.examination;
        args['prescription'] = obj.prescription;
        args['disease'] = obj.disease;
        args['treatment'] = obj.treatment;
        args['plan'] = obj.plan;
        args['freq'] = 0;
        args['owner'] = "public";
        
        _callServer("MERGE_GROUP", "sysServer.php", args, mergedGroup);
	} else {
		// 振り出しへ戻る
        _sampleNum++;
        loadSampleData();
	}
}

function mergedPriceList(answer){
	// priceList がマージされた
    show_message("_progress", answer);

    mergePriceList();
}
function mergePriceList(){
	// メニュー・ソースを DB とマージ
	if (_num < _source.length){
        var obj = _source[_num++];
        obj["owner"] = owner();
                
        _callServer("MERGE_PRICE", "sysServer.php", obj, mergedPriceList);
	} else {
		// 振り出しへ戻る
        _sampleNum++;
        loadSampleData();
	}
}

function removedNoaDB(answer){
    // 振り出しへ戻る
    alert(answer); //##
    initRegister();
}
function removeNoaDB(){
    // NOA データベースを全て削除
    if (! confirm("NOA のデータベースを一旦すべて削除します")) return;
    
    _callServer("RREMOVE_NOA", "sysServer.php", null, removedNoaDB);
}

function createdTablesIfNeeded(answer){
	// 各テーブルの存在がチェックされすべて生成された
    //alert("createdTablesIfNeeded->"+answer); //##
    //_initDebug(true); //##
    
    if (answer && (answer.length > 0)){
        // テーブルの新規生成メッセージがあった場合、表示
        _debug("createdTablesIfNeeded->"+answer); //##
        
        // テーブルが生成されたのでデータ読込み
        _sampleNum = 0;
        loadSampleData();
    } else {
        // テーブルは既に存在したのでデータ読込みせず、次のステップへ
        _debug("createdTablesIfNeeded-> okay"); //##
        showHospitalInfo();
    }
}
function createTablesIfNeeded(){
	// DB に table が存在しなければ新たに生成
    _callServer("CREATE_TABLE_IF_NEEDED", "sysServer.php", null, createdTablesIfNeeded);
}

function closeReadRetry(){
    var elm = document.getElementById("readRetryArea");
    elm.innerHTML = "";
}
function readRetry(){
    // 読込がうまくいかない場合 NOA DB を削除してやり直しをうながす
    var elm = document.getElementById("readRetryArea");
    elm.innerHTML = "";
    elm.style.padding = "5px 5px";
    
    var bt = newBUTTON(elm, "", "NOA データベースを削除");
    bt.setAttribute("onclick", "removeNoaDB()");
    
    var sp = newSPAN(elm, "");
    sp.style.paddingLeft = "10px";
    sp.innerHTML = " した後、左メニューから「施設・ユーザ登録」をもう一度選択してください";
}

function checkedDataBase(answer){
	// NOA DB のテーブル類がそろっているかどうかサーバから返答
    //alert("checkedDataBase->"+answer); //##
    
    if (answer && (answer.length > 0)){
        // NOA DB の生成に失敗した
        _debug("ERROR *** "+answer); // Error message
        
        var elm = document.getElementById("message");
        var sp = newSPAN(elm, "");
        sp.innerHTML = "読み込みがうまく行きませんでしたので";
        sp.style.paddingRight = "5px";
        var a = newA(elm, "ここをクリック", "#", "");
        a.setAttribute("onclick", "readRetry()");
        var div = newDIV(elm, "readRetryArea");
    } else {
        // NOA DB は存在したか生成された
        _debug("checkDB okay"); // Error message
        
        // NOA TABLE が存在しなければ生成
        createTablesIfNeeded();
    }
}
function checkDataBase(){
	// NOA DB が存在するかチェック
    _callServer("CHECK_DB", "sysServer.php", null, checkedDataBase);
}

function gotHospitalTable(answer){
	// サーバから返された hospitalTable のレコード群から hospitalId ポップアップ生成
    //alert("gotHospitalTable ===\n"+answer);//###
    var items = new Array();
    if (answer && (answer.length > 0)){
        var records = JSON.parse(answer);
        //alert("records->"+encodeObject(records)); //##
        
        var old_hid = "";
        for (no in records){
            // hospitalInfo record から hospitalId の配列を作成
            var record = records[no];
            var hid = record.hospitalId;
            if (!isSame(hid, old_hid)) items.push(hid);
            old_hid = hid;
        }
    }
    setHospitalIdTable(items);
    items.splice(0,0,"");
    items.push("...その他");
    var elm = document.getElementById("popUpArea");
    elm.innerHTML = "";
    var pu = newPopupMenu(elm, "hospitalId", items, "");
    pu.setAttribute("onchange", "clickedHospitalId(this)");
}
function getHospitalTable(){
	// 保険者番号に一致する保険者名・電話番号を検索し表示
    var args = new Object();
    args["hospitalId"] = "";
    
	_callServer("GET_HOSPITAL_TABLE", "sysServer.php", args, gotHospitalTable);
}

function gotHospitalInfo(answer){
    //alert("gotHospitalInfo ===\n"+answer);//###
    var items = new Array();
    var records = JSON.parse(answer);
    //alert("gotHospitalInfo ==\n"+encodeObject(records));//##
    
    setRecords(records);
    showHospitalTable();
}
function getHospitalInfo(hospitalId){
	// 施設 ID に一致する施設属性を検索し表示
    if (hospitalId.length == 0){
        alert("施設ID が空です");
        return;
    }
    setHospitalId(hospitalId);
    
    var args = new Object();
    args["hospitalId"] = hospitalId;
    
	_callServer("GET_HOSPITAL_TABLE", "sysServer.php", args, gotHospitalInfo);
}

function saved(answer){
	// サーバから返された hospitalTable のレコード群から hospitalId 編集テーブル表示
    if (answer && (answer.length > 0)){
        // デバッグ用ログが返ってくる
        hideMessage("_message");
        
        // 職員リストを閉じる
        closeStaffInfo();
        
        // 初期画面へ戻る
        getHospitalInfo(hospitalId());
        showHospitalInfo();
        document.getElementById("hospitalEditorArea").innerHTML = "";
    }
}
function save(){
    // 施設情報をサーバへ保存
	if (incompleteHospitalInfo()) return;
	
    var args = new Object();
    for (i in records()){
        var rec = records()[i];
        _debug("rec->"+encodeObject(rec)); //##
        
        if (rec.changed)
            args[rec.key] = encodeSTRING(rec.value);
    }
    
    // 転送オブジェクトが２階層になると処理が面倒なので中に組み込んでしまう
    args['owner'] = owner();
    args['hospitalId'] = hospitalId();
        
    _callServer("PUT_HOSPITAL_TABLE", "sysServer.php", args, saved);
    
	function changedRecords(){
        _debug("changedRecords ---"); //##
        
		var results = new Object();
        for (i in records()){
			var rec = records()[i];
            _debug("rec->"+encodeObject(rec)); //##
            
			if (rec.changed)
                results[rec.key] = encodeSTRING(rec.value);
		}
        
        // 転送オブジェクトが２階層になると処理が面倒なので中に組み込んでしまう
        results['owner'] = owner();
        results['hospitalId'] = hospitalId();
        
        _debug("results->"+encodeObject(results)); //##
        
		return results;
	}
	
	function incompleteHospitalInfo(){
		// 施設情報の入力内容が不十分なら true を返す
		_debug("incompleteHospitalInfo->"+_indispensables); //##
        
		var array = records();
		//_debug("records->"+encodeObject(array)); //##
        
        for (k in _indispensables){
			var key = _indispensables[k];
            _debug("key->"+key); //##
			var found = false;
            for (i in array){
				var rec = array[i];
				if (key == rec.key){
					found = true;
					if (!rec.value || (rec.value.length == 0)){
                        alert("「" + key + "」が空欄で保存できません");
						return true;
					}
				}
			}
            
			if (found == false){
                alert("「" + key + "」が空欄で保存できません");
				return true;
			}
		}
		return false;
	}
}

function removedHospital(answer){
	// 施設が削除された
    //alert("removedHospital ===\n"+value);//###
    
    hideMessage("_message");
    
    // 職員リストを閉じる
    closeStaffInfo();
    
    // 初期画面へ戻る
    getHospitalInfo(hospitalId());
    showHospitalInfo();
    document.getElementById("hospitalEditorArea").innerHTML = "";
}
function removeHospital(){
	// 施設ID ならびにその施設情報すべてを削除
	if (confirm(hospitalId() + " の施設を（所属スタッフを含め）削除していいですか")){
        var args = new Object();
        args['hospitalId'] = hospitalId();
        
        _callServer("REMOVE_HOSPITAL", "sysServer.php", args, removedHospital);
	}
}

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


////////////////////////////////////////////////////////
///// FACILITY EDITOR //////////////////////////////////

function showHospitalTable(){
	// records を元に HospitalTable 内容を表示
	var elm = document.getElementById("hospitalEditorArea");
	elm.innerHTML = "";
	var tbl = newTABLE(elm, "base-table");
	tbl.style.backgroundColor = "#ffc";
	tbl.style.fontSize = "10pt";
    
	//alert("showHospitalTable ==\n"+encodeObject(records()));//##
    
	// title
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", "属性");
	var td = newTD(tr, "label-left", "設定値");
	var td = newTD(tr, "label-left", "更新年月日");
	var td = newTD(tr, "label-left", "オーナー");
	
	// 決め打ちの contents
	var tr = newTR(tbl, "", "");
    
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", "施設ID");
	var td = newTD(tr, "label-left", hospitalId()); // 新規施設登録で必要
    
	makeFieldRow(tbl, "施設名");
	makeFieldRow(tbl, "住所");
	makeFieldRow(tbl, "電話番号");
	makeFieldRow(tbl, "FAX");
	makeFieldRow(tbl, "都道府県番号");
	makeFieldRow(tbl, "点数表番号");
	makeFieldRow(tbl, "保険医療機関コード");
	var items = new Array("日","月","火","水","木","金","土");
	makeCheckBoxesRow(tbl, "診療曜日", items);
	timeRangeRow(tbl, "診療時間");
    makeRegisteredItems(tbl, "施設基準届出事項");
	makeComboBoxRow(tbl, "施設", ["","病院","診療所"], "");
	var comment = " x 保険点数 = 自費料金";
	makeComboBoxRow(tbl, "自費係数", ["","10","20"], comment);
	var items = ["","0","1","2","3","4","5","6","7","8","9"];
	var comment = " 1:八捨九入 5:四捨五入 9:零捨一入";
	makeComboBoxRow(tbl, "まるめ係数", items, comment);
	var times = ["","1","3","6","12","24"];
	makeComboBoxRow(tbl, "訂正可能時間", times, " この時間内ならカルテ修正可能");
	
	// controls
	var tr = newTR(tbl, "yellow-bar", "");
	var td = newTD(tr, "right-side", "");
	td.setAttribute("colspan","4");
	var bt = newBUTTON(td, "", "この施設を削除");
	bt.setAttribute("onclick", "removeHospital()");
	var sp = newSPAN(td, "saveArea");
    
    // timeRangeRow() の内容をサーバへリクエスト： Ajax にアクセス
    // この Ajax の後に getStaffList() の Ajax を発出
    makeConsultationHours("診療時間");
}

function changeValue(key){
	// _records オブジェクト中の key に該当するレコードを更新
    //_initDebug(false); //##
    
	var obj = moduleForId(key);
    //_debug("moduleForId( "+key+" )->"+obj); //##
	if (obj){
		//_debug("changeValue:"+key+"("+obj.value+") type( "+obj.type+" )"); //##
		if (obj.type == "checkBoxes"){
			var array = obj.value.split(",");
			if (isEmptyArray(array))
				setValueForKey("", key);
			else
				setValueForKey(obj.value, key);
		} else if (obj.type == "checkbox"){ // dom.js で指定
			setValueForKey(obj.value, key);
		} else if (obj.type == "timeRange"){
            // 設定値を CSV で表示
            var csv = encodeCSV(conditions());;
            setValueForKey(csv, key);
		} else {
            var val = document.getElementById(key).value;
            setValueForKey(val, key);
        }
	} else {
		var elm = document.getElementById(key);
		var val = elm.value;
		//_debug("changeValue: "+key+"("+elm.value+") "); //##
		setValueForKey(val, key);
	}
    
	// 保存ボタンを表示
	var elm = document.getElementById("saveArea");
	elm.innerHTML = "";
    
	var bt = newBUTTON(elm, "", "保存");
	bt.setAttribute("onclick", "save()");
	
	function twoColumns(num){
		// 2桁表示にして返す
		num = num * 1; // 数値にする
		if (num < 10)
			return "0" + num;
		else
			return "" + num;
	}
	
	function isEmptyArray(array){
		// array 内のオブジェクトの長さがすべてゼロなら その配列は空と判定する
		for (var i=0,ct=array.length; i < ct; i++){
			if (array[i].length > 0) return false;
		}
		return true;
	}
}

function isNeeded(label){
    // 必須項目なら赤で表示
    for (num in _indispensables){
        if (label == _indispensables[num]) return true;
    }
    return false;
}

function makeFieldRow(tbl, key){
	// 入力フィールドを生成
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", key);
    if (isNeeded(key)) td.style.color = "#f30";
    
	var record = recordForKey(key);
    _debug("makeFieldRow->"+key+"->"+record); //##
    
	if (record){ // DB に存在するレコード
		var td = newTD(tr, "label-left", "");
		var fd = newFIELD(td, key, "", 50, record.value);
		fd.setAttribute("onchange", "changeValue('" + key +"')");
		var td = newTD(tr, "label-left", record.updateTime);
        td.style.fontSize = "9pt";
		var td = newTD(tr, "label-left", record.owner);
	} else { // 新規追加されまだ DB に保存されないレコード
		var td = newTD(tr, "label-left", "");
		var fd = newFIELD(td, key, "", 50, "");
		fd.setAttribute("onchange", "changeValue('" + key +"')");
		var td = newTD(tr, "", "");
		var td = newTD(tr, "", "");
	}
	setModuleForId(fd, key); // dom.js
	return tr;
}

function _cb_clicked(key){
	// チェックボックスがチェックされる毎に this.value を更新
	var obj = moduleForId(key);
	var elm = document.getElementById(key);
	obj.value = (elm.checked) ? "yes" : "no";
	changeValue(key);
}
function makeCheckBoxRow(tbl, key){
	// チェックボックスの入力フィールドを生成
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", key);
    if (isNeeded(key)) td.style.color = "#f30";
	var td = newTD(tr, "label-left", "");
	var record = recordForKey(key);
	if (record){
		var status = (record.value && (record.value == "yes")) ? 1 : 0;
		var cb = newCHECKBOX(td, key, "", status);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	} else {
		var cb = newCHECKBOX(td, key, "", 0);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	}
	setModuleForId(cb, key); // dom.js
}

function _cbx_clicked(id){
	// チェックボックスがチェックされる毎に this.value を更新
	// obj の各チェックボックスの状態を obj.value へ以下のような文字列として吐き出す
	// ",,火,水,,金," -- "on" は空白以外なら何の文字でも構わない
	var array = new Array();
	var obj = moduleForId(id);
	var items = obj.items;
	var id = obj.id;
	for (var i=0,ct=items.length; i < ct; i++){
		var stat = document.getElementById(id+"_cbx_"+items[i]).checked;
		array[i] = (stat) ? items[i] : "";
	}
	// this.value を更新
	obj.value = array.join(",");
	//alert("cbx_makeValue:"+id+"->"+obj.value); //##
	changeValue(id);
}
function makeCHECKBOXES(elm, id, items, statuses){
	// 複数のチェックボックスを表示
	// コンストラクタ：
	//   var items = new Array("foo","bar");
	//   var statuses = new Array("","on"); // on は空白以外なら何でも可
	//   var obj = new newCHECKBOXES(elm, id, items, statuses) で生成
	// チェック状態を外部から取得：
	//   var st = obj.value; で取得できる
	//   チェックされた item を",月,,水,,," のように","で区切った文字列として取得
	var array = new Array();
	var boxes = new Array();
	for (var i=0,ct=items.length; i < ct; i++){
		var status = (statuses && (statuses.length >= i))
		? statuses[i].length : 0;
		var cb = newCHECKBOX(elm, id+"_cbx_"+items[i], items[i], status);
		cb.setAttribute("onchange", "_cbx_clicked('"+id+"')");
		array[i] = (statuses && (statuses[i].length)) ? items[i] : "";
		boxes[i] = cb;
	}
	this.value = array.join(",");
	this.checkboxes = boxes;
	this.id = id;
	this.items = items;
	this.type = "checkBoxes";
	setModuleForId(this, id);
	
	return this;
}
function makeCheckBoxesRow(tbl, key, items, comment){
	// チェックボックスの並んだ入力フィールドを生成
    //_initDebug(false); //##
    
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", key);
    if (isNeeded(key)) td.style.color = "#f30";
	var record = recordForKey(key);
	if (record){ // DB に存在するレコード
		var td = newTD(tr, "label-left", "");
		var statuses = itemsOfRecord(record);
		td.style.color = "#a63"; //brown
		makeCHECKBOXES(td, key, items, statuses);
		if (comment) var tx = newTEXT(td, comment);
		var td = newTD(tr, "label-left", record.updateTime);
        td.style.fontSize = "9pt";
		var td = newTD(tr, "label-left", record.owner);
	} else { // 新規追加されまだ DB に保存されないレコード
		var td = newTD(tr, "label-left", "");
		td.style.color = "#a63"; //brown
		makeCHECKBOXES(td, key, items);
		if (comment) var tx = newTEXT(td, comment);
		var td = newTD(tr, "", "");
		var td = newTD(tr, "", "");
	}
	return tr;
	
	function itemsOfRecord(record){
		if (record)
			return record.value.split(",");
		else
			return new Array();
	}
}

function timeRangeRow(tbl, key){
	// "09:00..12:00,15:00..17:00" のように診療時間を入力するモジュール
    // １行目：診療時間帯ごとのレコードを表示
	var tr = newTR(tbl, "timeRangeArea", "");
	var td = newTD(tr, "label-left", key);
    td.style.verticalAlign = "top";
    
	var td = newTD(tr, "label-left", "");
    td.setAttribute("colspan", "3");
    var dv = newDIV(td, "timeEditArea");
    
    // ２行目：診療時間情報を CSV で表示
	var tr = newTR(tbl, "timeRangeArea", "");
    var td = newTD(tr, "label-left", ""); // 空ラベル
    var td = newTD(tr, "consultResultArea", ""); // 診療時間：設定値
    td.setAttribute("colspan", "3");
    td.style.paddingLeft = "9px";
    td.style.width = "150px";
    
	// timeRangeRow 用のオブジェクトを生成し登録
	var obj = new Object();
	obj.type = "timeRange";
	setModuleForId(obj, key);
}

function clickedComboPopup(elm, key){
	// comboBox のポップアップメニューがクリックされた
	document.getElementById(key).value = elm.value;
	changeValue(key);
}
function makeRegisteredItems(tbl, title){
    // 保険局に登録した項目をチェックする
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", title);
	var td = newTD(tr, "", "");

    // 明細書発行体制等加算
    var sp = newSPAN(td, "");
    sp.style.paddingRight = "5px";
    var key = "明細書発行体制等加算";
    if (isNeeded(key)) sp.style.color = "#f30";
	var record = recordForKey(key);
	if (record){
		var status = (record.value && (record.value == "yes")) ? 1 : 0;
		var cb = newCHECKBOX(sp, key, key, status);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	} else {
		var cb = newCHECKBOX(sp, key, key, 0);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	}
	setModuleForId(cb, key); // dom.js
    // 夜間・早朝等加算
    var sp = newSPAN(td, "");
    sp.style.paddingRight = "5px";
    var key = "夜間・早朝等加算";
    if (isNeeded(key)) sp.style.color = "#f30";
	var record = recordForKey(key);
	if (record){
		var status = (record.value && (record.value == "yes")) ? 1 : 0;
		var cb = newCHECKBOX(sp, key, key, status);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	} else {
		var cb = newCHECKBOX(sp, key, key, 0);
		cb.setAttribute("onchange", "_cb_clicked('"+key+"')");
	}
	setModuleForId(cb, key); // dom.js
}
function makeComboBoxRow(tbl, key, items, comment){
	// comboBox 入力フィールドを生成
	var tr = newTR(tbl, "", "");
	var td = newTD(tr, "label-left", key);
    if (isNeeded(key)) td.style.color = "#f30";
	var record = recordForKey(key);
	if (record){ // DB に存在するレコード
		var td = newTD(tr, "label-left", "");
		td.style.color = "#a63"; //brown
		comboBox(td, key, items, record.value);
		var tx = newTEXT(td, comment);
		var td = newTD(tr, "label-left", record.updateTime);
        td.style.fontSize = "9pt";
		var td = newTD(tr, "label-left", record.owner);
	} else { // 新規追加されまだ DB に保存されないレコード
		var td = newTD(tr, "label-left", "");
		td.style.color = "#a63"; //brown
		comboBox(td, key, items, "");
		var tx = newTEXT(td, comment);
		var td = newTD(tr, "", "");
		var td = newTD(tr, "", "");
	}
	return tr;
	
	function comboBox(elm, key, items, val){
		var pu = newPopupMenu(elm, "", items, val);
		pu.setAttribute("onchange", "clickedComboPopup(this,'"+key+"')");
		var fd = newFIELD(td, key, "", 10, val);
		fd.setAttribute("onchange", "changeValue('" + key +"')");
	}
}

function clickedHospitalId(elm){
	// hospitalId ポップアップが選択された
	var hid = elm.value;
	//alert("clickedHospitalId: " + hid); //## debug
	setHospitalId(hid);
	if (hid.length == 0){
		// 空の hospitalId が選択された
        // 施設情報エリアを削除
        document.getElementById("hospitalEditorArea").innerHTML = "";
        // 職員情報エリアを閉じる
		closeStaffInfo(); // staff.js
		return;
	} else if (isSame(hid, "...その他")){
		hid = window.prompt("新規施設ID（数字）を入力してください","");
		if (hid.length == 0) return;
        setHospitalId(hid);
	}
	getHospitalInfo(hid);
}

function closeIdHelp(){
    document.getElementById("hospitalInfoHelpArea").innerHTML = "";
}
function showIdIHelp(){
	var elm = document.getElementById("hospitalInfoHelpArea");
    if (elm.innerHTML.length){
        closeIdHelp();
    } else {
        var div = newDIV(elm, "");
        div.style.padding = "10px 10px";
        var st = "施設ID は医療機関を特定するためのものです。ポップアップ・メニューから施設IDを選択しますが、施設IDが未登録なら「...その他」の選択で施設情報を新規登録できます。「保険医療機関コード」など一意のIDを使うのがよいでしょう。他医療機関との連携予定がなければ 施設IDに何を設定しても問題ありません。";
        var sp = newSPAN(div, "");
        sp.style.color = "#a63"; //brown
        sp.innerHTML = st;
        var a = newA(div, "閉じる", "#", "");
        a.setAttribute("onclick", "closeIdHelp()");
    }
}
function showRedHelp(){
	var elm = document.getElementById("hospitalInfoHelpArea");
    if (elm.innerHTML.length){
        closeIdHelp();
    } else {
        var div = newDIV(elm, "");
        div.style.padding = "10px 10px";
        var st = "とりあえず使って見るには赤文字の必須項目入力のみでも構いません。<br>黒文字の項目は 紹介状、処方箋、診療費計算、診療予約などに必要です。";
        var sp = newSPAN(div, "");
        sp.style.color = "#a63"; //brown
        sp.innerHTML = st;
        var a = newA(div, "閉じる", "#", "");
        a.setAttribute("onclick", "closeIdHelp()");
    }
}

function toggleHospitalInfo(){
    // 施設情報ペーンを開閉
	var elm = document.getElementById("hospitalEditorArea");
    if (elm.innerHTML.length){
        elm.innerHTML = "";
    } else {
        getHospitalInfo(hospitalId());
    }
}
function showHospitalInfo(){
    // 施設情報を表示
	// 施設情報の入力画面を表示
    _debug("== showHospitalInfo"); //##
    
	var elm = document.getElementById("hospitalInfoArea");
	elm.innerHTML = "";
    
    // 施設ID選択バー
    var div  = newDIV(elm, "yellow-bar");
    // ラベル
    var sp = newSPAN(div, "");
    sp.style.paddingRight = "5px";
    sp.innerHTML = "施設 ID";
    sp.setAttribute("onclick", "toggleHospitalInfo()");
    // 施設ポップアップ
    var sp = newSPAN(div, "popUpArea");
    sp.style.paddingRight = "5px";
    // ヘルプ・アンカー
    var sp = newSPAN(div, "");
    sp.style.fontSize = "9pt";
    var a = newA(sp, "施設IDとは", "#", "");
    a.setAttribute("onclick", "showIdIHelp()");
    var sp = newSPAN(div, "");
    sp.style.fontSize = "9pt";
    sp.style.marginLeft = "10px";
    var a = newA(sp, "赤文字の項目について", "#", "");
    a.setAttribute("onclick", "showRedHelp()");
    
    // 施設ヘルプ・エリア
    var div  = newDIV(elm, "hospitalInfoHelpArea");
    
    // 施設情報編集エリア
    var div = newDIV(elm, "hospitalEditorArea");
	
    getHospitalTable();
}

///// FACILITY EDITOR //////////////////////////////////
////////////////////////////////////////////////////////

function closeMessage(){
    // メッセージを削除
    document.getElementById("topMessage").innerHTML = "";
}

function initRegister(){
    var elm = document.getElementById("base");
    elm.style.padding = "5px 5px";
    elm.style.fontSize = "10pt";
    elm.style.backgroundColor = "#ffc";
    
    var dv = newDIV(elm, "");
    dv.style.color = "#a63"; //brown
    dv.innerHTML = "NOA を使うには、必ず施設とユーザの登録を行ってください。";
    
	// 施設情報ポップアップ・エリア
    var div = newDIV(elm, "hospitalInfoArea");
    
    // 職員情報ポップアップ・エリア
    var div = newDIV(elm, "staffInfoArea");
    
	// HospitalTable, UserTable が存在しなければ作成
	checkDataBase();
}