FisheyeList.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. // wrapped by build app
  2. define("dojox/widget/FisheyeList", ["dijit","dojo","dojox","dojo/require!dijit/_Widget,dijit/_Templated,dijit/_Container,dijit/_Contained"], function(dijit,dojo,dojox){
  3. dojo.provide("dojox.widget.FisheyeList");
  4. dojo.require("dijit._Widget");
  5. dojo.require("dijit._Templated");
  6. dojo.require("dijit._Container");
  7. dojo.require("dijit._Contained");
  8. dojo.declare("dojox.widget.FisheyeList", [dijit._Widget, dijit._Templated, dijit._Container], {
  9. // summary:
  10. // Menu similar to the fish eye menu on the Mac OS
  11. // example:
  12. // | <div dojoType="FisheyeList"
  13. // | itemWidth="40" itemHeight="40"
  14. // | itemMaxWidth="150" itemMaxHeight="150"
  15. // | orientation="horizontal"
  16. // | effectUnits="2"
  17. // | itemPadding="10"
  18. // | attachEdge="center"
  19. // | labelEdge="bottom">
  20. // |
  21. // | <div dojoType="FisheyeListItem"
  22. // | id="item1"
  23. // | onclick="alert('click on' + this.label + '(from widget id ' + this.widgetId + ')!');"
  24. // | label="Item 1"
  25. // | iconSrc="images/fisheye_1.png">
  26. // | </div>
  27. // | ...
  28. // | </div>
  29. //
  30. constructor: function(){
  31. //
  32. // TODO
  33. // fix really long labels in vertical mode
  34. //
  35. this.pos = {'x': -1, 'y': -1}; // current cursor position, relative to the grid
  36. // for conservative trigger mode, when triggered, timerScale is gradually increased from 0 to 1
  37. this.timerScale = 1.0;
  38. },
  39. EDGE: {
  40. CENTER: 0,
  41. LEFT: 1,
  42. RIGHT: 2,
  43. TOP: 3,
  44. BOTTOM: 4
  45. },
  46. templateString: '<div class="dojoxFisheyeListBar" dojoAttachPoint="containerNode"></div>',
  47. snarfChildDomOutput: true,
  48. // itemWidth: Integer
  49. // width of menu item (in pixels) in it's dormant state (when the mouse is far away)
  50. itemWidth: 40,
  51. // itemHeight: Integer
  52. // height of menu item (in pixels) in it's dormant state (when the mouse is far away)
  53. itemHeight: 40,
  54. // itemMaxWidth: Integer
  55. // width of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
  56. itemMaxWidth: 150,
  57. // itemMaxHeight: Integer
  58. // height of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
  59. itemMaxHeight: 150,
  60. imgNode: null,
  61. // orientation: String
  62. // orientation of the menu, either "horizontal" or "vertical"
  63. orientation: 'horizontal',
  64. // isFixed: Boolean
  65. // toggle to enable additional listener (window scroll) if FisheyeList is in a fixed postion
  66. isFixed: false,
  67. // conservativeTrigger: Boolean
  68. // if true, don't start enlarging menu items until mouse is over an image;
  69. // if false, start enlarging menu items as the mouse moves near them.
  70. conservativeTrigger: false,
  71. // effectUnits: Number
  72. // controls how much reaction the menu makes, relative to the distance of the mouse from the menu
  73. effectUnits: 2,
  74. // itemPadding: Integer
  75. // padding (in pixels) betweeen each menu item
  76. itemPadding: 10,
  77. // attachEdge: String
  78. // controls the border that the menu items don't expand past;
  79. // for example, if set to "top", then the menu items will drop downwards as they expand.
  80. // values
  81. // "center", "left", "right", "top", "bottom".
  82. attachEdge: 'center',
  83. // labelEdge: String
  84. // controls were the labels show up in relation to the menu item icons
  85. // values
  86. // "center", "left", "right", "top", "bottom".
  87. labelEdge: 'bottom',
  88. postCreate: function(){
  89. var e = this.EDGE;
  90. dojo.setSelectable(this.domNode, false);
  91. var isHorizontal = this.isHorizontal = (this.orientation == 'horizontal');
  92. this.selectedNode = -1;
  93. this.isOver = false;
  94. this.hitX1 = -1;
  95. this.hitY1 = -1;
  96. this.hitX2 = -1;
  97. this.hitY2 = -1;
  98. //
  99. // only some edges make sense...
  100. //
  101. this.anchorEdge = this._toEdge(this.attachEdge, e.CENTER);
  102. this.labelEdge = this._toEdge(this.labelEdge, e.TOP);
  103. if(this.labelEdge == e.CENTER){ this.labelEdge = e.TOP; }
  104. if(isHorizontal){
  105. if(this.anchorEdge == e.LEFT){ this.anchorEdge = e.CENTER; }
  106. if(this.anchorEdge == e.RIGHT){ this.anchorEdge = e.CENTER; }
  107. if(this.labelEdge == e.LEFT){ this.labelEdge = e.TOP; }
  108. if(this.labelEdge == e.RIGHT){ this.labelEdge = e.TOP; }
  109. }else{
  110. if(this.anchorEdge == e.TOP){ this.anchorEdge = e.CENTER; }
  111. if(this.anchorEdge == e.BOTTOM){ this.anchorEdge = e.CENTER; }
  112. if(this.labelEdge == e.TOP){ this.labelEdge = e.LEFT; }
  113. if(this.labelEdge == e.BOTTOM){ this.labelEdge = e.LEFT; }
  114. }
  115. //
  116. // figure out the proximity size
  117. //
  118. var effectUnits = this.effectUnits;
  119. this.proximityLeft = this.itemWidth * (effectUnits - 0.5);
  120. this.proximityRight = this.itemWidth * (effectUnits - 0.5);
  121. this.proximityTop = this.itemHeight * (effectUnits - 0.5);
  122. this.proximityBottom = this.itemHeight * (effectUnits - 0.5);
  123. if(this.anchorEdge == e.LEFT){
  124. this.proximityLeft = 0;
  125. }
  126. if(this.anchorEdge == e.RIGHT){
  127. this.proximityRight = 0;
  128. }
  129. if(this.anchorEdge == e.TOP){
  130. this.proximityTop = 0;
  131. }
  132. if(this.anchorEdge == e.BOTTOM){
  133. this.proximityBottom = 0;
  134. }
  135. if(this.anchorEdge == e.CENTER){
  136. this.proximityLeft /= 2;
  137. this.proximityRight /= 2;
  138. this.proximityTop /= 2;
  139. this.proximityBottom /= 2;
  140. }
  141. },
  142. startup: function(){
  143. // summary: create our connections and setup our FisheyeList
  144. this.children = this.getChildren();
  145. //original postCreate() --tk
  146. this._initializePositioning();
  147. //
  148. // in liberal trigger mode, activate menu whenever mouse is close
  149. //
  150. if(!this.conservativeTrigger){
  151. this._onMouseMoveHandle = dojo.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
  152. }
  153. if(this.isFixed){
  154. this._onScrollHandle = dojo.connect(document,"onscroll",this,"_onScroll");
  155. }
  156. // Deactivate the menu if mouse is moved off screen (doesn't work for FF?)
  157. this._onMouseOutHandle = dojo.connect(document.documentElement, "onmouseout", this, "_onBodyOut");
  158. this._addChildHandle = dojo.connect(this, "addChild", this, "_initializePositioning");
  159. this._onResizeHandle = dojo.connect(window,"onresize", this, "_initializePositioning");
  160. },
  161. _initializePositioning: function(){
  162. this.itemCount = this.children.length;
  163. this.barWidth = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth;
  164. this.barHeight = (this.isHorizontal ? 1 : this.itemCount) * this.itemHeight;
  165. this.totalWidth = this.proximityLeft + this.proximityRight + this.barWidth;
  166. this.totalHeight = this.proximityTop + this.proximityBottom + this.barHeight;
  167. //
  168. // calculate effect ranges for each item
  169. //
  170. for(var i=0; i<this.children.length; i++){
  171. this.children[i].posX = this.itemWidth * (this.isHorizontal ? i : 0);
  172. this.children[i].posY = this.itemHeight * (this.isHorizontal ? 0 : i);
  173. this.children[i].cenX = this.children[i].posX + (this.itemWidth / 2);
  174. this.children[i].cenY = this.children[i].posY + (this.itemHeight / 2);
  175. var isz = this.isHorizontal ? this.itemWidth : this.itemHeight;
  176. var r = this.effectUnits * isz;
  177. var c = this.isHorizontal ? this.children[i].cenX : this.children[i].cenY;
  178. var lhs = this.isHorizontal ? this.proximityLeft : this.proximityTop;
  179. var rhs = this.isHorizontal ? this.proximityRight : this.proximityBottom;
  180. var siz = this.isHorizontal ? this.barWidth : this.barHeight;
  181. var range_lhs = r;
  182. var range_rhs = r;
  183. if(range_lhs > c+lhs){ range_lhs = c+lhs; }
  184. if(range_rhs > (siz-c+rhs)){ range_rhs = siz-c+rhs; }
  185. this.children[i].effectRangeLeft = range_lhs / isz;
  186. this.children[i].effectRangeRght = range_rhs / isz;
  187. //dojo.debug('effect range for '+i+' is '+range_lhs+'/'+range_rhs);
  188. }
  189. //
  190. // create the bar
  191. //
  192. this.domNode.style.width = this.barWidth + 'px';
  193. this.domNode.style.height = this.barHeight + 'px';
  194. //
  195. // position the items
  196. //
  197. for(i=0; i<this.children.length; i++){
  198. var itm = this.children[i];
  199. var elm = itm.domNode;
  200. elm.style.left = itm.posX + 'px';
  201. elm.style.top = itm.posY + 'px';
  202. elm.style.width = this.itemWidth + 'px';
  203. elm.style.height = this.itemHeight + 'px';
  204. itm.imgNode.style.left = this.itemPadding+'%';
  205. itm.imgNode.style.top = this.itemPadding+'%';
  206. itm.imgNode.style.width = (100 - 2 * this.itemPadding) + '%';
  207. itm.imgNode.style.height = (100 - 2 * this.itemPadding) + '%';
  208. }
  209. //
  210. // calc the grid
  211. //
  212. this._calcHitGrid();
  213. },
  214. _overElement: function(/* DomNode|String */node, /* Event */e){
  215. // summary:
  216. // Returns whether the mouse is over the passed element.
  217. // Node: Must must be display:block (ie, not a <span>)
  218. node = dojo.byId(node);
  219. var mouse = {x: e.pageX, y: e.pageY};
  220. var absolute = dojo.position(node, true);
  221. var top = absolute.y;
  222. var bottom = top + absolute.h;
  223. var left = absolute.x;
  224. var right = left + absolute.w;
  225. return (mouse.x >= left
  226. && mouse.x <= right
  227. && mouse.y >= top
  228. && mouse.y <= bottom
  229. ); // boolean
  230. },
  231. _onBodyOut: function(/*Event*/ e){
  232. // clicking over an object inside of body causes this event to fire; ignore that case
  233. if( this._overElement(dojo.body(), e) ){
  234. return;
  235. }
  236. this._setDormant(e);
  237. },
  238. _setDormant: function(/*Event*/ e){
  239. // summary: called when mouse moves out of menu's range
  240. if(!this.isOver){ return; } // already dormant?
  241. this.isOver = false;
  242. if(this.conservativeTrigger){
  243. // user can't re-trigger the menu expansion
  244. // until he mouses over a icon again
  245. dojo.disconnect(this._onMouseMoveHandle);
  246. }
  247. this._onGridMouseMove(-1, -1);
  248. },
  249. _setActive: function(/*Event*/ e){
  250. // summary: called when mouse is moved into menu's range
  251. if(this.isOver){ return; } // already activated?
  252. this.isOver = true;
  253. if(this.conservativeTrigger){
  254. // switch event handlers so that we handle mouse events from anywhere near
  255. // the menu
  256. this._onMouseMoveHandle = dojo.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
  257. this.timerScale=0.0;
  258. // call mouse handler to do some initial necessary calculations/positioning
  259. this._onMouseMove(e);
  260. // slowly expand the icon size so it isn't jumpy
  261. this._expandSlowly();
  262. }
  263. },
  264. _onMouseMove: function(/*Event*/ e){
  265. // summary: called when mouse is moved
  266. if( (e.pageX >= this.hitX1) && (e.pageX <= this.hitX2) &&
  267. (e.pageY >= this.hitY1) && (e.pageY <= this.hitY2) ){
  268. if(!this.isOver){
  269. this._setActive(e);
  270. }
  271. this._onGridMouseMove(e.pageX-this.hitX1, e.pageY-this.hitY1);
  272. }else{
  273. if(this.isOver){
  274. this._setDormant(e);
  275. }
  276. }
  277. },
  278. _onScroll: function(){
  279. this._calcHitGrid();
  280. },
  281. onResized: function(){
  282. this._calcHitGrid();
  283. },
  284. _onGridMouseMove: function(x, y){
  285. // summary: called when mouse is moved in the vicinity of the menu
  286. this.pos = {x:x, y:y};
  287. this._paint();
  288. },
  289. _paint: function(){
  290. var x=this.pos.x;
  291. var y=this.pos.y;
  292. if(this.itemCount <= 0){ return; }
  293. //
  294. // figure out our main index
  295. //
  296. var pos = this.isHorizontal ? x : y;
  297. var prx = this.isHorizontal ? this.proximityLeft : this.proximityTop;
  298. var siz = this.isHorizontal ? this.itemWidth : this.itemHeight;
  299. var sim = this.isHorizontal ?
  300. (1.0-this.timerScale)*this.itemWidth + this.timerScale*this.itemMaxWidth :
  301. (1.0-this.timerScale)*this.itemHeight + this.timerScale*this.itemMaxHeight ;
  302. var cen = ((pos - prx) / siz) - 0.5;
  303. var max_off_cen = (sim / siz) - 0.5;
  304. if(max_off_cen > this.effectUnits){ max_off_cen = this.effectUnits; }
  305. //
  306. // figure out our off-axis weighting
  307. //
  308. var off_weight = 0, cen2;
  309. if(this.anchorEdge == this.EDGE.BOTTOM){
  310. cen2 = (y - this.proximityTop) / this.itemHeight;
  311. off_weight = (cen2 > 0.5) ? 1 : y / (this.proximityTop + (this.itemHeight / 2));
  312. }
  313. if(this.anchorEdge == this.EDGE.TOP){
  314. cen2 = (y - this.proximityTop) / this.itemHeight;
  315. off_weight = (cen2 < 0.5) ? 1 : (this.totalHeight - y) / (this.proximityBottom + (this.itemHeight / 2));
  316. }
  317. if(this.anchorEdge == this.EDGE.RIGHT){
  318. cen2 = (x - this.proximityLeft) / this.itemWidth;
  319. off_weight = (cen2 > 0.5) ? 1 : x / (this.proximityLeft + (this.itemWidth / 2));
  320. }
  321. if(this.anchorEdge == this.EDGE.LEFT){
  322. cen2 = (x - this.proximityLeft) / this.itemWidth;
  323. off_weight = (cen2 < 0.5) ? 1 : (this.totalWidth - x) / (this.proximityRight + (this.itemWidth / 2));
  324. }
  325. if(this.anchorEdge == this.EDGE.CENTER){
  326. if(this.isHorizontal){
  327. off_weight = y / (this.totalHeight);
  328. }else{
  329. off_weight = x / (this.totalWidth);
  330. }
  331. if(off_weight > 0.5){
  332. off_weight = 1 - off_weight;
  333. }
  334. off_weight *= 2;
  335. }
  336. //
  337. // set the sizes
  338. //
  339. for(var i=0; i<this.itemCount; i++){
  340. var weight = this._weighAt(cen, i);
  341. if(weight < 0){weight = 0;}
  342. this._setItemSize(i, weight * off_weight);
  343. }
  344. //
  345. // set the positions
  346. //
  347. var main_p = Math.round(cen);
  348. var offset = 0;
  349. if(cen < 0){
  350. main_p = 0;
  351. }else if(cen > this.itemCount - 1){
  352. main_p = this.itemCount -1;
  353. }else{
  354. offset = (cen - main_p) * ((this.isHorizontal ? this.itemWidth : this.itemHeight) - this.children[main_p].sizeMain);
  355. }
  356. this._positionElementsFrom(main_p, offset);
  357. },
  358. _weighAt: function(/*Integer*/ cen, /*Integer*/ i){
  359. var dist = Math.abs(cen - i);
  360. var limit = ((cen - i) > 0) ? this.children[i].effectRangeRght : this.children[i].effectRangeLeft;
  361. return (dist > limit) ? 0 : (1 - dist / limit); // Integer
  362. },
  363. _setItemSize: function(p, scale){
  364. if(this.children[p].scale == scale){ return; }
  365. this.children[p].scale = scale;
  366. scale *= this.timerScale;
  367. var w = Math.round(this.itemWidth + ((this.itemMaxWidth - this.itemWidth ) * scale));
  368. var h = Math.round(this.itemHeight + ((this.itemMaxHeight - this.itemHeight) * scale));
  369. if(this.isHorizontal){
  370. this.children[p].sizeW = w;
  371. this.children[p].sizeH = h;
  372. this.children[p].sizeMain = w;
  373. this.children[p].sizeOff = h;
  374. var y = 0;
  375. if(this.anchorEdge == this.EDGE.TOP){
  376. y = (this.children[p].cenY - (this.itemHeight / 2));
  377. }else if(this.anchorEdge == this.EDGE.BOTTOM){
  378. y = (this.children[p].cenY - (h - (this.itemHeight / 2)));
  379. }else{
  380. y = (this.children[p].cenY - (h / 2));
  381. }
  382. this.children[p].usualX = Math.round(this.children[p].cenX - (w / 2));
  383. this.children[p].domNode.style.top = y + 'px';
  384. this.children[p].domNode.style.left = this.children[p].usualX + 'px';
  385. }else{
  386. this.children[p].sizeW = w;
  387. this.children[p].sizeH = h;
  388. this.children[p].sizeOff = w;
  389. this.children[p].sizeMain = h;
  390. var x = 0;
  391. if(this.anchorEdge == this.EDGE.LEFT){
  392. x = this.children[p].cenX - (this.itemWidth / 2);
  393. }else if(this.anchorEdge == this.EDGE.RIGHT){
  394. x = this.children[p].cenX - (w - (this.itemWidth / 2));
  395. }else{
  396. x = this.children[p].cenX - (w / 2);
  397. }
  398. this.children[p].domNode.style.left = x + 'px';
  399. this.children[p].usualY = Math.round(this.children[p].cenY - (h / 2));
  400. this.children[p].domNode.style.top = this.children[p].usualY + 'px';
  401. }
  402. this.children[p].domNode.style.width = w + 'px';
  403. this.children[p].domNode.style.height = h + 'px';
  404. if(this.children[p].svgNode){
  405. this.children[p].svgNode.setSize(w, h);
  406. }
  407. },
  408. _positionElementsFrom: function(p, offset){
  409. var pos = 0;
  410. var usual, start;
  411. if(this.isHorizontal){
  412. usual = "usualX";
  413. start = "left";
  414. }else{
  415. usual = "usualY";
  416. start = "top";
  417. }
  418. pos = Math.round(this.children[p][usual] + offset);
  419. if(this.children[p].domNode.style[start] != (pos + 'px')){
  420. this.children[p].domNode.style[start] = pos + 'px';
  421. this._positionLabel(this.children[p]);
  422. }
  423. // position before
  424. var bpos = pos;
  425. for(var i=p-1; i>=0; i--){
  426. bpos -= this.children[i].sizeMain;
  427. if(this.children[p].domNode.style[start] != (bpos + 'px')){
  428. this.children[i].domNode.style[start] = bpos + 'px';
  429. this._positionLabel(this.children[i]);
  430. }
  431. }
  432. // position after
  433. var apos = pos;
  434. for(i=p+1; i<this.itemCount; i++){
  435. apos += this.children[i-1].sizeMain;
  436. if(this.children[p].domNode.style[start] != (apos + 'px')){
  437. this.children[i].domNode.style[start] = apos + 'px';
  438. this._positionLabel(this.children[i]);
  439. }
  440. }
  441. },
  442. _positionLabel: function(itm){
  443. var x = 0;
  444. var y = 0;
  445. var mb = dojo.marginBox(itm.lblNode);
  446. if(this.labelEdge == this.EDGE.TOP){
  447. x = Math.round((itm.sizeW / 2) - (mb.w / 2));
  448. y = -mb.h;
  449. }
  450. if(this.labelEdge == this.EDGE.BOTTOM){
  451. x = Math.round((itm.sizeW / 2) - (mb.w / 2));
  452. y = itm.sizeH;
  453. }
  454. if(this.labelEdge == this.EDGE.LEFT){
  455. x = -mb.w;
  456. y = Math.round((itm.sizeH / 2) - (mb.h / 2));
  457. }
  458. if(this.labelEdge == this.EDGE.RIGHT){
  459. x = itm.sizeW;
  460. y = Math.round((itm.sizeH / 2) - (mb.h / 2));
  461. }
  462. itm.lblNode.style.left = x + 'px';
  463. itm.lblNode.style.top = y + 'px';
  464. },
  465. _calcHitGrid: function(){
  466. var pos = dojo.coords(this.domNode, true);
  467. this.hitX1 = pos.x - this.proximityLeft;
  468. this.hitY1 = pos.y - this.proximityTop;
  469. this.hitX2 = this.hitX1 + this.totalWidth;
  470. this.hitY2 = this.hitY1 + this.totalHeight;
  471. },
  472. _toEdge: function(inp, def){
  473. return this.EDGE[inp.toUpperCase()] || def;
  474. },
  475. _expandSlowly: function(){
  476. // summary: slowly expand the image to user specified max size
  477. if(!this.isOver){ return; }
  478. this.timerScale += 0.2;
  479. this._paint();
  480. if(this.timerScale<1.0){
  481. setTimeout(dojo.hitch(this, "_expandSlowly"), 10);
  482. }
  483. },
  484. destroyRecursive: function(){
  485. // need to disconnect when we destroy
  486. dojo.disconnect(this._onMouseOutHandle);
  487. dojo.disconnect(this._onMouseMoveHandle);
  488. dojo.disconnect(this._addChildHandle);
  489. if(this.isFixed){ dojo.disconnect(this._onScrollHandle); }
  490. dojo.disconnect(this._onResizeHandle);
  491. this.inherited("destroyRecursive",arguments);
  492. }
  493. });
  494. dojo.declare("dojox.widget.FisheyeListItem", [dijit._Widget, dijit._Templated, dijit._Contained], {
  495. /*
  496. * summary
  497. * Menu item inside of a FisheyeList.
  498. * See FisheyeList documentation for details on usage.
  499. */
  500. // iconSrc: String
  501. // pathname to image file (jpg, gif, png, etc.) of icon for this menu item
  502. iconSrc: "",
  503. // label: String
  504. // label to print next to the icon, when it is moused-over
  505. label: "",
  506. // id: String
  507. // will be set to the id of the orginal div element
  508. id: "",
  509. templateString:
  510. '<div class="dojoxFisheyeListItem">' +
  511. ' <img class="dojoxFisheyeListItemImage" dojoAttachPoint="imgNode" dojoAttachEvent="onmouseover:onMouseOver,onmouseout:onMouseOut,onclick:onClick">' +
  512. ' <div class="dojoxFisheyeListItemLabel" dojoAttachPoint="lblNode"></div>' +
  513. '</div>',
  514. _isNode: function(/* object */wh){
  515. // summary:
  516. // checks to see if wh is actually a node.
  517. if(typeof Element == "function"){
  518. try{
  519. return wh instanceof Element; // boolean
  520. }catch(e){}
  521. }else{
  522. // best-guess
  523. return wh && !isNaN(wh.nodeType); // boolean
  524. }
  525. return false;
  526. },
  527. _hasParent: function(/*Node*/node){
  528. // summary:
  529. // returns whether or not node is a child of another node.
  530. return Boolean(node && node.parentNode && this._isNode(node.parentNode)); // boolean
  531. },
  532. postCreate: function(){
  533. // set image
  534. var parent;
  535. if((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4)==".png") && dojo.isIE < 7){
  536. /* we set the id of the new fisheyeListItem to the id of the div defined in the HTML */
  537. if(this._hasParent(this.imgNode) && this.id != ""){
  538. parent = this.imgNode.parentNode;
  539. parent.setAttribute("id", this.id);
  540. }
  541. this.imgNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='scale')";
  542. this.imgNode.src = this._blankGif.toString();
  543. }else{
  544. if(this._hasParent(this.imgNode) && this.id != ""){
  545. parent = this.imgNode.parentNode;
  546. parent.setAttribute("id", this.id);
  547. }
  548. this.imgNode.src = this.iconSrc;
  549. }
  550. // Label
  551. if(this.lblNode){
  552. this.lblNode.appendChild(document.createTextNode(this.label));
  553. }
  554. dojo.setSelectable(this.domNode, false);
  555. this.startup();
  556. },
  557. startup: function(){
  558. this.parent = this.getParent();
  559. },
  560. onMouseOver: function(/*Event*/ e){
  561. // summary: callback when user moves mouse over this menu item
  562. // in conservative mode, don't activate the menu until user mouses over an icon
  563. if(!this.parent.isOver){
  564. this.parent._setActive(e);
  565. }
  566. if(this.label != "" ){
  567. dojo.addClass(this.lblNode, "dojoxFishSelected");
  568. this.parent._positionLabel(this);
  569. }
  570. },
  571. onMouseOut: function(/*Event*/ e){
  572. // summary: callback when user moves mouse off of this menu item
  573. dojo.removeClass(this.lblNode, "dojoxFishSelected");
  574. },
  575. onClick: function(/*Event*/ e){
  576. // summary: user overridable callback when user clicks this menu item
  577. }
  578. });
  579. });