dojo.provide("dojox.widget.FisheyeList");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit._Container");
dojo.require("dijit._Contained");
dojo.declare("dojox.widget.FisheyeList", [dijit._Widget, dijit._Templated, dijit._Container], {
 // summary:
 // Menu similar to the fish eye menu on the Mac OS
 // example:
 // | 
 // |  itemWidth="40" itemHeight="40"
 // |  itemMaxWidth="150" itemMaxHeight="150"
 // |  orientation="horizontal"
 // |  effectUnits="2"
 // |  itemPadding="10"
 // |  attachEdge="center"
 // |  labelEdge="bottom">
 // |
 // |  
 // |   id="item1"
 // |   onclick="alert('click on' + this.label + '(from widget id ' + this.widgetId + ')!');"
 // |   label="Item 1"
 // |   iconSrc="images/fisheye_1.png">
 // |  
 // |  ...
 // | 
 //
 constructor: function(){
  //
  // TODO
  // fix really long labels in vertical mode
  // 
  this.pos = {'x': -1, 'y': -1}; // current cursor position, relative to the grid  
  // for conservative trigger mode, when triggered, timerScale is gradually increased from 0 to 1
  this.timerScale = 1.0; 
 },
 EDGE: {
  CENTER: 0,
  LEFT: 1,
  RIGHT: 2,
  TOP: 3,
  BOTTOM: 4
 },
 templateString: '
',
 snarfChildDomOutput: true, 
 // itemWidth: Integer
 // width of menu item (in pixels) in it's dormant state (when the mouse is far away)
 itemWidth: 40, 
 // itemHeight: Integer
 // height of menu item (in pixels) in it's dormant state (when the mouse is far away)
 itemHeight: 40, 
 // itemMaxWidth: Integer
 // width of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
 itemMaxWidth: 150, 
 // itemMaxHeight: Integer
 // height of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
 itemMaxHeight: 150,
 imgNode: null, 
 // orientation: String
 // orientation of the menu, either "horizontal" or "vertical"
 orientation: 'horizontal',
 // isFixed: Boolean
 // toggle to enable additional listener (window scroll) if FisheyeList is in a fixed postion
 isFixed: false, 
 // conservativeTrigger: Boolean
 // if true, don't start enlarging menu items until mouse is over an image;
 // if false, start enlarging menu items as the mouse moves near them.
 conservativeTrigger: false, 
 // effectUnits: Number
 // controls how much reaction the menu makes, relative to the distance of the mouse from the menu
 effectUnits: 2,  
 // itemPadding: Integer
 // padding (in pixels) betweeen each menu item
 itemPadding: 10, 
 // attachEdge: String
 // controls the border that the menu items don't expand past;
 // for example, if set to "top", then the menu items will drop downwards as they expand.
 // values
 // "center", "left", "right", "top", "bottom".
 attachEdge: 'center',
 // labelEdge: String
 // controls were the labels show up in relation to the menu item icons
 // values
 // "center", "left", "right", "top", "bottom".
 labelEdge: 'bottom',
 postCreate: function(){
  var e = this.EDGE;
  dojo.setSelectable(this.domNode, false);
  var isHorizontal = this.isHorizontal = (this.orientation == 'horizontal');
  this.selectedNode = -1;
  this.isOver = false;
  this.hitX1 = -1;
  this.hitY1 = -1;
  this.hitX2 = -1;
  this.hitY2 = -1;
  //
  // only some edges make sense...
  //
  this.anchorEdge = this._toEdge(this.attachEdge, e.CENTER);
  this.labelEdge  = this._toEdge(this.labelEdge,  e.TOP);
  if(this.labelEdge == e.CENTER){ this.labelEdge = e.TOP; }
  if(isHorizontal){
   if(this.anchorEdge == e.LEFT){ this.anchorEdge = e.CENTER; }
   if(this.anchorEdge == e.RIGHT){ this.anchorEdge = e.CENTER; }
   if(this.labelEdge == e.LEFT){ this.labelEdge = e.TOP; }
   if(this.labelEdge == e.RIGHT){ this.labelEdge = e.TOP; }
  }else{
   if(this.anchorEdge == e.TOP){ this.anchorEdge = e.CENTER; }
   if(this.anchorEdge == e.BOTTOM){ this.anchorEdge = e.CENTER; }
   if(this.labelEdge == e.TOP){ this.labelEdge = e.LEFT; }
   if(this.labelEdge == e.BOTTOM){ this.labelEdge = e.LEFT; }
  }
  //
  // figure out the proximity size
  //
  var effectUnits = this.effectUnits;
  this.proximityLeft   = this.itemWidth  * (effectUnits - 0.5);
  this.proximityRight  = this.itemWidth  * (effectUnits - 0.5);
  this.proximityTop    = this.itemHeight * (effectUnits - 0.5);
  this.proximityBottom = this.itemHeight * (effectUnits - 0.5); 
  if(this.anchorEdge == e.LEFT){
   this.proximityLeft = 0;
  }
  if(this.anchorEdge == e.RIGHT){
   this.proximityRight = 0;
  }
  if(this.anchorEdge == e.TOP){
   this.proximityTop = 0;
  }
  if(this.anchorEdge == e.BOTTOM){
   this.proximityBottom = 0;
  }
  if(this.anchorEdge == e.CENTER){
   this.proximityLeft   /= 2;
   this.proximityRight  /= 2;
   this.proximityTop    /= 2;
   this.proximityBottom /= 2;
  }
 }, 
 startup: function(){
  // summary: create our connections and setup our FisheyeList
  this.children = this.getChildren();
  //original postCreate() --tk
  this._initializePositioning(); 
  //
  // in liberal trigger mode, activate menu whenever mouse is close
  //
  if(!this.conservativeTrigger){
   this._onMouseMoveHandle = dojo.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
  }
  if(this.isFixed){
   this._onScrollHandle = dojo.connect(document,"onscroll",this,"_onScroll");
  }   
  // Deactivate the menu if mouse is moved off screen (doesn't work for FF?)
  this._onMouseOutHandle = dojo.connect(document.documentElement, "onmouseout", this, "_onBodyOut");
  this._addChildHandle = dojo.connect(this, "addChild", this, "_initializePositioning");
  this._onResizeHandle = dojo.connect(window,"onresize", this, "_initializePositioning");
 }, 
 _initializePositioning: function(){
  this.itemCount = this.children.length; 
  this.barWidth  = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth;
  this.barHeight = (this.isHorizontal ? 1 : this.itemCount) * this.itemHeight; 
  this.totalWidth  = this.proximityLeft + this.proximityRight  + this.barWidth;
  this.totalHeight = this.proximityTop  + this.proximityBottom + this.barHeight; 
  //
  // calculate effect ranges for each item
  //
  for(var i=0; i
   this.children[i].posX = this.itemWidth  * (this.isHorizontal ? i : 0);
   this.children[i].posY = this.itemHeight * (this.isHorizontal ? 0 : i);
   this.children[i].cenX = this.children[i].posX + (this.itemWidth  / 2);
   this.children[i].cenY = this.children[i].posY + (this.itemHeight / 2);
   var isz = this.isHorizontal ? this.itemWidth : this.itemHeight;
   var r = this.effectUnits * isz;
   var c = this.isHorizontal ? this.children[i].cenX : this.children[i].cenY;
   var lhs = this.isHorizontal ? this.proximityLeft : this.proximityTop;
   var rhs = this.isHorizontal ? this.proximityRight : this.proximityBottom;
   var siz = this.isHorizontal ? this.barWidth : this.barHeight;
   var range_lhs = r;
   var range_rhs = r;
   if(range_lhs > c+lhs){ range_lhs = c+lhs; }
   if(range_rhs > (siz-c+rhs)){ range_rhs = siz-c+rhs; }
   this.children[i].effectRangeLeft = range_lhs / isz;
   this.children[i].effectRangeRght = range_rhs / isz;
   //dojo.debug('effect range for '+i+' is '+range_lhs+'/'+range_rhs);
  }
  //
  // create the bar
  //
  this.domNode.style.width = this.barWidth + 'px';
  this.domNode.style.height = this.barHeight + 'px';
  //
  // position the items
  //
  for(i=0; i   var itm = this.children[i];
   var elm = itm.domNode;
   elm.style.left   = itm.posX + 'px';
   elm.style.top    = itm.posY + 'px';
   elm.style.width  = this.itemWidth + 'px';
   elm.style.height = this.itemHeight + 'px';
   
   itm.imgNode.style.left = this.itemPadding+'%';
   itm.imgNode.style.top = this.itemPadding+'%';
   itm.imgNode.style.width = (100 - 2 * this.itemPadding) + '%';
   itm.imgNode.style.height = (100 - 2 * this.itemPadding) + '%';
  }
  //
  // calc the grid
  //
  this._calcHitGrid();
 },
 _overElement: function(/* DomNode|String */node, /* Event */e){
  // summary:
  // Returns whether the mouse is over the passed element.
  // Node: Must must be display:block (ie, not a )
  node = dojo.byId(node);
  var mouse = {x: e.pageX, y: e.pageY};
  var bb = dojo._getBorderBox(node);
  var absolute = dojo.coords(node, true);
  var top = absolute.y;
  var bottom = top + bb.h;
  var left = absolute.x;
  var right = left + bb.w;
  return (mouse.x >= left
   && mouse.x <= right
   && mouse.y >= top
   && mouse.y <= bottom
  ); // boolean
 },
 _onBodyOut: function(/*Event*/ e){
  // clicking over an object inside of body causes this event to fire; ignore that case
  if( this._overElement(dojo.body(), e) ){
   return;
  }
  this._setDormant(e);