
/* ------------------------
main.js
--必須のもの、onload関数

*グローバル関数
*ControlFunc - グローバル関数寄せ集め
*Const
*window.onload
*/

var Control;
var Root;

/* ====================
** 設定値
*/
function Config() {
}

// スクリプト中で勝手に書き換える
Config.isAddOn = false;
Config.isMML = false;

/* ====================
** グローバル関数
*/

// デバッグ用
function printCsl(str) {
    var Console = document.getElementById('console');
    Console.replaceChild(document.createTextNode(str), Console.firstChild);
}

// 継承を実現するグローバル関数
function inherit(subClass, superClass) {
    var Temp = new Function();
    Temp.prototype = superClass.prototype;
    subClass.prototype = new Temp;
    subClass.prototype.constructor = subClass;
}

// 既定のインスタンス変数を設定
function setProperties(currentClass, props) {
    for(var key in props) {
        currentClass.prototype[key] = props[key];
    }
}

/* ====================
** グローバルにいろいろするクラス
*/
function ControlFunc() {
    this.idnum = 1;
    this.focusID = '';
    this.timeoutObj = ''; // グローバルから見えない場所からsetTimeoutを使用するとき用
    //this.dummyInput = document.getElementById('dummyInput');
    this.previewarea = document.getElementById('preview');
    if(!Config.isMML) {
        this.img = document.createElement('img');
        this.previewarea.appendChild(this.img);
    }
    
    // ユニークなID番号を発行
    this.issueID = function() {
        return this.idnum++;
    }
    
    // 現在フォーカスがある場所を登録する
    this.submitFocus = function(obj, id) {
	var prevObj = this.focusObj;
        this.focusObj = obj;
        this.focusID = id;
	if(prevObj) {
	    prevObj.setIsHide();
	    prevObj.layout();
	}
	this.focusObj.isHidden = false;
	this.focusObj.layout();
    }
    
    // 自動プレビュー処理
    this.updatePreview = function() {
        if(document.getElementById('check_livepreview').checked) {
            clearTimeout(this.previewTimeout);
            this.previewTimeout = setTimeout('Control.preview()', 500);
        }
    }
    
    // プレビュー
    this.preview = function() {
        if(Config.isMML == true) {
            this.previewarea.innerHTML = Root.outputMathML(true);
        }
        else{
            this.img.src = encodeURI(Const.mimeTexURI + Const.backslash + 'Large ' + Root.outputLatex());
        }
    }

    // 位置を動かす
    this.setPosition = function(obj, left, top) {
        obj.style.left = left + 'px';
        obj.style.top = top + 'px';
    }
    
    // 大きさを変更する
    this.setSize = function(obj, width, height) {
        obj.style.width = width + 'px';
        obj.style.height = height + 'px';
    }
    
    // タグ付けする
    this.applyTag = function(tag, content) {
        return '<' + tag + '>' + content + '</' + tag + '>';
    }
    
    // ボタンを置く
    this.setupButton = function() {
        var mis = new SymbolButtons('identifier', Symbol.identifiers);
        var mos = new SymbolButtons('operator', Symbol.operators);
    }

}

/* ====================
** 定数クラス
*/
function Const() {
}

Const.version = '0.3.20060806';
Const.imgDir = 'img/';
Const.containerImgDir = 'button/';
Const.rowSpacing = 6;
Const.entity = new Object;
Const.mimeTexURI = 'mimetex.cgi?';
Const.backslash = '\\';
Const.tokenWidth = [0, 10, 7];
Const.tokenHeight = [0, 22, 18];
Const.tokenMimeSize = ['', 'large', 'normal'];
Const.containerButtonSize = [36, 26];

/* ====================
** main
*/
onload = function() {
    
    // グローバルインスタンス作成
    Control = new ControlFunc();
    Root = new MathFormulaContainer(document.getElementById('formula'));
    
    // 文字実体参照登録
    var ents = document.getElementById('entityReferences').getElementsByTagName('span');
    for(var i = 0; i < ents.length; i++) {
        var entid = ents[i].id.substring(ents[i].id.indexOf('_') + 1);
        Const.entity[entid] = ents[i].firstChild.nodeValue;
    }

    // ボタン作成
    var containerbuttons = new ContainerButtons();
    Symbol.setupSymbols();

    // ソース出力部管理オブジェクト生成
    var sourcearea = new SourceArea();

    // バージョン埋め込み
    document.getElementById('version').appendChild(document.createTextNode('version ' + Const.version));

    // プレビューボタンハンドラ設定
    document.getElementById('button_preview').onmouseup = function() {
        Control.preview();
    }

    // アドオン用のボタン設定
    if(Config.isAddOn == true) {
	var additions = new AddOn();
    }
}


/* ------------------------
Symbol.js
*/

/* ====================
** 実体参照シンボルが書いてある画像かテキストノードを格納したspan
*/
function Symbol(wrap, tid, fixsize) {
    this.id = tid;
    this.level = wrap.level;
    this.obj = document.createElement('span');
    this.obj.className = 'symbol ' + ((this.level == 1) ? 'level1' : 'level2');
    if(Config.isMML) {
        this.expand = Const.entity[this.id];
        this.node = document.createTextNode(this.expand);
        this.obj.appendChild(this.node);
        this.width = Const.tokenWidth[this.level];
        this.height = Const.tokenHeight[this.level];
    }
    else {
        this.tex = Symbol.symbols[this.id].tex;
        this.node = document.createElement('img');
        var simg = Symbol.symbols[this.id].img[this.level];
        this.node.src = simg.obj.src;
        if(fixsize) {
            var s = Const.tokenHeight[this.level] - 2;
            this.node.width = Math.min(simg.width, s-2);
            this.node.height = Math.min(simg.height, s-2);
            this.node.hspace = Math.floor((s - this.node.width) / 2);
            this.node.vspace = Math.floor((s - this.node.height) / 2);
        }
        else {
            this.width = simg.width;
            this.height = simg.height;
        }
        this.obj.appendChild(this.node);
    }
}

// 必要サイズ応答
Symbol.prototype.getPreferredSize = function() {
    return {
        top: this.height / 2,
        bottom: this.height / 2,
        height: this.height,
        width: this.width
    };
}

Symbol.prototype.removeObj = function() {
    if(!Config.isMML) {
        this.obj.removeChild(this.node);
    }
    this.obj.parentNode.removeChild(this.obj);
}

Symbol.prototype.getMathML = function(expand) {
    return (expand) ? (Const.entity[this.id]) : ('&' + this.id + ';');
}

Symbol.prototype.getLatex = function() {
    return Const.backslash + Symbol.symbols[this.id].tex;
}

// クラス変数
Symbol.operators = {
    sdot: {tex: 'cdot'},
    times: {tex: 'times'},
    divide: {tex: 'div'},
    
    PlusMinus: {tex: 'pm'},
    MinusPlus: {tex: 'mp'},
    NotEqual: {tex: 'neq'},
    lE: {tex: 'leq'},
    gE: {tex: 'geq'},
    Sub: {tex: 'subset'},
    Sup: {tex: 'supset'},
    sube: {tex: 'subseteq'},
    supe: {tex: 'supseteq'},
    isin: {tex: 'in'},
    ni: {tex: 'ni'},
    cup: {tex: 'cup'},
    cap: {tex: 'cap'},
    larr: {tex: 'leftarrow'},
    rarr: {tex: 'rightarrow'},
    lArr: {tex: 'Leftarrow'},
    rArr: {tex: 'Rightarrow'},
    hArr: {tex: 'Leftrightarrow'},
    cir: {tex: 'circ'},
    ctdot: {tex: 'cdots'},
    equiv: {tex: 'equiv'},
    perp: {tex: 'perp'}
};
//  gE: {tex: 'geqq', isImg: true},
//  lE: {tex: 'leqq', isImg: true},
//  efDot: {tex: 'fallingdotseq', isImg: true}
//  bsim: {tex: 'backsim'},
//  parsl: {},
//  nparsl: {},

Symbol.identifiers = {
    alpha: {tex: 'alpha'},
    beta: {tex: 'beta'},
    delta: {tex: 'delta'},
    epsiv: {tex: 'varepsilon'},
    gamma: {tex: 'gamma'},
    omega: {tex: 'omega'},
    phi: {tex: 'phi'},
    phiv: {tex: 'varphi'},
    theta: {tex: 'theta'},
    Delta: {tex: 'Delta'},
    pi: {tex: 'pi'},
    infin: {tex: 'infty'},
    utri: {tex: 'triangle'}
};
//  angle: {tex: 'angle', isImg: true}
//  deg: {tex: 'deg', isImg: true},

Symbol.parts = {
    sum: {tex: 'sum'},
    math_int: {tex: 'int'}
};

Symbol.symbols = new Object;

// シンボルのmimeTeX画像先読み
Symbol.setupSymbols = function() {
    Symbol.loadedNumber = 0;
    var processed = 0;
    // シンボル連結
    for(var ids in Symbol.identifiers) {
        Symbol.symbols[ids] = Symbol.identifiers[ids];
    }
    for(var ops in Symbol.operators) {
        Symbol.symbols[ops] = Symbol.operators[ops];
    }
    for(var ops in Symbol.parts) {
        Symbol.symbols[ops] = Symbol.parts[ops];
    }
    for(var syb in Symbol.symbols) {
        Symbol.symbols[syb].img = new Array;
        for(var i = 1; i <= 2; i++) {
            processed++;
            Symbol.symbols[syb].img[i] = new Object;
            Symbol.symbols[syb].img[i].obj = document.createElement('img');
            Symbol.symbols[syb].img[i].obj.onload = function() {
                Symbol.loadedNumber++;
                if(Symbol.loadedNumber == processed) {
                    Symbol.setImgSize();
                }
            }
            Symbol.symbols[syb].img[i].obj.src = Const.mimeTexURI + Const.backslash + Const.tokenMimeSize[i] + ' ' + Const.backslash + Symbol.symbols[syb].tex;
        }
    }

}

Symbol.setImgSize = function() {
    for(var syb in Symbol.symbols) {
        for(var i = 1; i <= 2; i++) {
            Symbol.symbols[syb].img[i].width = Symbol.symbols[syb].img[i].obj.width;
            Symbol.symbols[syb].img[i].height = Symbol.symbols[syb].img[i].obj.height;
        }
    }
    Control.setupButton();
}



/* ------------------------
parts.js
-- コンテナオブジェクト内部品, その他部品

*Separator - 横線
*StringSpan - 文字
*----
*MathFormulaContainer - つまりMathRowの番兵
*SymbolButtons - 実体参照文字入力ボタンの行
*ContainerButtons - コンテナオブジェクト挿入ボタンの行
*/

/* ====================
** ただの横線
*/
function Separator(wrap) {
    this.objType = 'separator';
    this.obj = document.createElement('div');
    this.obj.className = 'separator';
}

// 表示部品削除
Separator.prototype.removeObj = function() {
    this.obj.parentNode.removeChild(this.obj);
}

/* ====================
** ただの文字
*/
function StringSpan(wrap, str) {
    this.objType = 'stringspan';
    this.str = str;
    this.level = wrap.level;
    this.obj = document.createElement('span');
    this.obj.className = 'stringspan ' + ((this.level == 1) ? 'level1' : 'level2');
    this.node = document.createTextNode(this.str);
    this.obj.appendChild(this.node);
    this.width = this.str.length * Const.tokenWidth[this.level];
    this.height = Const.tokenHeight[this.level];
}

// 必要サイズ応答
StringSpan.prototype.getPreferredSize = function() {
    return {
        top: this.height / 2,
        bottom: this.height / 2,
        height: this.height,
        width: this.width
    };
}

// 表示部品削除
StringSpan.prototype.removeObj = function() {
    this.obj.parentNode.removeChild(this.obj);
}

//StringSpan.levelWidth = [0, 10, 7];
//StringSpan.levelHeight = [0, 16, 12];

/* ====================
** 基底に置くオブジェクト
*/
function MathFormulaContainer(par) {
    this.backCursor = function() {
    }
    
    this.fwdCursor = function() {
    }
    // 再配置 された時にはプレビューを更新
    this.layout = function(){
        Control.updatePreview();
    }
    
    // 式全体のMathMLコード書き出し
    this.outputMathML = function(expand) {
        var out = '<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">' + "\n";
        out += this.content.outputMathML(expand);
        out += '</math>' + "\n";
        return out;
    }
    
    this.outputLatex = function() {
        return this.content.outputLatex(true);
    }
    
    this.content = new MathRow(this, 1);
    par.appendChild(this.content.obj);
    // 選択
    this.content.setInitialValue('');
    
}

/* ====================
** ボタンまとめオブジェクト
*/
function SymbolButtons(ttype, syms) {
    this.type = ttype;
    this.wrap = document.getElementById(this.type + '_buttons');
    this.buttons = new Array;
    this.level = 1;
    var self = this;
    var i = 0;
    for(var sym in syms) {
        var obj = document.createElement('div');
        obj.className = 'function_button symbol_button';
        obj.id = 'button_' + sym;
        obj.onclick = function() {
            self._myOnClick(this);
        }
        var symbol = new Symbol(this, sym, true);
        this.buttons.push(symbol);
        obj.appendChild(symbol.obj);
        this.wrap.appendChild(obj);
        Control.setSize(obj, Const.tokenHeight[1] + 2, Const.tokenHeight[1] + 2);
        Control.setPosition(obj, i++ * (Const.tokenHeight[1] + 6), 0);
    }
}

SymbolButtons.prototype._myOnClick = function(btn) {
    var bid = btn.id.substring(btn.id.indexOf('_') + 1);
    Control.focusObj.insert(new MathSymbol(Control.focusObj.wrap, bid, this.type));
}

/* ====================
** コンテナオブジェクト挿入ボタン管理
*/

function ContainerButtons() {
    this.wrap = document.getElementById('container_buttons');
    this.buttons = new Array;
    var count = 0;
    var self = this;
    for(var i = 0; i < ContainerButtons.buttonArray.length; i++) {
        if(ContainerButtons.buttonArray[i] != null) {
        var btn = ContainerButtons.buttonArray[i];
            var obj = document.createElement('div');
            obj.className = 'container_button';
            obj.id = 'containerbutton_' + i;
            obj.onclick = function() {
                self._myOnClick(this);
            }
            Control.setSize(obj, Const.containerButtonSize[0] + 4, Const.containerButtonSize[1] + 2);
            var img = document.createElement('img');
            img.src = Const.containerImgDir + btn.name + '.png';
            obj.appendChild(img);
            this.wrap.appendChild(obj);
            Control.setPosition(obj, count++ * (Const.containerButtonSize[0] + 8), 0);
        }
    }
}

// 申請受付用配列
ContainerButtons.buttonArray = new Array;

// ボタン表示の申請受付
ContainerButtons.submit = function(tid, tname, tfunc) {
    ContainerButtons.buttonArray[tid] = {name: tname, func: tfunc};
}

// private
ContainerButtons.prototype._myOnClick = function(btn) {
    var i = btn.id.substring(btn.id.indexOf('_') + 1);
    Control.focusObj.insert(ContainerButtons.buttonArray[i].func(Control.focusObj.wrap));
}

/* ====================
** ソース出力部分管理
*/
function SourceArea() {
    this.obj = document.getElementById('sourceOutput');
    this.isVisible = false;
    
    var self = this;

    // ボタンハンドラ設定
    document.getElementById('button_viewmathml').onmouseup = function() {
	self.put(Root.outputMathML(false));
    }
    document.getElementById('button_viewlatex').onmouseup = function() {
	self.put(Root.outputLatex());
    }
}

// 渡された内容を表示
SourceArea.prototype.put = function(str) {
    if(this.isVisible == false) {
	this.obj.style.display = 'block';
	this.isVisible = true;
	this.height = 1;
	this.appearAnim();
    }
    this.obj.value = str;
}

// 表示アニメーション
SourceArea.prototype.appearAnim = function() {
    if(this.height <= 10) {
	this.obj.style.height = this.height + 'em';
	this.height += 3;
	Control.timeoutObj = this;
	setTimeout("Control.timeoutObj.appearAnim()", 60);
    }
}


/* ------------------------
ContainerAbst.js
-- コンテナオブジェクト系抽象クラス

*ContainerBase
*Container
*/

/* ====================
** (コンテナオブジェクト抽象クラス + MathRowオブジェクト) 抽象クラス
*/
function ContainerBase(par) {
}

ContainerBase.prototype.constructBase = function(par) {
    this.wrap = par;
    this.obj = document.createElement('div');
    this.obj.id = this.objType + Control.issueID();
}

// 必要サイズ応答
ContainerBase.prototype.getPreferredSize = function() {
    return {
        top: this.top,
        bottom: this.bottom,
        height: this.height,
        width: this.width
    };
}

// 再配置
ContainerBase.prototype.layout = function() {
    this._layout();
    this.wrap.layout();
}

/* ====================
** コンテナオブジェクト抽象クラス
*/
function Container(par) {
}

inherit(Container, ContainerBase);

Container.prototype.construct = function(par, levels) {
    this.constructBase(par);
    this.obj.className = 'Container';
    this.rows = new Array;
    for(var i = 0; i < this.rowCount; i++) {
        this.rows[i] = new MathRow(this, (levels) ? levels[i] : this.wrap.level);
        this.obj.appendChild(this.rows[i].obj);
    }
    this.content = this.rows[0];
}

// 初期の内容を決める
Container.prototype.setInitialValue = function(val) {
    this.content.setInitialValue(val);
}

// 前のブロックにカーソルを移動
Container.prototype.backCursor = function(bid) {
    if(bid == this.rows[0].obj.id) {
        this.wrap.backCursor(this.obj.id);
    }
    else {
        for(var i = 1; i < this.rowCount; i++) {
            if(bid == this.rows[i].obj.id) {
                this.rows[i-1].setCursor(-1);
                return;
            }
        }
    }
}

// 次のブロックにカーソルを移動
Container.prototype.fwdCursor = function(bid) {
    if(bid == this.rows[this.rowCount - 1].obj.id) {
        this.wrap.fwdCursor(this.obj.id);
    }
    else {
        for(var i = 0; i < this.rowCount - 1; i++) {
            if(bid == this.rows[i].obj.id) {
                this.rows[i+1].setCursor(0);
                return;
            }
        }
    }
}

// 端にカーソルを置く
Container.prototype.setCursor = function(num) {
    if(num == 0) {
        this.rows[0].setCursor(0);
    }
    else if(num == -1) {
        this.rows[this.rowCount - 1].setCursor(-1);
    }
}

// MathMLコード書き出し
Container.prototype.outputMathML = function(expand) {
    var ret = '';
    for(var i = 0; i < this.rowCount; i++) {
        ret += this.rows[i].outputMathML(expand);
    }
    return this._tagMML(ret);
}

// LaTeXコード書き出し
Container.prototype.outputLatex = function() {
    var ret = '';
    for(var i = 0; i < this.rowCount; i++) {
        ret += this.rows[i].outputLatex();
    }
    return this._commandTex(ret);
}

// 表示部品削除
Container.prototype.removeObj = function() {
    this._removeObj();
}

// ---------- private

// 表示部品削除
Container.prototype._removeObj = function() {
    for(var i = 0; i < this.rowCount; i++) {
        this.rows[i].removeObj();
    }
    this.obj.parentNode.removeChild(this.obj);
}

// MathMLコード書き出し
Container.prototype._tagMML = function(content) {
    if(this.mathmlTag != '') {
        var out = '<' + this.mathmlTag + '>' + "\n";
        out += content;
        out += '</' + this.mathmlTag + '>' + "\n";
        return out;
    }
}

// LaTeXコード書き出し
Container.prototype._commandTex = function(content) {
    if(this.texCommand != '') {
        return Const.backslash + this.texCommand + content;
    }
}

// 計算して保存
Container.prototype._calcPosition = function(otop, obottom, owidth) {
    var prefer = this.content.getPreferredSize();
    this.top = prefer.top + otop;
    this.bottom = prefer.bottom + obottom;
    this.height = this.top + this.bottom;
    this.width = prefer.width + owidth;
}


/* ------------------------
Token.js
-- トークンオブジェクト

*Token - 抽象クラス
*MathSymbol - 実体参照文字
*MathString - sin, cos, ...
*/

/* ====================
** トークンオブジェクト抽象クラス
*/
function Token() {
}

Token.prototype.construct = function(par) {
    this.wrap = par;
    this.obj = document.createElement('div');
    this.obj.id = this.objType + Control.issueID();
    this.obj.className = 'Token';
}

// 初期の内容は入らないので破棄する
Token.prototype.setInitialValue = function(val) {
    this.wrap.layout();
    this.wrap.fwdCursor(this.obj.id);
}

// 端にカーソルを置く ことはできないので隣へ移動
Token.prototype.setCursor = function(num) {
    if(num == 0) {
        this.wrap.fwdCursor(this.obj.id);
    }
    else if(num == -1) {
        this.wrap.backCursor(this.obj.id);
    }
}

// 必要サイズ応答
Token.prototype.getPreferredSize = function() {
    return {
        top: this.height / 2,
        bottom: this.height / 2,
        height: this.height,
        width: this.width
    };
}

// MathMLコード書き出し
Token.prototype.outputMathML = function(expand) {
    return this._tagMML(this.value);
}

// 表示部品削除
Token.prototype.removeObj = function() {
    this.obj.parentNode.removeChild(this.obj);
}

// ----------private
// MathMLコード書き出し
Token.prototype._tagMML = function(content) {
    var tag = (this.tagType == 'identifier') ? 'mi' : 'mo';
    var out = '<' + tag + '>';
    out += content;
    out += '</' + tag + '>';
    return out;
}


/* ====================
** 実体参照シンボル
*/
function MathSymbol(par, tid, tagtype) {
    this.construct(par);
    this.id = tid;
    this.tagType = tagtype;
    this.level = this.wrap.level;
    this.obj.className += ' math_symbol';
    this.content = new Symbol(this, this.id, true);
    this.obj.appendChild(this.content.obj);
    this.width = this.height = Const.tokenHeight[this.level];
    //this.top = this.bottom = this.height / 2;
    Control.setSize(this.obj, this.width, this.height);
}

inherit(MathSymbol, Token);

setProperties(MathSymbol, {
    objType: 'symbol'
});

// MathMLコード書き出し
MathSymbol.prototype.outputMathML = function(expand) {
    return this._tagMML(this.content.getMathML(expand));
}

// LaTeXコード書き出し
MathSymbol.prototype.outputLatex = function() {
    return this.content.getLatex();
}

/* ====================
** 関数とか
*/
function MathString(par, val) {
    this.construct(par);
    this.value = val;
    this.level = this.wrap.level;
    this.content = new StringSpan(this, this.value);
    this.obj.appendChild(this.content.obj);
    
    // レイアウト
    this.width = this.content.width;
    this.height = this.content.height;
    Control.setSize(this.obj, this.width, this.height);
}

inherit(MathString, Token);

setProperties(MathString, {
    objType: 'string',
    tagType: 'identifier'
});

// LaTeXコード書き出し
MathString.prototype.outputLatex = function() {
    return Const.backslash + this.value;
}




/* ------------------------
MathInput.js
*/

/* ====================
** inputフォーム管理オブジェクト（<mi><mo><mn>相当）
*/
function MathInput(par, v) {
    this.wrap = par;
    this.objType = 'input';
    this.obj = document.createElement('textarea');
    this.obj.className = (this.wrap.level == 1) ? 'level1' : 'level2';
    this.level = this.wrap.level;
    this.obj.id = 'input' + Control.issueID();
    this.selection = new Selection(this.obj);
    this.isHidden = (v == 'visible') ? false : true;
    var self = this;
    this.optimizeSize();
    
    // イベントハンドラ指定
    this.obj.onkeydown = function(evt) {
	self._myKeyDown(evt);
    }
    this.obj.onkeypress = function(evt) {
        return self._myKeyPress(evt);
    }
    this.obj.onkeyup = function(evt) {
        return self._myKeyUp(evt);
    }
    this.obj.onfocus = function() {
        Control.submitFocus(self, self.obj.id);
	// self.isHidden = false;
	// self.layout();
    }
    this.obj.onblur = function() {
	//self.isHidden = self._isHide();
	//self.layout();
    }
}

// ---- クラス変数

// オブジェクトに置き換える文字列
MathInput.insertStrings = {
    'sin': function(w) {return new MathString(w, 'sin')},
    'cos': function(w) {return new MathString(w, 'cos')},
    'tan': function(w) {return new MathString(w, 'tan')},
    'log': function(w) {return new MathString(w, 'log')},
    'sqrt': function(w) {return new MathSqrt(w)},
    'frac': function(w) {return new MathFrac(w)},
    'lim': function(w) {return new MathLimit(w)}
}

// キーコードで呼び出す関数
MathInput.keyCodeSwitch = new Array;
MathInput.keyCodeSwitch[8] = '_onBackSpace';
MathInput.keyCodeSwitch[37] = '_onLeftArrow';
MathInput.keyCodeSwitch[39] = '_onRightArrow';
MathInput.keyCodeSwitch[38] = '_onUpArrow';
MathInput.keyCodeSwitch[40] = '_onDownArrow';


// ---- global

// 指定位置にカーソルを置く
MathInput.prototype.setCursor = function(num) {
    if(num == -1) num = this.obj.value.length;
    this.selection.setCursor(num);
}

// inputフォーム中にフォーカスがある状態で機能ボタンが押された時の動作
MathInput.prototype.insert = function(insertObj) {
    var selpos = this.selection.create();
    var txt = this.obj.value;
    var prefix = txt.substring(0, selpos.start);
    var content = txt.substring(selpos.start, selpos.end);
    var postfix = txt.substring(selpos.end, txt.length);
    var postobj = new MathInput(this.wrap, ((postfix != '') ? 'visible' : ''));
    var thispos = this.wrap.getPos(this.obj.id);
    this.wrap.add(postobj, thispos + 1);
    this.wrap.add(insertObj, thispos + 1);
    this.obj.value = prefix;
    postobj.obj.value = postfix;
    postobj.optimizeSize();
    this.optimizeSize();
    insertObj.setInitialValue(content);
}

// 文字を囲ってコンテナを作った場合
MathInput.prototype.setInitialValue = function(val) {
    this.obj.value = val;
    this.selection.selectAll();
    this.layout();
}

// 隠すかどうかを設定
MathInput.prototype.setIsHide = function() {
    var pos = this.wrap.getPos(this.obj.id);
    this.isHidden =
	((this.wrap.array.length > 1) // 行に2つ以上要素があって
	 && (this.obj.value.length == 0) // 中身がなくて
	 // (右隣があって必要とされていて、なおかつ左隣がない) の逆ならば隠す
	 && ((pos > 1) || (pos == this.wrap.array.length - 1) || (!this.wrap.array[pos + 1].isNeedPrev)));
}

// 親への伝播なしの再配置
MathInput.prototype.optimizeSize = function() {
    this.obj.className = (this.wrap.level == 1) ? 'level1' : 'level2';
    this.obj.className += (this.isHidden) ? ' hidden' : '';
    this.obj.className += (this.obj.id == Control.focusID) ? ' selected' : '';
    this.obj.style.width = this._calcWidth() + 'px';
}

// 再配置
MathInput.prototype.layout = function() {
    this.optimizeSize();
    this.wrap.layout();
}

// 必要サイズを応答
MathInput.prototype.getPreferredSize = function() {
    var h = Const.tokenHeight[this.level] + 4;
    return {
        top: h / 2,
        bottom: h / 2,
        height: h,
        width: this._calcWidth() + 4
    };
}

// MathMLコード書き出し
MathInput.prototype.outputMathML = function(expand) {
    var v = this.obj.value;
    if(v == '') return null;
    v = v.replace(/</, "&lt;");
    v = v.replace(/>/, "&gt;");
    v = v.replace(/\//g,"//"); // スラッシュ記号が判別不可能になるので一時退避
    v = v.replace(/([a-zA-Z]+)/g,"<mi>$1</mi>");
    v = v.replace(/(\d+(\.\d+)?)/g, "<mn>$1</mn>");
    v = v.replace(/([-=+\*]|\/\/)/g, "<mo>$1</mo>");
    v = v.replace(/\/\//g, "/"); //スラッシュ記号を戻す
    v = v.replace(/(<\w+>[^<]+?<\/\w+>$)/, ""); // 最後の要素を分離
    //alert(this.obj.value + ' /// ' + v + ' /// ' +  RegExp.$1);
    return {
        front: v, 
        rear: RegExp.$1
    };
}

// LaTeXコード書き出し
MathInput.prototype.outputLatex = function() {
    return this.obj.value;
}

// 表示部品削除
MathInput.prototype.removeObj = function() {
    this.obj.parentNode.removeChild(this.obj);
}

// ----private

// inputフォーム内でキー入力があった時の動作
MathInput.prototype._myKeyDown = function(evt) {
    evt = (evt) ? evt : ((window.event) ? event : null);
    this.isKeyProcessed = false;
    if(MathInput.keyCodeSwitch[evt.keyCode] != null) {
	this.selpos = this.selection.create();
	Control.timeout = this;
	setTimeout('Control.timeout.' + MathInput.keyCodeSwitch[evt.keyCode] + '()', 0);
	this.isKeyProcessed = true;
    }
}

// BackSpaceキー（キーコード8）
MathInput.prototype._onBackSpace = function() {
    if(this.selpos.end == 0) {
        var pos = this.wrap.getPos(this.obj.id) - 1;
        if(pos >= 0) this.wrap.del(pos);
    }
}

// 「←」キー（キーコード37）
MathInput.prototype._onLeftArrow = function() {
    if(this.selpos.end == 0) {
	this.wrap.backCursor(this.obj.id);
    }
}

// 「→」キー（キーコード39）
MathInput.prototype._onRightArrow = function() {
    if(this.selpos.start == this.obj.value.length) {
	this.wrap.fwdCursor(this.obj.id);
    }
}

// 「↑」キー（キーコード38）
MathInput.prototype._onUpArrow = function() {
    // とりあえず殺しておく
}

// 「↓」キー（キーコード40）
MathInput.prototype._onDownArrow = function() {
    // とりあえず殺しておく
}


// inputフォーム中で文字が入力された時の動作
MathInput.prototype._myKeyPress = function(evt) {
    evt = (evt) ? evt : ((window.event) ? event : null);
    if(evt && (!this.isKeyProcessed)) {
	// Firefox以外ではkeyCodeで文字コードが渡るので対処
        var charCode = (evt.charCode) ? (evt.charCode) : (evt.keyCode);
        // Enter の場合
        switch(charCode) {
            // 改行されると困るので殺す
            case 13:
                return false;
            // 「（」で括弧を作る
            case 40:
                this.insert(new MathFenced(this.wrap));
                return false;
            // 「）」でも括弧を作る
            case 41:
                this.insert(new MathFenced(this.wrap));
                return false;
            // 「＾」で累乗を作る
            case 94:
                this.insert(new MathSup(this.wrap));
                return false;
            // 「＿」で添字を作る
            case 95:
                this.insert(new MathSub(this.wrap));
                return false;
        }
        // 文字入力でオブジェクトを作成する
        for(var str in MathInput.insertStrings) {
            if(charCode == str.charCodeAt(str.length - 1)) {
                var selpos = this.selection.create();
                if(this.obj.value.substring(selpos.end - str.length + 1, selpos.end) == str.substring(0, str.length - 1)) {
                    this.obj.value = this.obj.value.substring(0, selpos.end - str.length + 1);
                    this.insert(MathInput.insertStrings[str](this.wrap));
                    // this.optimizeSize();
                    return false;
                }
            }
        }
    }
}

// inputフォーム中でキーを離した時の動作
MathInput.prototype._myKeyUp = function(evt) {
    this.layout();
}

// 幅の計算
MathInput.prototype._calcWidth = function() {
    if(this.isHidden) {
	return 4;
    }
    else {
	return Math.floor((this.obj.value.length + 2) * Const.tokenWidth[this.level] );
    }
}


/* ------------------------
MathRow.js
*/

/* ====================
** <mrow>オブジェクト
*/
function MathRow(par, level) {
    this.objType = 'row';
    //this.wrap = par;
    //this.obj = document.createElement('div');
    //this.obj.id = this.objType + Control.issueID();
    this.constructBase(par);
    this.level = level;
    this.array = new Array;
    this.obj.className = 'MathRow';

    this.add(new MathInput(this, 'visible'), 0);
    this._layout();
}

inherit(MathRow, ContainerBase);

// 配列中の順番を返す
MathRow.prototype.getPos = function(id) {
    for(var i = 0; i < this.array.length; i++) {
        if(this.array[i].obj.id == id) return i;
    }
    return null;
}

// 要素を追加
MathRow.prototype.add = function(child, pos) {
    var at = this.array.splice(0, pos);
    this.array = at.concat(child, this.array);
    this.obj.appendChild(child.obj);
}

// 要素を削除
MathRow.prototype.del = function(pos) {
    this.array[pos].removeObj();
    this.array.splice(pos, 1);
    if((pos > 0) && (this.array[pos-1].objType == 'input')) {
        var aftercursor = this.array[pos-1].obj.value.length;
        this.array[pos-1].obj.value += this.array[pos].obj.value;
        this.array[pos].removeObj();
        this.array.splice(pos, 1);
        //this.array[pos-1].optimizeSize();
        this.array[pos-1].setCursor(aftercursor);
    }
    //this.layout();
}

// 初期の内容を決める
MathRow.prototype.setInitialValue = function(val) {
    this.array[0].setInitialValue(val);
}

// 前のブロックにカーソルを移動
MathRow.prototype.backCursor = function(bid) {
    bnum = this.getPos(bid);
    if(bnum == 0) {
        this.wrap.backCursor(this.obj.id);
    }
    else {
        bnum--;
        this.array[bnum].setCursor(-1);
    }
}

// 次のブロックにカーソルを移動
MathRow.prototype.fwdCursor = function(bid) {
    bnum = this.getPos(bid);
    if(bnum == this.array.length - 1) {
        this.wrap.fwdCursor(this.obj.id);
    }
    else {
        bnum++;
        this.array[bnum].setCursor(0);
    }
}

// 端にカーソルを置く
MathRow.prototype.setCursor = function(num) {
    if(num == 0) {
        this.array[0].setCursor(0);
    }
    else if(num == -1) {
        this.array[this.array.length - 1].setCursor(-1);
    }
}

// MathMLコード書き出し
MathRow.prototype.outputMathML = function(expand) {
    var tmp = '';
    var out = '<mrow>';
    var curr;
    var inputout = new Object;
    for(var i = 0; i < this.array.length; i++) {
        curr = this.array[i]
        if(curr.objType == 'sup' || curr.objType == 'sub') {
            tmp = curr.outputMathML(expand, tmp);
        }
        else {
            if(curr.objType == 'input') {
                inputout = curr.outputMathML(expand);
                if(inputout != null) {
                    out += tmp;
                    out += inputout.front;
                    tmp = inputout.rear;
                }
            }
            else {
                out += tmp;
                tmp = curr.outputMathML(expand);
            }
        }
    }
    out += tmp;
    out += '</mrow>' + "\n";
    return out;
}

// LaTeXコード書き出し
MathRow.prototype.outputLatex = function(noFence) {
    var tmp = '';
    var i;
    for(i = 0; i < this.array.length - 1; i++) {
        tmp += this.array[i].outputLatex();
        tmp += ' ';
    }
    tmp += this.array[this.array.length - 1].outputLatex();
    
    if(noFence) {
        return tmp;
    }
    else {
        return '{' + tmp + '}';
    }
}

// 表示部品削除
MathRow.prototype.removeObj = function() {
    for(var o in this.array) {
        this.array[o].removeObj();
    }
    this.obj.parentNode.removeChild(this.obj);
}

// ---- private

MathRow.prototype._layout = function() {
    // 計算して保存
    var ptmp = new Array;
    var ptop = 0, pbottom = 0, pwidth = 0;
    for(i = 0; i < this.array.length; i++) {
        ptmp[i] = this.array[i].getPreferredSize();
        if(ptop < ptmp[i].top) ptop = ptmp[i].top;
        if(pbottom < ptmp[i].bottom) pbottom = ptmp[i].bottom;
        pwidth += ptmp[i].width;
    }
    pwidth += (this.array.length - 1) * Const.rowSpacing;
    this.top = ptop;
    this.bottom = pbottom;
    this.height = ptop + pbottom;
    this.width = pwidth;
    // 適応
    Control.setSize(this.obj, this.width, this.height);
    var left = 0;
    var curr;
    for(i = 0; i < this.array.length; i++) {
        curr = this.array[i];
        curr.obj.style.top = (this.top - ptmp[i].top) + 'px';
        curr.obj.style.left = left + 'px';
        left += (4 + ptmp[i].width);
    }
}

/* ------------------------
Container.js
-- コンテナオブジェクトで直接的にMathML要素と対応するもの

*MathFrac - mfrac - 分数
*MathSqrt - msqrt - 平方根
*MathRoot - mroot - 累乗根
*MathFenced - mfenced - 括弧
*MathSup - msup - 累乗
*MathSub - msub - 添字
*/

/* ====================
** 分数（<mfrac>）
*/
function MathFrac(par) {
    this.construct(par);
    this.separator = new Separator();
    this.obj.appendChild(this.separator.obj);
    this._layout();
}

inherit(MathFrac, Container);

setProperties(MathFrac, {
    objType: 'frac',
    rowCount: 2,
    mathmlTag: 'mfrac',
    texCommand: 'frac'
});


ContainerButtons.submit(2, 'frac', function(wrap){return new MathFrac(wrap);});

// 表示部品削除
MathFrac.prototype.removeObj = function() {
    this.separator.removeObj();
    this._removeObj();
}

// --- private
MathFrac.prototype._layout = function() {
    // 計算して保存
    var nume = this.rows[0].getPreferredSize();
    var denomi = this.rows[1].getPreferredSize();
    this.width = (nume.width > denomi.width) ? nume.width : denomi.width;
    this.width += 4;
    this.top = nume.height + 2;
    this.bottom = denomi.height + 3;
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.rows[0].obj, (this.width - nume.width) / 2, 0);
    Control.setPosition(this.rows[1].obj, (this.width - denomi.width) / 2, (nume.height + 5));
    Control.setPosition(this.separator.obj, 0, (nume.height + 2));
}

/* ====================
** 平方根（<msqrt>）
*/
function MathSqrt(par) {
    this.construct(par);
    this.separator = new Separator();
    this.obj.appendChild(this.separator.obj);
    this.img = document.createElement('img');
    this.img.src = Const.imgDir + 'sqrt.png';
    this.img.className = 'containerParts';
    this.obj.appendChild(this.img);
    this._layout();
}

inherit(MathSqrt, Container);

setProperties(MathSqrt, {
    objType: 'sqrt',
    rowCount: 1,
    mathmlTag: 'msqrt',
    texCommand: 'sqrt'
});

ContainerButtons.submit(21, 'sqrt', function(wrap){return new MathSqrt(wrap);});

// 表示部品削除
MathSqrt.prototype.removeObj = function() {
    this.separator.removeObj();
    this.obj.removeChild(this.img);
    this._removeObj();
}

// ---- private
MathSqrt.prototype._layout = function() {
    this._calcPosition(4, 2, 14);
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 14, 4);
    Control.setSize(this.separator.obj, this.width - 10, 1);
    Control.setPosition(this.separator.obj, 10, 0);
    Control.setSize(this.img, 10, this.height);
}

/* ====================
** 累乗根（<mroot>）
*/
function MathRoot(par) {
    this.construct(par,[2, par.level]);
    this.content = this.rows[1];
    this.separator = new Separator();
    this.obj.appendChild(this.separator.obj);
    this.img = document.createElement('img');
    this.img.src = Const.imgDir + 'sqrt.png';
    this.img.className = 'containerParts';
    this.obj.appendChild(this.img);
    this._layout();
}

inherit(MathRoot, Container);

setProperties(MathRoot, {
    objType: 'root',
    rowCount: 2,
    mathmlTag: 'mroot',
    texCommand: 'sqrt'
});

ContainerButtons.submit(22, 'root', function(wrap){return new MathRoot(wrap);});

// MathMLコード書き出し
MathRoot.prototype.outputMathML = function(expand) {
    return this._tagMML(this.rows[1].outputMathML(expand) + this.rows[0].outputMathML(expand));
}

// LaTeXコード書き出し
MathRoot.prototype.outputLatex = function() {
    return this._commandTex('[' + this.rows[0].outputLatex(true) + ']'+ this.rows[1].outputLatex());
}

// 表示部品削除
MathRoot.prototype.removeObj = function() {
    this.separator.removeObj();
    this.obj.removeChild(this.img);
    this._removeObj();
}

// ---- private
MathRoot.prototype._layout = function() {
    var proot = this.rows[0].getPreferredSize();
    var prow = this.rows[1].getPreferredSize();
    this.top = (proot.height > (prow.top + 4)) ? proot.height : (prow.top + 4);
    this.bottom = prow.bottom + 2;
    this.height = this.top + this.bottom;
    this.width = prow.width + proot.width + 14;
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, proot.width + 14, this.top - prow.top);
    Control.setSize(this.separator.obj, prow.width + 4, 1);
    Control.setPosition(this.separator.obj, proot.width + 10, this.top - prow.top - 4);
    Control.setSize(this.img, 10, prow.height + 6);
    Control.setPosition(this.img, proot.width, this.top - prow.top - 4);
}

/* ====================
** 括弧（<mfenced>）
*/
function MathFenced(par, kind) {
    this.construct(par);
    // 開き括弧画像
    this.imgOpen = document.createElement('img');
    this.imgOpen.src = Const.imgDir + 'openParenthesis.png';
    this.imgOpen.className = 'containerParts';
    this.obj.appendChild(this.imgOpen);
    // 閉じ括弧画像
    this.imgClose = document.createElement('img');
    this.imgClose.src = Const.imgDir + 'closeParenthesis.png';
    this.imgClose.className = 'containerParts';
    this.obj.appendChild(this.imgClose);
    this._layout();
}

inherit(MathFenced, Container);

setProperties(MathFenced, {
    objType: 'fenced',
    rowCount: 1,
    mathmlTag: 'mfenced'
});

ContainerButtons.submit(1, 'fenced', function(wrap){return new MathFenced(wrap);});

// MathMLコード書き出し
MathFenced.prototype.outputMathML = function(expand) {
    var out = '<mfenced>' + "\n";
    out += this.content.outputMathML(expand);
    out += '</mfenced>' + "\n";
    return out;
}

// LaTeXコード書き出し
MathFenced.prototype.outputLatex = function() {
    var out = Const.backslash + 'left(';
    out += this.content.outputLatex(true);
    out += Const.backslash + 'right)';
    return out;
}

// 表示部品削除
MathFenced.prototype.removeObj = function() {
    this.obj.removeChild(this.imgOpen);
    this.obj.removeChild(this.imgClose);
    this._removeObj();
}

// ---------- private
// コンテナ内配置
MathFenced.prototype._layout = function() {
    this._calcPosition(2, 2, 24);

    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 12, 2);
    Control.setSize(this.imgOpen, 10, this.height);
    Control.setPosition(this.imgOpen, 0, 0);
    Control.setSize(this.imgClose, 10, this.height);
    Control.setPosition(this.imgClose, this.width - 10, 0);
}


/* ====================
** 累乗（<msup>）
*/
function MathSup(par) {
    this.construct(par, [2]);
    this._layout();
}

inherit(MathSup, Container);

setProperties(MathSup, {
		  objType: 'sup',
		  rowCount: 1,
		  mathmlTag: 'msup',
		  isNeedPrev: true
});

ContainerButtons.submit(11, 'sup', function(wrap){return new MathSup(wrap);});

// MathMLコード書き出し
MathSup.prototype.outputMathML = function(expand, out) {
    if(out == '') out = '<mo></mo>';
    out += this.content.outputMathML(expand);
    return this._tagMML(out);
}

// LaTeXコード書き出し
MathSup.prototype.outputLatex = function() {
    return '^' + this.content.outputLatex();
}

// ---- private
MathSup.prototype._layout = function() {
    // 計算して保存
    var prefer = this.content.getPreferredSize();
    this.top = prefer.height + 4;
    this.bottom = 0;
    this.height = this.top + this.bottom;
    this.width = prefer.width + 4;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 2, 2);
}

/* ====================
** 添字（<msub>）
*/
function MathSub(par) {
    this.construct(par, [2]);
    this._layout();
}

inherit(MathSub, Container);

setProperties(MathSub, {
		  objType: 'sub',
		  rowCount: 1,
		  mathmlTag: 'msub',
		  isNeedPrev: true
});

ContainerButtons.submit(12, 'sub', function(wrap){return new MathSub(wrap);});

// MathMLコード書き出し
MathSub.prototype.outputMathML = function(expand, out) {
    if(out == '') out = '<mo></mo>';
    out += this.content.outputMathML(expand);
    return this._tagMML(out);
}

// LaTeXコード書き出し
MathSub.prototype.outputLatex = function() {
    return '_' + this.content.outputLatex();
}

// ----- private
MathSub.prototype._layout = function() {
    // 計算して保存
    var prefer = this.content.getPreferredSize();
    this.top = 0;
    this.bottom = prefer.height + 4;
    this.height = this.top + this.bottom;
    this.width = prefer.width + 4;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 2, 2);
}


/* ------------------------
Container2.js
-- コンテナオブジェクトでレイアウトのみにMathML要素を使用するもの

*MathOver - mover - オーバーライン, ベクトル, 弧
*MathPermutation - mmultiscripts - 順列, 組合せ, 重複組合せ
*MathSum - munderover - 総和
*MathIntegral - msubsup - 積分
*MathInterval - msubsup - 定積分後の区間表示
*MathLimit - munder - 極限
*/

/* ====================
** オーバーライン（\overline）
*/
function MathOver(par) {
    this.construct(par);

    this.overline = new Separator();
    this.obj.appendChild(this.overline.obj);
    this._layout();
}

inherit(MathOver, Container);

setProperties(MathOver, {
    objType: 'over',
    rowCount: 1,
    mathmlTag: 'mover',
    texCommand: 'overline'
});

ContainerButtons.submit(31, 'over', function(wrap){return new MathOver(wrap);});

// MathMLコード書き出し
MathOver.prototype.outputMathML = function(expand) {
    var out = this.content.outputMathML(expand);
    out += '<mo>';
    out += (expand) ? Const.entity.OverBar : '&OverBar;';
    out += '</mo>';
    return this._tagMML(out);
}

// 表示部品削除
MathOver.prototype.removeObj = function() {
    this.overline.removeObj();
    this._removeObj();
}

// ---------- private
MathOver.prototype._layout = function() {
    this._calcPosition(4, 0, 4);
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 2, 4);
    Control.setSize(this.overline.obj, this.width, 1);
    Control.setPosition(this.overline.obj, 0, 2);
}

/* ====================
** 順列, 組み合わせ（<mmultiscripts>）
*/
function MathPermutation(par, kind) {
    this.construct(par, [2, 2]);
    this.value = MathPermutation.values[kind];
    this.level = this.wrap.level;
    this.symbol = new StringSpan(this, this.value);
    this.obj.appendChild(this.symbol.obj);
    this._layout();
}

MathPermutation.values = {
    permutation: 'P',
    combination: 'C',
    homogeneous: 'H'
};

inherit(MathPermutation, Container);

setProperties(MathPermutation, {
    objType: 'permutation',
    rowCount: 2,
    mathmlTag: 'mmultiscripts'
});

ContainerButtons.submit(101, 'permutation', function(wrap){return new MathPermutation(wrap, 'permutation');});

// MathMLコード書き出し
MathPermutation.prototype.outputMathML = function(expand) {
    var ret = Control.applyTag('mi', this.value) + "\n";
    ret += this.rows[1].outputMathML();
    ret += '<none/>' + "\n";
    ret += '<mprescripts/>' + "\n";
    ret += this.rows[0].outputMathML();
    ret += '<none/>' + "\n";
    return this._tagMML(ret);
}

// LaTeXコード書き出し
MathPermutation.prototype.outputLatex = function() {
    return '{{}_' + this.rows[0].outputLatex() + this.value + '_' + this.rows[1].outputLatex() + '}';
}

// 表示部品削除
MathPermutation.prototype.removeObj = function() {
    this.symbol.removeObj();
    this._removeObj();
}

// コンテナ内配置
MathPermutation.prototype._layout = function() {
    // 計算して保存
    var psymbol = this.symbol.getPreferredSize();
    var pleft = this.rows[0].getPreferredSize();
    var pright = this.rows[1].getPreferredSize();
    this.width = pleft.width + psymbol.width + pright.width + 8;
    this.top = psymbol.top;
    this.bottom = Math.max(Math.max(pleft.height, pright.height), psymbol.bottom) + 2;
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.symbol.obj, pleft.width + 4, 0);
    Control.setPosition(this.rows[0].obj, 2, this.top);
    Control.setPosition(this.rows[1].obj, pleft.width + psymbol.width + 6, this.top);
}

/* ====================
** 総和（<munderover>）
*/

function MathSum(par) {
    this.construct(par, [2, 2]);

    this.level = this.wrap.level;
    this.symbol = new Symbol(this, 'sum');
    this.obj.appendChild(this.symbol.obj);
    this._layout();
}

inherit(MathSum, Container);

setProperties(MathSum, {
    objType: 'sum',
    rowCount: 2,
    mathmlTag: 'munderover',
    texCommand: 'sum'
});

ContainerButtons.submit(41, 'sum', function(wrap){return new MathSum(wrap);});

// MathMLコード書き出し
MathSum.prototype.outputMathML = function(expand) {
    var ret = Control.applyTag('mo', this.symbol.getMathML(expand));
    ret += "\n";
    ret += this.rows[0].outputMathML(expand);
    ret += this.rows[1].outputMathML(expand);
    return this._tagMML(ret);
}

// LaTeXコード書き出し
MathSum.prototype.outputLatex = function() {
    return this.symbol.getLatex() + '_' + this.rows[0].outputLatex() + '^' + this.rows[1].outputLatex();
}

// 表示部品削除
MathSum.prototype.removeObj = function() {
    this.symbol.removeObj();
    this._removeObj();
}

// コンテナ内配置
MathSum.prototype._layout = function() {
    // 計算して保存
    var psymbol = this.symbol.getPreferredSize();
    var punder = this.rows[0].getPreferredSize();
    var pover = this.rows[1].getPreferredSize();
    this.width = Math.max(Math.max(punder.width, pover.width), psymbol.width);
    this.top = psymbol.top + pover.height;
    this.bottom = psymbol.bottom + punder.height;
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.symbol.obj, (this.width - psymbol.width) / 2, pover.height);
    Control.setPosition(this.rows[0].obj, (this.width - punder.width) / 2, pover.height + psymbol.height);
    Control.setPosition(this.rows[1].obj, (this.width - pover.width) / 2, 0);
}


/* ====================
** 積分（<msubsup>）
*/

function MathIntegral(par) {
    this.construct(par, [2, 2]);

    this.level = this.wrap.level;
    this.symbol = new Symbol(this, 'math_int');
    this.obj.appendChild(this.symbol.obj);
    this._layout();
}

inherit(MathIntegral, Container);

setProperties(MathIntegral, {
    objType: 'int',
    rowCount: 2,
    mathmlTag: 'msubsup',
    texCommand: 'int'
});

ContainerButtons.submit(51, 'integral', function(wrap){return new MathIntegral(wrap);});

// MathMLコード書き出し
MathIntegral.prototype.outputMathML = function(expand) {
    var ret = Control.applyTag('mo', this.symbol.getMathML(expand));
    ret += "\n";
    ret += this.rows[0].outputMathML(expand);
    ret += this.rows[1].outputMathML(expand);
    return this._tagMML(ret);
}

// LaTeXコード書き出し
MathIntegral.prototype.outputLatex = function() {
    return this.symbol.getLatex() + '_' + this.rows[0].outputLatex() + '^' + this.rows[1].outputLatex();
}

// 表示部品削除
MathIntegral.prototype.removeObj = function() {
    this.symbol.removeObj();
    this._removeObj();
}

// コンテナ内配置
MathIntegral.prototype._layout = function() {
    // 計算して保存
    var psymbol = this.symbol.getPreferredSize();
    var punder = this.rows[0].getPreferredSize();
    var pover = this.rows[1].getPreferredSize();
    this.width = psymbol.width + Math.max(punder.width, pover.width) + 4;
    this.top = pover.top + (pover.bottom + punder.top) / 2 
    this.bottom = punder.bottom + (pover.bottom + punder.top) / 2 
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.symbol.obj, 0, this.top - psymbol.top);
    Control.setPosition(this.rows[0].obj, psymbol.width + 4, this.height - punder.height);
    Control.setPosition(this.rows[1].obj, psymbol.width + 4, 0);
}


/* ====================
** 定積分後の区間（<msubsup>）
*/

function MathInterval(par) {
    this.construct(par, [par.level, 2, 2]);
    // 開き括弧画像
    this.imgOpen = document.createElement('img');
    this.imgOpen.src = Const.imgDir + 'openBracket.png';
    this.imgOpen.className = 'containerParts';
    this.obj.appendChild(this.imgOpen);
    // 閉じ括弧画像
    this.imgClose = document.createElement('img');
    this.imgClose.src = Const.imgDir + 'closeBracket.png';
    this.imgClose.className = 'containerParts';
    this.obj.appendChild(this.imgClose);
    this._layout();
}

inherit(MathInterval, Container);

setProperties(MathInterval, {
    objType: 'interval',
    rowCount: 3,
    mathmlTag: 'msubsup'
});

ContainerButtons.submit(52, 'interval', function(wrap){return new MathInterval(wrap);});

// MathMLコード書き出し
MathInterval.prototype.outputMathML = function(expand) {
    var out = '<mfenced open="[" close="]">' + "\n";
    out += this.content.outputMathML(expand);
    out += '</mfenced>' + "\n";
    out += this.rows[1].outputMathML(expand);
    out += this.rows[2].outputMathML(expand);
    return this._tagMML(out);
}

// LaTeXコード書き出し
MathInterval.prototype.outputLatex = function() {
    var out = Const.backslash + 'left[';
    out += this.content.outputLatex(true);
    out += Const.backslash + 'right]';
    out += '_' + this.rows[1].outputLatex();
    out += '^' + this.rows[2].outputLatex();
    return out;
}

// 表示部品削除
MathInterval.prototype.removeObj = function() {
    this.obj.removeChild(this.imgOpen);
    this.obj.removeChild(this.imgClose);
    this._removeObj();
}

// コンテナ内配置
MathInterval.prototype._layout = function() {
    // 計算して保存
    var pcontent = this.content.getPreferredSize();
    var punder = this.rows[1].getPreferredSize();
    var pover = this.rows[2].getPreferredSize();
    this.width = pcontent.width + Math.max(punder.width, pover.width) + 24;
    this.top = pover.top + (pover.bottom + punder.top) / 2;
    this.bottom = punder.bottom + (pover.bottom + punder.top) / 2;
    this.top = Math.max(this.top, pcontent.top);
    this.bottom = Math.max(this.bottom, pcontent.bottom);
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.content.obj, 12, this.top - pcontent.top);
    Control.setSize(this.imgOpen, 10, pcontent.height);
    Control.setPosition(this.imgOpen, 0, this.top - pcontent.top);
    Control.setSize(this.imgClose, 10, pcontent.height);
    Control.setPosition(this.imgClose, pcontent.width + 14, this.top - pcontent.top);
    Control.setPosition(this.rows[2].obj, pcontent.width + 24, 0);
    Control.setPosition(this.rows[1].obj, pcontent.width + 24, this.height - punder.height);
}


/* ====================
** 極限（<munder>）
*/
function MathLimit(par) {
    this.construct(par, [2]);
    this.level = this.wrap.level;
    this.string = new StringSpan(this, 'lim');
    this.obj.appendChild(this.string.obj);
    this._layout();
}

inherit(MathLimit, Container);

setProperties(MathLimit, {
    objType: 'limit',
    rowCount: 1,
    mathmlTag: 'munder',
    texCommand: 'lim_'
});

// MathMLコード書き出し
MathLimit.prototype.outputMathML = function(expand) {
    var ret = Control.applyTag('mi', 'lim') + "\n";
    ret += this.content.outputMathML(expand);
    return this._tagMML(ret);
}

// 表示部品削除
MathLimit.prototype.removeObj = function() {
    this.string.removeObj();
    this._removeObj();
}

// コンテナ内配置
MathLimit.prototype._layout = function() {
    // 計算して保存
    var pstring = this.string.getPreferredSize();
    var pcontent = this.content.getPreferredSize();
    this.width = Math.max(pstring.width, pcontent.width) + 4;
    this.top = pstring.top;
    this.bottom = pstring.bottom + pcontent.height;
    this.height = this.top + this.bottom;
    // 適用
    Control.setSize(this.obj, this.width, this.height);
    Control.setPosition(this.string.obj, (this.width - pstring.width) / 2, 0);
    Control.setPosition(this.content.obj, (this.width - pcontent.width) / 2, pstring.height - 1);
}


/* ------------------------
MathMatrix.js
*/

/* ====================
** 行列
*/
function MathMatrix(par) {
    this.wrap = par;
    this.obj = document.createElement('table');
    this.obj.id = this.objType + Control.issueID();
    this.obj.className = 'MathMatrix';
    this.mrs = new Array;
    this.ths = new Array;
    this.tds = new Array;
    this.rowcount = 2;
    this.colcount = 2;
    for(var row = 0; row < this.rowcount; row++) {
        this.mrs[row] = new Array;
        this.tds[row]= new Array;
        this.ths[row] = this.obj.insertRow(row);
        for(var col = 0; col < this.colcount; col++) {
            this.mrs[row][col] = new MathRow(this, this.wrap.level);
            this.mrs[row][col].obj.className += ' inMathMatrix';
            this.tds[row][col] = this.ths[row].insertCell(col);
            this.tds[row][col].appendChild(this.mrs[row][col].obj);
        }
    }
}

inherit(MathMatrix, ContainerBase);

ContainerButtons.submit(61, 'matrix', function(wrap){return new MathMatrix(wrap);});

// 初期の内容を決める
MathMatrix.prototype.setInitialValue = function(val) {
    this.mrs[0][0].setInitialValue(val);
}

// 前のブロックにカーソルを移動
MathMatrix.prototype.backCursor = function(bid) {
    if(bid == this.mrs[0][0].obj.id) {
        this.wrap.backCursor(this.obj.id);
    }
    else {
        for(var i = 1; i < this.rowcount * this.colcount; i++) {
            if(bid == this.mrs[Math.floor(i / this.rowcount)][i % this.rowcount].obj.id) {
                i--;
                this.mrs[Math.floor(i / this.rowcount)][i % this.rowcount].setCursor(-1);
                return;
            }
        }
    }
}

// 次のブロックにカーソルを移動
MathMatrix.prototype.fwdCursor = function(bid) {
    if(bid == this.mrs[this.rowcount - 1][this.colcount - 1].obj.id) {
        this.wrap.fwdCursor(this.obj.id);
    }
    else {
        for(var i = 0; i < this.rowcount * this.colcount - 1; i++) {
            if(bid == this.mrs[Math.floor(i / this.rowcount)][i % this.rowcount].obj.id) {
                i++;
                this.mrs[Math.floor(i / this.rowcount)][i % this.rowcount].setCursor(0);
                return;
            }
        }
    }
}

// 端にカーソルを置く
MathMatrix.prototype.setCursor = function(num) {
    if(num == 0) {
        this.mrs[0][0].setCursor(0);
    }
    else if(num == -1) {
        this.mrs[this.rowcount - 1][this.colcount - 1].setCursor(-1);
    }
}

// MathMLコード書き出し
MathMatrix.prototype.outputMathML = function(expand) {
    var ret = '<mtable>' + "\n";
    for(var row = 0; row < this.rowcount; row++) {
        ret += '<mtr>' + "\n";
        for(var col = 0; col < this.colcount; col++) {
            ret += '<mtd>' + "\n";
            ret += this.mrs[row][col].outputMathML(expand);
            ret += '</mtd>' + "\n";
        }
        ret += '</mtr>' + "\n";
    }
    ret += '</mtable>' + "\n";
    return ret;
}

// LaTeXコード書き出し
MathMatrix.prototype.outputLatex = function() {
    var ret = Const.backslash + 'begin{array}{' + 'cccccccccc'.substring(0, this.colcount) + '}' + "\n";
    for(var row = 0; row < this.rowcount; row++) {
        for(var col = 0; col < this.colcount - 1; col++) {
            ret += this.mrs[row][col].outputLatex() + ' & ';
        }
        ret += this.mrs[row][col].outputLatex();
        if(row < this.rowcount){
            ret +=  Const.backslash + Const.backslash + "\n";
        }
    }
    ret += Const.backslash + 'end{array}' + "\n";
    return ret;
}

// 表示部品削除
MathMatrix.prototype.removeObj = function() {
    for(var row = 0; row < this.rowcount; row++) {
        for(var col = 0; col < this.colcount; col++) {
            this.mrs[row][col].removeObj();
            this.ths[row].deleteCell(0);
        }
        this.obj.deleteRow(0);
    }
    this.obj.parentNode.removeChild(this.obj);
}

// コンテナ内配置
MathMatrix.prototype._layout = function() {
    this.width = 0;
    this.height = 0;
    var roww = new Array;
    var colh = 0;
    var mr;
    for(var col = 0; col < this.colcount; col++) {
        roww[col] = 0;
    }
    for(var row = 0; row < this.rowcount; row++) {
        colh = 0;
        for(var col = 0; col < this.colcount; col++) {
            mr = this.mrs[row][col].getPreferredSize();
            colh = Math.max(mr.height, colh);
            roww[col] = Math.max(mr.width, roww[col]);
        }
        this.height += colh;
    }
    for(var col = 0; col < this.colcount; col++) {
        this.width += roww[col];
    }
    this.width += this.colcount + 1;
    this.height += this.rowcount + 1;
    this.top = Math.floor(this.height / 2);
    this.bottom = Math.ceil(this.height / 2);
}





