// //////////////////////////////////////////////////////////////
// Filename    : hierarchicalMenu.js
// Author      : MIZUBAYASHI Kosuke <miz@mizba.net>
// Created     : Wed Aug 18 23:57:29 JST 2004
// Updated     : Wed Aug 18 23:57:29 JST 2004
// Description : 動的な階層メニューを表示するライブラリ
// Version     : 1.0
// $Id:$
// //////////////////////////////////////////////////////////////
//
// (C) Copyright 2004  MIZUBAYASHI Kosuke <miz@mizba.net>
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2 as
//  published by the Free Software Foundation.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License version 2 for more details.
//
//  You can read GNU General Public License version 2 from the URI below.
//  http://mt.mizba.net/files/GPL.txt
//  or
//  http://www.gnu.org/licenses/gpl.txt
//  
//
//  本プログラムはフリーソフトウェアです．GNU General Public License
//  version 2 の定める条件を満たす限りにおいて，再頒布，改変することが
//  可能です．
//
//  本プログラムは有用であることを願って頒布しますが，一切の保証はありません．
//  商用として成り立ちうる可能性，特定目的への適合性は，非明示的なものを含めて
//  一切存在しません．
//  詳しくは GNU General Public License version 2 をご覧下さい．
//
//  GNU General Public License version 2 全文は以下の URI で読むことができます．
//  http://mt.mizba.net/files/GPL.txt
//  or
//  http://www.gnu.org/licenses/gpl.txt
//
// //////////////////////////////////////////////////////////////
// ChangeLog:
// 2004-08-18 23:57 MIZUBAYASHI Kosuke <miz@mizba.net>
//   created
//   Version 0.1
// //////////////////////////////////////////////////////////////

// マウスが外れてから閉じるまでの遅延時間
var CLOSE_DELAY_MS = 200;

// Block クラスを保持しておく連想配列
var blocks = new Array ();

// //////////////////////////////////////////////////////////////
// BEGIN: クラス定義

function block_open () {
  if ( this.htmlBlock && this.htmlBlock.style ) {
    this.htmlBlock.style.display = "block";
  }
}
function block_close () {
  if ( this.htmlBlock && this.htmlBlock.style ) {
    this.htmlBlock.style.display = "none";
  }
}
function block_onMouseOver ( p_html_block ) {
  // 親に onMouseOver の情報を伝播
  this.onMouseOverAncestor ();
  // 子を開く
  this.openChildren ();
}
function block_onMouseOut ( p_html_block ) {
  // 親に onMouseOut の情報を伝播
  this.onMouseOutAncestor ();
  // 自分にタイムアウトを設定
  this.timeoutEventId = window.setTimeout ( "doTimeout ( '" + this.htmlBlock.id +"')" , CLOSE_DELAY_MS );
}
function block_onMouseOverAncestor () {
  // 子孫が on mouse 状態であることを示す
  this.mouseOnDescendantFlag = true;
  // 自分のタイムアウトを解除
  if ( this.timeoutEventId ) {
    window.clearTimeout ( this.timeoutEventId );
  }
  // 親に onMouseOver の情報を伝播
  if ( this.parent ) {
    this.parent.onMouseOverAncestor ();
  }
}
function block_onMouseOutAncestor () {
  // 子孫が on mouse 状態から抜けたことを示す
  this.mouseOnDescendantFlag = false;
  // 親に onMouseOut の情報を伝播
  if ( this.parent ) {
    this.parent.onMouseOutAncestor ();
  }
}
function block_doTimeout () {
  // 子を再帰的に閉じる
  this.closeChildrenRecursive ();
  // 親に伝播
  this.onChildClosedEvent ();
}
function block_setParent ( p_parent ) {
  if ( this.parent != null ) {
    return;
  }
  this.parent = p_parent;
}
function block_addChild ( p_child ) {
  this.children.push ( p_child );
  p_child.setParent ( this );
}
function block_openChildren () {
  for ( i = 0 ; i < this.children.length ; ++ i ) {
    var v_child = this.children [i];
    v_child.open ();
  }
}
function block_closeChildren () {
  var i;
  for ( i = 0 ; i < this.children.length ; ++ i ) {
    var v_child = this.children [i];
    v_child.close ();
  }
}
function block_closeChildrenRecursive () {
  var i;
  for ( i = 0 ; i < this.children.length ; ++ i ) {
    var v_child = this.children [i];
    v_child.closeChildrenRecursive ();
  }
  this.closeChildren ();
}
function block_onChildClosedEvent () {
  // 子孫のいずれかが on mouse 状態であれば閉じない
  if ( this.mouseOnDescendantFlag ) {
    return;
  }
  // 子を閉じる
  this.closeChildren ();
  // 親に伝播
  if ( this.parent ) {
    this.parent.onChildClosedEvent ();
  }
}
function Block ( p_html_block_id ) {
  // フィールド
  blocks [ p_html_block_id ] = this;
  this.htmlBlock = document.getElementById ( p_html_block_id );
  //
  this.mouseOnDescendantFlag = false;
  this.timeoutEventId = null;
  this.parent = null;
  this.children = new Array ();
  // 関数
  this.open = block_open;
  this.close = block_close;
  this.onMouseOver = block_onMouseOver;
  this.onMouseOut = block_onMouseOut;
  this.onMouseOverAncestor = block_onMouseOverAncestor;
  this.onMouseOutAncestor = block_onMouseOutAncestor;
  this.doTimeout = block_doTimeout;
  this.setParent = block_setParent;
  this.addChild = block_addChild;
  this.openChildren = block_openChildren;
  this.closeChildren = block_closeChildren;
  this.closeChildrenRecursive = block_closeChildrenRecursive;
  this.onChildClosedEvent = block_onChildClosedEvent;
}

// END: クラス定義
// ////////////////////////////////////////////////////////////
// BEGIN: イベントハンドラ等

/**
 * HTML の DOM オブジェクトから，
 * 対応する JS の Block クラスを取得する．
 */
function getBlock ( p_html_block ) {
  return blocks [ p_html_block.id ];
}
/**
 * Block クラスの doTimeout をたたくエントリポイント．
 * @param p_html_block_id
 */
function doTimeout ( p_html_block_id ) {
  var block = blocks [ p_html_block_id ];
  if ( block != null ) {
    block.doTimeout ();
  }
}
/**
 * Block クラスの onMouseOver をたたくエントリポイント．
 * @param p_html_block DOM オブジェクト
 */
function doMouseOver ( p_html_block ) {
  var block = getBlock ( p_html_block );
  block.onMouseOver ();
}
/**
 * Block クラスの onMouseOut をたたくエントリポイント．
 * @param p_html_block DOM オブジェクト
 */
function doMouseOut ( p_html_block ) {
  var block = getBlock ( p_html_block );
  block.onMouseOut ();
}

