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);