PinFreezeContainer.js 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995
  1. /*
  2. *+------------------------------------------------------------------------+
  3. *| Licensed Materials - Property of IBM
  4. *| IBM Cognos Products: Viewer
  5. *| (C) Copyright IBM Corp. 2014, 2018
  6. *|
  7. *| US Government Users Restricted Rights - Use, duplication or
  8. *| disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  9. *|
  10. *+------------------------------------------------------------------------+
  11. */
  12. /**
  13. * Support pin and freeze for a specific layout container (crosstab or list)
  14. */
  15. function PinFreezeContainer(pinFreezeManager, lid, viewerID, freezeTop, freezeSide, containerNode, index) {
  16. this.m_pinFreezeManager=pinFreezeManager;
  17. this.m_lid=lid;
  18. this.m_lidNS=lid + viewerID + index;
  19. this.m_viewerId=viewerID;
  20. this.m_freezeTop=freezeTop;
  21. this.m_freezeSide=freezeSide;
  22. this.m_cachedReportDiv=null;
  23. this.m_cachedPFContainer=null;
  24. this.m_cachedBaseContainer=containerNode; //The crosstab/list within main output.
  25. this.m_containerMargin = {"top" : 0, "left" : 0};
  26. if (this.m_cachedBaseContainer && this.m_cachedBaseContainer.style) {
  27. if (this.m_cachedBaseContainer.style.marginTop) {
  28. this.m_containerMargin.top = Number(this.m_cachedBaseContainer.style.marginTop.replace('px', ''));
  29. }
  30. if (this.m_cachedBaseContainer.style.marginLeft) {
  31. this.m_containerMargin.left = Number(this.m_cachedBaseContainer.style.marginLeft.replace('px', ''));
  32. }
  33. }
  34. this.m_cachedContainerIndex=index;
  35. this.m_sectionCache=null;
  36. this.m_homeCellNodes={};
  37. this.m_fixedWidth=null; //Resize width will have no effect when container style (width) has been specified by the report author.
  38. this.m_clientWidth=700; //The total visible width of the container (in BUX, usually the widget width).
  39. this.m_scrollableClientWidth=700; //The visible width of the scrollable region (usu the clientWidth-the homeCell width)
  40. this.m_fixedHeight=null; //Resize height will have no effect when container style (height) has been specified by the report author.
  41. this.m_clientHeight=300;
  42. this.m_scrollableClientHeight=300;
  43. this.m_wrapFlag=false; //For FF
  44. this.c_pageMargin=(this.m_freezeTop && this.m_freezeSide) ? 50 : 20; //Default page margin.
  45. this.touchScrollSections=false; //True when touch scrolling of sections is underway (false if move is delegated)
  46. this.touchPreviousX=-1; //represents the "previous touch location" prior to the current touch move event
  47. this.touchPreviousY=-1; //set to -1 initially and at "touchEnd".
  48. }
  49. /**
  50. * Called when saving the dashboard. This function will serialize all the information
  51. * needed to correctly reload the dashboard with frozen containers
  52. */
  53. PinFreezeContainer.prototype.toJSONString = function() {
  54. var sResponse = '{';
  55. sResponse += '"m_clientWidth":' + this.m_clientWidth + '';
  56. sResponse += ',"m_scrollableClientWidth":' + this.m_scrollableClientWidth + '';
  57. sResponse += ',"m_clientHeight":' + this.m_clientHeight + '';
  58. sResponse += ',"m_scrollableClientHeight":' + this.m_scrollableClientHeight + '';
  59. sResponse += '}';
  60. return sResponse;
  61. };
  62. PinFreezeContainer.prototype.copyProperties = function(oldObj) {
  63. this.m_clientWidth = oldObj.m_clientWidth;
  64. this.m_scrollableClientWidth = oldObj.m_scrollableClientWidth;
  65. this.m_clientHeight = oldObj.m_clientHeight;
  66. this.m_scrollableClientHeight = oldObj.m_scrollableClientHeight;
  67. };
  68. PinFreezeContainer.prototype.setViewerId = function(id) {
  69. this.m_viewerId = id;
  70. };
  71. PinFreezeContainer.prototype.getLid = function() {
  72. return this.m_lid;
  73. };
  74. /**
  75. * Modify the DOM for the report by overlaying a pin freeze template
  76. * that establishes zones for the container in the report (frozen side headings, top headings, data area).
  77. * Populate the frozen headings with a minimal subset of the main report
  78. */
  79. PinFreezeContainer.prototype.createPFContainer = function(oReport, containsChildContainers) {
  80. var oTemp=document.createElement('temp');
  81. if (this.m_cachedBaseContainer) {
  82. this.applyAuthoredFixedSizes(this.m_cachedBaseContainer);
  83. this.m_cachedReportDiv=oReport;
  84. var oBaseContainerParent=this.m_cachedBaseContainer.parentNode;
  85. var templateHTML=this.loadTemplateHTML();
  86. if (templateHTML) {
  87. oTemp.innerHTML=templateHTML;
  88. var pfContainer = this.getContainerByLID(oTemp);
  89. var pfMainOutput=this.getSectionByLID(oTemp.firstChild, "pfMainOutput");
  90. if (pfMainOutput) {
  91. //To avoid clone....mark the initial position oBaseContainer had it in its parent list.
  92. var i=this.getChildPosition(oBaseContainerParent, this.m_cachedBaseContainer);
  93. if (i!=-1) {
  94. //Set White space Style for advance setting "ADVANCED_PROPERTY_FREEZE_DEFAULT_WRAP"
  95. var m_oCV=this.m_pinFreezeManager.m_oCV;
  96. if(m_oCV && m_oCV.envParams["freezeDefaultWrap"]) {
  97. if (this.m_cachedBaseContainer.style.whiteSpace === "" && m_oCV.envParams["freezeDefaultWrap"].toLowerCase() === "true" ) {
  98. var spanElements=this.m_cachedBaseContainer.getElementsByTagName("span");
  99. if(spanElements) {
  100. for (var k=0; k < spanElements.length; k++) {
  101. spanElements[k].style.whiteSpace = "nowrap";
  102. }
  103. }
  104. this.m_wrapFlag=true;
  105. }
  106. }
  107. // If we don't have any child containers, then make sure we keep the current
  108. // width and height of the container so we don't squish the contents.
  109. if (!containsChildContainers) {
  110. if (!this._getFixedWidth()) {
  111. // We have to distinguish between the width set by a report author and the width we set
  112. this.m_cachedBaseContainer.setAttribute("authoredFixedWidth", "false");
  113. this.m_addedFixedWidth = this.m_cachedBaseContainer.clientWidth + 1;
  114. this.m_cachedBaseContainer.style.width = this.m_addedFixedWidth + "px";
  115. }
  116. if (!this._getFixedHeight()) {
  117. // We have to distinguish between the height set by a report author and the height we set
  118. this.m_cachedBaseContainer.setAttribute("authoredFixedHeight", "false");
  119. this.m_addedFixedHeight = this.m_cachedBaseContainer.clientHeight;
  120. this.m_cachedBaseContainer.style.height = this.m_addedFixedHeight + "px";
  121. }
  122. // Needed the +2 here to stop a small movement that would happen when moving the contain to under the div. Now
  123. // The list/crosstab will not shift, it should stay exactly where it was
  124. pfMainOutput.style.width = this.m_cachedBaseContainer.clientWidth + 2 + "px";
  125. pfMainOutput.style.height = this.m_cachedBaseContainer.clientHeight + 2 + "px";
  126. }
  127. //move oBaseContainer to the new parent.
  128. pfMainOutput.appendChild(this.m_cachedBaseContainer);
  129. //insert pfContainer at the position where oBaseContainer was.
  130. this.insertAt(oBaseContainerParent, pfContainer, i);
  131. }
  132. if (this.m_cachedBaseContainer.style.border!=="") {
  133. //Transfer borders that are on the layout element to the outer pfContainer
  134. //since the original layout element is only a section of the output.
  135. pfContainer.style.border=this.m_cachedBaseContainer.style.border;
  136. this.m_cachedBaseContainer.style.border="";
  137. }
  138. }
  139. }
  140. }
  141. };
  142. PinFreezeContainer.prototype._getFixedWidth = function(oBaseContainer) {
  143. if (oBaseContainer && oBaseContainer.style.width && !oBaseContainer.getAttribute("authoredFixedWidth")) {
  144. var width = Number(oBaseContainer.style.width.split('px')[0]);
  145. return isNaN(width) ? null : width;
  146. }
  147. return null;
  148. };
  149. PinFreezeContainer.prototype._getFixedHeight = function(oBaseContainer) {
  150. if (oBaseContainer && oBaseContainer.style.height && !oBaseContainer.getAttribute("authoredFixedHeight")) {
  151. var height = Number(oBaseContainer.style.height.split('px')[0]);
  152. return isNaN(height) ? null : height;
  153. }
  154. return null;
  155. };
  156. /**
  157. * If the author has specified a width or height in report studio (in px units),
  158. * the pinFreeze container will be "fixed in size" in that dimension.
  159. */
  160. PinFreezeContainer.prototype.applyAuthoredFixedSizes = function(oBaseContainer)
  161. {
  162. var width = this._getFixedWidth(oBaseContainer);
  163. if (width) {
  164. this.m_fixedWidth=width;
  165. this.m_clientWidth=this.m_fixedWidth; //Author-specified width for the container.
  166. this.m_scrollableClientWidth=this.m_fixedWidth;
  167. }
  168. var height = this._getFixedHeight(oBaseContainer);
  169. if (height) {
  170. this.m_fixedHeight = height;
  171. this.m_clientHeight=this.m_fixedHeight; //Author-specified height for the container.
  172. this.m_scrollableClientHeight=this.m_fixedHeight;
  173. }
  174. };
  175. /**
  176. * Apply a template used when the side headings of a crosstab are frozen.
  177. * This template includes regions for the side headings, main output and bottom scrollbar
  178. */
  179. PinFreezeContainer.prototype.loadFreezeBothTemplateHTML = function()
  180. {
  181. var sPFOutput =
  182. '<table pflid="' + this.m_lidNS + '" pfclid="pfContainer_' + this.m_lidNS + '" cellpadding="0" style="white-space:nowrap; width:0px; height:0px;" cellspacing="0">' +
  183. '<tr class="BUXNoPrint" templatePart="freezeTop"><td align="center" templatePart="freezeSide"><div pflid="' + this.m_lidNS + '" pfslid="pfHomeCell_' + this.m_lidNS + '" style="overflow-x:hidden; overflow-y:hidden; width:100%; height:100%"/></td>' +
  184. '<td valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfTopHeadings_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;"/></td><td templatePart="freezeTop"></td></tr>' +
  185. '<tr><td class="BUXNoPrint" valign=top templatePart="freezeSide"><div pflid="' + this.m_lidNS + '" pfslid="pfSideHeadings_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;"/></td>' +
  186. '<td valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfMainOutput_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;">' +
  187. '</div></td>' +
  188. '<td class="BUXNoPrint" templatePart="freezeTop">' +
  189. '<div style="padding-right:1px;overflow-x:hidden; overflow-y:scroll;" pflid="' + this.m_lidNS + '" pfslid="pfVerticalScrollBar_' + this.m_lidNS + '" tabIndex="-1" onmouseup="stopEventBubble(event);" onmousedown="stopEventBubble(event);" onscroll="' + getCognosViewerObjectRefAsString(this.m_viewerId) + '.m_pinFreezeManager.getContainer(\'' + this.m_lid + '\', ' + this.m_cachedContainerIndex + ').synchVScroll()">' +
  190. '<div style="padding-right:1px;"/>' +
  191. '</div>' +
  192. '</td>' +
  193. '</tr>' +
  194. '<tr class="BUXNoPrint" templatePart="freezeSide"><td></td><td>' +
  195. '<div style="overflow-x:scroll; overflow-y:hidden;" pflid="' + this.m_lidNS + '" pfslid="pfHorizontalScrollBar_' + this.m_lidNS + '" tabIndex="-1" onmouseup="stopEventBubble(event);" onmousedown="stopEventBubble(event);" onscroll="' + getCognosViewerObjectRefAsString(this.m_viewerId) + '.m_pinFreezeManager.getContainer(\'' + this.m_lid + '\', ' + this.m_cachedContainerIndex + ').synchScroll()">' +
  196. '<div style="height:2px;">&nbsp;</div>' +
  197. '</div>' +
  198. '</td><td></td></tr></table>';
  199. return sPFOutput;
  200. };
  201. /**
  202. * Apply a template used when the side headings of a crosstab are frozen.
  203. * This template includes regions for the side headings, main output and bottom scrollbar
  204. */
  205. PinFreezeContainer.prototype.loadFreezeSideTemplateHTML = function()
  206. {
  207. var sPFOutput = '<table pflid="' + this.m_lidNS + '" pfclid="pfContainer_' + this.m_lidNS + '" cellpadding="0" style="white-space:nowrap; width:0px; height:0px;" cellspacing="0"><tr>' +
  208. '<td class="BUXNoPrint" valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfSideHeadings_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;"/></td>' +
  209. '<td valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfMainOutput_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;">' +
  210. '</div></td>' +
  211. '</tr>' +
  212. '<tr class="BUXNoPrint"><td></td><td>' +
  213. '<div style="overflow-x:scroll; overflow-y:hidden;" pflid="' + this.m_lidNS + '" pfslid="pfHorizontalScrollBar_' + this.m_lidNS + '" tabIndex="-1" onmouseup="stopEventBubble(event);" onmousedown="stopEventBubble(event);" onscroll="' + getCognosViewerObjectRefAsString(this.m_viewerId) + '.m_pinFreezeManager.getContainer(\'' + this.m_lid + '\', ' + this.m_cachedContainerIndex + ').synchScroll()">' +
  214. '<div style="height:2px;">&nbsp;</div>' +
  215. '</div>' +
  216. '</td></tr></table>';
  217. return sPFOutput;
  218. };
  219. /**
  220. * Apply a template used when the top headings of a crosstab are frozen.
  221. * This template includes regions for the top headings, main output and side scrollbar
  222. */
  223. PinFreezeContainer.prototype.loadFreezeTopTemplateHTML = function()
  224. {
  225. var sPFOutput = '<table pflid="' + this.m_lidNS + '" pfclid="pfContainer_' + this.m_lidNS + '" cellpadding="0" style="white-space:nowrap; width:0px; height:0px;" cellspacing="0">' +
  226. '<tr class="BUXNoPrint"><td valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfTopHeadings_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;"/></td><td></td></tr>' +
  227. '<tr><td valign=top><div pflid="' + this.m_lidNS + '" pfslid="pfMainOutput_' + this.m_lidNS + '" style="width:0px; height:0px; overflow-x:hidden; overflow-y:hidden; position:relative;"></div></td>' +
  228. '<td class="BUXNoPrint">' +
  229. '<div style="padding-right:1px;overflow-x:hidden; overflow-y:scroll;" pflid="' + this.m_lidNS + '" pfslid="pfVerticalScrollBar_' + this.m_lidNS + '" tabIndex="-1" onmouseup="stopEventBubble(event);" onmousedown="stopEventBubble(event);" onscroll="' + getCognosViewerObjectRefAsString(this.m_viewerId) + '.m_pinFreezeManager.getContainer(\'' + this.m_lid + '\', ' + this.m_cachedContainerIndex + ').synchVScroll()">' +
  230. '<div style="padding-right:1px;"/>' +
  231. '</div>' +
  232. '</td>' +
  233. '</tr></table>';
  234. return sPFOutput;
  235. };
  236. /**
  237. * Apply the appropriate template to the supplied html for a container based.
  238. */
  239. PinFreezeContainer.prototype.loadTemplateHTML = function()
  240. {
  241. if (this.m_freezeSide && this.m_freezeTop) {
  242. return this.loadFreezeBothTemplateHTML();
  243. } else if (this.m_freezeSide) {
  244. return this.loadFreezeSideTemplateHTML();
  245. } else if (this.m_freezeTop) {
  246. return this.loadFreezeTopTemplateHTML();
  247. }
  248. return null;
  249. };
  250. /**
  251. * Clone the main report DOM for a crosstab into the frozen side headings area.
  252. * The table element, the home cell and, after the labels after the column heading rows are visible.
  253. */
  254. PinFreezeContainer.prototype.createSideHeadings = function(oxTab)
  255. {
  256. var pfMainOutput = this.getSection("pfMainOutput");
  257. var mainSlid = pfMainOutput.getAttribute("pfslid");
  258. var pfSideHeadings = this.getSection("pfSideHeadings");
  259. var sideSlid = pfSideHeadings.getAttribute("pfslid");
  260. var oHomeCell = this.getMainOutputHomeCell();
  261. if (!oHomeCell) {
  262. return;
  263. }
  264. var oSourceTable=oxTab;
  265. var oTargetSideHeadings=pfSideHeadings;
  266. var bRemoveIds = this.isA11yEnabled(oSourceTable);
  267. //Clone The table element
  268. var oTargetTable=this.m_pinFreezeManager.deepCloneNode(oSourceTable);
  269. oTargetSideHeadings.appendChild(oTargetTable);
  270. var oTargetHomeCell = this.getSectionHomeCell(pfSideHeadings);
  271. if (!oTargetHomeCell) {
  272. return;
  273. }
  274. var oSourceTBodyArray=oSourceTable.getElementsByTagName("tbody");
  275. var oTargetTBodyArray=oTargetTable.getElementsByTagName("tbody");
  276. if (oSourceTBodyArray.length > 0 && oTargetTBodyArray.length > 0) {
  277. var oSourceTBody=oSourceTBodyArray[0];
  278. var oTargetTBody=oTargetTBodyArray[0];
  279. //Home cell
  280. var oSourceHomeCellTR=oSourceTBody.firstChild;
  281. var oTargetHomeCellTR=oTargetTBody.firstChild;
  282. var homeCellRowSpan=oHomeCell.rowSpan;
  283. this.markAsCopy(oHomeCell, oTargetHomeCell, mainSlid, sideSlid);
  284. for (var r=0; r<homeCellRowSpan; ++r) {
  285. var oTargetTR=oTargetTBody.rows[r];
  286. this.removeCTX(oTargetTR);
  287. }
  288. //clear unnecessary attributes from datavalue cells and mark the label cells as copies.
  289. for (var r=homeCellRowSpan; r<oTargetTBody.rows.length; ++r) {
  290. var oSourceTR=oSourceTBody.rows[r];
  291. var oTargetTR=oTargetTBody.rows[r];
  292. oTargetTR.style.visibility='hidden'; //WARNING: Do not remove this without testing IE and calculations
  293. for (var c=0; c<oTargetTR.cells.length; ++c) {
  294. var oTargetTD=oTargetTR.cells[c];
  295. if (bRemoveIds) {
  296. oTargetTD=this.m_pinFreezeManager.removeIdAttribute(oTargetTD);
  297. }
  298. if (oTargetTD.getAttribute("type") == "datavalue") {
  299. oTargetTD.removeAttribute("ctx");
  300. oTargetTD.removeAttribute("uid");
  301. oTargetTD.removeAttribute("name");
  302. } else {
  303. var oSourceTD=oSourceTR.cells[c];
  304. this.markAsCopy(oSourceTD, oTargetTD, mainSlid, sideSlid);
  305. }
  306. }
  307. oTargetTR.style.visibility='visible'; //WARNING: Do not remove this without testing IE and calculations
  308. }
  309. }
  310. };
  311. /**
  312. * When freezing headings, the right/bottom border around the homecell should be maintained.
  313. */
  314. PinFreezeContainer.prototype.applyNeighbouringBorderStylesToHomeCell = function(oSourceRows, oTargetHomeCell) {
  315. if (isFF() || isIE()) {
  316. if (oSourceRows && oSourceRows.length && oSourceRows[0].cells && oSourceRows[0].cells.length > 1) {
  317. if (this.m_freezeSide) {
  318. var nextCellBorder=this.getBorderInfo(oSourceRows[0].cells[1], "right");
  319. if (nextCellBorder) {
  320. oTargetHomeCell.style.borderRightWidth=nextCellBorder.borderRightWidth;
  321. oTargetHomeCell.style.borderRightStyle=nextCellBorder.borderRightStyle;
  322. oTargetHomeCell.style.borderRightColor=nextCellBorder.borderRightColor;
  323. }
  324. }
  325. if (this.m_freezeTop) {
  326. var nextCellBorder=this.getBorderInfo(oSourceRows[0].cells[1], "bottom");
  327. if (nextCellBorder) {
  328. oTargetHomeCell.style.borderBottomWidth=nextCellBorder.borderBottomWidth;
  329. oTargetHomeCell.style.borderBottomStyle=nextCellBorder.borderBottomStyle;
  330. oTargetHomeCell.style.borderBottomColor=nextCellBorder.borderBottomColor;
  331. }
  332. }
  333. }
  334. }
  335. };
  336. /**
  337. * Clone the main report DOM for a crosstab into the frozen top headings area.
  338. * The table element, the home cell and the first few rows above the data cells are visible.
  339. */
  340. PinFreezeContainer.prototype.createTopHeadings = function(oxTab)
  341. {
  342. var pfMain = this.getSection("pfMainOutput");
  343. var mainSlid = pfMain.getAttribute("pfslid");
  344. var pfTopHeadings = this.getSection("pfTopHeadings");
  345. var topSlid = pfTopHeadings.getAttribute("pfslid");
  346. var oHomeCell = this.getMainOutputHomeCell();
  347. if (!oHomeCell) {
  348. return;
  349. }
  350. var oSourceTable=oxTab;
  351. var oTargetTopHeadings=pfTopHeadings;
  352. var bRemoveIds = this.isA11yEnabled(oSourceTable);
  353. //Clone The table element
  354. var oTargetTable=this.m_pinFreezeManager.deepCloneNode(oSourceTable);
  355. oTargetTable.setAttribute("clonednode", "true");
  356. oTargetTopHeadings.appendChild(oTargetTable);
  357. var oSourceTBodyArray=oSourceTable.getElementsByTagName("tbody");
  358. var oTargetTBodyArray=oTargetTable.getElementsByTagName("tbody");
  359. if (oSourceTBodyArray.length > 0 && oTargetTBodyArray.length > 0) {
  360. var oSourceTBody=oSourceTBodyArray[0];
  361. var oTargetTBody=oTargetTBodyArray[0];
  362. //clear attributes from datacells and mark heading cells as copies.
  363. var homeCellRowSpan=oHomeCell.rowSpan;
  364. for (var r=0; r<oTargetTBody.rows.length; ++r) {
  365. var oSourceTR=oSourceTBody.rows[r];
  366. var oTargetTR=oTargetTBody.rows[r];
  367. if (bRemoveIds) {
  368. oTargetTR=this.m_pinFreezeManager.removeIdAttribute(oTargetTR);
  369. }
  370. oTargetTR.style.visibility='hidden'; //WARNING: Do not remove this without testing IE and calculations
  371. for (var c=0; c<oTargetTR.cells.length; ++c) {
  372. var oTargetTD=oTargetTR.cells[c];
  373. if (r > homeCellRowSpan || oTargetTD.getAttribute("type") == "datavalue") {
  374. oTargetTD.removeAttribute("ctx");
  375. oTargetTD.removeAttribute("uid");
  376. oTargetTD.removeAttribute("name");
  377. } else {
  378. var oSourceTD=oSourceTR.cells[c];
  379. this.markAsCopy(oSourceTD, oTargetTD, mainSlid, topSlid);
  380. if(oSourceTD === oHomeCell) {
  381. this.initializeHomeCellTabIndex(oTargetTD);
  382. this.applyNeighbouringBorderStylesToHomeCell(oSourceTBody.rows, oTargetTD);
  383. }
  384. }
  385. }
  386. oTargetTR.style.visibility='visible'; //WARNING: Do not remove this without testing IE and calculations
  387. }
  388. }
  389. };
  390. PinFreezeContainer.prototype.createHomeCellHeading = function() {
  391. var pfMainOutput = this.getSection("pfMainOutput");
  392. var mainSlid = pfMainOutput.getAttribute("pfslid");
  393. var pfHomeCellDiv = this.getSection("pfHomeCell");
  394. var oHomeCellWrapperTd = pfHomeCellDiv.parentNode;
  395. var pfHomeSlid = pfHomeCellDiv.getAttribute("pfslid");
  396. var oSourceHomeCell = this.getMainOutputHomeCell();
  397. if (!oSourceHomeCell) {
  398. return;
  399. }
  400. // Make sure the new home cell has the same height of the original home cell
  401. oHomeCellWrapperTd.style.height = "100%";
  402. var topHeadingSectionHeight=this.getTopHeadingSectionHeight(oSourceHomeCell);
  403. // The div is the one we'll use to put the left and bottom border on. Make sure the width
  404. // and height set take the margin of the container into account
  405. pfHomeCellDiv.style.height = topHeadingSectionHeight - this.m_containerMargin.top + "px";
  406. pfHomeCellDiv.style.width = this.getSideHeadingSectionWidth(oSourceHomeCell) - this.m_containerMargin.left + "px";
  407. pfHomeCellDiv.style.marginTop = this.m_containerMargin.top + "px";
  408. pfHomeCellDiv.style.marginLeft = this.m_containerMargin.left + "px";
  409. var oSourceTR = oSourceHomeCell.parentNode;
  410. var oTargetTR = oSourceTR.cloneNode(false);
  411. var bestGuessHomeCell = this._findBestGuessHomeCell(oSourceHomeCell);
  412. var sizeCalculatingDiv = document.createElement("div");
  413. sizeCalculatingDiv.style.width = "100%";
  414. sizeCalculatingDiv.style.height = "100%";
  415. /**
  416. * The home cell could be made up of a single TD (normal case), or it can be a bunch
  417. * of crosstab spacers. Go through all the TDs until the offsetLeft gets beyond the 'home cell' boundary
  418. */
  419. while (oSourceHomeCell.offsetLeft <= bestGuessHomeCell.offsetLeft) {
  420. oTargetHomeCell = this.m_pinFreezeManager.deepCloneNode(oSourceHomeCell);
  421. // For Firefox and IE we can't simply take the clientWidth, since that includes the padding. Use a hidden
  422. // div inside the TD and get it's width. This will tell use the inner width which we need to copy over to our copied TD's
  423. if (isFF() || isIE()) {
  424. oSourceHomeCell.appendChild(sizeCalculatingDiv);
  425. oTargetHomeCell.style.width = sizeCalculatingDiv.clientWidth + "px";
  426. oSourceHomeCell.removeChild(sizeCalculatingDiv);
  427. }
  428. else {
  429. oTargetHomeCell.style.width = oSourceHomeCell.clientWidth + 1 + "px";
  430. }
  431. // Make sure there's no bottom border on the TD. We'll have a bottom border on the wrapper TD
  432. oTargetHomeCell.style.borderBottomWidth="0px";
  433. oTargetTR.appendChild(oTargetHomeCell);
  434. this.markAsCopy(oSourceHomeCell, oTargetHomeCell, mainSlid, pfHomeSlid);
  435. if (oSourceHomeCell.nextSibling) {
  436. oSourceHomeCell = oSourceHomeCell.nextSibling;
  437. }
  438. else {
  439. break;
  440. }
  441. }
  442. // Don't need a right border on the last TD, we'll have that on the wrapper TD
  443. if (oTargetHomeCell) {
  444. oTargetHomeCell.style.borderRightWidth="0px";
  445. }
  446. var oSourceTBody = oSourceTR.parentNode;
  447. var oTargetTBody = oSourceTBody.cloneNode(false);
  448. oTargetTBody.appendChild(oTargetTR);
  449. var oSourceTable = oSourceTBody.parentNode;
  450. var oTargetTable = oSourceTable.cloneNode(false);
  451. oTargetTable.appendChild(oTargetTBody);
  452. oTargetTable.style.width = "100%";
  453. oTargetTable.style.height = "100%";
  454. // Since the margins have been set on the containing div, clear them on the table
  455. oTargetTable.style.marginLeft = "";
  456. oTargetTable.style.marginTop = "";
  457. pfHomeCellDiv.appendChild(oTargetTable);
  458. this.initializeHomeCellTabIndex(oTargetHomeCell);
  459. this.applyNeighbouringBorderStylesToHomeCell(pfMainOutput.firstChild.rows, pfHomeCellDiv);
  460. };
  461. /**
  462. * Given an element and its copy, add pointers between them and mark their slid.
  463. */
  464. PinFreezeContainer.prototype.markAsCopy = function(main, copy, mainSlid, copySlid) {
  465. if(!main.pfCopy) {
  466. main.setAttribute("pfslid", mainSlid);
  467. main.pfCopy = [];
  468. }
  469. main.pfCopy.push(copy);
  470. copy.pfMain = main;
  471. copy.setAttribute("pfslid", copySlid);
  472. };
  473. /**
  474. * Given an element, the visible copied version of it, if one exists
  475. */
  476. PinFreezeContainer.prototype.getCopy = function(element) {
  477. if(element.pfCopy) {
  478. var copies = {};
  479. for(var i in element.pfCopy) {
  480. var copy = element.pfCopy[i];
  481. if (copy.getAttribute) {
  482. var copySlid = copy.getAttribute("pfslid");
  483. if(copySlid) {
  484. var sectionName = PinFreezeContainer.getSectionNameFromSlid(copySlid);
  485. var copySection = this.getSection(sectionName);
  486. if(copySection && PinFreezeContainer.isSectionVisible(copySection)) {
  487. copies[sectionName] = copy;
  488. }
  489. }
  490. }
  491. }
  492. //Favour the home cell
  493. if(copies["pfHomeCell"]) {
  494. return copies["pfHomeCell"];
  495. }
  496. //If no home cell, return any copy arbitrarily
  497. for(i in copies) {
  498. return copies[i];
  499. }
  500. }
  501. return null;
  502. };
  503. /**
  504. * Given an element, return the original it is a copy of, if it is a copy
  505. */
  506. PinFreezeContainer.prototype.getMain = function(element) {
  507. if(element.pfMain) {
  508. return element.pfMain;
  509. }
  510. return null;
  511. };
  512. /**
  513. * Return whether the given section is visible to the user
  514. */
  515. PinFreezeContainer.isSectionVisible = function(section) {
  516. var node = section;
  517. if (!node) {
  518. return false; //A section that doesn't exist isn't visible.
  519. }
  520. //Search for a parent with display: none. Limit the search
  521. //to within the container.
  522. //This is necessary for times when the row a section is in
  523. //is hidden, rather than the section itself.
  524. while(node.parentNode && !node.getAttribute("pfclid")) {
  525. if(node.style && node.style.display === "none") {
  526. return false;
  527. }
  528. node = node.parentNode;
  529. }
  530. return (!node.style || node.style.display !== "none");
  531. };
  532. /**
  533. * Returns an object representing whether side and top headings
  534. * are actually frozen right now.
  535. */
  536. PinFreezeContainer.prototype.getSectionStructure = function() {
  537. var result = {
  538. isSideFrozen: false,
  539. isTopFrozen: false
  540. };
  541. if(this.m_freezeSide) {
  542. var side = this.getSection("pfSideHeadings");
  543. if(side) {
  544. result.isSideFrozen = PinFreezeContainer.isSectionVisible(side);
  545. }
  546. }
  547. if(this.m_freezeTop) {
  548. var top = this.getSection("pfTopHeadings");
  549. if(top) {
  550. result.isTopFrozen = PinFreezeContainer.isSectionVisible(top);
  551. }
  552. }
  553. return result;
  554. };
  555. /**
  556. * Compares the before and after section structures (returned from getSectionStructure)
  557. * and if there has been a change, informs the Viewer Widget.
  558. */
  559. PinFreezeContainer.prototype.checkSectionStructureChange = function(before, after) {
  560. if(before.isSideFrozen !== after.isSideFrozen || before.isTopFrozen !== after.isTopFrozen) {
  561. this.m_pinFreezeManager.sectionStructureChange();
  562. }
  563. };
  564. /**
  565. * Given an object representing the full report, find the specific container
  566. * and complete sizing and layout to cause it to freeze.
  567. */
  568. PinFreezeContainer.prototype.freezeContainerInReport = function(oReport) {
  569. this.cacheContainerAndSections(this.getContainerByLID(oReport));
  570. this.m_homeCellNodes = {};
  571. this.updateContainer();
  572. };
  573. /**
  574. * return true if frozen sections are actually required...
  575. * They are required when the width/height of the container is not large enough to present all the data
  576. */
  577. PinFreezeContainer.prototype.frozenSectionsRequired = function() {
  578. return (this.frozenSideHeadingsRequired() || this.frozenTopHeadingsRequired());
  579. };
  580. PinFreezeContainer.prototype.frozenSideHeadingsRequired = function() {
  581. var pfMainOutput=this.getSection("pfMainOutput");
  582. if (pfMainOutput) {
  583. if (this.m_freezeSide) {
  584. var mainScrollWidth=pfMainOutput.scrollWidth;
  585. return ((this.m_clientWidth < mainScrollWidth) || mainScrollWidth==0);
  586. }
  587. }
  588. return false;
  589. };
  590. PinFreezeContainer.prototype.frozenTopHeadingsRequired = function() {
  591. var pfMainOutput=this.getSection("pfMainOutput");
  592. if (pfMainOutput) {
  593. if (this.m_freezeTop) {
  594. var mainScrollHeight=pfMainOutput.scrollHeight;
  595. return ((this.m_clientHeight < mainScrollHeight) || mainScrollHeight==0);
  596. }
  597. }
  598. return false;
  599. };
  600. /**
  601. * set the display state (true=visible, false=hidden) to the specified template part
  602. */
  603. PinFreezeContainer.prototype.showTemplatePart = function(templatePartToChange, valueToSet) {
  604. var oTemplateRows=this.getContainer().rows;
  605. for (var r=0; r<oTemplateRows.length; ++r) {
  606. if (oTemplateRows[r].getAttribute("templatePart")===templatePartToChange) {
  607. oTemplateRows[r].style.display=((valueToSet) ? "" : "none");
  608. } else {
  609. var oTemplateCells=oTemplateRows[r].cells;
  610. for (var c=0; c<oTemplateCells.length; ++c) {
  611. if (oTemplateCells[c].getAttribute("templatePart")===templatePartToChange) {
  612. oTemplateCells[c].style.display=((valueToSet) ? "" : "none");
  613. }
  614. }
  615. }
  616. }
  617. };
  618. /**
  619. * For freeze both, if the report isn't wide enough to warrant side headings,
  620. * adjust sizes and scroll positions to reduce the output to a freezeTop.
  621. */
  622. PinFreezeContainer.prototype.showFreezeTopOnly = function(pfMainOutput) {
  623. if (!(this.m_freezeTop && this.m_freezeSide)) {
  624. //This function applies to freeze both only (the freezeBoth template).
  625. return;
  626. }
  627. var newClientWidth=(pfMainOutput.scrollWidth==0) ? pfMainOutput.clientWidth : pfMainOutput.scrollWidth;
  628. this.updateMainOutputWidth(newClientWidth);
  629. this.setScrollX(pfMainOutput, 0);
  630. if (this.getSection("pfTopHeadings")) {
  631. this.getSection("pfTopHeadings").style.width=newClientWidth + 'px';
  632. this.setScrollX(this.getSection("pfTopHeadings"), 0);
  633. }
  634. //Hide the part of the template for the side headings.
  635. this.showTemplatePart("freezeSide", false);
  636. };
  637. /**
  638. * For freeze both, if the report isn't tall enough to warrant top headings,
  639. * adjust sizes and scroll positions to reduce the output to a freezeSide.
  640. */
  641. PinFreezeContainer.prototype.showFreezeSideOnly = function(pfMainOutput) {
  642. if (!(this.m_freezeTop && this.m_freezeSide)) {
  643. //This function applies to freeze both only (the freezeBoth template).
  644. return;
  645. }
  646. var newClientHeight=(pfMainOutput.scrollHeight==0) ? pfMainOutput.clientHeight : pfMainOutput.scrollHeight;
  647. this.updateMainOutputHeight(newClientHeight);
  648. this.setScrollY(pfMainOutput, 0);
  649. if (this.getSection("pfSideHeadings")) {
  650. this.getSection("pfSideHeadings").style.height=newClientHeight + 'px';
  651. this.setScrollY(this.getSection("pfSideHeadings"),0);
  652. }
  653. //Hide the part of the template for the top headings.
  654. this.showTemplatePart("freezeTop", false);
  655. };
  656. /**
  657. * Show anything hidden by either showFreezeSideOnly or showFreezeTopOnly.
  658. */
  659. PinFreezeContainer.prototype.showAll = function() {
  660. if (!(this.m_freezeTop && this.m_freezeSide)) {
  661. //This function applies to freeze both only (the freezeBoth template)
  662. return;
  663. }
  664. this.showTemplatePart("freezeTop", true);
  665. this.showTemplatePart("freezeSide", true);
  666. };
  667. /**
  668. * when its not warranted to show frozen sections, show only the main output
  669. * at the full client width.
  670. */
  671. PinFreezeContainer.prototype.showMainOutputOnly = function(pfMainOutput) {
  672. //Hide sections when the report is not wide enough to need a scrollbar or frozen sections.
  673. this.updateMainOutputWidth((pfMainOutput.scrollWidth==0) ? pfMainOutput.clientWidth : pfMainOutput.scrollWidth);
  674. this.updateMainOutputHeight((pfMainOutput.scrollHeight==0) ? pfMainOutput.clientHeight : pfMainOutput.scrollHeight);
  675. this.setInitialScrollPosition(pfMainOutput, 0, 0);
  676. if (this.m_freezeSide && this.m_freezeTop) {
  677. this.getSection("pfHomeCell").style.display='none';
  678. }
  679. if (this.m_freezeSide) {
  680. this.getSection("pfSideHeadings").style.display='none';
  681. this.getSection("pfHorizontalScrollBar").style.display='none';
  682. }
  683. if (this.m_freezeTop) {
  684. this.getSection("pfTopHeadings").style.display='none';
  685. this.getSection("pfVerticalScrollBar").style.display='none';
  686. }
  687. };
  688. /**
  689. * return the inherited white-space css style of this element
  690. * to determine if cells are set to nowrap.
  691. */
  692. PinFreezeContainer.prototype.getWrap = function(el){
  693. if (el.currentStyle) { //IE
  694. return el.currentStyle.whiteSpace;
  695. } else if (window.getComputedStyle) { //Firefox
  696. return window.getComputedStyle(el, null).getPropertyValue("white-space");
  697. } else { //try and get inline style
  698. return el.style.whiteSpace;
  699. }
  700. };
  701. PinFreezeContainer.prototype.getStyleDisplay = function (el) {
  702. if (el.currentStyle) { //IE
  703. return el.currentStyle.display;
  704. } else if (window.getComputedStyle) { //Firefox
  705. return window.getComputedStyle(el, null).getPropertyValue("display");
  706. } else { //try and get inline style
  707. return el.style.display;
  708. }
  709. };
  710. /**
  711. * return true if this headings section has been created.
  712. */
  713. PinFreezeContainer.prototype.headingsCreated = function(headingsDiv) {
  714. return headingsDiv.firstChild ? true : false;
  715. };
  716. /**
  717. * Given the containing table object (the "pfContainer"), complete sizing, scrolling and layout tweeks to cause
  718. * it to produce the freeze effect.
  719. */
  720. PinFreezeContainer.prototype.updateContainer = function() {
  721. var pfMainOutput=this.getSection("pfMainOutput");
  722. var realHomeCell= this.getMainOutputHomeCell();
  723. if (realHomeCell) {
  724. //In some situations such as publish...only the client size (ie: size including headings) is known.
  725. //In this case, the scrollableSize may be set the same as the entire size or not set at all.
  726. //For this case, initialize a sensible scrollable size so that the result is not taller/wider than expected.
  727. if (this.m_scrollableClientHeight===this.m_clientHeight || !this.m_scrollableClientHeight) {
  728. this.m_scrollableClientHeight-=realHomeCell.offsetHeight;
  729. //to improve rendering in case when m_scrollableClientHeight less then calculated
  730. var minScrollableClientHeight = this.calculateMinCrossTabScrollableClientHeight();
  731. if (minScrollableClientHeight > this.m_scrollableClientHeight){
  732. this.m_scrollableClientHeight = minScrollableClientHeight;
  733. }
  734. }
  735. if (this.m_scrollableClientWidth===this.m_clientWidth || !this.m_scrollableClientWidth) {
  736. this.m_scrollableClientWidth-=this.getHomeCellOffsetWidth(realHomeCell);
  737. }
  738. }
  739. if (pfMainOutput && realHomeCell) {
  740. //Set the size of the scroll region for the actual cells.
  741. this.showAll();
  742. if (this.frozenSectionsRequired()) {
  743. this.updateMainOutputSize();
  744. //Initialize the real home cell so that its
  745. //tabIndex is removed when it is not visible
  746. this.initializeHomeCellTabIndex(realHomeCell);
  747. if (this.m_freezeSide) {
  748. var pfSideHeadings=this.getSection("pfSideHeadings");
  749. if (!this.headingsCreated(pfSideHeadings)) {
  750. this.createSideHeadings(this.m_cachedBaseContainer);
  751. if (this.m_freezeTop) {
  752. //When freezing side headings and NOT top headings, the main output
  753. //needs to scroll left and right but the side headings are totally fixed.
  754. //When freezing both, the side headings need to scroll (up and down)
  755. this.initializeTouchScrolling(pfSideHeadings);
  756. }
  757. }
  758. var pfHorizontalScrollBar = this.getSection("pfHorizontalScrollBar");
  759. pfHorizontalScrollBar.scrollLeft = "0px";
  760. }
  761. if (this.m_freezeTop) {
  762. var pfTopHeadings=this.getSection("pfTopHeadings");
  763. if (!this.headingsCreated(pfTopHeadings)) {
  764. this.createTopHeadings(this.m_cachedBaseContainer);
  765. //When freezing top headings and NOT side headings, the main output
  766. //needs to scroll up and down but the top headings are totally fixed.
  767. //When freezing both, the top headings need to scroll (left and right)
  768. if (this.m_freezeSide) {
  769. this.initializeTouchScrolling(pfTopHeadings);
  770. }
  771. }
  772. var pfVerticalScrollBar = this.getSection("pfVerticalScrollBar");
  773. pfVerticalScrollBar.scrollTop = "0px";
  774. }
  775. if (this.m_freezeSide && this.m_freezeTop) {
  776. //When both side and top headings are frozen, populate the home cell.
  777. var pfHomeCell=this.getSection("pfHomeCell");
  778. if (!this.headingsCreated(pfHomeCell)) {
  779. this.createHomeCellHeading();
  780. }
  781. pfHomeCell.style.display='';
  782. }
  783. var sideHeadingSectionWidth=this.updateSideHeadingSize(realHomeCell);
  784. var topHeadingSectionHeight=this.updateTopHeadingSize(realHomeCell);
  785. if (!this.frozenSectionsRequired()) {
  786. this.showMainOutputOnly(pfMainOutput);
  787. }
  788. this.setInitialScrollPosition(pfMainOutput, sideHeadingSectionWidth, topHeadingSectionHeight);
  789. if (this.m_freezeTop && this.m_freezeSide) {
  790. this.setInitialScrollPosition(this.getSection("pfSideHeadings"), 0, topHeadingSectionHeight);
  791. this.setInitialScrollPosition(this.getSection("pfTopHeadings"), sideHeadingSectionWidth, 0);
  792. }
  793. this.initializeTouchScrolling(pfMainOutput);
  794. } else {
  795. this.showMainOutputOnly(pfMainOutput);
  796. this.removeTouchScrolling();
  797. }
  798. this.updateTabIndexValues();
  799. }
  800. };
  801. PinFreezeContainer.prototype.calculateMinCrossTabScrollableClientHeight = function() {
  802. //calculate minCrossTabScrollableClientHeight in case if pinFreezeInfo wasn't calculated properly in CI published dashboard
  803. //pfMainOutput is "pfMainOutput"
  804. var retVal = 0;
  805. if (this.m_cachedPFContainer) {
  806. var oSectionTable = this.getElementByLID(this.m_cachedPFContainer, "table", this.m_lid + this.m_viewerId);
  807. if (oSectionTable) {
  808. var dataCells = 0;
  809. for (var r=0; r<oSectionTable.rows.length; r++) {
  810. var row = oSectionTable.rows[r];
  811. for (var c=0; c<row.cells.length; c++) {
  812. var cell = row.cells[c];
  813. if (cell.getAttribute("type") == "datavalue") {
  814. dataCells ++;
  815. if ( cell.childNodes.length === 1 && cell.childNodes[0].getAttribute && cell.childNodes[0].getAttribute("class") === "textItem"){
  816. retVal = retVal + cell.offsetHeight;
  817. } else {
  818. //may have embeded chart, then min height will be based on fix value
  819. dataCells ++;
  820. var pfVerticalScrollBar = this.getSection("pfVerticalScrollBar");
  821. if (pfVerticalScrollBar) {
  822. //using scroll bar's width for fix value rather then hardcoded value
  823. retVal = pfVerticalScrollBar.offsetWidth*2;
  824. }
  825. }
  826. break;
  827. }
  828. }
  829. if (dataCells >= 2){
  830. break;
  831. }
  832. }
  833. }
  834. }
  835. return retVal;
  836. };
  837. /**
  838. * update sizes of the side headings and horizontal scrollbar.
  839. * Return the width of the side headings.
  840. */
  841. PinFreezeContainer.prototype.updateSideHeadingSize = function(realHomeCell) {
  842. var sideHeadingSectionWidth=0;
  843. if (this.m_freezeSide) {
  844. var pfMainOutput=this.getSection("pfMainOutput");
  845. if (!pfMainOutput) {
  846. return 0;
  847. }
  848. if (!this.frozenSideHeadingsRequired()) {
  849. this.showFreezeTopOnly(pfMainOutput);
  850. return 0;
  851. }
  852. var pfSideHeadings=this.getSection("pfSideHeadings");
  853. sideHeadingSectionWidth=this.getSideHeadingSectionWidth(realHomeCell);
  854. var pfHorizontalScrollBar = this.getSection("pfHorizontalScrollBar");
  855. var sideHomeCell=this.getSectionHomeCell(pfSideHeadings);
  856. if (pfSideHeadings.style.display=='none') {
  857. pfSideHeadings.style.display='';
  858. pfHorizontalScrollBar.style.display='';
  859. }
  860. pfSideHeadings.style.width=sideHeadingSectionWidth + 'px';
  861. pfSideHeadings.style.height=pfMainOutput.clientHeight + 'px';
  862. }
  863. return sideHeadingSectionWidth;
  864. };
  865. /**
  866. * update sizes of the top headings and vertical scrollbar.
  867. * Return the height of the top headings.
  868. */
  869. PinFreezeContainer.prototype.updateTopHeadingSize = function(realHomeCell) {
  870. var topHeadingSectionHeight=0;
  871. if (this.m_freezeTop) {
  872. var pfMainOutput=this.getSection("pfMainOutput");
  873. if (!pfMainOutput) {
  874. return 0;
  875. }
  876. if (!this.frozenTopHeadingsRequired()) {
  877. this.showFreezeSideOnly(pfMainOutput);
  878. return 0;
  879. }
  880. var pfTopHeadings=this.getSection("pfTopHeadings");
  881. topHeadingSectionHeight=this.getTopHeadingSectionHeight(realHomeCell);
  882. var pfVerticalScrollBar = this.getSection("pfVerticalScrollBar");
  883. var topHomeCell=this.getSectionHomeCell(pfTopHeadings);
  884. if (pfTopHeadings.style.display=='none') {
  885. pfTopHeadings.style.display='';
  886. pfVerticalScrollBar.style.display='';
  887. }
  888. pfTopHeadings.style.height=topHeadingSectionHeight + 'px';
  889. pfTopHeadings.style.width=pfMainOutput.clientWidth + 'px';
  890. }
  891. return topHeadingSectionHeight;
  892. };
  893. //Abstract scrollX...since scrollLeft is scrollRight in Bidi (can also be overridden for testing).
  894. PinFreezeContainer.prototype.setScrollX = function(section, scrollX)
  895. {
  896. if(getElementDirection(section) === "rtl") {
  897. setScrollRight(section, scrollX);
  898. } else {
  899. setScrollLeft(section, scrollX);
  900. }
  901. };
  902. PinFreezeContainer.prototype.setScrollY = function(section, scrollY)
  903. {
  904. section.scrollTop=scrollY;
  905. };
  906. PinFreezeContainer.prototype.setInitialScrollPosition = function(section, scrollX, scrollY)
  907. {
  908. if(getElementDirection(section) === "rtl") {
  909. setScrollRight(section, scrollX);
  910. } else {
  911. setScrollLeft(section, scrollX);
  912. }
  913. section.scrollTop=scrollY;
  914. };
  915. /**
  916. * Get the visible width of the scroll region
  917. */
  918. PinFreezeContainer.prototype.getScrollableClientWidth = function() {
  919. return this.m_scrollableClientWidth;
  920. };
  921. PinFreezeContainer.prototype.setScrollableClientWidth = function(width) {
  922. this.m_scrollableClientWidth = width;
  923. };
  924. PinFreezeContainer.prototype.getContainerWidth = function() {
  925. return this.m_addedFixedWidth ? this.m_addedFixedWidth : this.m_clientWidth;
  926. };
  927. PinFreezeContainer.prototype.getClientWidth = function() {
  928. return this.m_clientWidth;
  929. };
  930. /**
  931. * Get the visible height of the scroll region
  932. */
  933. PinFreezeContainer.prototype.getScrollableClientHeight = function() {
  934. return this.m_scrollableClientHeight;
  935. };
  936. PinFreezeContainer.prototype.setScrollableClientHeight = function(height) {
  937. this.m_scrollableClientHeight = height;
  938. };
  939. PinFreezeContainer.prototype.getClientHeight = function() {
  940. return this.m_clientHeight;
  941. };
  942. /**
  943. * This function returns the clientHeight of a dom element.
  944. * The function can be used (and overriden) for testing in place of accessing the read-only member.
  945. */
  946. PinFreezeContainer.prototype.clientHeight = function(domEl) {
  947. return domEl.clientHeight;
  948. };
  949. /**
  950. * When freezing top headings, determine an appropriate height for the container
  951. * that considers other elements on the page (like page navigation links, footers, headers etc.)
  952. */
  953. PinFreezeContainer.prototype.findBestContainerHeight = function(visibleHeightOfWidget) {
  954. if (this.m_freezeTop && this.m_cachedReportDiv) {
  955. var oRVContent = this.m_cachedReportDiv.parentNode;
  956. if (oRVContent) {
  957. var restOfPageHeight = this._findRestOfPageHeight(this.getContainer());
  958. return visibleHeightOfWidget - restOfPageHeight - (this.c_pageMargin / 2) - this.m_containerMargin.top; //Page margin is approximately 2 scrollbars
  959. }
  960. }
  961. return visibleHeightOfWidget - this.c_pageMargin;
  962. };
  963. PinFreezeContainer.prototype.findBestContainerWidth = function(visibleWidth) {
  964. var node = this.getContainer();
  965. while (node && node.nodeName.toLowerCase() != 'td' && node.getAttribute("id") != ("mainViewerTable" + this.m_viewerId)) {
  966. node = node.parentNode;
  967. }
  968. if (!node) {
  969. return -1;
  970. }
  971. if (node.nodeName.toLowerCase() == 'td') {
  972. var restOfWidth = 0;
  973. var childNodes = node.parentNode.childNodes;
  974. for (var i=0; i < childNodes.length; i++) {
  975. if (childNodes[i] !== node) {
  976. restOfWidth += childNodes[i].clientWidth;
  977. }
  978. }
  979. return visibleWidth - restOfWidth - (this.c_pageMargin / 2); //Page margin is approximately 2 scrollbars
  980. }
  981. return visibleWidth;
  982. };
  983. /**
  984. * Starting from the container walk up the DOM until we reach the main Viewer table. Anytime
  985. * there are multiple siblings, get the height of the non-container sibligns (nav links, toolbars, titles, ...) so that we know how much room
  986. * the container actually has left.
  987. */
  988. PinFreezeContainer.prototype._findRestOfPageHeight = function(node) {
  989. var restOfPageHeight = 0;
  990. var parentNode = node.parentNode;
  991. if (!parentNode) {
  992. return restOfPageHeight;
  993. }
  994. if (parentNode.childNodes.length > 1) {
  995. for (var i=0; i < parentNode.childNodes.length; i++) {
  996. var childNode = parentNode.childNodes[i];
  997. if (childNode.nodeType == 1) { // Only element types allowed
  998. var display = this.getStyleDisplay (childNode);
  999. if (childNode != node && !isNaN(childNode.clientHeight) && display != "none" && display != "table-cell") {
  1000. restOfPageHeight += this.clientHeight(childNode);
  1001. }
  1002. }
  1003. }
  1004. }
  1005. // Keep going until we reach the main Viewer table
  1006. if (node.getAttribute("id") != ("mainViewerTable" + this.m_viewerId)) {
  1007. restOfPageHeight += this._findRestOfPageHeight(parentNode);
  1008. }
  1009. return restOfPageHeight;
  1010. };
  1011. /**
  1012. * resize the rendered container to width/height
  1013. */
  1014. PinFreezeContainer.prototype.resize = function(width, height, parentContainer, childContainerInfo) {
  1015. if (this.m_fixedWidth && this.m_fixedHeight) {
  1016. return;
  1017. }
  1018. width=(this.m_fixedWidth) ? this.m_fixedWidth : width;
  1019. height=(this.m_fixedHeight) ? this.m_fixedHeight : height;
  1020. var initialSectionStructure = this.getSectionStructure();
  1021. if (this.m_sectionCache && this.m_cachedPFContainer) {
  1022. var bestContainerHeight = 0;
  1023. if (height !== 0) {
  1024. bestContainerHeight = this.findBestContainerHeight(height);
  1025. // If we're a nested container, don't let our height get smaller then 300
  1026. if (parentContainer && bestContainerHeight < 300) {
  1027. bestContainerHeight = 300;
  1028. }
  1029. else if (bestContainerHeight < 100) {
  1030. // Anything under 100px is useless, so make sure we're not freezing to a size smaller then that
  1031. bestContainerHeight = 100;
  1032. }
  1033. }
  1034. this.m_clientHeight = bestContainerHeight > 0 ? bestContainerHeight : this.m_clientHeight;
  1035. var bestContainerWidth = 0;
  1036. if (width !== 0) {
  1037. bestContainerWidth = this.findBestContainerWidth(width);
  1038. }
  1039. // We'd sometimes get the browser scrollbars showing up in IE. Back off the width a little (-5) so that we never get them.
  1040. this.m_clientWidth = (bestContainerWidth > 0) ? bestContainerWidth - 5 - (this.c_pageMargin / 2) : this.m_clientWidth;
  1041. var pfMainOutput=this.getSection("pfMainOutput");
  1042. var realHomeCell= this.getSectionHomeCell(pfMainOutput);
  1043. if (realHomeCell) {
  1044. this.m_scrollableClientWidth=this.m_clientWidth-this.getSideHeadingSectionWidth(realHomeCell);
  1045. this.m_scrollableClientHeight=this.m_clientHeight-realHomeCell.offsetHeight;
  1046. }
  1047. if (childContainerInfo) {
  1048. // If we have a frozen child, then find the matching div's parent table and set the height. This is needed
  1049. // to make sure the parents containers columns line up correctly
  1050. var childContainerNodes = getElementsByAttribute(this.m_cachedPFContainer, "div", "pflid", childContainerInfo.lid);
  1051. if (childContainerNodes) {
  1052. var node = childContainerNodes[0];
  1053. while (node.nodeName.toLowerCase() != 'table') {
  1054. node = node.parentNode;
  1055. }
  1056. node.style.width = childContainerInfo.width + "px";
  1057. }
  1058. }
  1059. this.updateContainer();
  1060. } else {
  1061. this.m_clientWidth=width - this.c_pageMargin;
  1062. this.m_clientHeight=height - this.c_pageMargin;
  1063. }
  1064. var finalSectionStructure = this.getSectionStructure();
  1065. this.checkSectionStructureChange(initialSectionStructure, finalSectionStructure);
  1066. };
  1067. /**
  1068. * Size the main output section (which is used as the master).
  1069. * Size the scroll bars which are associated with it as well.
  1070. */
  1071. PinFreezeContainer.prototype.updateMainOutputSize = function()
  1072. {
  1073. if (this.m_freezeSide && this.m_freezeTop) {
  1074. if (this.frozenSideHeadingsRequired()) {
  1075. this.updateMainOutputWidth(this.getScrollableClientWidth());
  1076. }
  1077. if (this.frozenTopHeadingsRequired()) {
  1078. this.updateMainOutputHeight(this.getScrollableClientHeight());
  1079. }
  1080. } else if (this.m_freezeSide) {
  1081. this.updateMainOutputWidth(this.getScrollableClientWidth());
  1082. } else if (this.m_freezeTop) {
  1083. this.updateMainOutputHeight(this.getScrollableClientHeight());
  1084. }
  1085. };
  1086. /**
  1087. * When side headings are frozen, the scrolling width of the main output and horizontal scrollbar must be set on resize
  1088. */
  1089. PinFreezeContainer.prototype.updateMainOutputWidth = function(clientWidth)
  1090. {
  1091. var pfMainOutput= this.getSection("pfMainOutput");
  1092. if (!pfMainOutput) {
  1093. return;
  1094. }
  1095. //Set the side scrolling limit of the data
  1096. if (this.m_freezeSide==true) {
  1097. pfMainOutput.style.width=(clientWidth + 'px');
  1098. if (this.m_freezeTop==false || !this.frozenTopHeadingsRequired()) {
  1099. //If there's no vertical scrollbar, the vertical size should match the entire height of the output.
  1100. pfMainOutput.style.height=pfMainOutput.firstChild.clientHeight + 'px';
  1101. }
  1102. var pfHorizontalScrollBar = this.getSection("pfHorizontalScrollBar");
  1103. if (pfHorizontalScrollBar) {
  1104. pfHorizontalScrollBar.style.width=(clientWidth + 'px');
  1105. var pfHorizontalScrollBarChild = pfHorizontalScrollBar.firstChild;
  1106. if (pfHorizontalScrollBarChild) {
  1107. var realHomeCell = this.getSectionHomeCell(pfMainOutput);
  1108. var width = pfMainOutput.scrollWidth - this.getHomeCellOffsetWidth(realHomeCell);
  1109. pfHorizontalScrollBarChild.style.width = width + 'px';
  1110. }
  1111. }
  1112. }
  1113. };
  1114. /**
  1115. * When top headings are frozen, the scrolling height of the main output and scrollbar must be set on resize
  1116. */
  1117. PinFreezeContainer.prototype.updateMainOutputHeight = function(clientHeight)
  1118. {
  1119. var pfMainOutput= this.getSection("pfMainOutput");
  1120. if (!pfMainOutput) {
  1121. return;
  1122. }
  1123. //Set the top scrolling limit of the data
  1124. pfMainOutput.style.height=(clientHeight + 'px');
  1125. if (!this.m_freezeSide || !this.frozenSideHeadingsRequired()) {
  1126. // If there's no horizontal scrollbar, the horizontal size should match the entire width of the output.
  1127. // Give a little extra padding (+2) so that we don't clip the border of the list/crosstab
  1128. pfMainOutput.style.width=pfMainOutput.firstChild.clientWidth + 2 + 'px';
  1129. }
  1130. var pfVerticalScrollBar = this.getSection("pfVerticalScrollBar");
  1131. if (pfVerticalScrollBar) {
  1132. pfVerticalScrollBar.style.height=(clientHeight + 'px');
  1133. var pfVerticalScrollBarChild = pfVerticalScrollBar.firstChild;
  1134. if (pfVerticalScrollBarChild) {
  1135. var realHomeCell = this.getSectionHomeCell(pfMainOutput);
  1136. var height = pfMainOutput.scrollHeight - realHomeCell.offsetHeight;
  1137. pfVerticalScrollBarChild.style.height = height + 'px';
  1138. }
  1139. }
  1140. };
  1141. /**
  1142. * Given a node and a layout ID, find the first matching element.
  1143. * Note: lid's should be unique in the base report.
  1144. */
  1145. PinFreezeContainer.prototype.getElementByLID = function(oParent, tag, lid) {
  1146. var oLIDArray = getElementsByAttribute(oParent, tag, "lid", lid);
  1147. if (oLIDArray.length > 0) {
  1148. return oLIDArray[0];
  1149. }
  1150. return null;
  1151. };
  1152. /**
  1153. * use the pfclid to find the container
  1154. */
  1155. PinFreezeContainer.prototype.getContainerByLID = function(oParent) {
  1156. var opfLIDArray = getElementsByAttribute(oParent, "table", "pfclid", "pfContainer_" + this.m_lidNS);
  1157. if (opfLIDArray.length > 0) {
  1158. return opfLIDArray[0];
  1159. }
  1160. return null;
  1161. };
  1162. /**
  1163. * use the pfslid to find the container section
  1164. */
  1165. PinFreezeContainer.prototype.getSectionByLID = function(pfContainer, sectionName) {
  1166. var opfLIDArray = getElementsByAttribute(pfContainer, "div", "pfslid", sectionName + "_" + this.m_lidNS);
  1167. if (opfLIDArray.length > 0) {
  1168. return opfLIDArray[0];
  1169. }
  1170. return null;
  1171. };
  1172. /**
  1173. * Logic for extracting the section name from a slide
  1174. */
  1175. PinFreezeContainer.getSectionNameFromSlid = function(slid) {
  1176. return slid ? slid.split("_")[0] : null;
  1177. };
  1178. /**
  1179. * Logic for extracting the lid from a slid
  1180. */
  1181. PinFreezeContainer.getLidFromSlid = function(slid) {
  1182. return slid.split("_")[1];
  1183. };
  1184. PinFreezeContainer.nodeToSlid = function(element) {
  1185. while(element.parentNode && !element.getAttribute("pfslid")) {
  1186. element = element.parentNode;
  1187. }
  1188. if(element.getAttribute) {
  1189. return element.getAttribute("pfslid");
  1190. }
  1191. return null;
  1192. };
  1193. /**
  1194. * add section DOM elements into the sectionCache associative array key is "pfMainOutput", "pfSideHeadings" etc... (pfslid up to the _)
  1195. * When initialized, getSection() will cache the section objects for reuse.
  1196. */
  1197. PinFreezeContainer.prototype.cacheContainerAndSections = function(pfContainer)
  1198. {
  1199. if (!pfContainer) {
  1200. return pfContainer;
  1201. }
  1202. this.m_cachedPFContainer=pfContainer;
  1203. var opfLIDArray = getElementsByAttribute(this.m_cachedPFContainer, "div", "pflid", this.m_lidNS);
  1204. this.m_sectionCache = {};
  1205. for (var i=0; i<opfLIDArray.length; ++i) {
  1206. var key=opfLIDArray[i].getAttribute("pfslid");
  1207. key=key.split("_", 1);
  1208. this.m_sectionCache[key]=opfLIDArray[i];
  1209. }
  1210. return pfContainer;
  1211. };
  1212. /**
  1213. * return the cached DOM element for this container.
  1214. */
  1215. PinFreezeContainer.prototype.getContainer = function() {
  1216. return this.m_cachedPFContainer;
  1217. };
  1218. /**
  1219. * return the section in this container with the matching key.
  1220. */
  1221. PinFreezeContainer.prototype.getSection = function(key) {
  1222. if (!this.m_sectionCache) {
  1223. return null;
  1224. }
  1225. if (!this.m_sectionCache[key]) {
  1226. this.m_sectionCache[key]=this.getSectionByLID(this.m_cachedPFContainer, key);
  1227. }
  1228. return this.m_sectionCache[key];
  1229. };
  1230. /**
  1231. * Finds elements under the home cell with a tab index and records
  1232. * them so that the tab index can be changed when the home cell is
  1233. * hidden. (Necessary so that the user cannot tab to elements of the
  1234. * DOM that we're hiding.)
  1235. */
  1236. PinFreezeContainer.prototype.initializeHomeCellTabIndex = function(homeCell) {
  1237. var slid = PinFreezeContainer.nodeToSlid(homeCell);
  1238. if(!this.m_homeCellNodes[slid]) {
  1239. var elements = getElementsByAttribute(homeCell, "*", "tabIndex", "*");
  1240. for(var i in elements) {
  1241. //Skip dijits
  1242. if(!elements[i].getAttribute("widgetid")) {
  1243. this.m_homeCellNodes[slid] = elements[i];
  1244. break;
  1245. }
  1246. }
  1247. }
  1248. };
  1249. PinFreezeContainer.prototype.updateTabIndexValues = function() {
  1250. if(this.isContainerFrozen()) {
  1251. for(var slid in this.m_homeCellNodes) {
  1252. var tabIndex = this.m_pinFreezeManager.isNodeVisible(this.m_homeCellNodes[slid]) ? "0" : "-1";
  1253. this.m_homeCellNodes[slid].setAttribute("tabIndex", tabIndex);
  1254. }
  1255. } else {
  1256. for(var slid in this.m_homeCellNodes) {
  1257. var tabIndex = (PinFreezeContainer.getSectionNameFromSlid(slid) === "pfMainOutput") ? "0" : "-1";
  1258. this.m_homeCellNodes[slid].setAttribute("tabIndex", tabIndex);
  1259. }
  1260. }
  1261. };
  1262. /**
  1263. * Find the home cell in the supplied section object (ie: could be the home cell in the side headings, top headings or main output).
  1264. */
  1265. PinFreezeContainer.prototype.getSectionHomeCell = function(pfSection) {
  1266. if (pfSection) {
  1267. var oSectionTable=this.getElementByLID(pfSection, "table", this.m_lid + this.m_viewerId);
  1268. if (oSectionTable && oSectionTable.rows.length && oSectionTable.rows[0].cells.length) {
  1269. return oSectionTable.rows[0].cells[0];
  1270. }
  1271. }
  1272. return null;
  1273. };
  1274. /**
  1275. * Find the home cell in the main output.
  1276. */
  1277. PinFreezeContainer.prototype.getMainOutputHomeCell = function() {
  1278. //return the home cell from the main container (the one that scrolls)
  1279. var pfMainOutput = this.getSection("pfMainOutput");
  1280. if (!pfMainOutput) {
  1281. pfMainOutput = this.getSectionByLID(this.m_cachedPFContainer, "pfMainOutput");
  1282. }
  1283. return this.getSectionHomeCell(pfMainOutput);
  1284. };
  1285. /**
  1286. * find the position of oChild in oParent's element list
  1287. * return -1 if oChild does not exist in the list.
  1288. */
  1289. PinFreezeContainer.prototype.getChildPosition = function(oParent, oChild)
  1290. {
  1291. for (var i=0; i<oParent.childNodes.length; ++i) {
  1292. if (oParent.childNodes[i]==oChild) {
  1293. return i;
  1294. }
  1295. }
  1296. return -1;
  1297. };
  1298. /**
  1299. * insert element oChild as a child of oParent at position nPosition in oParent's element list.
  1300. */
  1301. PinFreezeContainer.prototype.insertAt = function(oParent, oChild, nPosition)
  1302. {
  1303. if (nPosition==oParent.childNodes.length) {
  1304. oParent.appendChild(oChild);
  1305. } else {
  1306. oParent.insertBefore(oChild, oParent.childNodes[nPosition]);
  1307. }
  1308. };
  1309. /**
  1310. * When a scroll event is received from a scroll bar, synchronize the main section to it.
  1311. */
  1312. PinFreezeContainer.prototype.synchScroll = function()
  1313. {
  1314. if (!this.m_cachedPFContainer) {
  1315. return;
  1316. }
  1317. //keep scrolling between sections in synch....
  1318. var realHomeCell= this.getMainOutputHomeCell();
  1319. var pfMainOutput = this.getSection("pfMainOutput");
  1320. var pfSideHeadings = this.getSection("pfSideHeadings");
  1321. if (pfSideHeadings!=null) {
  1322. var pfHorizontalScroll = this.getSection("pfHorizontalScrollBar");
  1323. if (pfHorizontalScroll) {
  1324. var offset = this.getSideHeadingSectionWidth(realHomeCell);
  1325. if(getElementDirection(pfMainOutput) === "rtl") {
  1326. offset = 0;
  1327. }
  1328. setScrollLeft(pfMainOutput, getScrollLeft(pfHorizontalScroll) + offset);
  1329. if (this.m_freezeTop) {
  1330. setScrollLeft(this.getSection("pfTopHeadings"), getScrollLeft(pfHorizontalScroll) + offset);
  1331. }
  1332. }
  1333. }
  1334. };
  1335. /**
  1336. * Accessibility: Update the scroll position of the appropriate sections
  1337. * based on the node in focus (a child of a TD).
  1338. * The main output, the corresponding scrollbar and, for freeze both, the corresponding heading
  1339. * have to be scrolled.
  1340. */
  1341. PinFreezeContainer.prototype.updateScroll = function(element) {
  1342. var slid = PinFreezeContainer.nodeToSlid(element);
  1343. if(!slid) {
  1344. return;
  1345. }
  1346. var section = PinFreezeContainer.getSectionNameFromSlid(slid);
  1347. if(!section) {
  1348. return;
  1349. }
  1350. var oReportDiv = document.getElementById("CVReport" + this.m_viewerId);
  1351. if (!oReportDiv) {
  1352. return;
  1353. }
  1354. if (!this.m_cachedPFContainer) {
  1355. return;
  1356. }
  1357. var parent = element.parentNode;
  1358. if(parent) {
  1359. var tagName = parent.tagName.toLowerCase();
  1360. if(tagName === "td" || tagName === "th") {
  1361. var realHomeCell= this.getMainOutputHomeCell();
  1362. var pfMainOutput = this.getSection("pfMainOutput");
  1363. if(section === "pfMainOutput" || section === "pfTopHeadings") {
  1364. var pfHorizontalScroll = this.getSection( "pfHorizontalScrollBar");
  1365. if (pfHorizontalScroll) {
  1366. var scrollLeft = PinFreezeContainer.calculateNewPosition(
  1367. parent.offsetLeft,
  1368. parent.offsetWidth,
  1369. getScrollLeft(pfMainOutput),
  1370. pfMainOutput.offsetWidth
  1371. );
  1372. var offset = this.getHomeCellOffsetWidth(realHomeCell);
  1373. if(getElementDirection(pfMainOutput) === "rtl") {
  1374. offset = 0;
  1375. }
  1376. setScrollLeft(pfHorizontalScroll, scrollLeft - offset);
  1377. setScrollLeft(pfMainOutput, scrollLeft);
  1378. }
  1379. }
  1380. if(section === "pfMainOutput" || section === "pfSideHeadings") {
  1381. var pfVerticalScroll = this.getSection("pfVerticalScrollBar");
  1382. if (pfVerticalScroll) {
  1383. var scrollTop = PinFreezeContainer.calculateNewPosition(
  1384. parent.offsetTop,
  1385. parent.offsetHeight,
  1386. pfMainOutput.scrollTop,
  1387. pfMainOutput.offsetHeight
  1388. );
  1389. pfVerticalScroll.scrollTop = scrollTop - realHomeCell.offsetHeight;
  1390. pfMainOutput.scrollTop = scrollTop;
  1391. }
  1392. }
  1393. }
  1394. }
  1395. };
  1396. /**
  1397. * Figure out the position of the node in focus (used for keyboard navigation to synch scroll bars and
  1398. * sections).
  1399. */
  1400. PinFreezeContainer.calculateNewPosition = function(cellPosition, cellSize, viewportPosition, viewportSize) {
  1401. var cellFarPosition = cellPosition + cellSize;
  1402. var viewportFarPosition = viewportPosition + viewportSize;
  1403. if(viewportPosition > cellPosition) {
  1404. //Cell scrolled off to the left/top
  1405. return cellPosition;
  1406. } else if(viewportFarPosition < cellFarPosition) {
  1407. //Cell scrolled off to the right/bottom
  1408. if(cellSize > viewportSize) {
  1409. //Cell is too wide/tall - left/top-align
  1410. return cellPosition;
  1411. }
  1412. return cellFarPosition - viewportSize;
  1413. }
  1414. return viewportPosition;
  1415. };
  1416. /**
  1417. * When a scroll event is received from a scroll bar, synchronize the main section to it.
  1418. */
  1419. PinFreezeContainer.prototype.synchVScroll = function()
  1420. {
  1421. if (!this.m_cachedPFContainer) {
  1422. return;
  1423. }
  1424. //keep scrolling between sections in synch....
  1425. var realHomeCell= this.getMainOutputHomeCell();
  1426. var pfMainOutput = this.getSection("pfMainOutput");
  1427. var pfTopHeadings = this.getSection("pfTopHeadings");
  1428. if (pfTopHeadings!=null) {
  1429. var pfVerticalScroll = this.getSection("pfVerticalScrollBar");
  1430. if (pfVerticalScroll) {
  1431. pfMainOutput.scrollTop = pfVerticalScroll.scrollTop + this.getTopHeadingSectionHeight(realHomeCell);
  1432. if (this.m_freezeSide) {
  1433. this.getSection("pfSideHeadings").scrollTop=pfVerticalScroll.scrollTop + this.getTopHeadingSectionHeight(realHomeCell);
  1434. }
  1435. }
  1436. }
  1437. };
  1438. /**
  1439. * The total home cell height is the entire region occupied by the home cell
  1440. * (its height plus its offset from its parent)
  1441. */
  1442. PinFreezeContainer.prototype.getTopHeadingSectionHeight = function(oHomeCell)
  1443. {
  1444. return oHomeCell.offsetHeight + oHomeCell.offsetTop + this.m_containerMargin.top;
  1445. };
  1446. /**
  1447. * We can't simply take the first TD in the first row and thing it's the home cell. Some customers
  1448. * hide the home cell and fill that spot with crosstab spacers - see Defect 35525. The only thing we know for sure,
  1449. * is that the rowSpan on the first cell is correct. So if we grab the row after that and cross the TD's until we
  1450. * find a datavalue, that should give use the width of the 'home cell'.
  1451. */
  1452. PinFreezeContainer.prototype._findBestGuessHomeCell = function(oHomeCell) {
  1453. if (this.m_bestGuessHomeCell) {
  1454. return this.m_bestGuessHomeCell;
  1455. }
  1456. if (oHomeCell) {
  1457. var parent = oHomeCell.parentNode.parentNode;
  1458. var rowSpan = oHomeCell.rowSpan ? (oHomeCell.rowSpan) : 1;
  1459. var tr = parent.childNodes[rowSpan]; // TR where we'll find a datavalue
  1460. if (tr) {
  1461. var tdCount = tr.childNodes.length;
  1462. var previousTd = null;
  1463. var td = null;
  1464. for (var i=0; i < tdCount; i++) {
  1465. td = tr.childNodes[i];
  1466. if (td.getAttribute("type") == "datavalue") {
  1467. // We've found a datavalue. The previous TD is our home cell boundary
  1468. break;
  1469. }
  1470. previousTd = td;
  1471. }
  1472. if (previousTd) {
  1473. this.m_bestGuessHomeCell = previousTd;
  1474. return this.m_bestGuessHomeCell;
  1475. }
  1476. }
  1477. else {
  1478. return oHomeCell;
  1479. }
  1480. }
  1481. return null;
  1482. };
  1483. /**
  1484. * Offsetwidth of the home cell
  1485. */
  1486. PinFreezeContainer.prototype.getHomeCellOffsetWidth = function(oHomeCell) {
  1487. var bestGuessHomeCell = this._findBestGuessHomeCell(oHomeCell);
  1488. return bestGuessHomeCell ? bestGuessHomeCell.offsetWidth : 0;
  1489. };
  1490. /**
  1491. * The total home cell width is the entire region occupied by the home cell
  1492. * (its width plus its offset from its parent)
  1493. */
  1494. PinFreezeContainer.prototype.getSideHeadingSectionWidth = function(oHomeCell)
  1495. {
  1496. var bestGuessHomeCell = this._findBestGuessHomeCell(oHomeCell);
  1497. if (bestGuessHomeCell) {
  1498. return bestGuessHomeCell.offsetWidth + bestGuessHomeCell.offsetLeft + this.m_containerMargin.left;
  1499. }
  1500. else {
  1501. return oHomeCell.offsetWidth + oHomeCell.offsetLeft;
  1502. }
  1503. };
  1504. PinFreezeContainer.prototype.isContainerFrozen = function()
  1505. {
  1506. return (this.m_freezeTop || this.m_freezeSide);
  1507. };
  1508. /**
  1509. * Completely unfreeze the container.
  1510. */
  1511. PinFreezeContainer.prototype.unfreeze = function(oReportDiv)
  1512. {
  1513. var pfContainer=this.getContainerByLID(oReportDiv);
  1514. this.m_freezeTop=false;
  1515. this.m_freezeSide=false;
  1516. if (pfContainer) {
  1517. var oContainerParent=pfContainer.parentNode;
  1518. pfMainOutput=this.getSectionByLID(pfContainer, "pfMainOutput");
  1519. if (pfMainOutput && oContainerParent) {
  1520. if (pfContainer.style.border!=="") {
  1521. //Any borders that were transferred to the outer pfContainer should be transferred back on unfreeze.
  1522. pfMainOutput.firstChild.style.border=pfContainer.style.border;
  1523. pfContainer.style.border="";
  1524. }
  1525. if(this.m_wrapFlag){
  1526. //undo the whitespace style which was set by advance setting "ADVANCED_PROPERTY_FREEZE_DEFAULT_WRAP"
  1527. var spanElements=pfMainOutput.firstChild.getElementsByTagName("span");
  1528. if(spanElements) {
  1529. for (var k=0; k < spanElements.length; k++) {
  1530. spanElements[k].style.whiteSpace = "";
  1531. }
  1532. }
  1533. this.m_wrapFlag=false;
  1534. }
  1535. this.updateTabIndexValues();
  1536. if (this.m_cachedBaseContainer.getAttribute("authoredFixedWidth")) {
  1537. this.m_cachedBaseContainer.removeAttribute("authoredFixedWidth");
  1538. this.m_cachedBaseContainer.style.width = "auto";
  1539. this.m_addedFixedWidth = null;
  1540. }
  1541. if (this.m_cachedBaseContainer.getAttribute("authoredFixedHeight")) {
  1542. this.m_cachedBaseContainer.removeAttribute("authoredFixedHeight");
  1543. this.m_cachedBaseContainer.style.height = "auto";
  1544. this.m_addedFixedHeight = null;
  1545. }
  1546. oContainerParent.replaceChild(this.m_pinFreezeManager.deepCloneNode(pfMainOutput.firstChild), pfContainer);
  1547. }
  1548. }
  1549. };
  1550. /**
  1551. * Get all information about a particular border...one of "left, right, top, bottom"
  1552. * @param el
  1553. * @param whichBorder
  1554. * @returns
  1555. */
  1556. PinFreezeContainer.prototype.getBorderInfo = function(el, whichBorder) {
  1557. var cssBorderStyle = {};
  1558. var baseCSSBorderProp="border-" + whichBorder + "-";
  1559. var baseJsCSSBorderProp="border" + whichBorder.charAt(0).toUpperCase() + whichBorder.substring(1);
  1560. if (el.currentStyle) { //IE
  1561. cssBorderStyle[baseJsCSSBorderProp + "Width"] = el.currentStyle[baseJsCSSBorderProp + "Width"];
  1562. cssBorderStyle[baseJsCSSBorderProp + "Style"] = el.currentStyle[baseJsCSSBorderProp + "Style"];
  1563. cssBorderStyle[baseJsCSSBorderProp + "Color"] = el.currentStyle[baseJsCSSBorderProp + "Color"];
  1564. } else if (window.getComputedStyle) { //Firefox
  1565. cssBorderStyle[baseJsCSSBorderProp + "Width"] = window.getComputedStyle(el,null).getPropertyValue(baseCSSBorderProp + "width");
  1566. cssBorderStyle[baseJsCSSBorderProp + "Style"] = window.getComputedStyle(el,null).getPropertyValue(baseCSSBorderProp + "style");
  1567. cssBorderStyle[baseJsCSSBorderProp + "Color"] = window.getComputedStyle(el,null).getPropertyValue(baseCSSBorderProp + "color");
  1568. } else {
  1569. return null;
  1570. }
  1571. return cssBorderStyle;
  1572. };
  1573. PinFreezeContainer.prototype.isA11yEnabled = function(oxTab) {
  1574. return (oxTab.getAttribute("role") === "grid");
  1575. };
  1576. PinFreezeContainer.isElementInMainOutput = function(element) {
  1577. var section=PinFreezeContainer.nodeToSlid(element);
  1578. if (section) {
  1579. return (section.indexOf("pfMainOutput_") === 0);
  1580. }
  1581. return false;
  1582. };
  1583. /**
  1584. * Remove the ctx attribute from an element and its descendants.
  1585. */
  1586. PinFreezeContainer.prototype.removeCTX= function(element) {
  1587. element.removeAttribute("ctx");
  1588. var elements = getElementsByAttribute(element, "*", "ctx", "*");
  1589. if(elements && elements.length) {
  1590. for(var i = 0; i < elements.length; i++) {
  1591. elements[i].removeAttribute("ctx");
  1592. }
  1593. }
  1594. };
  1595. /**
  1596. * Set up the event handlers to support touch scrolling for Frozen headings.
  1597. */
  1598. PinFreezeContainer.prototype.initializeTouchScrolling = function(pfSection) {
  1599. if (!this.m_pinFreezeManager.isIWidgetMobile())
  1600. return;
  1601. if (pfSection) {
  1602. pfSection.m_pinFreezeContainer = this;
  1603. if (document.attachEvent)
  1604. {
  1605. pfSection.attachEvent("touchstart", this.touchStart);
  1606. pfSection.attachEvent("touchmove", this.touchMove);
  1607. pfSection.attachEvent("touchend", this.touchEnd);
  1608. } else {
  1609. pfSection.addEventListener('touchstart', this.touchStart, false);
  1610. pfSection.addEventListener('touchmove', this.touchMove, false);
  1611. pfSection.addEventListener('touchend', this.touchEnd, false);
  1612. }
  1613. }
  1614. };
  1615. PinFreezeContainer.prototype.removeTouchScrolling = function() {
  1616. if (!this.m_pinFreezeManager.isIWidgetMobile())
  1617. return;
  1618. this.removeTouchScrollingEvents(this.getSection("pfMainOutput"));
  1619. this.removeTouchScrollingEvents(this.getSection("pfSideHeadings"));
  1620. this.removeTouchScrollingEvents(this.getSection("pfTopHeadings"));
  1621. };
  1622. /**
  1623. * Remove event handlers to support touch scrolling for Frozen headings.
  1624. */
  1625. PinFreezeContainer.prototype.removeTouchScrollingEvents = function(pfSection) {
  1626. if (!this.m_pinFreezeManager.isIWidgetMobile())
  1627. return;
  1628. if (pfSection) {
  1629. if (document.detachEvent)
  1630. {
  1631. pfSection.detachEvent("touchstart", this.touchStart);
  1632. pfSection.detachEvent("touchmove", this.touchMove);
  1633. pfSection.detachEvent("touchend", this.touchEnd);
  1634. } else {
  1635. pfSection.removeEventListener('touchstart', this.touchStart, false);
  1636. pfSection.removeEventListener('touchmove', this.touchMove, false);
  1637. pfSection.removeEventListener('touchend', this.touchEnd, false);
  1638. }
  1639. }
  1640. };
  1641. /**
  1642. * Process a touch move event captured by either main output or a heading div
  1643. */
  1644. PinFreezeContainer.prototype.touchMove = function(e) {
  1645. //To support standard pinch/zoom, ignore all but single finger touch moves (e.touches.length==1).
  1646. if (this.m_pinFreezeContainer && e && e.changedTouches && e.touches && e.touches.length == 1) {
  1647. var touchobj = e.changedTouches[0]; // reference first touch point for this event
  1648. if (touchobj && touchobj.clientX && touchobj.clientY) {
  1649. var touchCurrentX=parseInt(touchobj.clientX);
  1650. var touchCurrentY=parseInt(touchobj.clientY);
  1651. if (this.m_pinFreezeContainer.touchMoveHandler(touchCurrentX, touchCurrentY)) {
  1652. return stopEventBubble(e);
  1653. }
  1654. }
  1655. }
  1656. };
  1657. /**
  1658. * Process a touch start event to initialize a "start point" for touch move.
  1659. */
  1660. PinFreezeContainer.prototype.touchStart = function(e) {
  1661. if (this.m_pinFreezeContainer && e && e.changedTouches && e.touches && e.touches.length == 1) {
  1662. var touchobj = e.changedTouches[0]; // reference first touch point for this event
  1663. if (touchobj && touchobj.clientX && touchobj.clientY) {
  1664. var touchCurrentX=parseInt(touchobj.clientX);
  1665. var touchCurrentY=parseInt(touchobj.clientY);
  1666. this.m_pinFreezeContainer.touchStartHandler(touchCurrentX, touchCurrentY);
  1667. }
  1668. }
  1669. };
  1670. /**
  1671. * Process a touch start event to initialize a "start point" for touch move.
  1672. */
  1673. PinFreezeContainer.prototype.touchStartHandler = function(touchCurrentX, touchCurrentY) {
  1674. this.touchScrollSections=false; //This will be set to true only after the first move has been processed.
  1675. this.touchPreviousX=touchCurrentX;
  1676. this.touchPreviousY=touchCurrentY;
  1677. };
  1678. /**
  1679. * stop the touchEnd event bubble if this move was handled by the frozen container.
  1680. */
  1681. PinFreezeContainer.prototype.touchEnd = function(e) {
  1682. if (this.m_pinFreezeContainer && this.m_pinFreezeContainer.touchEndHandler()) {
  1683. stopEventBubble(e);
  1684. }
  1685. };
  1686. PinFreezeContainer.prototype.touchEndHandler = function() {
  1687. var wasScrolling=this.touchScrollSections;
  1688. this.touchScrollSections=false;
  1689. this.touchPreviousX=-1;
  1690. this.touchPreviousY=-1;
  1691. return wasScrolling;
  1692. };
  1693. /**
  1694. * Similar to synchScroll but for touch events.
  1695. * Set the Top and Left scroll positions of corresponding section s
  1696. * based on the delta between this event and the last one processed (saved as touchPreviousX, touchPreviousY).
  1697. *
  1698. * Don't allow scrolling to go below "width/height" of the home cell.
  1699. */
  1700. PinFreezeContainer.prototype.touchMoveHandler = function(touchCurrentX, touchCurrentY) {
  1701. var pfMainOutput=this.getSection("pfMainOutput");
  1702. if (!pfMainOutput)
  1703. return;
  1704. var oHomeCell=this.getSectionHomeCell(pfMainOutput)
  1705. var topHeadingSectionHeight=this.getTopHeadingSectionHeight(oHomeCell);
  1706. var sideHeadingSectionWidth = this.getSideHeadingSectionWidth(oHomeCell);
  1707. var disty = touchCurrentY - this.touchPreviousY;
  1708. var distx = touchCurrentX - this.touchPreviousX;
  1709. //touchScrollSections will be true when at least one "in bounds" move event has been processed.
  1710. if (this.touchScrollSections) {
  1711. if (disty != 0) {
  1712. //Scroll output/side headings down or up (stop scrolling up at the top of the data cells).
  1713. var newScrollTop=pfMainOutput.scrollTop - disty;
  1714. newScrollTop = (newScrollTop > topHeadingSectionHeight) ? newScrollTop : topHeadingSectionHeight;
  1715. pfMainOutput.scrollTop=newScrollTop;
  1716. var pfSideHeadings = this.getSection("pfSideHeadings");
  1717. if (pfSideHeadings) {
  1718. pfSideHeadings.scrollTop=newScrollTop;
  1719. }
  1720. }
  1721. if (distx != 0) {
  1722. //Scroll output/Left headings left or right (stop scrolling up at the leftmost data cells).
  1723. var newScrollLeft=pfMainOutput.scrollLeft - distx;
  1724. newScrollLeft=(newScrollLeft > sideHeadingSectionWidth) ? newScrollLeft : sideHeadingSectionWidth;
  1725. pfMainOutput.scrollLeft=newScrollLeft;
  1726. var pfTopHeadings = this.getSection("pfTopHeadings");
  1727. if (pfTopHeadings) {
  1728. pfTopHeadings.scrollLeft=newScrollLeft;
  1729. }
  1730. }
  1731. } else {
  1732. this.firstTouchMove(pfMainOutput, distx, disty, sideHeadingSectionWidth, topHeadingSectionHeight);
  1733. }
  1734. this.touchPreviousX=touchCurrentX;
  1735. this.touchPreviousY=touchCurrentY;
  1736. return this.touchScrollSections;
  1737. };
  1738. /**
  1739. * The first move event (after the touch start) should either start touchMove scrolling of the frozen sections
  1740. * or delegate the move if moving is "out of bounds" (eg: beyond the left/right or top/bottom scroll limits)
  1741. */
  1742. PinFreezeContainer.prototype.firstTouchMove = function(pfMainOutput, distx, disty, sideHeadingSectionWidth, topHeadingSectionHeight) {
  1743. var vertical = this.mostlyVerticalTouchMove(distx, disty); //Determine the general direction of the move.
  1744. var hasTopHeadings=PinFreezeContainer.isSectionVisible(this.getSection("pfTopHeadings"));
  1745. var hasSideHeadings=PinFreezeContainer.isSectionVisible(this.getSection("pfSideHeadings"));
  1746. if (vertical &&
  1747. (!hasTopHeadings ||
  1748. (disty > 0 && pfMainOutput.scrollTop<=topHeadingSectionHeight) ||
  1749. (disty < 0 && pfMainOutput.scrollTop+pfMainOutput.clientHeight>=pfMainOutput.scrollHeight))) {
  1750. this.touchScrollSections=false;
  1751. } else if (!vertical &&
  1752. (!hasSideHeadings ||
  1753. (distx > 0 && pfMainOutput.scrollLeft<=sideHeadingSectionWidth) ||
  1754. (distx < 0 && pfMainOutput.scrollLeft+pfMainOutput.clientWidth>=pfMainOutput.scrollWidth))) {
  1755. this.touchScrollSections=false;
  1756. } else {
  1757. this.touchScrollSections=true;
  1758. }
  1759. };
  1760. /**
  1761. * Return true if this is mostly a vertical touch move.
  1762. * Return false if this is a mostly horizontal touch move.
  1763. */
  1764. PinFreezeContainer.prototype.mostlyVerticalTouchMove = function(distX, distY) {
  1765. var totalX = (distX>0) ? distX: 0-distX;
  1766. var totalY = (distY>0) ? distY: 0-distY;
  1767. return (totalY > totalX);
  1768. };
  1769. PinFreezeContainer.prototype.destroy = function() {
  1770. this.removeTouchScrolling();
  1771. GUtil.destroyProperties(this);
  1772. };