﻿/* namespace nazono */ if( typeof(nazono)!='object' ) nazono={}; with(nazono) {
/**
  @fileoverview  
  @feature:
   - HTMLエレメントをドラッグ可能にします。
   - 簡単生成。nazono.floatingLayer.get( エレメントのID ).show(event); で、とりあえず生成されます。
   - ドラッガブル。レイヤのどこかをクリックしながらドラッグすると動きます。
   - マルチウィンドウ。何枚でも生成できます。
   - 自動消去。floatingLayer以外をクリックすると消えます。（ autohide = false を設定することで消えないようにもできます）
   - Windows IE上では、バグによりselectボックス(  <select id="ondocu"><option>こんなの</option><option>...</option></select> )が常にレイヤの前に来ますが、floatingLayerはその表示時に、自動的に背後のselectボックスを非表示にします。
   - もうすこし高度な「ウィンドウ」用には [[floatingWindow]]  があります。

  @browsers
   - Internet Explorer 5.0 SP2 for Windows
   - Internet Explorer 6 for Windows
   - Opera 7.6 for windows
   - Firefox 1.0 以上 for Windows
  @licence LGPL
  @version 0.1  Last Modified: Sat, 28 May 2005 09:28:37 +0900
  @auther nazoiking@gmail.com
  @sample
    <script src="xbrowser.js"></script>
    <script src="floatingLayer.js"></script>
    <script>
    function show_huga(event){
      var huga=nazono.floatingLayer.get( 'huga' );
      huga.autohide=false; // 自動的に隠さない
      if( huga.isVisible() ){
        huga.hide();    // 隠す
      }else{
        huga.show(event); // マウス近くに表示
      }
    }
    function show_hoge(event){
      var hoge=nazono.floatingLayer.get( 'hoge' );
      hoge.dontdrag=true; // ドラッグできない
      hoge.show(200,200); // 固定位置に表示
    }
    </script>
    <input type="button" onclick="show_huga(event);" value="show/hide huga">
    <div style="display:none;">
      <div id=huga style="background:#EEE">
        ドラッグできるレイヤ。<br>
        自動的に隠れない<br>
        <input type="button" onclick="show_hoge(event);" value="show hoge">
      </div>
      <div id=hoge style="background:#CCF">
        ドラッグできないレイヤ。<br>
        レイヤ外をクリックすると自動的に隠れる<br>
      </div>
    </div>
  @updates
    2005/05/28:
     - IE5 対応
*/
// -*- Encoding: utf8 -*-

/** @ignore */
nazono.floatingLayer={};
/**
  element to floating and draggable // ドラッガブルなレイヤーの生成
  
  直接 new せず、 floatingLayer.get を利用すること
  @see #get
  @requires xbrowser.js( xdom#get , xdom#setPoint, xdom#getPoint,
                         xdom#getSize, xdom#setSize, 
                         xdom#eventAttach , xdom#getMousePoint
                         xdom#eyesoreElementsHide
                         Point )
  @class draggable layer class
  @constructor
 */
floatingLayer = function(layName, inner, parentElement){
  if( void(0)==layName )return;
  /**
   * @type string
   */
  this.layName = layName;
  inner = (inner)? inner: "";
  this._makeLayer( inner, parentElement);
  this.setInner( inner );
}

/**
  setInner が終わった時点で呼ばれるコールバック
  @type Function
 */
floatingLayer.prototype.onSetInner= new Function();
/**
   表示時に呼び出されるコールバック
  @type Function
 */
floatingLayer.prototype.onShow= new Function();

/**
   表示時に呼び出されるコールバック
  @type Function
 */
floatingLayer.prototype.onHide= new Function();

/**
  クリックされたときに呼ばれるコールバック
  @type Function
 */
floatingLayer.prototype.onDragStart= new Function('event','');

/**
  マウスが放されたときに呼ばれるコールバック
  @type Function
 */
floatingLayer.prototype.onDragEnd=new Function('event','');


/**
  layer show // レイヤを表示する
 
  引数が渡されない(undefined)場合は現在の位置に表示<br>
  引数が数値二つの場合は、x座標 y座標と考えて表示<br>
  引数が Point の場合は、その位置に表示<br>
  引数が HTMLEventElement の場合はマウス位置 + this.offset に表示<br>
 */
floatingLayer.prototype.show=function( ){
  this.setPoint( this._getShowPoint( arguments[0],arguments[1] ) );
  this.moveFront();
  this.changeVisible(true);
  this.onShow.apply( this, arguments );
}

/**
  layer unvisible // レイヤを非表示にする
 */
floatingLayer.prototype.hide=function(){
  this.changeVisible(false);
  this.onHide.apply(this,arguments);
}


/**
  floatingLayer 以外のものをクリックしたときに消えるか？
  @type bool
 */
floatingLayer.prototype.autohide=true;

/**
  マウスでドラッグさせない
  @type bool
 */
floatingLayer.prototype.dontdrag=false;

/**
  表示時にマウス位置からこれだけずらす
  @type Point
 */
floatingLayer.prototype.offset=new Point( 10, -10 );

/**
  表示時されているか否か
  @type bool
  @private
 */
floatingLayer.prototype._isVisible=false;

/**
  表示時されているか否か
  @type bool
 */
floatingLayer.prototype.isVisible=function(){
  return (this.element().style.visibility!='hidden');
}

/**
  可視性を変更する
  @param {bool}   true で見えるようになり、 false で見えなくなる
  @requires xdom#eyesoreElementsHide
 */
floatingLayer.prototype.changeVisible=function(bool){
  this.element().style.visibility=( bool?'visible':'hidden' );
  if( !bool ) xdom.eyesoreElementsHide();
  this._isVisible = bool;
}

/**
  最前面に移動
  @requires xdom#eyesoreElementsHide
 */
floatingLayer.prototype.moveFront=function (){
  xdom.eyesoreElementsHide(this.element());
  this.setZIndex(floatingLayer.getMostFrontZIndex());
}

/**
  return this floatingLayer's DIV( HTMLElement )  // レイヤのDIVエレメントを返す
  @return {HTMLElement}
 */
floatingLayer.prototype.element = function(){
  return this._layerdiv;
}

/**
  move layer to Point // レイヤを移動する
  @param point
  @requires xdom#setPoint
 */
floatingLayer.prototype.setPoint=function(point){
  if( void(0)==point )return;
  xdom.setPoint( this.element(), new Point( arguments[0],arguments[1] ) );
}

/**
  内部を設定する
 
  inner が文字列なら、HTMLと考えて innerHTML を書く。
  それ以外の場合は HTMLElement と考えて、appendChild する（する前に現在の内容はクリアされる）
 **/
floatingLayer.prototype.setInner=function(inner){
  floatingLayer._setInner(this.element(),inner);
  this.onSetInner();
}

/**
  レイヤのサイズを設定する
  @param {int}  Width
  @param {int}  Height
  @requires xdom#setSize
 */
floatingLayer.prototype.setSize= function(){
  xdom.setSize( this.element(), arguments[0], arguments[1] );
}

/**
  レイヤのサイズを返す
  @return {Point}
  @requires xdom#getSize
 */
floatingLayer.prototype.getSize=function(){
  return xdom.getSize( this.element() );
}


/**
  レイヤの右上の位置を返す
  @return {Point}
  @requires xdom#getPoint
 */
floatingLayer.prototype.getPoint=function(){
  return xdom.getPoint( this.element() );
}

/**
  zIndex を設定する
  @param Integer  z-index
 */
floatingLayer.prototype.setZIndex= function (zindex){
  this.element().style.zIndex=zindex;
}

/**
  zIndex を取得する 
  @return {int}
 */
floatingLayer.prototype.getZIndex= function (){
  return this.element().style.zIndex;
}


/**
  フローティングさせるレイヤの生成
  @param inner         内部のHTML あるいは HTMLElement
  @param parentElement レイヤを生成する際の親。
  @requires xdom#get, xdom#getDocument
  @private
 */
floatingLayer.prototype._makeLayer= function( inner, parentElement ){
  parentElement = xdom.get(parentElement);
  if( !parentElement ) parentElement= ( typeof( inner )=='object' && inner != null )? xdom.getDocument( inner ).body : document.body;

  var div = xdom.getDocument( parentElement ).createElement( 'div' );
  div.id = floatingLayer.idprefix + this.layName;
  with(div.style){
    visibility= 'hidden';display = 'block';position = 'absolute';
  }
  this._layerdiv = div;
  this._layerdiv.controller = this;

  this.attachDragEvent(div);
  parentElement.appendChild( div );
}

/**
  エレメントをクリックしたときにレイヤがドラッグされないようにする
  @param {HTMLElement} el  attatchElement
  @requires xdom#eventAttach, xdom#eventStop
 */
floatingLayer.prototype.attachDontDragEvent= function(el){
  var div=this.element();
  xdom.eventAttach( el, 'mousedown', function(event){
    floatingLayer.clickElement = div;
    floatingLayer.clickElement_dragstart = false;
    floatingLayer.domousedown(event);
    div.controller.onDragStart(event);
    xdom.eventStop(event);
  } );
}

/**
  エレメントをクリックしたときにレイヤがドラッグされるようにする
  @param {HTMLElement} el  attatchElement
  @requires xdom#eventAttach
 */
floatingLayer.prototype.attachDragEvent= function(el){
  var div=this.element();
  xdom.eventAttach( el, 'mousedown', function(event){
    floatingLayer.clickElement=div;
    floatingLayer.clickElement_dragstart = !div.controller.dontdrag;
    floatingLayer.domousedown(event);
    div.controller.onDragStart(event);
  });
}

/**
  引数から表示位置を決定する
  @return Point
  @requires xdom#getMousePoint
  @private
 */
floatingLayer.prototype._getShowPoint= function(event){
    if( void(0)==event){ return event; }
    if( typeof(event)!='object'){ return new nazono.Point( arguments[0],arguments[1] ); }
    if( event instanceof nazono.Point ) return event.add( this.offset );
    return xdom.getMousePoint(event).add( this.offset );
}


/**
  create dragable layer  // ドラッグできるレイヤ・エレメントを作る。
 
  IDが同じものがある場合はそれを返す
  @param  {string}  レイヤのID
  @param  inner     内部に設定するHTML あるいは HTMLElement
                   未指定の場合は layerName と同じIDのエレメントを探し、それを入れる
  @param  {HTMLElement}  appendChild するHTMLElement。未指定の場合は document.body
  @return {floatingLayer}
 */
floatingLayer.get =function( layName, inner, parentElement ){
  if( ! floatingLayer._layers[layName] ){
    if( !inner ) inner = xdom.get(layName);
    floatingLayer._layers[layName]= new this( layName, inner, parentElement );
  }
  return floatingLayer._layers[layName];
}

/**
  レイヤのIDのプレフィックス
  @type string
 */
floatingLayer.idprefix="floatingLayer";


/**
  内部 DOM の設定
  @param HTMLElement e    HTMLElement
  @param HTMLElement inner
  @static
  @private
 */
floatingLayer._setInner= function(e,inner){
  if( void(0)==inner || inner ==null ) return;
  if( typeof(inner)=='object' ){
    e.innerHTML='';
    e.appendChild(inner);
  }else{
    e.innerHTML=inner;
  }
}

/**
  現在の最前面のレイヤの zIndexを取得
  @return {int}
  @todo  この方法では他のライブラリに最前面を取られてしまう
 */
floatingLayer.getMostFrontZIndex=function(z){
  if( z ){
    floatingLayer.getMostFrontZIndex._zindex = Math.max( floatingLayer.getMostFrontZIndex._zindex, z );
  }
  return ++floatingLayer.getMostFrontZIndex._zindex;
}
floatingLayer.getMostFrontZIndex._zindex = 1;

/**
   autohideが設定されているすべてのレイヤを隠す
 */
floatingLayer.autohideLayers=function(){
  for(var i in floatingLayer._layers )
    if( floatingLayer._layers[i].autohide && floatingLayer._layers[i].isVisible() )
      floatingLayer._layers[i].hide();
}

/**
  floatingLayer オブジェクトのハッシュ
  @private
 */
floatingLayer._layers =new Object;
/**
  現在ドラッグ中のレイヤ
  @private
 */
floatingLayer.clickElement = null;

/**
  ドラッグするか？
  @private
 */
floatingLayer.clickElement_dragstart =false;

/**
  ドラッグ中のマウス位置とエレメントの位置の差
  @private
 */
floatingLayer.clickElement_mouseoffset = null;

/**
  マウスボタンを押し下げた時
 
  一番上に移動し、マウス位置とエレメントの位置の差を記録
  @requires xdom#getMousePoint, xdom#getPoint
 */
floatingLayer.domousedown  = function(event){
  if( !floatingLayer.clickElement ) return;
  floatingLayer.clickElement_mouseoffset = xdom.getMousePoint( event ).sub( xdom.getPoint( floatingLayer.clickElement ) );
  floatingLayer.clickElement.controller.moveFront();
}
/**
  マウスを動かした時
 
  レイヤを移動する
  @requires xdom#getMousePoint
 */
floatingLayer.document_domousemove = function (event){
  if(!floatingLayer.clickElement) return;
  if( floatingLayer.clickElement_dragstart )
    floatingLayer.clickElement.controller.setPoint( xdom.getMousePoint( event ).sub( floatingLayer.clickElement_mouseoffset ) );
}
/**
  マウスボタンが放された時
  
  autohide処理
 */
floatingLayer.document_domouseup =function(event) {
  if( !floatingLayer.clickElement )floatingLayer.autohideLayers();
  else floatingLayer.clickElement.controller.onDragEnd(event);
  floatingLayer.clickElement = null;
}

/**
  ドキュメントへのイベント追加
  
  @requires xdom#eventAttach
 */
floatingLayer.set_document_events=function(document){
  xdom.eventAttach(document,'mousemove',nazono.floatingLayer.document_domousemove);
  xdom.eventAttach(document,'mouseup',nazono.floatingLayer.document_domouseup);
}

/** {{{ 中途半端にnn4対応 *****************************************/
// この部分のコードは古いので対応できてないかも
if( document.layers ){
  floatingLayer.set_document_events.forall = floatingLayer.set_document_events;
  floatingLayer.set_document_events = function(){
    floatingLayer.set_document_events.forall();
    document.captureEvents(Event.MOUSEMOVE);
    document.captureEvents(Event.MOUSEUP);
  }
  floatingLayer.prototype._makeLayer=function(inner){
    document.write( '<layer name="'+floatingLayer.idprefix+this.layName+'" left="0" top="0" onfocus="with(window.nazono.floatingLayer){clickElement=this;domousedown(event);}return false" domousedown="with(window.nazono.floatingLayer){clickElement=this;domousedown(event);}return false">'+inner+'</layer>' );
    parentElement.appendChild( div );
  }
  floatingLayer._setInner=function(e,html){
    with(e.document){
      open();write(html);close();
    }
  }
  floatingLayer.prototype.element = function(){
    if( !this._layerdiv ){
      this._layerdiv = xdom.get( floatingLayer.idprefix+this.layName );
      this._layerdiv.controller = this;
    }
    return this._layerdiv;
  }
  floatingLayer.prototype.setZIndex=function(index){
    this.element().zIndex=index;
  }
  floatingLayer.prototype.changeVisible=function(bool){
    this.element().visibility= bool?'visible':'hidden';
    if( !bool )xdom.setPoint( e, new Point( -300,-300 ) );
    this._isVisible = bool;
  }
}
/** }}} 中途半端にnn4対応 *****************************************/





/*  イベントを設定する  */
floatingLayer.set_document_events(document);


/****** namespace nazono * */ }

// export
if( !nazono.manualexport ){
  floatingLayer = nazono.floatingLayer;
}
