FisheyeList.js 23 KB

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