FisheyeList.js 20 KB

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