// メニューツリー構築用クラス //
var MyMenuTree = null;
void function() {
MyMenuTree = MenuTree;
// メニューノード用のMVCの組合せを返す。
chainPrototype(MenuNodeMVCFactory,AbstractNodeMVCFactory);
function MenuNodeMVCFactory(treeNode,tree) {
	AbstractNodeMVCFactory.call(this,treeNode,tree);
	var isTopNode = tree.inTopNode(treeNode);
	this.overrideMVC(
		new (!isTopNode?MenuNodeModel:TopMenuNodeModel)(treeNode,tree),
		new MenuNodeView(treeNode,tree),
		new (!isTopNode?MenuNodeControler:TopMenuNodeControler)(treeNode,tree));
	this.setup = function() {
		this.getControler().bind();
		this.getControler().onMouseOver = this.getModel().open;
		this.getControler().onMouseOut = this.getModel().blur;
		this.getControler().onTouchOther = this.getModel().close;
		if(isTopNode) this.getControler().onMouseDown = this.getModel().toggle;
		this.getModel().init();
		// 子要素の設定
		var childNodes = tree.getChildNodes(treeNode);
		for(var i=0;i<childNodes.length;i++)
			(new tree.NodeMVCFactory(childNodes[i],tree)).setup();
	};
	this.unset = function() {
		this.getControler().unbind();
	};
}

//  通常レベルのメニューノードについて //
// メニューノードの動作を定義するモデル。
chainPrototype(MenuNodeModel,AbstractNodeModel);
function MenuNodeModel(treeNode,tree) {
	AbstractNodeModel.call(this,treeNode,tree);
	this.open = function() {
		var view = (new tree.NodeMVCFactory(treeNode,tree)).getView();
		// ビューへ選択を指示。
		view.select();
		// ビューへ自分自身を開くよう指示。
		view.open();
		// 子を閉じる
		var children = tree.getChildNodes(treeNode);
		for(var i=0;i<children.length;i++) {
			var mvc = new tree.NodeMVCFactory(children[i],tree);
			mvc.getModel().close();
		}
		// 兄弟を閉じる
		var siblings = tree.getSiblingNodes(treeNode);
		for(var i=0;i<siblings.length;i++) {
			if(siblings[i] == treeNode)continue;
			var mvc = new tree.NodeMVCFactory(siblings[i],tree);
			mvc.getModel().close();
		}
	};
	this.close = function() {
		var view = (new tree.NodeMVCFactory(treeNode,tree)).getView();
		// ビューへ選択解除を指示。
		view.unselect();
		// ビューへ自分自身を閉じるよう指示。
		view.close();
	};
	this.blur = function() {
		// 子があれば遠慮する。
		if(tree.getChildNodes(treeNode).length > 0)return;
		var mvc = new tree.NodeMVCFactory(treeNode,tree);
		mvc.getView().unselect();
	};
	this.isOpen = function() {
		// 仕方ないのでViewに問い合わせる。
		var mvc = new tree.NodeMVCFactory(treeNode,tree);
		return mvc.getView().isOpen();
	};
	this.init = function() {
		this.close();
	};
}

// メニューノードの表示方法を定義するビュー。
chainPrototype(MenuNodeView,AbstractNodeView);
function MenuNodeView(treeNode,tree) {
	AbstractNodeView.call(this,treeNode,tree);
	this.open = function() {
		var myElements = tree.getMyElements(treeNode);
		for(var i=0;i<myElements.length;i++)
		if(hasClass(myElements[i],"content")) {
			myElements[i].style.visibility = "visible";
			removeClass(myElements[i],"displayNone");
		}
	};
	this.close = function() {
		var myElements = tree.getMyElements(treeNode);
		for(var i=0;i<myElements.length;i++)
		if(hasClass(myElements[i],"content")) {
			myElements[i].style.visibility = "";
			addClass(myElements[i],"displayNone");
		}
	};
	this.isOpen = function() {
		var myElements = tree.getMyElements(treeNode);
		for(var i=0;i<myElements.length;i++)
		if(hasClass(myElements[i],"content"))
			return myElements[i].style.visibility == "visible";
	};
	this.select = function() {
		addClass(treeNode,"selected");
	};
	this.unselect = function() {
		removeClass(treeNode,"selected");
	};
}

// メニューノードへのユーザ入力を検出するコントローラ。
chainPrototype(MenuNodeControler,AbstractNodeControler);
function MenuNodeControler(treeNode,tree) {
	AbstractNodeControler.call(this,treeNode,tree);
	this.bind = function() {
		bw.addEventListener(treeNode,"mouseover",_mouseover);
		bw.addEventListener(treeNode,"mouseout",_mouseout);
		bw.addEventListener(document.documentElement,"mousedown",_touchOther);
	};
	this.unbind = function() {
		bw.removeEventListener(treeNode,"mouseover",_mouseover);
		bw.removeEventListener(treeNode,"mouseout",_mouseout);
		bw.removeEventListener(document.documentElement,"mousedown",_touchOther);
		this.init();
	};
	var This = this;
	this.init();
	function _mouseover(event) {
		// イベントのバブルを中止。
		var oldEvent = new bw.MouseEvent(event);
		oldEvent.stopPropagation();
		// ポップアップ領域や子要素へのイベントは無視。
		if(!_hasContentBetween(treeNode,oldEvent.getTarget()))
			This.onMouseOver();
		// 木の外部からの場合は木のルートへイベントを転送。
		var newEvent = bw.createEvent("MouseEvents");
		if(_isAncestorElement(tree.root,oldEvent.getRelatedTarget()))return;
		with(oldEvent) newEvent.initMouseEvent(
			getType(),0,
			getScreenX(),getScreenY(),
			getClientX(),getClientY(),
			getCtrlKey(),getAltKey(),getShiftKey(),
			getButton(),getRelatedTarget());
		bw.dispatchEvent(tree.root,newEvent);
	}
	function _mouseout(event) {
		// イベントのバブルを中止。
		var _event = new bw.MouseEvent(event);
		_event.stopPropagation();
		// 要素の外に出る場合だけ処理。
		if(!_isAncestorElement(treeNode,_event.getRelatedTarget()))
			This.onMouseOut();
	}
	function _touchOther(event) {
		var touchElement = (new bw.MouseEvent(event)).getTarget();
		if(_isAncestorElement(treeNode,touchElement))return;
		This.onTouchOther();
	}
	function _hasContentBetween(root,element) {
		for(var i=element;i!=root;i=i.parentNode)
			if(hasClass(i,"content"))return true;
		return false;
	}
	function _isAncestorElement(root,element) {
		for(var i=element;i!=null;i=i.parentNode)
			if(i==root)return true;
		return false;
	}
}
MenuNodeControler.prototype.init = function() {
	this.onMouseOver = function() {/* overridable */};
	this.onTouchOther = function() {/* overridable */};
	this.onMouseOut = function() {/* overridable */};
};

//  トップレベルのメニューノードについて //
// メニューノードの動作を定義するモデル。
chainPrototype(TopMenuNodeModel,MenuNodeModel);
function TopMenuNodeModel(treeNode,tree) {
	MenuNodeModel.call(this,treeNode,tree);
	var This = this;
	var oldOpen = this.open,oldBlur = this.blur;
	this.open = function() {
		if(tree.isActive())
			oldOpen.call(This); // 基底クラスの動作を実行。
		else {
			// 選択のみ実行。
			var view = (new tree.NodeMVCFactory(treeNode,tree)).getView();
			view.select();
			// 兄弟の選択を解除。
			var siblings = tree.getSiblingNodes(treeNode);
			for(var i=0;i<siblings.length;i++) {
				if(siblings[i] == treeNode)continue;
				var mvc = new tree.NodeMVCFactory(siblings[i],tree);
				mvc.getView().unselect();
			}
		}
	};
	this.blur = function() {
		if(tree.isActive())
			oldBlur.call(This); // 基底クラスの動作を実行。
		else {
			var mvc = new tree.NodeMVCFactory(treeNode,tree);
			mvc.getView().unselect();
		}
	};
	this.toggle = function() {
		tree.setActive(!This.isOpen());
		if(tree.isActive())This.open();
	};
}
// メニューノードへのユーザ入力を検出するコントローラ。
chainPrototype(TopMenuNodeControler,MenuNodeControler);
function TopMenuNodeControler(treeNode,tree) {
	MenuNodeControler.call(this,treeNode,tree);
	var oldBind = this.bind, oldUnbind = this.bind;
	this.bind = function() {
		bw.addEventListener(treeNode,"mousedown",_mousedown);
		oldBind.call(this);
	};
	this.unbind = function() {
		bw.removeEventListener(treeNode,"mousedown",_mousedown);
		oldUnbind.call(this);
	};
	var This = this;
	function _mousedown(event) {
		// ポップアップ領域や子要素へのイベントは無視。
		var _event = new bw.MouseEvent(event);
		if(!_hasContentBetween(treeNode,_event.getTarget()))
			This.onMouseDown();
	}
	function _hasContentBetween(root,element) {
		for(var i=element;i!=root;i=i.parentNode)
			if(hasClass(i,"content"))return true;
		return false;
	}
}
TopMenuNodeControler.prototype.init = function() {
	MenuNodeControler.prototype.init.call(this);
	this.onMouseDown = function() {/* overridable */};
};

// メニューの木構造を決定するクラス
chainPrototype(MenuTree,Tree);
function MenuTree(root) {
	Tree.call(this,root,isTreeNode,MenuNodeMVCFactory);
	var This = this;
	var _isActive = false;
	// 活性状態の取得と設定
	this.isActive = function() { return _isActive; };
	this.setActive = function(value) {
		_isActive = value;
		// 活性状態なら、非活性に戻すためのリスナを設置。
		if(value) {
			addEventListenerAfterBubble(document.documentElement,"mousedown",deActivateByMousedown);
			bw.addEventListener(window,"unload",deActivate);
		}
		// 切替時ハンドラの呼び出し。
		this.onActiveToggle.exec(this,[value]);
	};
	// 初期化
	this.onActiveToggle = new jsutil.FuncSet();
	this.onActiveToggle.set(this.closeAllIfDeActive);
	this.setActive(false);
	function deActivateByMousedown(event) {
		// トップレベルノードまたはその外のマウスダウンでのみ動作。
		if(!This.outOfTopNodeOrTopNode((new bw.MouseEvent(event)).getTarget()))return;
		deActivate();
	}
	// 非活性状態へ
	function deActivate() {
		bw.removeEventListener(document.documentElement,"mousedown",deActivateByMousedown);
		bw.removeEventListener(window,"unload",deActivate);
		This.setActive(false);
	}
	// 木構造の節かどうか判断。
	function isTreeNode(element) {
		return hasClass(element,"node");
	}
}
// トップレベルノード内か確認
MenuTree.prototype.inTopNode = function(element) {
	for(var i=element;;i=i.parentNode)
	if(i==null) return false;
	else if(this.isTreeNode(i)) return this.getParentNode(i) == this.root;
};
// トップレベルノードまたはその外か確認
MenuTree.prototype.outOfTopNodeOrTopNode = function(element) {
	for(var i=element;;i=i.parentNode)
	if(i==null) return true;
	else if(hasClass(i,"content")) return false;
	else if(this.isTreeNode(i)) return this.getParentNode(i) == this.root;
};
// 非活性状態ならツリーを全て閉じる
MenuTree.prototype.closeAllIfDeActive = function() {
	if(!this.isActive())this.closeAll();
};
// ツリーを全て閉じます。
MenuTree.prototype.closeAll = function(element) {
	var topLevelNodes = this.getChildNodes(this.root);
	for(var i=0;i<topLevelNodes.length;i++)
		(new this.NodeMVCFactory(topLevelNodes[i],this)).getModel().close();
};
}();

// 雑多なメソッド //
// バブル後にリスナを追加
function addEventListenerAfterBubble(ancestor,eventName,handler) {
	bw.addEventListener(ancestor,eventName,setHandler);
	function setHandler() {
		bw.removeEventListener(ancestor,eventName,arguments.callee);
		bw.addEventListener(ancestor,eventName,handler);
	}
}
