StackedPanel.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. /* Copyright (c) Business Objects 2006. All rights reserved. */
  2. if (typeof(bobj.crv.StackedPanel) == 'undefined') {
  3. bobj.crv.StackedPanel = {};
  4. }
  5. if (typeof(bobj.crv.StackedTab) == 'undefined') {
  6. bobj.crv.StackedTab = {};
  7. }
  8. /**
  9. * Constructor.
  10. *
  11. * @param id [String] DHTML id
  12. * @param width [int] Width of the panel in pixels
  13. * @param height [int] Height of the panel in pixels
  14. */
  15. bobj.crv.newStackedPanel = function(kwArgs) {
  16. var mb = MochiKit.Base;
  17. var UPDATE = mb.update;
  18. var BIND = mb.bind;
  19. kwArgs = UPDATE({
  20. id: bobj.uniqueId(),
  21. width: null,
  22. height: null,
  23. isAnimated: true
  24. }, kwArgs);
  25. var o = newWidget(kwArgs.id);
  26. o.widgetType = 'StackedPanel';
  27. bobj.fillIn(o, kwArgs);
  28. o._tabs = [];
  29. o._initWidget = o.init;
  30. o._resizeWidget = o.resize;
  31. UPDATE(o, bobj.crv.StackedPanel);
  32. o._onTabCollapse = BIND(o._onTabCollapse, o);
  33. o._onTabExpand = BIND(o._onTabExpand, o);
  34. return o;
  35. };
  36. bobj.crv.StackedPanel.init = function() {
  37. this._initWidget();
  38. var tabs = this._tabs;
  39. // Tabs that were added after getHTML must be written now
  40. var index = this._numTabsWritten;
  41. while (index < tabs.length) {
  42. append(this.layer, tabs[index].getHTML(), document);
  43. index++;
  44. }
  45. for (var i = 0, len = tabs.length; i < len; ++i) {
  46. tabs[i].init();
  47. }
  48. };
  49. bobj.crv.StackedPanel.getHTML = function() {
  50. var DIV = bobj.html.DIV;
  51. var layerStyle = {
  52. overflow: 'auto',
  53. position: 'relative'
  54. };
  55. if (this.height) {
  56. layerStyle.height = bobj.unitValue(this.height);
  57. }
  58. if (this.width) {
  59. layerStyle.width = bobj.unitValue(this.width);
  60. }
  61. return DIV({id: this.id, style: layerStyle, tabIndex: "-1"}, this._getTabsHTML());
  62. };
  63. bobj.crv.StackedPanel._getTabsHTML = function() {
  64. var tabsHTML = '';
  65. var tabs = this._tabs;
  66. var tabsLen = tabs.length;
  67. for (var i = 0; i < tabsLen; ++i) {
  68. tabsHTML += tabs[i].getHTML();
  69. }
  70. this._numTabsWritten = tabsLen;
  71. return tabsHTML;
  72. };
  73. /**
  74. * Add a tab to the panel. Must be called before getHTML is called.
  75. *
  76. * @param tab [StackedTab]
  77. */
  78. bobj.crv.StackedPanel.addTab = function(tab) {
  79. if (tab) {
  80. tab.collapseCB = this._onTabCollapse;
  81. tab.expandCB = this._onTabExpand;
  82. this._tabs.push(tab);
  83. if (this.layer) {
  84. append(this.layer, tab.getHTML());
  85. tab.init();
  86. }
  87. }
  88. };
  89. bobj.crv.StackedPanel.getNumTabs = function() {
  90. return this._tabs.length;
  91. };
  92. bobj.crv.StackedPanel.getTab = function(index) {
  93. return this._tabs[index];
  94. };
  95. bobj.crv.StackedPanel.removeTab = function(index) {
  96. if (index >= 0 && index < this._tabs.length) {
  97. var tab = this._tabs[index];
  98. this._tabs.splice(index, 1);
  99. delete _widgets[this._tabs.widx];
  100. if (tab.layer) {
  101. tab.layer.parentNode.removeChild(tab.layer);
  102. }
  103. }
  104. };
  105. bobj.crv.StackedPanel.resize = function(w, h) {
  106. // Exclude margins for safari as it miscalculates left/top margins
  107. var excludeMargins = !_saf;
  108. bobj.setOuterSize(this.layer, w, h, excludeMargins);
  109. var tabs = this._tabs;
  110. var tabsLen = tabs.length;
  111. if (tabsLen) {
  112. // Ensure that the vertical scrollbar never covers the content
  113. var tabWidth = this.layer.clientWidth;
  114. // IE changes the value of clientWidth after resizing the first child...
  115. tabs[0].resize(tabWidth);
  116. if (tabWidth != this.layer.clientWidth) {
  117. tabWidth = this.layer.clientWidth;
  118. tabs[0].resize(tabWidth);
  119. }
  120. for (var i = 1; i < tabsLen; ++i) {
  121. tabs[i].resize(tabWidth);
  122. }
  123. }
  124. };
  125. bobj.crv.StackedPanel._onTabCollapse = function(tab) {
  126. this.resize();
  127. };
  128. bobj.crv.StackedPanel._onTabExpand = function(tab) {
  129. this.resize();
  130. };
  131. /*
  132. ================================================================================
  133. StackedTab
  134. ================================================================================
  135. */
  136. bobj.crv.newStackedTab = function(kwArgs) {
  137. var UPDATE = MochiKit.Base.update;
  138. kwArgs = UPDATE({
  139. id: bobj.uniqueId(),
  140. label: '',
  141. width: 300,
  142. height: null, // null height means grow as big as necessary
  143. isAnimated: true,
  144. expandCB: null,
  145. openAdvCB: null,
  146. selectCB: null,
  147. collapseCB: null,
  148. expandImgPos: "right"
  149. }, kwArgs);
  150. var o = newWidget(kwArgs.id);
  151. o.widgetType = 'StackedTab';
  152. bobj.fillIn(o, kwArgs);
  153. o._content = null;
  154. o._leftIcons = [];
  155. o._rightIcons = [];
  156. o._IMG_WIDTH = 16;
  157. o._IMG_HEIGHT = 16;
  158. o._ICON_WIDTH = 20;
  159. o._initWidget = o.init;
  160. o._resizeWidget = o.resize;
  161. UPDATE(o, bobj.crv.StackedTab);
  162. return o;
  163. };
  164. bobj.crv.StackedTab.init = function() {
  165. var CONNECT = MochiKit.Signal.connect;
  166. this._initWidget();
  167. if (this._content) {
  168. this._content.init();
  169. }
  170. this._labelCtn = document.getElementById(this.id + '_labelCtn');
  171. this._textCtn = document.getElementById(this.id + '_textCtn');
  172. this._expandCtn = document.getElementById(this.id + '_expandCtn');
  173. this._expandImg = document.getElementById(this.id + '_expand');
  174. this._contentCtn = document.getElementById(this.id + '_contentCtn');
  175. if(this.openAdvCB) {
  176. CONNECT(this._labelCtn, 'ondblclick',this.openAdvCB);
  177. }
  178. if(this.selectCB) {
  179. CONNECT(this._labelCtn, 'onclick',this.selectCB);
  180. }
  181. this.setMinMaxIconToolTip();
  182. };
  183. bobj.crv.StackedTab.getHTML = function() {
  184. var DIV = bobj.html.DIV;
  185. var ctnStyle = {
  186. width: this.width + 'px',
  187. overflow: 'hidden'
  188. };
  189. var labelCtnAtt = {
  190. id: this.id + '_labelCtn',
  191. 'class': 'stackedTabLabel crvnoselect thumbtxt '
  192. };
  193. var expandCtnAtt = {
  194. id: this.id + '_expandCtn',
  195. onclick: "bobj.crv.StackedTab._onExpandClick('" + this.id + "')",
  196. 'class': 'stackedTabIconCtn',
  197. style: {cursor : 'pointer'}
  198. };
  199. if (this.expandImgPos === "left") {
  200. expandCtnAtt.style.left = "0px";
  201. }
  202. else {
  203. expandCtnAtt.style.right = "-1px";
  204. }
  205. var contentHTML = this._content ? this._content.getHTML() : '';
  206. var imgW = this._IMG_WIDTH;
  207. var imgH = this._IMG_HEIGHT;
  208. var html = DIV({id: this.id, style: ctnStyle},
  209. DIV(labelCtnAtt,
  210. DIV(expandCtnAtt,
  211. imgOffset(bobj.crvUri('images/param_panel.gif'), imgW, imgH, imgW, 16, this.id + '_expand')),
  212. DIV({'class': 'stackedTabText', style: this._getTextCtnStyle(), id: this.id + '_textCtn'}, this.label),
  213. this._getIconsHtml(this._leftIcons, true) + this._getIconsHtml(this._rightIcons, false)),
  214. DIV({id: this.id + '_contentCtn'}, contentHTML));
  215. return html;
  216. };
  217. /*
  218. * Sets the icon's tooltip using bobj.crv.tooltip after elements are added to document
  219. */
  220. bobj.crv.StackedTab.setMinMaxIconToolTip = function() {
  221. var rightIcons = this._rightIcons;
  222. for(var i = 0, len = rightIcons.length; i < len; i++) {
  223. var iconID = rightIcons[i].id;
  224. var icontooltip = rightIcons[i].tooltip;
  225. if(iconID.indexOf('_icnInfo') >= 0) {
  226. var icon = getLayer(iconID);
  227. this._setIconTooltip(icon, icontooltip);
  228. }
  229. }
  230. };
  231. bobj.crv.StackedTab._setIconTooltip = function(icon,tooltip) {
  232. if(icon !== null && tooltip !== null) {
  233. bobj.crv.Tooltip.setElementTooltip(icon,tooltip);
  234. }
  235. };
  236. /**
  237. * Get html for any icons that have been added
  238. *
  239. * @param icons [Array] List of icon objects
  240. * @param isLeftAligned [Boolean] when true, icon is placed on the left side
  241. *
  242. * @return [String] Returns an html fragment
  243. */
  244. bobj.crv.StackedTab._getIconsHtml = function(icons, isLeftAligned) {
  245. var DIV = bobj.html.DIV;
  246. var iconsHtml = '';
  247. for (var i = 0, len = icons.length; i < len; ++i) {
  248. var icon = icons[i];
  249. var isInfoIcon = (icon.id.indexOf('_icnInfo') >= 0) ? true : false;
  250. iconsHtml += DIV({id: icon.id, 'class': 'stackedTabIconCtn', style: this._getIconCtnStyle(icon, isLeftAligned, i)},
  251. imgOffset(icon.url, this._IMG_WIDTH, this._IMG_HEIGHT, icon.dx, icon.dy, null, null, (isInfoIcon? null : icon.tooltip)));
  252. }
  253. return iconsHtml;
  254. };
  255. /**
  256. * Count the number of icons in a list that are visible
  257. *
  258. * @param list [Array] List of icon info objects
  259. *
  260. * @return [int] Returns the number of visible icons
  261. */
  262. bobj.crv.StackedTab._countVisibleIcons = function(list) {
  263. var filteredList = MochiKit.Base.filter(function(icon) {
  264. return icon.isVisible;
  265. }, list);
  266. return filteredList.length;
  267. };
  268. /**
  269. * Get the style properties of the div that contains an icon.
  270. *
  271. * @param icon [Object] Icon info object
  272. * @param isLeftAligned [bool] When true, icon is positioned on the left side
  273. * @param index [int] Index of icon in the list of left or right icons
  274. *
  275. * @return [Object] Returns style properties that position the icon and set its
  276. * visibility
  277. */
  278. bobj.crv.StackedTab._getIconCtnStyle = function(icon, isLeftAligned, index) {
  279. var list = isLeftAligned ? this._leftIcons : this._rightIcons;
  280. var xPos = this._countVisibleIcons(bobj.slice(list, 0, index)) * this._ICON_WIDTH -1;
  281. var style = {
  282. display: icon.isVisible ? 'block' : 'none'
  283. };
  284. if (isLeftAligned) {
  285. if (this.expandImgPos === 'left') {
  286. xPos += this._ICON_WIDTH;
  287. }
  288. style.left = xPos + 'px';
  289. }
  290. else {
  291. if (this.expandImgPos === 'right') {
  292. xPos += this._ICON_WIDTH;
  293. }
  294. style.right = xPos + 'px';
  295. }
  296. return style;
  297. };
  298. /**
  299. * Get the style of the div that contains the label text
  300. *
  301. * @param useCamelCase [bool] When true, camelCase style names are used. When
  302. * false, hyphenated style names are used.
  303. *
  304. * @return [Object] Returns style properties that position the text
  305. */
  306. bobj.crv.StackedTab._getTextCtnStyle = function(useCamelCase) {
  307. var lMarg = 0;
  308. var rMarg = 0;
  309. var iconW = this._ICON_WIDTH;
  310. if (this.expandImgPos === 'left') {
  311. lMarg += iconW;
  312. }
  313. else {
  314. rMarg += iconW;
  315. }
  316. lMarg += this._countVisibleIcons(this._leftIcons) * iconW;
  317. rMarg += this._countVisibleIcons(this._rightIcons) * iconW;
  318. lMarg = Math.max(lMarg, 2);
  319. rMarg = Math.max(rMarg, 2);
  320. var textStyle = {};
  321. if (useCamelCase) {
  322. textStyle.marginLeft = lMarg + 'px';
  323. textStyle.marginRight = rMarg + 'px';
  324. }
  325. else {
  326. textStyle['margin-left'] = lMarg + 'px';
  327. textStyle['margin-right'] = rMarg + 'px';
  328. }
  329. if (MochiKit.Base.isIE() && bobj.isQuirksMode()) {
  330. textStyle.width = '100%';
  331. }
  332. return textStyle;
  333. };
  334. /**
  335. * Add an icon to the label
  336. *
  337. * @param url [String] Url of the icon image (16x16 pixels)
  338. * @param dx [int] the horizontal offset in combined image
  339. * @param dy [int] the vertical offset in combined image
  340. * @param tooltip [String - optional] Icon tooltip
  341. * @param isLeftAligned [bool - optional] When true, place on left. When false, place on right.
  342. * @param isVisible [bool - optional] Initial visibility of the icon
  343. * @param id [String - optional] ID of the icon (valid dhtml ID)
  344. *
  345. * @return [String] Returns an ID for the icon that can be used to reference it
  346. * later, when showing or hiding it, for example.
  347. */
  348. bobj.crv.StackedTab.addIcon = function(url, dx, dy, tooltip, isLeftAligned, isVisible, id) {
  349. id = id || bobj.uniqueId();
  350. var iconInfo = {
  351. url: url,
  352. dx:dx,
  353. dy:dy,
  354. tooltip: tooltip,
  355. isVisible: isVisible,
  356. id: id
  357. };
  358. if (isLeftAligned) {
  359. this._leftIcons.push(iconInfo);
  360. }
  361. else {
  362. this._rightIcons.push(iconInfo);
  363. }
  364. return id;
  365. };
  366. /**
  367. * Find an icon info object using its ID
  368. *
  369. * @param id [String] ID of theicon to find
  370. *
  371. * @return [Object] Returns icon information or null
  372. */
  373. bobj.crv.StackedTab._findIcon = function(id) {
  374. var list = this._leftIcons;
  375. do {
  376. for (var i = 0, len = list.length; i < len; ++i) {
  377. var icon = list[i];
  378. if (icon.id == id) {
  379. return icon;
  380. }
  381. }
  382. list = (list === this._leftIcons) ? this._rightIcons : null;
  383. } while(list);
  384. return null;
  385. };
  386. /**
  387. * Make an icon visible
  388. *
  389. * @param id [String] ID of the icon to make visible
  390. */
  391. bobj.crv.StackedTab.showIcon = function(id) {
  392. var icon = this._findIcon(id);
  393. if (icon) {
  394. icon.isVisible = true;
  395. var dom = document.getElementById(id);
  396. if (dom) {
  397. var needPaint = dom.style.display != 'block';
  398. dom.style.display = 'block';
  399. if (needPaint) {
  400. this._paintLabel();
  401. }
  402. }
  403. }
  404. };
  405. /**
  406. * Check whether an icon is visible
  407. *
  408. * @param id [string] ID of the icon
  409. *
  410. * @return [bool] returns true if icon is visible
  411. */
  412. bobj.crv.StackedTab.isIconShowing = function(id) {
  413. var icon = this._findIcon(id);
  414. if (icon) {
  415. return icon.isVisible;
  416. }
  417. return false;
  418. };
  419. /**
  420. * Hide an icon
  421. *
  422. * @param id [String] ID of the icon to make visible
  423. */
  424. bobj.crv.StackedTab.hideIcon = function(id) {
  425. var icon = this._findIcon(id);
  426. if (icon) {
  427. icon.isVisible = false;
  428. var dom = document.getElementById(id);
  429. if (dom) {
  430. var needPaint = dom.style.display != 'none';
  431. dom.style.display = 'none';
  432. if (needPaint) {
  433. this._paintLabel();
  434. }
  435. }
  436. }
  437. };
  438. /**
  439. * Set an icon's tooltip
  440. *
  441. * @param id [String] ID of the icon
  442. * @param tooltip [String] Tooltip to display
  443. */
  444. bobj.crv.StackedTab.setIconTooltip = function(id, tooltip) {
  445. var icon = this._findIcon(id);
  446. if (icon) {
  447. icon.tooltip = tooltip;
  448. var dom = document.getElementById(id);
  449. if (dom) {
  450. dom.title = tooltip;
  451. }
  452. }
  453. };
  454. /**
  455. * Reposition text and icons in the label
  456. */
  457. bobj.crv.StackedTab._paintLabel = function() {
  458. var list = this._leftIcons;
  459. do {
  460. for (var i = 0, len = list.length; i < len; ++i) {
  461. var icon = list[i];
  462. var dom = document.getElementById(icon.id);
  463. if (dom) {
  464. MochiKit.Base.update(dom.style, this._getIconCtnStyle(icon, list === this._leftIcons, i));
  465. }
  466. }
  467. list = (list === this._leftIcons) ? this._rightIcons : null;
  468. } while(list);
  469. MochiKit.Base.update(this._textCtn.style, this._getTextCtnStyle(true));
  470. };
  471. bobj.crv.StackedTab.resize = function(w) {
  472. if(this._labelCtn) {
  473. // Exclude margins for safari as it miscalculates left/top margins
  474. var excludeMargins = !_saf;
  475. bobj.setOuterSize(this._labelCtn, w, excludeMargins);
  476. }
  477. if (this._content) {
  478. this._content.resize(w);
  479. }
  480. bobj.setOuterSize(this.layer, w);
  481. };
  482. /**
  483. * Set the widget that is displayed below the tab. Must be called before getHTML.
  484. *
  485. * @param widget [Widget] Widget that appears below the tab when the tab is expanded
  486. */
  487. bobj.crv.StackedTab.setContent = function(widget) {
  488. this._content = widget;
  489. };
  490. /**
  491. * Get the widget that is displayed below the tab.
  492. *
  493. * @return [Widget] Widget that appears below the tab
  494. */
  495. bobj.crv.StackedTab.getContent = function() {
  496. return this._content;
  497. };
  498. /**
  499. * Show the tab's content below the tab label
  500. */
  501. bobj.crv.StackedTab.expand = function() {
  502. if (!this.isExpanded()) {
  503. changeOffset(this._expandImg, 16, 16);
  504. var cb = MochiKit.Base.partial(this.expandCB || MochiKit.Base.noop, this);
  505. if (this.isAnimated) {
  506. var options = {duration: 0.2, afterFinish: cb};
  507. if(MochiKit.Base.isIE()) {
  508. options.restoreAfterFinish = false;
  509. options.scaleContent = true;
  510. }
  511. MochiKit.Visual.blindDown(this._contentCtn, options);
  512. this.resize();
  513. }
  514. else {
  515. this._contentCtn.style.display = '';
  516. this.resize();
  517. cb();
  518. }
  519. }
  520. };
  521. /**
  522. * @return True if and only if the tab's content is displayed
  523. */
  524. bobj.crv.StackedTab.isExpanded = function() {
  525. return this._contentCtn.style.display != 'none';
  526. };
  527. /**
  528. * Hide the tab's content
  529. */
  530. bobj.crv.StackedTab.collapse = function() {
  531. changeOffset(this._expandImg, 0, 16);
  532. var cb = MochiKit.Base.partial(this.collapseCB || MochiKit.Base.noop, this);
  533. if (this.isAnimated) {
  534. MochiKit.Visual.blindUp(this._contentCtn, {duration: 0.2, afterFinish: cb, scaleContent: true});
  535. }
  536. else {
  537. this._contentCtn.style.display = 'none';
  538. cb();
  539. }
  540. };
  541. /**
  542. * Private. Handles click events on the tab's label.
  543. */
  544. bobj.crv.StackedTab._onExpandClick = function(id) {
  545. var o = getWidgetFromID(id);
  546. if (o.isExpanded()) {
  547. o.collapse();
  548. }
  549. else {
  550. o.expand();
  551. }
  552. };
  553. /**
  554. * Private. Handles mouseover events on the tab's label.
  555. */
  556. bobj.crv.StackedTab._onMouseOver = function(id) {
  557. var o = getWidgetFromID(id);
  558. MochiKit.DOM.addElementClass(o._labelCtn, 'stackedTabLabelHover');
  559. };
  560. /**
  561. * Private. Handles mouseout events on the tab's label.
  562. */
  563. bobj.crv.StackedTab._onMouseOut = function(id) {
  564. var o = getWidgetFromID(id);
  565. MochiKit.DOM.removeElementClass(o._labelCtn, 'stackedTabLabelHover');
  566. };
  567. /**
  568. * Override the MochiKit Scale effect's setDimensions method to work around an
  569. * IE bug. The Scale effect is used by the blindUp effect, which is used to
  570. * animate tab collapse. When the element dimension is set to 0, IE displays it
  571. * with its natural height, causing serious flicker at the end of the animation.
  572. */
  573. if (!MochiKit.Visual.Scale.prototype._crvSetDimensions) {
  574. MochiKit.Visual.Scale.prototype._crvSetDimensions = MochiKit.Visual.Scale.prototype.setDimensions;
  575. MochiKit.Visual.Scale.prototype.setDimensions = function(height, width) {
  576. height = Math.round(height) || 1;
  577. width = Math.round(width) || 1;
  578. MochiKit.Visual.Scale.prototype._crvSetDimensions.call(this, height, width);
  579. };
  580. }