
var _attributes = ["fixed","unit","range","menu","type","value","dateFormat"];

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

// NOA 組込みの場合は patientId() などを直接取得できるが
// 外付けツールにも出来るようあえて以下の仕組みを使う

function owner(){
	return parent().owner();
}

function patientId(){
	return parent().patientId();
}

function currentDate(){
	return parent().currentDate();
}

function currentTag(){
	return parent().currentTag();
}

function cellLabel(){
    var cell = parent().cellForTag(parent().currentTag());
    return cell.label;
}

function cellValue(){
    var obj = parent().valueForTag(currentTag(), currentDate());
    var val = (obj && obj.value) ? obj.value : "";

    return val;
}

function gotValueFromTool(value, status){
    // 周辺ツールから値 val を受け取るための API
    // NOA の CELL へ値を書き戻すために使う
    parent().gotValueFromTool(value, status);
}

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

///////////////////////////////////////////////////////////
///// 各伝票の構造とデータを記憶するオブジェクト
///// 伝票ツールの最初に起動される getItems() で読み込まれる
///// _menuObj には 「NOA の記憶する全ての伝票の構造」 が読み込まれる
///// _currentSlips には 「現在表示されている伝票の構造」が保持される

var _menuObj; // 各伝票の構造とデータを記憶するオブジェクト
function initItemRecords(){
	_menuObj = new Object();
}
function menuObj(){
    // 各伝票の構造とデータを記憶したオブジェクトを返す
    return _menuObj;
}
function addItemRecord(slipName, rec){
    // slipName は伝票名：(ex.特定健診 など)
	// rec = {slipName:{owner,tag,value,public,freq,menu,template},,} 形式
    // value = "fixed|unit|range|menu|type|value|dateFormat"; 形式
	// 他人が owner のものも記憶
    if (!rec.template){
        _menuObj[slipName] = rec;
    }
}
function menuObjFor(slipName){
    // 伝票名 slipName のオブジェクトを返す
    return _menuObj[slipName];
}
function formForSlipName(slipName){
    // 伝票名に対応する伝票構造オブジェクトを返す
    // "fixed|unit|range|menu|type|value|dateFormat"; 形式を返す
    //_debug("== formForSlipName->"+slipName); //##
    
	var rec = _menuObj[slipName];
    //_debug("rec->"+encodeObject(rec)); //##
    //alert("formForSlipName->"+encodeObject(rec)); //##
    
    if (rec && rec.value){
        //_debug("<p>"+slipName+" rec.value->"+rec.value+"</p>"); //##
        
        // "label(|||),," 型式を "label(fixed()unit(),,),," 型式に変換して返す
        var recs = decodeObject(rec.value);
        return slitToObj(recs);
    }
    return null;
}
function valueForItem(slipName){
    // 伝票名に対応するレコード文字列を返す：decodeObject() するとレコードオブジェクトになる
	var rec = _menuObj[slipName];
	return (rec) ? rec.value : "";
}
function addNewLabelTo(slipName, newLabel){
    // slipName 伝票に新規ラベル newLabel を挿入
	var rec = _menuObj[slipName];
	if (rec && rec.value && rec.value.length){
        var obj = decodeObject(rec.value);
        obj[newLabel] = "||||||"; // 新規ラベルを挿入
    } else {
        if (! _menuObj[slipName]) _menuObj[slipName] = new Object();
        if (! _menuObj[slipName].value) _menuObj[slipName].value = new Object();
        var obj = new Object();
        obj[newLabel] = "||||||"; // 新規ラベルを挿入
    }
    _menuObj[slipName].value = encodeObject(obj); // _menuObj へ書き戻す
}

function slitToObj(slipObj){
    // "label(|||),," 型式を "label(fixed()unit(),,),," 型式に変換
    //_debug("== slitToObj->"+encodeObject(slipObj)); //##
    
    var resultObj = new Object();
    for (label in slipObj){
        var val = slipObj[label];
        var array = val.split('|');

        var obj = new Object();
        obj.fixed = "";
        obj.unit = "";
        obj.range = ""; // 旧バージョンとの互換性のため dummy を入れておく
        obj.menu = "";
        obj.type = "";
        obj.value = "";
        obj.dateFormat = "";

        if (array.length == 1){
            // 文字列に '|' が含まれない場合は value と判断
            obj.value = val;
        } else {
            if (array.length > 0) obj.fixed = array[0]; // 必要
            if (array.length > 1) obj.unit = array[1];
            // 4: range はスキップ
            if (array.length > 3) obj.menu = array[3];
            if (array.length > 4) obj.type = array[4];
            if (array.length > 5) obj.value = array[5];
            if (array.length > 6) obj.dateFormat = array[6];
        }

        resultObj[label] = obj;
    }
    return resultObj;
}


// _currentSlips には 「現在表示されている伝票の構造」が保持される
var _currentSlips;
function currentSlips(){
    // 伝票のオブジェクト群を入れたオブジェクト
    return _currentSlips;
}
function currentSlip(slipName){
    // 対象となっている伝票のオブジェクト
    return _currentSlips[slipName];
}
function addCurrentSlips(slipName, obj){
    // 表示された各伝票を記憶：その後変更されたデータは反映されていない
    if (!_currentSlips) _currentSlips = new Object();
    
    //alert("addCurrentSlips->"+encodeObject(obj)); //##
    
    _currentSlips[slipName] = obj;
}
function removeCurrentSlips(slipName){
    // 表示された各伝票リストから slipName の伝票を削除
    
    delete _currentSlips[slipName];
}

///// 各伝票の構造とデータを記憶するオブジェクト ///
///////////////////////////////////////////////////////////


function formSeparator(){
	return "<br>　"; // スペースは２バイト文字のスペース
}

function cellForLabel(parentObj, label){
	// parentObj の構造中で label と同じ名称の labelを持つ cell を返す
	if (parentObj == null) return null;
    
	for (var i=0,count=parentObj.children.length; i < count; i++){
		var obj = parentObj.children[i];
		var aLabel = trim(obj.label);
		if (obj.hasChild == 0){
			if (isSame(aLabel, label)) return obj;
		} else {
			var anObj = cellForLabel(obj, label);
			if (anObj && isSame(anObj.label, label)) return anObj;
		}
	}
	return null;
}

function setValueForId(parentCell, cellId, label, value){
	// EDITOR: parentCell の中を解析して id に対応する object に value をセット
	var i, num, count = parentCell.children.length;
	for (i=num=0; i < count; i++){
		var cell = parentCell.children[i];
		if ((cell.hasChild != null) && (cell.hasChild > 0))
			setValueForId(cell, cellId, label, value);
		else if (isSame(cell.id, cellId)){
			cell.label = label;
			cell.value = value;
			return;
		}
	}
}

///////////////////////////////////////////////////
///// showFORM() showEditor() 共通に使われる関数 /////

function valueOfRecord(rec){
	// レコード "50|mg|10..20" の中の値 "50" だけを返す
	// レコード "50 MG|mg|10..20" の場合は "50" を返す
    if (rec && (rec.length > 0)){
        var array = rec.split("|");
        
        var value = array[0];
        var ary = value.split(" ");
        if (ary.length > 1)
            var value = ary[0];
        return value;
    } else {
        return "";
    }
}
function unitOfRecord(rec){
	// レコード "50|mg|10..20" の中の値 "mg" だけを返す
	// レコード "50 MG|mg|10..20" の場合は "MG" を返す
    if (rec && (rec.length > 0)){
        var array = rec.split("|");
        
        var value = array[0];
        var ary = value.split(" ");
        if (ary.length > 1)
            var unit = ary[1];
        else
            var unit = (array.length > 1) ? array[1] : "";
        return unit;
    } else {
        return "";
    }
}
function rangeOfRecord(rec){
	// レコード "50|mg|10..20" の中の値 "10..20" だけを返す
    /*
    if (rec && (rec.length > 0)){
        var array = rec.split("|");
        var range = (array.length > 2) ? array[2] : "";
        return range;
    } else {
        return "";
    }
     */
    return ""; // 旧バージョンとの互換のため
}
function menuOfRecord(rec){
	// レコード "50|mg|10..20|1,2,3" の中の値 "1,2,3" だけを返す
    if (rec && (rec.length > 0)){
        var array = rec.split("|");
        var menu = (array.length > 3) ? array[3] : null;
        return menu; // ### menu ない場合は "" ではなく null を返す
    } else {
        return null;
    }
}
function typeOfRecord(rec){
	// レコード "50|mg|10..20||dateType" の中の値 "dateType" だけを返す
    if (rec && (rec.length > 0)){
        var array = rec.split("|");
        var type = (array.length > 4) ? array[4] : null;
        return type; // ### menu ない場合は "" ではなく null を返す
    } else {
        return "";
    }
}

function titleCell(id){
	// タイトルになる cell を生成
	var obj = new Object();
	obj.id = id + ".0";
	obj.label = "項目名";
	obj.value = "単位（オプション）";
	obj.hasChild = 0;
	obj.children = new Array();
	return obj;
}

function selectedItem(){
	return document.getElementById("itemPopUp").value;
}

function popUpClicked(elm){
    // 伝票名ポップアップ・メニューがクリックされた
    var item = elm.value;
    
    //_initDebug(false); //##
    _debug("popUpClicked->"+currentTag()+"->"+item); //##

	// 選択されたメニュー・アイテムが　"...その他" なら新規登録
	if (item == "...その他"){
        var slipName = prompt("新規伝票名");
        if (slipName.length > 0){
            addNewOrderSlip(slipName); // _menuObj[slipName] を生成
            openSlipEditor(slipName);
            return;
        }
	}
    var cell = parent().cellForTag(currentTag());
    _debug("cell->"+encodeObject(cell)); //##
    _debug("study->"+cell.study); //##
    
    if (cell.study * 1 > 0){ // 頻度学習が on なら頻度学習を行う
        // freq = "" なら、使用されたメニュー項目の頻度を increment
        // ### doNothing の代わりに null や "" を指定すると正常終了しない
        put_group_menu(owner(), item, "", [], doNothing);
    }
}

function addGroupForId(parentCell, cellId, label, aCell){
	// parentCell へ cellId のセルを挿入して返す
	var newCell = new Object();
	newCell.id = parentCell.id;
	newCell.label = parentCell.label;
	newCell.children = new Array();
	newCell.hasChild = (parentCell.hasChild) ? parentCell.hasChild : 0;
	var i, num, count = parentCell.children.length;
	
	for (i=num=0; i < count; i++){
		var cell = parentCell.children[i];
		cell.id = parentCell.id + "." + num; // id を付け直す
		if ((cell.hasChild != null) && (cell.hasChild > 0))
			newCell.children[num++] = addGroupForId(cell, cellId, label, aCell);
		else
			newCell.children[num++] = cell;
		if (isSame(cell.id, cellId)){
			// 新しい行を挿入 value は不要
			aCell.id = parentCell.id + "." + num; // id を付け直す
			newCell.children[num++] = aCell;
		}
	}
	return newCell;
}

function changedMenu(elm, fid){
    // 伝票フィールドのメニューが選択された
    var menuItem = elm.value;
	var fd = document.getElementById(fid);
    if (fd){
        fd.value = menuItem;
        checkValue(fid);
    } else {
        alert(fid + " に相当する入力欄がありません");
    }
}

function removeOrderSlip(slipName){
    // slipName の伝票を画面から削除
    _debug("== removeOrderSlip->"+slipName); //##
    
    // 伝票リストから削除しておく
    removeCurrentSlips(slipName);
    
    // 伝票を再表示
	document.getElementById("editorArea").innerHTML = "";
    var slips = currentSlips(); // 表示されている伝票を入れたオブジェクト
    _debug("slips->"+encodeObject(slips)); //##
    
    for (slipName in slips){
        var slip = slips[slipName]; // 最初に表示時点での伝票構造とデータのオブジェクト
        showOrderSlip(slipName, slip);
    }
}

function slipWithString(value){
    // value 文字列をオブジェクトにして返す
    /* value は以下のような文字列 -------------
     class(||I|I,II,IIIa,IIIb,IV,V)内膜(|||-,+,判定不能)採取器具(綿棒|||スクレーパ,綿棒,頚管ブラシ)メモ(|||炎症性変化,sq.metaplastic cell,軽度核腫大細胞,follow up,atrophic change,扁平上皮化生,酵母様真菌,mild dysplasia,moderate dysplasia,内膜細胞みとめず判定不能,トリコモナス,出血あり内膜も採取,細菌,ホルモン活性の強い細胞診)
     */
    var objects = new Object();
    var records = decodeObject(value);
    for (label in records){
        var ln = records[label];
        var obj = new Object();
        if (ln.length > 0){
            var array = ln.split('|');
            for (num in array){
                // VALUE (and UNIT) ---------------
                var st = array[0];
                var ary = st.split(' ');
                // "50 MG|mg|10..20" なら "50" を obj.fixed に入れる
                obj.fixed = ary[0]; // default 値
                if (ary.length > 1){
                    // "50 MG|mg|10..20" なら "MG" を obj.unit に入れる
                    obj.unit = ary[1];
                }
                // UNIT ---------------------------
                // "50|mg|10..20" なら "mg" を obj.unit に入れる
                // "50 MG|mg|10..20" なら "MG" を obj.unit に入れる
                if (!obj.unit && (array.length > 1)) obj.unit = array[1];
                // RANGE --------------------------
                // "50|mg|10..20" なら "10..20" を obj.range に入れる
                //obj.range = (array.length > 2) ? array[2] : null;
                // MENU ---------------------------
                // "50|mg|10..20|1,2,3" なら "1,2,3" を obj.menu に入れる
                obj.menu = (array.length > 3) ? array[3] : null;
                // TYPE ---------------------------
                // "||||dateType" なら "dateType" を obj.type に入れる
                obj.type = (array.length > 4) ? array[4] : null;
                // DATE FORMAT -------------------
                // "|||||dateFormat" なら "dateFormat" を obj.dateFormat に入れる
                obj.dateFormat = (array.length > 6) ? array[6] : null;
            }
        }
        objects[label] = obj;
    }
    return objects;
}

function addNewOrderSlip(slipName){
    // 伝票名：slipName の伝票オブジェクトを追加
    // 伝票リストに追加しておく
    addCurrentSlips(slipName, menuObjFor(slipName));
}

function moveOrderSlip(label1, label2){
    // label1 を label2 の位置へ移動：その他のレコードは順送りになる
    //_initDebug(true); //##
    _debug("== moveOrderSlip "+label1+"->"+label2); //##
    
    var obj = menuObjFor(_slipName); // 既存の伝票オブジェクト
    //_debug("obj-> "+encodeObject(obj)); //##
    
    var st = obj.value; // "日付(||||)身長(||||)体重(||||)" 形式
    var recs = decodeObject(st); // 既存の伝票オブジェクト
    
    // label1 のフィールドを移動
    var targetRec = recs[label1]; // 移動させるフィールド
    _debug("----- targetRec->"+targetRec); //##
    var newRecs = new Object(); // 並べ替えた伝票オブジェクト
    for (name in recs){
        var rec = recs[name];
        //_debug(name+"->"+rec); //##

        if (name == label1){
            continue;
        } else if (name == label2){
            //_debug("-- put 1->"+label1+"->"+targetRec); //##
            newRecs[label1] = targetRec;
            //_debug("-- put 2->"+name+"->"+rec); //##
            newRecs[name] = rec;
        } else {
            //_debug("-- put->"+name+"->"+rec); //##
            newRecs[name] = rec;
        }
    }
    _debug("moved->"+encodeObject(newRecs)); //##
    
    // 並べ替えの済んだ伝票オブジェクトを記憶
    obj.value = encodeObject(newRecs);

    _debug("obj.value->"+obj.value); //##
    _debug("=========="); //##

    // 伝票エディターを再表示
    openSlipEditor(_slipName);
}

function changeValue(slipName, label, attribute, value){
    // 属性の設定値が変更された
    //_debug("== changeValue->"+slipName+"->"+label+"->"+attribute+"->"+encodeObject(_menuObj[slipName])); //##

    // _menuObj[slipName]['value'] に入っている文字列をオブジェクトに解凍
    var st = _menuObj[slipName]['value'];
    if (st && st.length){
        var rec = decodeObject(st);
        var val = rec[label];
        var array = val.split('|');
    } else {
        var rec = new Object();
        rec[label] = new Object();
        var array = ["","","","","","",""];
    }
    
    // オブジェクト中の該当属性の値をセット
    if (attribute == 'fixed')
        array[0] = value;
    else if (attribute == 'unit')
        array[1] = value;
    //else if (attribute == 'range')
    //    array[2] = value;
    else if (attribute == 'menu')
        array[3] = value;
    else if (attribute == 'type')
        array[4] = value;
    else if (attribute == 'value')
        array[5] = value;
    else if (attribute == 'dateFormat')
        array[6] = value;
    
    // 属性列を | で区切った文字列にエンコードしオブジェクトに格納
    rec[label] = array.join('|');
    
    //_debug("-- rec[label]==>"+rec[label]); //##
    
    // 属性値を更新したオブジェクトをエンコードし再び _menuObj[slipName]['value'] に収納
    _menuObj[slipName]['value'] = encodeObject(rec);
    
    //_debug("=== _menuObj[slipName]->"+encodeObject(_menuObj[slipName])); //##
}

function changeLabel(elm, slipName, label){
    // ラベル（フィールド名）が変更された
    //var newLabel = inner_text(elmFor(label+".label"));
    var newLabel = document.getElementById(label+".label").innerText;
    //_initDebug(true); //##
    _debug("=== changeLabel->"+label+"->"+newLabel); //##
    
    var st = _menuObj[slipName]['value'];
    if (st && st.length){
        var rec = decodeObject(st);
        var val = rec[label];
        
        // 新規フィールドを挿入
        rec[newLabel] = val;
        
        // 変更されたフィールドを削除
        delete rec[label];
        
        // 属性値を更新したオブジェクトをエンコードし再び _menuObj[slipName]['value'] に収納
        _menuObj[slipName]['value'] = encodeObject(rec);
        
        _debug("=== changeLabel->"+encodeObject(_menuObj[slipName])); //##
    }
}
function changeFixed(elm, slipName, label){
    // 規定値が変更された
    changeValue(slipName, label, 'fixed', elm.value);
}
function changeUnit(elm, slipName, label){
    // 単位が変更された
    changeValue(slipName, label, 'unit', elm.value);
}
function changeMenu(elm, slipName, label){
    // メニューが変更された
    var ary = elm.value.split('\n');
    var value = ary.join(',');

    changeValue(slipName, label, 'menu', value);
}
function changeType(slipName, label, val){
    // タイプが変更された
    changeValue(slipName, label, 'type', val);
}
function changeDateType(elm, slipName, label){
    // 日付の型式が変更された ### 日付の表示形式は dateFormat として記憶 ###
    // _menuObj[slipName]['value'] に入っている文字列をオブジェクトに解凍
    var st = _menuObj[slipName]['value'];
    if (st && st.length){
        var rec = decodeObject(st);
        var val = rec[label];
        var array = val.split('|');
    } else {
        var rec = new Object();
        rec[label] = new Object();
        var array = ["","","","","","",""];
    }
    
    // オブジェクト中の日付属性の値をセット
    array[6] = elm.value; // dateFormat

    // 属性列を | で区切った文字列にエンコードしオブジェクトに格納
    rec[label] = array.join('|');
    
    // 属性値を更新したオブジェクトをエンコードし再び _menuObj[slipName]['value'] に収納
    _menuObj[slipName]['value'] = encodeObject(rec);
}

function fieldId(slipName, label){
    // 入力欄の ID を生成して返す
    return slipName + "^" + label; 
}

function setNow(fid){
    // fid の年月日ポップアップに本日の年月日を設定
    var array = fid.split("^"); // fid = slipName + "^" + label
    var slipName = array[0];
    var label = array[1];
    var formObj = formForSlipName(slipName); // 伝票構造フォーマット
    var fObj = formObj[label];

    var dv = document.getElementById(fid+".dateArea");
    dv.innerHTML = "";
    
    // 表示形式に従って日付ポップアップを表示
    var dateArray = arrayWithDateTime(todayAndTime());
    showDatePopup(dv, fid, fObj.dateFormat, dateArray);
}

function showDatePopup(elm, fid, type, dateArray){
    // 表示形式に従って日付ポップアップを表示
    var sp = newSPAN(elm, "");
    sp.innerHTML = "";
    sp.style.paddingRight = "5px";
    var img = newIMAGE(sp, "", "./timer-set.png", "time");
    img.style.height = "14px";
    img.setAttribute("onclick", "setNow('" + fid +"')");
    img.style.position = "relative";
    img.style.top = "5px";
    img.setAttribute("class", "expandIcon");
    var dv = newSPAN(elm, fid+".dateArea");

    var yy = dateArray[0];
    var mm = dateArray[1];
    var dd = dateArray[2];
    // type は rec.menu として記憶されている
    if (type == "年月"){
        var dp = newDatePopUp(dv, fid, "平成", yy, mm, dd, true, NRNoAction);
    } else if (type == "年月日時分"){
        // ## 時分を表示できるポップアップ・メニューを設定
        var h = dateArray[3];
        var m = dateArray[4];
        var s = dateArray[5];
        var dp = newDatePopUp(dv, fid, "平成", yy, mm, dd, false, NRNoAction);
        newBR(dv);
        // 時
        var sp = newSPAN(dv, "");
        sp.style.paddingLeft = "20px";
        var pp = newPopupMenu(sp, fid+".hour", hours(), h);
        var sp = newSPAN(dv, "");
        sp.innerHTML = "時";
        // 分
        var pp = newPopupMenu(dv, fid+".min", minitues(), m);
        var sp = newSPAN(dv, "");
        sp.innerHTML = "分";
    } else { // 年月日タイプ
        var dp = newDatePopUp(dv, fid, "平成", yy, mm, dd, false, NRNoAction);
    }
    
    function hours(){
        var array = [""];
        for (i=0; i < 24; i++) array.push(i);
        return array;
    }
    function minitues(){
        var array = [""];
        for (i=0; i < 60; i++) array.push(i);
        return array;
    }
}

function showFormPanel(){
    // FORM を別ウインドウで開く
    window.open("./form.php","FORM"
                ,"width=500,height=1000,scrollbars=yes,dependent=yes");
}

function showOrderSlip(slipName, records){
    // 伝票を開く
    //_initDebug(true); //##
    //_debug("== showOrderSlip->"+slipName+"->"+encodeObject(records)); //##
    
    addCurrentSlips(slipName, records); // 表示された各伝票を記憶

	var elm = document.getElementById("editorArea");
    
    // HEADER ============================================
    var div = newDIV(elm, "/tool-header");
    // LEFT SIDE ---------------------------------
    var dv = newDIV(div, "/left-side");
    dv.style.width = "60%";
    // クローズ・アイコン
    var im = newIMAGE(dv, "icon", "./close.png", "X");
    im.setAttribute("onclick", "removeOrderSlip('"+slipName+"')");
    im.setAttribute("class", "expandIcon");
    // 伝票名
    var sp = newSPAN(dv, "");
    sp.innerHTML = slipName;
    sp.style.paddingLeft = "10px";
    // RIGHT SIDE ---------------------------------
    var dv = newDIV(div, "/right-side");
    dv.style.position = "relative";
    dv.style.top = "4px";
    // テンプレート・アイコン
    var sp = newSPAN(dv, slipName + ".templateArea");
    sp.style.marginRight = "10px";
    var img = newIMAGE(sp, "icon", "./menu.png", "+");
    var action = "getTemplateMenu('"+slipName+"')";
    img.setAttribute("onclick", action);
    img.style.height = "11px";
    img.setAttribute("class", "expandIcon");
    // ハンマー・アイコン
    var sp = newSPAN(dv, "");
    var img = newIMAGE(sp, "icon", "./hammer.png", "X");
    img.setAttribute("onclick", "openSlipEditor('" + slipName + "')");
    img.setAttribute("class", "expandIcon");
    
    // CONTENTS ============================================
    var pane = newDIV(elm, "");
    pane.style.backgroundColor = "#bfd";
    pane.style.padding = "5px";
    var tbl = newTABLE(pane, "/base-table");
    tbl.style.fontSize = "10pt";

    var formObj = formForSlipName(slipName); // 伝票構造フォーマット
    //_debug("formObj->"+slipName+"->"+encodeObject(formObj)); //##
    //_initDebug(true); //##
    for (label in records){
        var rec = records[label];
        var fid = fieldId(slipName, label);
        
    //    _debug(label+"->"+encodeObject(rec)); //##
    //    _debug(label+"->"+fid); //##
        
        var tr = newTR(tbl, "", "");
        // LABEL
        var td = newTD(tr, "", "");
        td.style.width = "70px";
        td.style.verticalAlign = "top";
        var sp = newSPAN(td, "");
        sp.innerHTML = label;

        // POPUP & VALUE
        var td = newTD(tr, "", "");
        td.style.borderBottom = "thin solid #ccc";
        td.style.paddingLeft = "5px";
        //  VALUE OR DATE POPUP
        if (formObj){
            var fObj = formObj[label];
        }
        if (fObj && (fObj.type == "dateType")){
            var dv = newDIV(td, fid+".dateArea");
            dv.style.padding= "3px 0";
            var dateArray = ["","","","","",""];
            if (rec.value) // メモリーに記憶されていた場合
                dateArray = arrayWithDateTime(rec.value);
            
            // 日付の表示形式で年月日ポップアップを表示：表示形式は dateFormat として記憶
            showDatePopup(dv, fid, fObj.dateFormat, dateArray);
        } else {
            var val = ""; // NOA データがなければ valueOfRecord(rec)
            if (rec.value && (rec.value.length > 0))
                val = rec.value; // メモリーに記憶されていた場合
            else if (rec.fixed && (rec.fixed.length > 0))
                val = rec.fixed; // default 値があった場合
            
            //_debug("-- fixed->"+rec.fixed+"val->"+val); //##
            
            //  POPUP MENU
            if (rec.menu && (rec.menu.length > 0)){
                var array = rec.menu.split(',');
                array.splice(0,0,"");
                var pp = newPopupMenu(td, label+".menu", array, val);
                pp.setAttribute("onchange", "changedMenu(this,'"+fid+"')");
            }
            // VALUE
            var fd = newFIELD(td, fid, "", 20, val);
            // onkeydownn だと iOS の場合 checkValue() 値に入力文字が追加されてしまう
            fd.setAttribute("onkeyup", "checkValue('" + fid + "')");
            checkValue(fid);
            
            // UNIT
            if (rec.unit){
                var sp = newSPAN(td, "");
                sp.innerHTML = rec.unit;
                sp.style.paddingLft = "5px";
            }
        }
    }
}

function addTemplate(){
	// 選択ポップアップ名：item を元にテンプレート取得し伝票ツール画面に追加
	var slipName = selectedItem(); // 伝票名
    
    //_initDebug(true); //##
    _debug("slipName->"+slipName); //##
    
	var obj = formForSlipName(slipName);
    _debug("obj->"+obj); //##
    
	if (obj){
        addCurrentSlips(slipName, obj); // 伝票を画面に追加
        
        // 追加された伝票を表示
        showOrderSlip(slipName, obj);

        // 新規伝票が追加された場合はテンプレート・メニューを開く
        // この後、サーバで slipName の頻度が更新される
        getTemplateMenu(slipName);
    }
}

///// showFORM() showEditor() 共通に使われる関数 /////
///////////////////////////////////////////////////


////////////////////////////////
/// neuron.js による処理 /////////

function showItemPopUp(answer){
	// 検査フォーム名のリストをサーバから読込み
	// ### formEditor.js からも使われる
    //alert("== showItemPopUp ->"+answer); //##
    
	var array = JSON.parse(answer);
    
    //_initDebug(true); //##
	_debug("=== showItemPopUp === "+encodeObject(array)); //##
    
	initItemRecords();
	var menuItems = new Array();
	for (num in array){
		// rec = "owner(ohashi)tag(検査)value(class^[^||I|I,II,IIIa,IIIb,IV,V^]^内膜^[^|||-,+,判定不能^]^採取器具^[^綿棒|||スクレーパ,綿棒,頚管ブラシ^]^メモ^[^|||炎症性変化,sq.metaplastic cell,軽度核腫大細胞,follow up,atrophic change,扁平上皮化生,酵母様真菌,mild dysplasia,moderate dysplasia,内膜細胞みとめず判定不能,トリコモナス,出血あり内膜も採取,細菌^]^)public(0)freq(711)menu(smear)template()" 形式
		var rec = array[num];
        //alert("rec->"+encodeObject(rec)); //##
        
        // template 値を持つものはテンプレート・メニュー用なのでスキップ
        if (rec.template && rec.template.length) continue;
        
        // value（伝票構造）を持たないものはスキップ：過去のバグ・データ除去のため
        if (!rec.value || (rec.value.length == 0)) continue;
        
        addItemRecord(rec.menu, rec);
        
		if (rec.template) continue; // テンプレート用はメニュー・アイテムに入れない
        
        menuItems.push(rec.menu);
	}
    menuItems.push("...その他");
    //alert("=== showItemPopUp ->"+encodeObject(_menuObj)); //##
	
	// テンプレート選択用ポップアップを更新
	var elm = document.getElementById("popUpArea");
	elm.innerHTML = "";
    
    var dv = newDIV(elm, "/left-side");
	dv.style.width = "80%";
	var pu = newPopupMenu(dv, "itemPopUp", menuItems, "");
	pu.setAttribute("onchange", "popUpClicked(this)");
    var bt = newDIV(dv, "/flatButton");
    bt.innerHTML = "追加";
	bt.setAttribute("onclick", "addTemplate()");
    bt.style.position = "relative";
    bt.style.top = "1px";
    bt.style.marginLeft = "3px";
    
    var dv = newDIV(elm, "hammerArea/right-side");
    dv.style.padding = "5px 20px 5px 0";
    dv.style.width = "25px";
    
    
    // =====================================================
	// === カルテから送られた内容を表示 =========================
    // =====================================================
    
    // ### value には HTML コントロール文字が入っているが、このままでよい ###
    // HTML コントロール文字を削除すると [a<=b] のような文字列が壊れてしまう
    var value = cellValue();
    
    //_initDebug(true); //##
    _debug("----- value("+value+")"); //##
    
	if (trim(value).length > 0){
        // 伝票の元となる CELL 記述内容があった
        elmFor("editorArea").innerHTML = ""; // 画面をクリアー
        
        var obj = decodeObject(value);
        //_debug("----- obj("+encodeObject(obj)+")"); //##
        
        // CELL に記述された伝票文字列を解析
        for (slipName in obj){
            var rec = obj[slipName]; // CELL 記述の伝票オブジェクト
            var cellObj = slitToObj(rec);
            _debug("<p>----- "+slipName+"->"+encodeObject(cellObj)+"</p>"); //##
            
            // slipName のテンプレート・オブジェクトを取り出す
            var tempObj = formForSlipName(slipName);
            //_debug("-- recTemp->"+slipName+"->"+encodeObject(tempObj)); //##
            
            // _menuObj に CELL からの rec を merge する
            for (label in cellObj){
                // フィールド・ラベル毎に CELL 内容をテンプレート構造へマージ
                var labelObj = cellObj[label]; // ラベル毎のフィールド・オブジェクト
                var tLabelObj = (tempObj) ? tempObj[label] : null; // テンプレートのフィールド
                if (tLabelObj){
                    // テンプレート属性へ CELL 属性をマージ
                    for (name in tLabelObj){
                        var attribute = tLabelObj[name]; // テンプレートの属性データ
                        var cellAtr = labelObj[name]; // CELL 記述から抽出した属性データ
                        if (cellAtr){
                            // unit が指定されているなら value データから単位名を削除
                            if (name == 'value'){
                                var unit = tLabelObj['unit'];
                                var pos = cellAtr.indexOf(unit);
                                // 単位名の前の空白を含めて削除
                                if (pos > 0) cellAtr = cellAtr.substr(0, pos - 1);
                            }
                            // cellAtr を _menuObj へ上書きする
                            changeValue(slipName, label, name, cellAtr);
                            //_debug("==== cellAtr->"+cellAtr); //##
                        }
                    }
                } else { // label はテンプレートに存在しない属性だった
                    _debug(label+" はテンプレートに存在しないラベル"); //##
                    // _menuObj へ label を追加し tempObj を再読込
                    addNewLabelTo(slipName, label);
                    
                    for (num in _attributes){ // fixed など全ての属性をスキャン
                        var name = _attributes[num]; // テンプレートの属性名
                        var cellAtr = labelObj[name]; // CELL 記述から抽出した属性データ
                        //_debug("==== 属性："+name+"->"+cellAtr); //##
                        if (cellAtr){
                            // cellAtr を _menuObj へ上書きする
                            changeValue(slipName, label, name, cellAtr);
                            //_debug("--- new cellAtr->"+cellAtr); //##
                        }
                    }
                }
            }
            // changeValue() で　_menuObj が更新されたので tempObj を再度読み出す
            var tempObj = formForSlipName(slipName);

            // 伝票リスト（現在表示するもののみ）に追加しておく
            addCurrentSlips(slipName, tempObj); // <==### tempObj フォーマットが違う？
            
            // 追加された伝票を表示
            showOrderSlip(slipName, tempObj);
        }
    }
}
function getItems(){
	// サーバから 検査伝票フォーム のリストを取り寄せ _menuObj として保存
    // ### FORM 用メニューは第２因子が tag ではなく cell.label ###
    // cell.label は "検査" -- MenuTalbe から tag=="検査" を取り出す
    // ちなみに CELL MENU では第２引数が tag
    /* --- MenuTable の内容 ---
     tag: 検査 --- 検査欄であることを示す（ここを tag にすると検査欄メニューになる）
     menu: 尿一般 --- 伝票名
     template: null --- テンプレート・メニュー用では「初期値」など
     value: 伝票構造が入っている
     */
	get_menu(owner(), cellLabel(), showItemPopUp);
}

function updateMenuFreq(slipName){
	// サーバのメニュー項目を更新
    
    // 伝票では第２引数が cell.tag テンプレートでは第２引数が cell.label
    // var value = valueForItem(slipName);
    // ### value があると予期せぬ値がテンプレートに書き込まれるバグを発生することあり
	put_menu(owner(), cellLabel(), slipName, "", "", NRNoAction);
}

/// neuron.js による処理 /////////
////////////////////////////////


///////////////////////////////////////////
///// showFORM() //////////////////////////

function BMI_with(slipName, height, weight){
	// ### BMI 計算のための特殊埋め込み関数 ###
	// height, weight ラベルの値から BMI を計算して返す
	// cell.value はリアルタイムな値ではないので、リアルな値を取る
    var fid = fieldId(slipName, height);
	var elm = document.getElementById(fid);
	if (!elm) return "";
	
	var ht = trim(elm.value) / 100;
	
    var fid = fieldId(slipName, weight);
	var elm = document.getElementById(fid);
	if (!elm) return "";
	
	var wt = trim(elm.value) * 1;
	if (ht * wt == 0) return "";
	
	var bmi = wt / (ht * ht);
	bmi = Math.round(bmi * 10);
	bmi = Math.floor(bmi) / 10;
	return bmi;
}
function EFW_with(slipName, bpdF, aptdF, ttdF, flF){
	// ### EFW:胎児推定体重 計算のための特殊埋め込み関数 ###
    var bpd = 0;
    var aptd = 0;
    var ttd = 0;
    var fl = 0;
    
    var fid = fieldId(slipName, bpdF);
	var elm = document.getElementById(fid);
	if (elm) bpd = trim(elm.value); // フィールド値
    var fid = fieldId(slipName, aptdF);
	var elm = document.getElementById(fid);
	if (elm) aptd = trim(elm.value); // フィールド値
    var fid = fieldId(slipName, ttdF);
	var elm = document.getElementById(fid);
	if (elm) ttd = trim(elm.value); // フィールド値
    var fid = fieldId(slipName, flF);
	var elm = document.getElementById(fid);
	if (elm) fl = trim(elm.value); // フィールド値
    
	var efw = Math.floor((1.07 * bpd*bpd*bpd + 3.42 * aptd * ttd * fl)/1000);
    if (bpd * aptd * ttd * fl == 0) efw = 0;
    
    if (efw > 0){
        return efw;
    } else {
        return "";
    }
}
function TSAT_with(slipName, ironF, tibcF){
    // ### TSAT 計算のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
    // iron, tibc ラベルの値から TSAT を計算して返す
    // cell.value はリアルタイムな値ではないので、リアルな値を取る
    var fid = fieldId(slipName, ironF);
	var elm = document.getElementById(fid);
	if (elm) iron = trim(elm.value); // フィールド値
    
    var fid = fieldId(slipName, tibcF);
	var elm = document.getElementById(fid);
	if (elm) tibc = trim(elm.value); // フィールド値
    if (iron * tibc == 0) return "";
    
    var tsat = iron / tibc *100;
    tsat = Math.round(tsat * 10);
    tsat = Math.floor(tsat) / 10;
    return tsat;
}
function MCV_with(slipName, hematoF, rbcF){
    // ### MCV 計算のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
    // hemato, rbc ラベルの値から MCV を計算して返す
    // BMIの関数を参考にして作成
    var fid = fieldId(slipName, hematoF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var hemato = trim(elm.value)
        
        var fid = fieldId(slipName, rbcF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var rbc = trim(elm.value)
        if (hemato * rbc == 0) return "";
    
    var mcv = hemato / rbc *1000;
    mcv = Math.round(mcv * 10);
    mcv = Math.floor(mcv) / 10;
    return mcv;
}
function MCH_with(slipName, hgbF, rbcF){
    // ### MCH 計算のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
    // hgb, rbc ラベルの値から MCH を計算して返す
    // BMIの関数を参考にして作成
    var fid = fieldId(slipName, hgbF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var hgb = trim(elm.value)
        
        var fid = fieldId(slipName, rbcF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var rbc = trim(elm.value)
        if (hgb * rbc == 0) return "";
    
    var mch = hgb / rbc *1000;
    mch = Math.round(mch * 10);
    mch = Math.floor(mch) / 10;
    return mch;
}
function MCHC_with(slipName, hematoF, hgbF){
    // ### MCHC 計算のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
    // hemato, hgb ラベルの値から MCHC を計算して返す
    // BMIの関数を参考にして作成
    var fid = fieldId(slipName, hematoF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var hemato = trim(elm.value)
        
        var fid = fieldId(slipName, hgbF);
	var elm = document.getElementById(fid);
	if (!elm) return ""
        
        var hgb = trim(elm.value)
        if (hemato * hgb == 0) return "";
    
    var mchc = hgb / hemato *100;
    mchc = Math.round(mchc * 10);
    mchc = Math.floor(mchc) / 10;
    return mchc;
}

function checkValue(fid){
	// 入力された値のチェックを行う
    //_initDebug(true); //##
    //_debug("== checkValue->"+ fid); //##

	var textField = document.getElementById(fid); // dynamically
    
    var ary = fid.split("^"); // fid は "slipName^label" 型式
    var slipName = ary[0]; // 伝票名
    var label = ary[1]; // フィールド・ラベル

    if (label == "BMI"){
		// ### BMI 計算のための特殊埋め込み関数 ###
		var val = BMI_with(slipName, "身長", "体重");
		textField.value = val;
        setFrameColor(textField);
	} else if (label == "EFW"){
		// ### EFW:胎児推定体重 計算のための特殊埋め込み関数 ###
		var val = EFW_with(slipName, "BPD", "APTD", "TTD", "FL");
		textField.value = val;
        setFrameColor(textField);
    } else if (label == "TSAT"){
        // ### TSAT:トランスフェリン飽和度 計算のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
        var val = TSAT_with(slipName, "血清鉄", "TIBC");
        textField.value = val;
        setFrameColor(textField);
    } else if (label == "MCV"){
        // ### MCV:平均赤血球容積のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
        var val = MCV_with(slipName, "ヘマトクリット", "赤血球数");
        textField.value = val;
        setFrameColor(textField);
    } else if (label == "MCH"){
        // ### MCH:平均赤血球容積のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
        var val = MCH_with(slipName, "ヘモグロビン", "赤血球数");
        textField.value = val;
        setFrameColor(textField);
    } else if (label == "MCHC"){
        // ### MCHC:平均赤血球血色素濃度のための特殊埋め込み関数 ### by Dr.K.YAMASAKI
        var val = MCHC_with(slipName, "ヘマトクリット", "ヘモグロビン");
        textField.value = val;
        setFrameColor(textField);
    }
	var value = textField.value; // dynamically
    if (!value || value.length == 0) return;

    // 指定範囲を取り出す
    var slipObj = currentSlip(slipName); // 表示されている伝票を入れたオブジェクト
    
    if (slipObj[label]) parent().checkStandard(textField, label, value);
    
    
    function setFrameColor(elm){
        // 埋め込み関数の設定された入力フィールドの枠の色を薄くする
        elm.style.border = "1px solid #eee";
    }
	
	function label(cellId){
		// cellId に相当するラベル名を返す
		var tr = document.getElementById(cellId);
		var td = tr.childNodes[0]; // left side FORM
		return td.innerHTML;
	}
}

function closeForm(){
	// FORM 全体を閉じる
    if (window.name == "tools")
        document.getElementById("base").innerHTML = "";
    else
        window.close();
}

function saveOrder(){
	// 確定ボタンが押された：伝票のデータを NOA 本体へ転送
    //_initDebug(true); //##
    _debug("== saveOrder"); //##
    
    var withUnit = valueWithUnit(); // データに単位を添えて書き戻すかのステータス
    var slipArray = new Array(); // 伝票群の構造・データを記憶する配列
    var slips = currentSlips(); // 表示されている伝票を入れたオブジェクト
    for (slipName in slips){
        var slip = slips[slipName]; // 最初に表示時点での伝票構造とデータのオブジェクト

        var slipObj = new Object(); // 伝票の各フィールドを記憶するオブジェクト
        for (label in slip){
            var labelObj = slip[label];
            // 通常の値は fid から取得できる
            var fid = fieldId(slipName, label);
            var val = document.getElementById(fid).value;
            _debug(label+"("+val+")"); //##
            
            // 空データの項目は省略
            if ((val.length == 0) && (ignoreBlankForm())) continue;
            
            // 単位表示チェックが入っていれば単位を添えて書き戻す
            if (withUnit && (labelObj.unit.length))
                val += " " + labelObj.unit;

            slipObj[label] = val;

            // 時分のある場合は時分を取得
            var helm = document.getElementById(fid+".hour");
            if (helm){
                var dateTime = val; // "2013-09-19" のような型式：時刻はない場合あり
                var times = arrayWithDateTime(dateTime); // 日時の配列：時分秒も
                
                // 配列の時の部分を置換
                times[3] = (helm.value.length > 0) ? helm.value : "00"; 
                
                var melm = document.getElementById(fid+".min");
                if (melm) 
                    times[4] = (melm.value.length > 0) ? melm.value : "00"; // 配列の分の部分を置換
                
                var st = dateTimeWithArray(times);
                if (times[0] * times[1] * times[2] == 0)
                    slipObj[label] = " ";
                else
                    slipObj[label] = st;
            }
            
            // 整形モードの場合、各項目の末尾に改行を入れる
            //if (formatMode()) slipObj[label] += "#<br>";
        }
        var recObj = new Object(); // 一伝票の構造・データを含むオブジェクト
        recObj[slipName] = slipObj;
        var rec = encodeObject(recObj); // １伝票の構造・データを含む文字列

        slipArray.push(rec); // １伝票の文字列を配列に入れる
    }
    var value = slipArray.join("<br>"); // 伝票文字列の区切りに改行を入れる
    _debug("<p>value->"+value+"</p>"); //##
    
    // 本日のページから開かれた場合：DB へ直書きせず NOA の field へ書き戻す
    var status = document.getElementById("touchAndGoCheck").checked;
    gotValueFromTool(value, status);
    
    // FORM パネルを閉じる
    if (status) closeForm();
}

///// showFORM() //////////////////////////
///////////////////////////////////////////

function initForm(){
	var base = document.getElementById("base");
    base.innerHTML = "";

    // ===== FORM 表示エリア ==========================
    var elm = newDIV(base, "formBase");
    // === HEADER =======================
    var div = newDIV(elm, "/tool-header");
    // --- LEFT SIDE ---
    var dv = newDIV(div, "/left-side");
    dv.style.width = "40%";
    var sp = newSPAN(dv, "");
    sp.style.marginLeft = "5px";
    sp.innerHTML = "伝票";
    // --- RIGHT SIDE ---
    var dv = newDIV(div, "/right-side");
    dv.style.width = "30%";
    // 外部ウインドー・アイコン -------
    if (window.name == "tools"){
        var sp = newSPAN(dv, "outWindow/outArrowArea");
        sp.setAttribute("onclick", "showFormPanel()");
        sp.style.marginRight = "10px";
        setInfoTip("outWindow", "別ウインドーで開く"); // HELP
        var img = newIMAGE(sp, "", "./outArrow.png", "?");
        img.style.height = "12px";
        img.style.position = "relative";
        img.style.top = "3px";
        img.setAttribute("class", "expandIcon");
    }
    // HELP ICON
	var im = newIMAGE(dv, "", "./help.png", "?");
	im.setAttribute("onclick", "openHelp('formHelp.html')");
	im.style.height = "18px";
    im.setAttribute("class", "expandIcon");

	// popUpMenu
    var div = newDIV(elm, "popUpArea");
	
	// ===== 編集エリア ==================================
    var div = newDIV(elm, "editorArea");

    // === FOOTER =======================
    var div = newDIV(elm, "/tool-footer");
    // --- LEFT SIDE ---
    var dv = newDIV(div, "/right-side");
    dv.style.paddingRight = "0px";
    // 単位表示
    var sp = newSPAN(dv, "");
    sp.style.paddingRight = "5px";
    var cb = newCHECKBOX(sp, "withUnitCheck", "単位表示", valueWithUnit());
    cb.setAttribute("onchange", "setValueWithUnit(this)");
    // 「空欄を書き戻さない」チェックボックスを表示
    var sp = newSPAN(dv, "");
    sp.style.paddingRight = "5px";
	var cb = newCHECKBOX(sp, "", "空欄無視", ignoreBlankForm());
	cb.setAttribute("onclick", "setIgnoreBlankForm(this)"); // localStorage.js
    /* // 「整形」チェックボックスを表示
     var sp = newSPAN(elm, "");
     sp.style.paddingRight = "5px";
     var cb = newCHECKBOX(sp, "", "整形", formatMode());
     cb.setAttribute("onclick", "setFormatMode(this)"); // localStorage.js
     */
    // 確定保存チェックボックス
    var cb = newCHECKBOX(dv, "touchAndGoCheck", "確定保存", isFormTouchAndGo());
    cb.setAttribute("onchange", "setFormTouchAndGo(this)");
    // 確定ボタン
    var bt = newDIV(dv, "/fixButton");
    bt.innerHTML = "確定";
    bt.style.margin = "0 10px";
    bt.setAttribute("onclick", "saveOrder()");
    
    // VERSION
    var div = newDIV(base, "");
    div.innerHTML = fm_version();
    div.style.padding = "3px 0 0 10px";
    div.style.fontSize = "9pt";
    div.style.color = "#aaa";
    
	// ポップアップメニューを読み込み、カルテデータをセット
	getItems();
}

function fm_version(){
	return "Ver.140614";
}
