ReportAlbum.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. /* Copyright (c) Business Objects 2006. All rights reserved. */
  2. if (typeof(bobj.crv.ReportAlbum) == 'undefined') {
  3. bobj.crv.ReportAlbum = {};
  4. }
  5. if (typeof(bobj.crv.Tab) == 'undefined') {
  6. bobj.crv.Tab = {};
  7. }
  8. if (typeof(bobj.crv.TabBar) == 'undefined') {
  9. bobj.crv.TabBar = {};
  10. }
  11. if (typeof(bobj.crv.ButtonList) == 'undefined') {
  12. bobj.crv.ButtonList = {};
  13. }
  14. /**
  15. * ReportAlbum Constructor
  16. */
  17. bobj.crv.newReportAlbum = function(kwArgs) {
  18. var mb = MochiKit.Base;
  19. var UPDATE = mb.update;
  20. var BIND = mb.bind;
  21. var ALBUM = bobj.crv.ReportAlbum;
  22. kwArgs = UPDATE({
  23. id: bobj.uniqueId(),
  24. initTabIdx: 0, // Index of tab to select when initializing
  25. width: 800,
  26. height: 500,
  27. displayDrilldownTab : true
  28. }, kwArgs);
  29. var o = newTabbedZone(kwArgs.id, null, kwArgs.width, kwArgs.height);
  30. // Override TabbedZone's TabBarWidget
  31. o.tabs = bobj.crv.newTabBar({displayDrilldownTab : kwArgs.displayDrilldownTab});
  32. o.tabs.removeCB = BIND(ALBUM._onCloseTab, o);
  33. o.tabs.selectCB = BIND(ALBUM._onSelectTab, o);
  34. bobj.fillIn(o, kwArgs);
  35. o.widgetType = 'ReportAlbum';
  36. o._children = [];
  37. o._curView = null;
  38. o._curSigs = [];
  39. // Attach member functions
  40. o.selectOld = o.select;
  41. UPDATE(o, ALBUM);
  42. return o;
  43. };
  44. bobj.crv.ReportAlbum.init = function() {
  45. var connect = MochiKit.Signal.connect;
  46. var signal = MochiKit.Signal.signal;
  47. var partial = MochiKit.Base.partial;
  48. // This is not nice... Copied super class' init code here so that select
  49. // will be called only once.
  50. this.tzOldInit();
  51. this.tabs.init();
  52. if (this._children.length) {
  53. if (this.initTabIdx < 0 || this.initTabIdx >= this._children.length) {
  54. this.initTabIdx = 0;
  55. }
  56. this.select(this.initTabIdx);
  57. connect(this._curView, 'grpDrilldown', partial(signal, this, 'grpDrilldown'));
  58. connect(this._curView, 'grpNodeRetrieveChildren', partial(signal, this, 'grpNodeRetrieveChildren'));
  59. connect(this._curView, 'grpNodeCollapse', partial(signal, this, 'grpNodeCollapse'));
  60. connect(this._curView, 'grpNodeExpand', partial(signal, this, 'grpNodeExpand'));
  61. connect(this._curView, 'paramAdvanced', partial(signal, this, 'paramAdvanced'));
  62. connect(this._curView, 'paramApply', partial(signal, this, 'paramApply'));
  63. }
  64. };
  65. bobj.crv.ReportAlbum.update = function(update, updatePack) {
  66. if(update && update.cons == "bobj.crv.newReportAlbum" && this.getSelectedView()) {
  67. var update_activeView = update.children[update.args.initTabIdx];
  68. if(update_activeView) {
  69. if(update_activeView.args.viewStateId == this.getSelectedView().viewStateId) {
  70. this.getSelectedView().update(update_activeView, updatePack);
  71. }
  72. }
  73. }
  74. };
  75. bobj.crv.ReportAlbum.addChild = function(widget) {
  76. if (widget) {
  77. this._children.push(widget);
  78. this.add(widget.label, widget.tooltip);
  79. }
  80. };
  81. bobj.crv.ReportAlbum.getHTML = function() {
  82. var html = this.beginHTML();
  83. var children = this._children;
  84. for (var i = 0, len = children.length; i < len; ++i) {
  85. html += this.beginTabHTML(i);
  86. html += children[i].getHTML();
  87. html += this.endTabHTML();
  88. }
  89. html += this.endHTML();
  90. return html + bobj.crv.getInitHTML(this.widx);
  91. };
  92. /**
  93. * Resize the outer dimensions of the ReportAlbum. The standard resize method,
  94. * inherited from TabbedZoneWidget, resizes the container. We can't override it
  95. * without breaking things.
  96. */
  97. bobj.crv.ReportAlbum.resizeOuter = function(w, h) {
  98. // TabbedZoneWidget uses the numeric literals 10 and 33 to increase its
  99. // width and height beyond those passed to resize.
  100. var ISNUMBER = bobj.isNumber;
  101. if (ISNUMBER(w)) {
  102. w = Math.max(0, w - 10);
  103. }
  104. if (ISNUMBER(h)) {
  105. h = Math.max(0, h - 33);
  106. }
  107. this.resize(w, h);
  108. this.tabs.resize(w);
  109. if (this._curView) {
  110. this._curView.resize(); // Notify ReportView of resize
  111. }
  112. };
  113. /**
  114. * @return Returns a suggested size for the widget as an object with width and
  115. * height integer properties that specify the dimensions in pixels.
  116. */
  117. bobj.crv.ReportAlbum.getBestFitSize = function() {
  118. var w = 10; // Numeric literal from TabbedZoneWidget
  119. var h = 33; // Numeric literal from TabbedZoneWidget
  120. if (this._curView){
  121. var viewSize = this._curView.getBestFitSize();
  122. w += viewSize.width;
  123. h += viewSize.height;
  124. }
  125. return {
  126. width: w,
  127. height: h
  128. };
  129. };
  130. /**
  131. * Overrides parent. Opens a tab with a positioned div. The positioning prevents
  132. * the ReportView from disappearing in IE.
  133. */
  134. bobj.crv.ReportAlbum.beginTabHTML = function(index){
  135. return bobj.html.openTag('div', {
  136. id: 'tzone_tab_' + index + '_' + this.id,
  137. style: {
  138. display: 'none',
  139. width: this.w + 'px',
  140. height: this.h + 'px',
  141. position: 'relative'
  142. }
  143. });
  144. };
  145. /**
  146. * @return Returns the select ReportView or null if no view is selected
  147. */
  148. bobj.crv.ReportAlbum.getSelectedView = function() {
  149. return this._curView;
  150. };
  151. /**
  152. * @return Returns true if the selected view has a tool panel and it's displayed
  153. */
  154. bobj.crv.ReportAlbum.isToolPanelDisplayed = function() {
  155. return this._curView && this._curView.toolPanel && this._curView.toolPanel.isDisplayed();
  156. };
  157. /**
  158. * Set the display state of the tool panel in the current view, if it exists.
  159. *
  160. * @param disp [bool] The new display state of the active tool panel
  161. */
  162. bobj.crv.ReportAlbum.setDisplayToolPanel = function(disp) {
  163. if (this._curView) {
  164. this._curView.setDisplayToolPanel(disp);
  165. }
  166. };
  167. /**
  168. * Overrides parent. Selects a report view to display.
  169. *
  170. * @param index [int] Index of the report view
  171. */
  172. bobj.crv.ReportAlbum.select = function(index) {
  173. var ms = MochiKit.Signal;
  174. var DISCONNECT = ms.disconnect;
  175. var CONNECT = ms.connect;
  176. var SIGNAL = ms.signal;
  177. if (index >= 0 && index < this._children.length) {
  178. var curSigs = this._curSigs;
  179. while (curSigs.length) {
  180. DISCONNECT(curSigs.pop());
  181. }
  182. this._curView = this._children[index];
  183. var _self = this;
  184. curSigs.push(CONNECT(
  185. this._curView,
  186. 'hideToolPanel',
  187. function () {SIGNAL(_self, 'hideToolPanel');} ));
  188. curSigs.push(CONNECT(
  189. this._curView,
  190. 'resizeToolPanel',
  191. function (width) {SIGNAL(_self, 'resizeToolPanel', width);} ));
  192. this.selectOld(index);
  193. }
  194. };
  195. /**
  196. * Remove a view from the album
  197. *
  198. * @param index [int] Index of the view to remove
  199. */
  200. bobj.crv.ReportAlbum.remove = function(index) {
  201. var viewCtnr = this.zoneLayers[index];
  202. if (viewCtnr && viewCtnr.parentNode) {
  203. viewCtnr.parentNode.removeChild(viewCtnr);
  204. }
  205. arrayRemove(this, 'zoneLayers', index);
  206. arrayRemove(this, '_children', index);
  207. var newIdx = this.tabs.getSelection();
  208. if (newIdx >= 0) {
  209. this.select(newIdx);
  210. }
  211. };
  212. /**
  213. * Private. Handles tab close events.
  214. */
  215. bobj.crv.ReportAlbum._onCloseTab = function(tab, index) {
  216. var SIGNAL = MochiKit.Signal.signal;
  217. var view = this._children[index];
  218. this.remove(index);
  219. SIGNAL(this, 'removeView', view);
  220. SIGNAL(this, 'selectView', this._curView);
  221. };
  222. /**
  223. * Private. Handles tab select events.
  224. */
  225. bobj.crv.ReportAlbum._onSelectTab = function(tab, index) {
  226. this.select(index);
  227. MochiKit.Signal.signal(this, 'selectView', this._curView);
  228. };
  229. //** Tab Widget ****************************************************************
  230. /**
  231. * Tab Constructor
  232. */
  233. bobj.crv.newTab = function(kwArgs) {
  234. var update = MochiKit.Base.update;
  235. kwArgs = update({
  236. id: bobj.uniqueId(),
  237. hasCloseBtn: true,
  238. closeCB: null,
  239. tabWidth: 50 // Width of tabs (will shrink when there's not enough room)
  240. }, kwArgs);
  241. kwArgs.isTop = true; // bottom tabs are not supported
  242. var o = newTabWidget(
  243. kwArgs.id,
  244. kwArgs.isTop,
  245. kwArgs.label,
  246. kwArgs.clickCB,
  247. kwArgs.value,
  248. kwArgs.icon,
  249. kwArgs.iconW,
  250. kwArgs.iconH,
  251. kwArgs.iconOffX,
  252. kwArgs.iconOffY,
  253. kwArgs.dblClickCB,
  254. kwArgs.alt);
  255. o._crvTabOldInit = o.init;
  256. o._closeBtn = null;
  257. bobj.fillIn(o, kwArgs);
  258. update(o, bobj.crv.Tab);
  259. return o;
  260. };
  261. /**
  262. * Private. Image sizes and offsets.
  263. */
  264. bobj.crv.Tab._imageOffsets = {
  265. closeBtn: {
  266. normal: {x:0, y:18, w:18, h:18},
  267. mouseOver: {x:0, y:0, w:18, h:18}
  268. },
  269. tab: {
  270. top: {
  271. selected: {
  272. left: {x:0, y:0, w:15, h:24},
  273. middle: {x:0, y:24},
  274. right: {
  275. normal: {x:0, y:48, w:15, h:24},
  276. narrow: {x:10, y:48, w:5, h:24}
  277. }
  278. },
  279. unselected: {
  280. left: {x:0, y:72, w:15, h:24},
  281. middle: {x:0, y:96},
  282. right: {
  283. normal: {x:0, y:120, w:15, h:24},
  284. narrow: {x:10, y:120, w:5, h:24}
  285. }
  286. }
  287. }
  288. }
  289. };
  290. bobj.crv.Tab.init = function() {
  291. var bind = MochiKit.Base.bind;
  292. var TAB = bobj.crv.Tab;
  293. this._crvTabOldInit();
  294. var closeBtn = getLayer('tabWidgetCloseBtn_' + this.id);
  295. if (closeBtn) {
  296. closeBtn.onmouseover = bind(TAB._onCloseBtnMouseOver, this);
  297. closeBtn.onmouseout = bind(TAB._onCloseBtnMouseOut, this);
  298. closeBtn.parentNode.onclick = bind(TAB._onCloseBtnClick, this);
  299. }
  300. this._closeBtn = closeBtn;
  301. };
  302. /**
  303. * Private. Highlights the close button on mouseover.
  304. */
  305. bobj.crv.Tab._onCloseBtnMouseOver = function() {
  306. var off = this._imageOffsets.closeBtn.mouseOver;
  307. changeOffset(this._closeBtn, off.x, off.y);
  308. };
  309. /**
  310. * Private. Removes highlight from close button on mouseout.
  311. */
  312. bobj.crv.Tab._onCloseBtnMouseOut = function() {
  313. var off = this._imageOffsets.closeBtn.normal;
  314. changeOffset(this._closeBtn, off.x, off.y);
  315. };
  316. /**
  317. * Private. Delegates close button clicks to the attached callback.
  318. */
  319. bobj.crv.Tab._onCloseBtnClick = function() {
  320. if (this.closeCB) {
  321. this.closeCB();
  322. }
  323. };
  324. bobj.crv.Tab.getHTML = function() {
  325. var o = this;
  326. var update = MochiKit.Base.update;
  327. var h = bobj.html;
  328. var off = this._imageOffsets;
  329. var cls = "thumbtxt" + (o.isSelected ? "sel" : "");
  330. var cb = _codeWinName + ".TabWidget_clickCB('" + o.id + "');return false";
  331. var dblcb = _codeWinName + ".TabWidget_dblclickCB('" + o.id + "');return false";
  332. var keycb = _codeWinName + ".TabWidget_keyDownCB('" + o.id + "',event);";
  333. var menu = _codeWinName + ".TabWidget_contextMenuCB('" + o.id + "',event);return false";
  334. var icon = o.icon ? o.icon : _skin + "../transp.gif";
  335. var iconTDWidth = o.icon ? 3 : 0;
  336. // Choose image offsets for the tab based on whether the tabs is selected
  337. // and whether it's a top or bottom tab
  338. var tabOff = off.tab[this.isTop ? 'top': 'bottom'];
  339. tabOff = tabOff[this.selected ? 'selected' : 'unselected'];
  340. var lOff = tabOff.left;
  341. var rOff = tabOff.right[this.hasCloseBtn ? 'narrow' : 'normal'];
  342. var mOff = tabOff.middle;
  343. var cBtOff = off.closeBtn.normal;
  344. // Attributes for the tab's outermost tag
  345. var widgetAtts = {
  346. onmouseover: 'return true;',
  347. onclick: cb,
  348. id: this.id,
  349. ondblclick: dblcb,
  350. onkeydown: keycb,
  351. oncontextmenu: menu,
  352. style: {cursor: _hand},
  353. cellspacing: '0',
  354. cellpadding: '0',
  355. border: '0'
  356. };
  357. // Styles applied to the label cell and close button cell
  358. var midCellStyle = {
  359. 'background-image': "url('" + _skin + "tabs.gif')",
  360. 'background-position': (-mOff.x) + 'px '+ (-mOff.y) + 'px'
  361. };
  362. if (this.isTop) {
  363. midCellStyle['padding-top'] = '1px';
  364. }
  365. else {
  366. midCellStyle['padding-bottom'] = '3px';
  367. }
  368. // Style properties for the table that contains the tab's icon
  369. var imgCellStyle = update({
  370. 'padding-right:': iconTDWidth + 'px',
  371. 'width': (this.iconW + iconTDWidth),
  372. 'align': "left"
  373. }, midCellStyle);
  374. if (this.isTop) {
  375. imgCellStyle['padding-top'] = '1px';
  376. }
  377. else {
  378. imgCellStyle['padding-bottom'] = '2px';
  379. }
  380. // Styles aplied to the cel that contains the close button
  381. var closeCellStyle = update({
  382. display: this.hasCloseBtn ? '' : 'none'
  383. }, midCellStyle);
  384. // Create html for the tab
  385. var html = h.TABLE(widgetAtts,
  386. h.TBODY(null,
  387. h.TR({ valign: "middle", height: lOff.h},
  388. h.TD({width: lOff.w},
  389. imgOffset(_skin + 'tabs.gif', lOff.w, lOff.h, lOff.x, lOff.y, "tabWidgetLeft_" + o.id)),
  390. h.TD({id: 'tabWidgetImg_' + o.id, style:imgCellStyle},
  391. imgOffset(icon, o.iconW, o.iconH, o.iconOffX, o.iconOffY, "tabWidgetIcon_" + o.id, null, o.iconAlt)),
  392. h.TD({width: o.tabWidth, id: 'tabWidgetMid_' + this.id, style: midCellStyle},
  393. h.SPAN({style:{'white-space': 'nowrap'}},
  394. lnk(convStr(o.name,true), null, cls, "tabWidgetLnk_" + o.id))),
  395. h.TD({width: cBtOff.w, id: 'tabWidgetClose_' + o.id, style: closeCellStyle},
  396. h.A({href: 'javascript:void(0)', title:L_bobj_crv_Close},
  397. imgOffset(_skin + 'dialogelements.gif', cBtOff.w, cBtOff.h, cBtOff.x, cBtOff.y, "tabWidgetCloseBtn_" + o.id))),
  398. h.TD({width: rOff.w},
  399. imgOffset(_skin + 'tabs.gif', rOff.w, rOff.h, rOff.x, rOff.y, "tabWidgetRight_" + o.id)))));
  400. return html;
  401. };
  402. /**
  403. * Override's parent so that close button's cell is also changed.
  404. *
  405. * @see TabWidget_changeContent
  406. */
  407. bobj.crv.Tab.changeContent = function(changeOnlySelection) {
  408. var o = this;
  409. // Get layers
  410. if (!o.lnkLayer) {
  411. o.lnkLayer = getLayer("tabWidgetLnk_" + o.id);
  412. o.leftImgLayer = getLayer("tabWidgetLeft_" + o.id);
  413. o.rightImgLayer = getLayer("tabWidgetRight_" + o.id);
  414. o.midImgLayer = getLayer("tabWidgetMid_" + o.id);
  415. o.imgImgLayer = getLayer("tabWidgetImg_" + o.id);
  416. o.closeImgLayer = getLayer("tabWidgetClose_" + o.id);
  417. o.iconLayer = getLayer("tabWidgetIcon_" + o.id);
  418. }
  419. // Change icon and text
  420. if (!changeOnlySelection) {
  421. o.lnkLayer.innerHTML = convStr(o.name, true);
  422. var iconLayer = o.iconLayer;
  423. changeOffset(iconLayer, o.iconOffX, o.iconOffY, o.icon ? o.icon: _skin + "../transp.gif");
  424. iconLayer.alt = o.iconAlt;
  425. iconLayer.style.width = "" + o.iconW + "px";
  426. iconLayer.style.height = "" + o.iconH + "px";
  427. var iconTDWidth = o.icon ? 3: 0;
  428. var imgL = o.imgImgLayer;
  429. imgL.style.paddingRight = "" + iconTDWidth + "px";
  430. imgL.style.width = "" + (iconTDWidth + ((o.icon && bobj.isNumber(o.iconW)) ? o.iconW : 0)) + "px";
  431. if (_moz && !_saf) {
  432. imgL.width = (iconTDWidth + ((o.icon && bobj.isNumber(o.iconW)) ? o.iconW : 0));
  433. }
  434. }
  435. var off = this._imageOffsets;
  436. var tabOff = off.tab[this.isTop ? 'top': 'bottom'];
  437. tabOff = tabOff[this.isSelected ? 'selected' : 'unselected'];
  438. var lOff = tabOff.left;
  439. var rOff = tabOff.right[this.hasCloseBtn ? 'narrow' : 'normal'];
  440. var mOff = tabOff.middle;
  441. changeOffset(o.leftImgLayer, lOff.x, lOff.y);
  442. changeOffset(o.midImgLayer, mOff.x, mOff.y);
  443. changeOffset(o.closeImgLayer, mOff.x, mOff.y);
  444. changeOffset(o.imgImgLayer, mOff.x, mOff.y);
  445. changeOffset(o.rightImgLayer, rOff.x, rOff.y);
  446. o.lnkLayer.className = "thumbtxt" + (o.isSelected ? "sel" : "");
  447. };
  448. //** TabBar Widget *************************************************************
  449. /**
  450. * TabBar Constructor
  451. */
  452. bobj.crv.newTabBar = function(kwArgs) {
  453. var UPDATE = MochiKit.Base.update;
  454. kwArgs = UPDATE({
  455. id: bobj.uniqueId(),
  456. removeCB: null, // function to call when tab is removed
  457. selectCB: null, // function to call when tab is selected
  458. displayDrilldownTab : true
  459. }, kwArgs);
  460. kwArgs.isTop = true; // Bottom tabs are not supported
  461. var o = newWidget(kwArgs.id);
  462. bobj.fillIn(o, kwArgs);
  463. o.widgetType = 'TabBar';
  464. o._tabs = [];
  465. o._tabRowLayer = null;
  466. o._tabBarLayer = null;
  467. o._selIndex = 0;
  468. o._tabBarOldInit = o.init;
  469. o._tabBarOldResize = o.resize;
  470. o._selTabXOffset = 0; // Number of pixels of the the tab left of the
  471. // selected tab that should be visible after scrolling
  472. o._menuBtnWidth = 24;
  473. var bind = MochiKit.Base.bind;
  474. o._tabMenuBtn = bobj.crv.newButtonList({
  475. id: o.id + '_sel',
  476. buttonWidth: o._menuBtnWidth,
  477. changeCB: bind(bobj.crv.TabBar._onMenuChange, o)
  478. });
  479. UPDATE(o, bobj.crv.TabBar);
  480. return o;
  481. };
  482. bobj.crv.TabBar.init = function() {
  483. this._tabBarOldInit();
  484. if (this._tabMenuBtn){
  485. this._tabMenuBtn.init();
  486. }
  487. if(this.displayDrilldownTab === false) {
  488. this.hide();
  489. }
  490. this._tabRowLayer = getLayer(this.id + '_tabRow');
  491. this._tabBarLayer = getLayer(this.id + '_tabBar');
  492. this._tabCtnLayer = this._tabBarLayer.parentNode;
  493. this._tabSelLayer = getLayer(this.id + '_tabSel');
  494. for (var i = 0; i < this._tabs.length; ++i){
  495. var tab = this._tabs[i];
  496. tab.init();
  497. tab.select(i == this._selIndex);
  498. }
  499. setTimeout(MochiKit.Base.bind(this._setMenuVis, this), 0);
  500. };
  501. /**
  502. * Add a tab to the bar.
  503. *
  504. * @param label [String] Tab label
  505. * @param value [String - optional] a value that is used to find it again
  506. * @param icon [String - optional] an image URL
  507. * @param iconW [int - optional] displayed image width
  508. * @param iconH [int - optional] displayed image height
  509. * @param iconOffX [int - optional] x offset in the icon (for combined images)
  510. * @param iconOffY [int - optional] y offset in the icon (for combined images)
  511. * @param hasCloseBtn [bool - optional] When true, tab has a close button. Defaults
  512. * to false for first tab and true for others
  513. */
  514. bobj.crv.TabBar.add = function(label, value, idx, icon, iconW, iconH, iconOffX, iconOffY, alt, hasCloseBtn) {
  515. var kwArgs = {
  516. label: label,
  517. value: value,
  518. idx : idx,
  519. icon: icon,
  520. iconW : iconW,
  521. iconH: iconH,
  522. iconOffX: iconOffX,
  523. iconOffY: iconOffY,
  524. alt: alt,
  525. isTop: this.isTop
  526. };
  527. if (bobj.isBoolean(hasCloseBtn)) {
  528. kwArgs.hasCloseBtn = hasCloseBtn;
  529. }
  530. else {
  531. kwArgs.hasCloseBtn = (this._tabs.length ? true : false);
  532. }
  533. return this.kwAdd(kwArgs);
  534. };
  535. /**
  536. * Add a tab using keyword arguments. Must be called before getHTML is called.
  537. *
  538. * @see bobj.crv.TabBar.add
  539. */
  540. bobj.crv.TabBar.kwAdd = function(kwArgs) {
  541. var bind = MochiKit.Base.bind;
  542. kwArgs.id = bobj.uniqueId();
  543. var tab = bobj.crv.newTab(kwArgs);
  544. tab.cb = bind(this._onTabClick, this, tab);
  545. tab.closeCB = bind(this._onTabCloseBtnClick, this, tab);
  546. this._tabs.push(tab);
  547. this._tabMenuBtn.add(kwArgs.label);
  548. return tab;
  549. };
  550. /**
  551. * Removes a tab
  552. *
  553. * @param tab [Tab or Number] A tab in the bar or the index of a tab in the bar
  554. */
  555. bobj.crv.TabBar.remove = function(tab) {
  556. var o = this;
  557. var items = o._tabs;
  558. var len = items.length;
  559. var idx = bobj.isNumber(tab) ? tab : this.getIndex(tab);
  560. if (idx >= 0 && idx < len) {
  561. var elem = items[idx];
  562. var l = elem.layer;
  563. arrayRemove(o, "_tabs",idx);
  564. o._tabMenuBtn.getMenu().del(idx);
  565. items = o._tabs;
  566. len = items.length;
  567. if (l) {
  568. // we used to remove the layer from the DOM but for whatever reason
  569. // this would cause the next request to be aborted in IE6
  570. l.parentNode.style.display = 'none';
  571. }
  572. o._setMenuVis();
  573. if (o._selIndex > idx) {
  574. o.select(o._selIndex - 1);
  575. }
  576. else if (o._selIndex == idx && len > 0) {
  577. o.select(Math.min(idx, len - 1));
  578. }
  579. else {
  580. o.select(o._selIndex);
  581. }
  582. }
  583. };
  584. bobj.crv.TabBar.hide = function()
  585. {
  586. try {
  587. var parent1 = this.layer.parentNode; // TD
  588. var parent2 = parent1.parentNode; // TR
  589. parent2.style.display = 'none';
  590. }
  591. catch(ex) {
  592. if (bobj.crv.config.isDebug) {
  593. throw ex;
  594. }
  595. }
  596. };
  597. /**
  598. * @return [int] The index of a tab in the tab bar or -1 if the tab is not found
  599. */
  600. bobj.crv.TabBar.getIndex = function(tab) {
  601. return MochiKit.Base.findIdentical(this._tabs, tab);
  602. };
  603. /**
  604. * @return [int] The index of the selected tab
  605. */
  606. bobj.crv.TabBar.getSelection = function() {
  607. return this._selIndex;
  608. };
  609. /**
  610. * @return [int] The number of tabs in the bar
  611. */
  612. bobj.crv.TabBar.getCount = function() {
  613. return this._tabs.length;
  614. };
  615. /**
  616. * @return [Tab] The tab at the specified index or undefined for an invalid index
  617. */
  618. bobj.crv.TabBar.getTabAt = function(index) {
  619. return this._tabs[index];
  620. };
  621. /**
  622. * Select a tab
  623. *
  624. * @param tab [Number] index of tab to select
  625. */
  626. bobj.crv.TabBar.select = function(tab) {
  627. if (!bobj.isNumber(tab)) {
  628. tab = this.getIndex(tab);
  629. }
  630. if (bobj.isNumber(tab)) {
  631. var index = tab;
  632. var len = this._tabs.length;
  633. if (index == -1) {
  634. if (this._selIndex >= 0 && this._selIndex < len) {
  635. return; // if there is already a valid selection keep it
  636. }
  637. index = 0; // there wasn't a valid selection so lets select the MainReport
  638. }
  639. if (index >= 0 && index < len) {
  640. if (this._selIndex >= 0 && this._selIndex != index && this._selIndex < len) {
  641. this._tabs[this._selIndex].select(false);
  642. }
  643. this._selIndex = index;
  644. this._tabs[index].select(true);
  645. if (this._tabMenuBtn) {
  646. this._tabMenuBtn.getMenu().select(index);
  647. }
  648. this.scroll(null, this._selIndex);
  649. }
  650. }
  651. };
  652. /**
  653. * Resize the tab bar
  654. *
  655. * @param w [int] Width in pixels
  656. * @param h [int] Height in pixels
  657. */
  658. bobj.crv.TabBar.resize = function(w, h) {
  659. this._tabBarOldResize(w, h);
  660. if (bobj.isNumber(w) && this._tabBarLayer) {
  661. this._tabCtnLayer.style.width = Math.max(0, w) + 'px';
  662. this._setMenuVis();
  663. this.scroll(this._selIndex);
  664. }
  665. };
  666. /**
  667. * Private. Shows the tab menu button when there are too many tabs and hides it
  668. * when there's enough space for all tabs.
  669. */
  670. bobj.crv.TabBar._setMenuVis = function() {
  671. var bar = this._tabBarLayer;
  672. var ctn = this._tabCtnLayer;
  673. var sel = this._tabSelLayer;
  674. if (bar && ctn && sel) {
  675. if (ctn.offsetWidth < bar.offsetWidth) {
  676. // Show the tab selector menu button
  677. sel.style.display = '';
  678. }
  679. else {
  680. // Hide the tab selector menu button
  681. sel.style.display = 'none';
  682. }
  683. }
  684. };
  685. /**
  686. * Scroll the tab bar to make the specified tab visible.
  687. *
  688. * @param tab Can be any of the following:
  689. * [Tab] The tab widget to make visible
  690. * [Number] The index of a tab to make visible
  691. * [String] 'first', 'previous', next, and 'last' are accepted
  692. *
  693. * @param idx [Number] (deprecated) If a null value is passed for tab, idx will
  694. * be checked.
  695. */
  696. bobj.crv.TabBar.scroll = function(tab, idx) {
  697. if (!this._tabBarLayer) {
  698. return;
  699. }
  700. idx = bobj.isNumber(idx) ? idx : -1;
  701. if (bobj.isString(tab)) {
  702. if (tab == 'first') {
  703. idx = 0;
  704. }
  705. else if (tab == 'previous'){
  706. idx = this._selIndex - 1;
  707. }
  708. else if (tab == 'next'){
  709. idx = this._selIndex + 1;
  710. }
  711. else if (tab == 'last'){
  712. idx = this.getCount() - 1;
  713. }
  714. }
  715. else if (bobj.isNumber(tab)) {
  716. idx = tab;
  717. }
  718. else if (tab) {
  719. idx = this.getIndex(tab);
  720. }
  721. if (idx >= 0 && idx < this.getCount()) {
  722. var bar = this._tabBarLayer; // The tab bar
  723. var barCtn = bar.parentNode; // The bar's container
  724. var barStyle = bar.style;
  725. // Left and right edges of the entire tab bar relative to its container
  726. var barLeftX = parseInt(barStyle.left, 10) || 0;
  727. var barRightX = barLeftX + bar.offsetWidth;
  728. // Left and right edges of the tab relative to the bar's container
  729. var tabLeftX = this._getItemXPos(idx) + barLeftX;
  730. var tabRightX = tabLeftX + this.getTabAt(idx).getWidth();
  731. // Width of the tab bar container's visible area
  732. var visAreaW = barCtn.offsetWidth - this._tabMenuBtn.getWidth();
  733. var scroll = 0;
  734. if (tabLeftX < 0) {
  735. // Scroll the tab bar to the right
  736. scroll = (-1 * tabLeftX);
  737. }
  738. else if (tabRightX > visAreaW){
  739. // Scroll the tab bar to the left
  740. scroll = -(tabRightX - visAreaW);
  741. }
  742. else if (barRightX < visAreaW && barLeftX < 0) {
  743. // There's room in the container and not all tabs are visible
  744. scroll = Math.min(visAreaW - barRightX, -barLeftX);
  745. }
  746. if (scroll) {
  747. barStyle.left = (barLeftX + scroll) + 'px';
  748. }
  749. }
  750. };
  751. bobj.crv.TabBar.getHTML = function() {
  752. var h = bobj.html;
  753. var barHeight = 24;
  754. var wgtStyle = {
  755. height: barHeight + 'px',
  756. overflow: 'hidden',
  757. width: '100%',
  758. position: 'relative'
  759. };
  760. var barStyle = {
  761. position: 'relative'
  762. };
  763. var selDivStyle = {
  764. width: this._menuBtnWidth + 'px',
  765. height: barHeight + 'px',
  766. display: 'none',
  767. position: 'absolute',
  768. top: '0px',
  769. right: '0px',
  770. 'padding-top': '3px'
  771. };
  772. var tabsHtml = '';
  773. for (var i = 0; i < this._tabs.length; ++i) {
  774. tabsHtml += h.TD(null, this._tabs[i].getHTML());
  775. }
  776. var widgetHtml = h.DIV({id: this.id, style: wgtStyle},
  777. h.TABLE({cellspacing: '0', cellpadding: '0', border: '0', style: {width: '100%'}},
  778. h.TBODY(null,
  779. h.TR({valign: 'bottom', height: barHeight},
  780. h.TD(null,
  781. h.DIV({style: {overflow:'hidden', position:'relative'}},
  782. h.TABLE({id: this.id + '_tabBar', style: barStyle, cellspacing:'0', cellpadding:'0', border:'0'},
  783. h.TBODY(null,
  784. h.TR({id: this.id + '_tabRow'}, tabsHtml)))))))),
  785. h.DIV({id: this.id + '_tabSel', 'class': 'panelzone', style: selDivStyle}, this._tabMenuBtn.getHTML()));
  786. return widgetHtml;
  787. };
  788. /**
  789. * Private. Selects a tab when it's clicked.
  790. */
  791. bobj.crv.TabBar._onTabClick = function(tab) {
  792. var idx = this.getIndex(tab);
  793. if (idx < 0)
  794. return; // the tab has been removed
  795. this.select(idx);
  796. if (this.selectCB) {
  797. this.selectCB(tab, idx);
  798. }
  799. };
  800. /**
  801. * Private. Removes a tab when its close button is clicked.
  802. */
  803. bobj.crv.TabBar._onTabCloseBtnClick = function(tab) {
  804. var idx = this.getIndex(tab);
  805. this.remove(idx);
  806. if (this.removeCB) {
  807. this.removeCB(tab, idx);
  808. }
  809. };
  810. /**
  811. * Private. Selects the associated tab when the tab menu selection changes.
  812. */
  813. bobj.crv.TabBar._onMenuChange = function() {
  814. var menu = this._tabMenuBtn.getMenu();
  815. if (menu) {
  816. var selected = menu.getSelection();
  817. if (selected) {
  818. this.select(selected.index);
  819. if (this.selectCB) {
  820. this.selectCB(this.getTabAt(selected.index), selected.index);
  821. }
  822. }
  823. }
  824. };
  825. /**
  826. * Private. Caculates the left (x) offset of a tab within the tab bar.
  827. */
  828. bobj.crv.TabBar._getItemXPos = function(index) {
  829. var x = 0;
  830. for (var i = 0; i < index; i++){
  831. x += parseInt(this._tabs[i].getWidth(), 10);
  832. }
  833. return x;
  834. };
  835. //** ButtonList Widget *************************************************************
  836. /**
  837. * ButtonList Constructor
  838. */
  839. bobj.crv.newButtonList = function(kwArgs) {
  840. kwArgs = MochiKit.Base.update({
  841. id: bobj.uniqueId(),
  842. numLines: null, // null allows the height to grow (will fit within the viewport)
  843. buttonWidth: 24,
  844. buttonTooltip: L_bobj_crv_TabList,
  845. changeCB: null,
  846. label: null,
  847. tabIndex: 0,
  848. multiSelect: false,
  849. menuWidth: null,
  850. menuTooltip: null
  851. }, kwArgs);
  852. var o = newButtonWidget(
  853. kwArgs.id,
  854. kwArgs.label,
  855. bobj.crv.ButtonList._onClick,
  856. kwArgs.buttonWidth,
  857. null,
  858. kwArgs.buttonTooltip,
  859. kwArgs.tabIndex,
  860. 0, _skin+"menus.gif", 7, 16, 0, 81, true, 0, 97);
  861. o._menu = newListWidget(
  862. kwArgs.id + "_menu",
  863. MochiKit.Base.bind(bobj.crv.ButtonList._onChange, o),
  864. kwArgs.multiSelect,
  865. kwArgs.menuWidth,
  866. kwArgs.numLines || 2,
  867. kwArgs.menuTooltip,
  868. null, //dblClickCB
  869. null); //keyUpCB
  870. o._listItems = [];
  871. o._blOldInit = o.init;
  872. o._blOldGetHTML = o.getHTML;
  873. o._menuDiv = null;
  874. o._captureClicks = MenuWidget_captureClicks;
  875. o._releaseClicks = MenuWidget_releaseClicks;
  876. bobj.fillIn(o, kwArgs);
  877. o.widgetType = 'ButtonList';
  878. MochiKit.Base.update(o, bobj.crv.ButtonList);
  879. return o;
  880. };
  881. /**
  882. * @return [Widget] Menu/list widget associated with this button.
  883. */
  884. bobj.crv.ButtonList.getMenu = function() {
  885. return this._menu;
  886. };
  887. /**
  888. * Add an item to the menu
  889. *
  890. * @param label [String] The text to display in the menu
  891. * @param value [any - opt.] The value associated with the new menu item
  892. * @param isSelected [bool - opt.] True if item should be selected after being added
  893. * @param id [String - opt.] DHTML id associated with menu item;
  894. */
  895. bobj.crv.ButtonList.add = function(label, value, isSelected, id) {
  896. if (this._menu && this._menu.layer) {
  897. this._menu.add(label, value, isSelected, id);
  898. }
  899. else {
  900. this._listItems.push({lbl:label, val:value, sel:isSelected, id:id});
  901. }
  902. };
  903. bobj.crv.ButtonList.init = function() {
  904. var menu = this._menu;
  905. this._blOldInit();
  906. menu.init();
  907. this._menuDiv = getLayer(this.id + '_menuDiv');
  908. var listItems = this._listItems;
  909. for (var i = 0, len = listItems.length; i < len; ++i) {
  910. var it = listItems[i];
  911. menu.add(it.lbl, it.val, it.sel, it.id);
  912. }
  913. this._listItems = [];
  914. };
  915. bobj.crv.ButtonList.getHTML = function() {
  916. var h = bobj.html;
  917. var menuDivAtts = {
  918. id: this.id + '_menuDiv',
  919. onmousedown: 'event.cancelBubble=true',
  920. 'class': 'menuFrame',
  921. style: {
  922. visibility: 'hidden',
  923. position: 'absolute',
  924. 'z-index': 5000
  925. }
  926. };
  927. return this._blOldGetHTML() + h.DIV(menuDivAtts, this._menu.getHTML());
  928. };
  929. /**
  930. * @return [bool] True if and only if the menu is visible
  931. */
  932. bobj.crv.ButtonList.isMenuShowing = function() {
  933. return this._menuDiv && this._menuDiv.style.visibility != 'hidden';
  934. };
  935. /**
  936. * Hide the menu
  937. */
  938. bobj.crv.ButtonList.hideMenu = function() {
  939. if (this._menuDiv) {
  940. this._menuDiv.style.visibility = 'hidden';
  941. }
  942. };
  943. /**
  944. * Position and show the menu
  945. */
  946. bobj.crv.ButtonList.showMenu = function() {
  947. if (this._menuDiv) {
  948. this._captureClicks();
  949. var body = document.body;
  950. if (this._menuDiv.parentNode !== body) {
  951. body.appendChild(this._menuDiv);
  952. }
  953. var divStyle = this._menuDiv.style;
  954. divStyle.left = '-1000px';
  955. divStyle.top = '-1000px';
  956. divStyle.visibility = 'visible';
  957. var winDim = MochiKit.Style.getViewportDimensions();
  958. var w = this._menu.layer.offsetWidth;
  959. var h = this._menu.getHeight();
  960. // If numLines wasn't specified, use as much space as necessary
  961. // while remaining within the viewport
  962. if (!this.numLines) {
  963. h = Math.min(this._menu.layer.scrollHeight + 10, winDim.h - 10);
  964. this._menu.resize(null, h);
  965. }
  966. // Place the menu below the button and aligned with the left edge
  967. var btnPos = getPosScrolled(this.layer);
  968. var x = btnPos.x;
  969. var y = btnPos.y + this.getHeight();
  970. // Change coordinates so the whole menu is on the screen
  971. var xRight = x + w + 4;
  972. var yBottom = y + h + 4;
  973. var xMax = winDim.w + body.scrollLeft - Math.max(0, (winDim.w - body.offsetWidth));
  974. if (xRight > xMax) {
  975. x = Math.max(0, x - (xRight - xMax));
  976. }
  977. var yMax = winDim.h + body.scrollTop;
  978. if (yBottom > yMax) {
  979. y = Math.max(0, y - (yBottom - yMax));
  980. }
  981. divStyle.left = x + 'px';
  982. divStyle.top = y + 'px';
  983. }
  984. };
  985. /**
  986. * Private. Capture clicks in the current document so that the menu can be
  987. * hidden automatically.
  988. */
  989. bobj.crv.ButtonList._captureClicks = function() {
  990. var BIND = MochiKit.Base.bind;
  991. try {
  992. this.layer.onmousedown = BIND(this._onCaptureClick, this, true);
  993. this._oldMousedown = document.onmousedown;
  994. document.onmousedown = BIND(this._onCaptureClick, this, false);
  995. }
  996. catch(ex)
  997. {
  998. if (bobj.crv.config.isDebug) {
  999. throw ex;
  1000. }
  1001. }
  1002. };
  1003. /**
  1004. * Private. Stop capturing clicks.
  1005. */
  1006. bobj.crv.ButtonList._releaseClicks = function() {
  1007. if (this.layer.onmousedown) { // non-null if clicks are being captured
  1008. this.layer.onmousedown = null;
  1009. document.onmousedown = this._oldMousedown;
  1010. }
  1011. };
  1012. /**
  1013. * Private. Button click callback.
  1014. */
  1015. bobj.crv.ButtonList._onClick = function() {
  1016. if (!this._cancelNextClick) {
  1017. this.showMenu();
  1018. }
  1019. this._cancelNextClick = false;
  1020. };
  1021. /**
  1022. * Private. Menu change callback.
  1023. */
  1024. bobj.crv.ButtonList._onChange = function() {
  1025. this._releaseClicks();
  1026. this.hideMenu();
  1027. if (this.changeCB) {
  1028. this.changeCB();
  1029. }
  1030. };
  1031. /**
  1032. * Private. Called when a click is captured (after _captureClicks has been called)
  1033. */
  1034. bobj.crv.ButtonList._onCaptureClick = function(cancelNext, e) {
  1035. this._cancelNextClick = cancelNext;
  1036. eventCancelBubble(e);
  1037. this.hideMenu();
  1038. this._releaseClicks();
  1039. };