/* *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: Viewer *| (C) Copyright IBM Corp. 2014, 2018 *| *| US Government Users Restricted Rights - Use, duplication or *| disclosure restricted by GSA ADP Schedule Contract with IBM Corp. *| *+------------------------------------------------------------------------+ */ /** * Support pin and freeze for a specific layout container (crosstab or list) */ function PinFreezeContainer(pinFreezeManager, lid, viewerID, freezeTop, freezeSide, containerNode, index) { this.m_pinFreezeManager=pinFreezeManager; this.m_lid=lid; this.m_lidNS=lid + viewerID + index; this.m_viewerId=viewerID; this.m_freezeTop=freezeTop; this.m_freezeSide=freezeSide; this.m_cachedReportDiv=null; this.m_cachedPFContainer=null; this.m_cachedBaseContainer=containerNode; //The crosstab/list within main output. this.m_containerMargin = {"top" : 0, "left" : 0}; if (this.m_cachedBaseContainer && this.m_cachedBaseContainer.style) { if (this.m_cachedBaseContainer.style.marginTop) { this.m_containerMargin.top = Number(this.m_cachedBaseContainer.style.marginTop.replace('px', '')); } if (this.m_cachedBaseContainer.style.marginLeft) { this.m_containerMargin.left = Number(this.m_cachedBaseContainer.style.marginLeft.replace('px', '')); } } this.m_cachedContainerIndex=index; this.m_sectionCache=null; this.m_homeCellNodes={}; this.m_fixedWidth=null; //Resize width will have no effect when container style (width) has been specified by the report author. this.m_clientWidth=700; //The total visible width of the container (in BUX, usually the widget width). this.m_scrollableClientWidth=700; //The visible width of the scrollable region (usu the clientWidth-the homeCell width) this.m_fixedHeight=null; //Resize height will have no effect when container style (height) has been specified by the report author. this.m_clientHeight=300; this.m_scrollableClientHeight=300; this.m_wrapFlag=false; //For FF this.c_pageMargin=(this.m_freezeTop && this.m_freezeSide) ? 50 : 20; //Default page margin. this.touchScrollSections=false; //True when touch scrolling of sections is underway (false if move is delegated) this.touchPreviousX=-1; //represents the "previous touch location" prior to the current touch move event this.touchPreviousY=-1; //set to -1 initially and at "touchEnd". } /** * Called when saving the dashboard. This function will serialize all the information * needed to correctly reload the dashboard with frozen containers */ PinFreezeContainer.prototype.toJSONString = function() { var sResponse = '{'; sResponse += '"m_clientWidth":' + this.m_clientWidth + ''; sResponse += ',"m_scrollableClientWidth":' + this.m_scrollableClientWidth + ''; sResponse += ',"m_clientHeight":' + this.m_clientHeight + ''; sResponse += ',"m_scrollableClientHeight":' + this.m_scrollableClientHeight + ''; sResponse += '}'; return sResponse; }; PinFreezeContainer.prototype.copyProperties = function(oldObj) { this.m_clientWidth = oldObj.m_clientWidth; this.m_scrollableClientWidth = oldObj.m_scrollableClientWidth; this.m_clientHeight = oldObj.m_clientHeight; this.m_scrollableClientHeight = oldObj.m_scrollableClientHeight; }; PinFreezeContainer.prototype.setViewerId = function(id) { this.m_viewerId = id; }; PinFreezeContainer.prototype.getLid = function() { return this.m_lid; }; /** * Modify the DOM for the report by overlaying a pin freeze template * that establishes zones for the container in the report (frozen side headings, top headings, data area). * Populate the frozen headings with a minimal subset of the main report */ PinFreezeContainer.prototype.createPFContainer = function(oReport, containsChildContainers) { var oTemp=document.createElement('temp'); if (this.m_cachedBaseContainer) { this.applyAuthoredFixedSizes(this.m_cachedBaseContainer); this.m_cachedReportDiv=oReport; var oBaseContainerParent=this.m_cachedBaseContainer.parentNode; var templateHTML=this.loadTemplateHTML(); if (templateHTML) { oTemp.innerHTML=templateHTML; var pfContainer = this.getContainerByLID(oTemp); var pfMainOutput=this.getSectionByLID(oTemp.firstChild, "pfMainOutput"); if (pfMainOutput) { //To avoid clone....mark the initial position oBaseContainer had it in its parent list. var i=this.getChildPosition(oBaseContainerParent, this.m_cachedBaseContainer); if (i!=-1) { //Set White space Style for advance setting "ADVANCED_PROPERTY_FREEZE_DEFAULT_WRAP" var m_oCV=this.m_pinFreezeManager.m_oCV; if(m_oCV && m_oCV.envParams["freezeDefaultWrap"]) { if (this.m_cachedBaseContainer.style.whiteSpace === "" && m_oCV.envParams["freezeDefaultWrap"].toLowerCase() === "true" ) { var spanElements=this.m_cachedBaseContainer.getElementsByTagName("span"); if(spanElements) { for (var k=0; k < spanElements.length; k++) { spanElements[k].style.whiteSpace = "nowrap"; } } this.m_wrapFlag=true; } } // If we don't have any child containers, then make sure we keep the current // width and height of the container so we don't squish the contents. if (!containsChildContainers) { if (!this._getFixedWidth()) { // We have to distinguish between the width set by a report author and the width we set this.m_cachedBaseContainer.setAttribute("authoredFixedWidth", "false"); this.m_addedFixedWidth = this.m_cachedBaseContainer.clientWidth + 1; this.m_cachedBaseContainer.style.width = this.m_addedFixedWidth + "px"; } if (!this._getFixedHeight()) { // We have to distinguish between the height set by a report author and the height we set this.m_cachedBaseContainer.setAttribute("authoredFixedHeight", "false"); this.m_addedFixedHeight = this.m_cachedBaseContainer.clientHeight; this.m_cachedBaseContainer.style.height = this.m_addedFixedHeight + "px"; } // Needed the +2 here to stop a small movement that would happen when moving the contain to under the div. Now // The list/crosstab will not shift, it should stay exactly where it was pfMainOutput.style.width = this.m_cachedBaseContainer.clientWidth + 2 + "px"; pfMainOutput.style.height = this.m_cachedBaseContainer.clientHeight + 2 + "px"; } //move oBaseContainer to the new parent. pfMainOutput.appendChild(this.m_cachedBaseContainer); //insert pfContainer at the position where oBaseContainer was. this.insertAt(oBaseContainerParent, pfContainer, i); } if (this.m_cachedBaseContainer.style.border!=="") { //Transfer borders that are on the layout element to the outer pfContainer //since the original layout element is only a section of the output. pfContainer.style.border=this.m_cachedBaseContainer.style.border; this.m_cachedBaseContainer.style.border=""; } } } } }; PinFreezeContainer.prototype._getFixedWidth = function(oBaseContainer) { if (oBaseContainer && oBaseContainer.style.width && !oBaseContainer.getAttribute("authoredFixedWidth")) { var width = Number(oBaseContainer.style.width.split('px')[0]); return isNaN(width) ? null : width; } return null; }; PinFreezeContainer.prototype._getFixedHeight = function(oBaseContainer) { if (oBaseContainer && oBaseContainer.style.height && !oBaseContainer.getAttribute("authoredFixedHeight")) { var height = Number(oBaseContainer.style.height.split('px')[0]); return isNaN(height) ? null : height; } return null; }; /** * If the author has specified a width or height in report studio (in px units), * the pinFreeze container will be "fixed in size" in that dimension. */ PinFreezeContainer.prototype.applyAuthoredFixedSizes = function(oBaseContainer) { var width = this._getFixedWidth(oBaseContainer); if (width) { this.m_fixedWidth=width; this.m_clientWidth=this.m_fixedWidth; //Author-specified width for the container. this.m_scrollableClientWidth=this.m_fixedWidth; } var height = this._getFixedHeight(oBaseContainer); if (height) { this.m_fixedHeight = height; this.m_clientHeight=this.m_fixedHeight; //Author-specified height for the container. this.m_scrollableClientHeight=this.m_fixedHeight; } }; /** * Apply a template used when the side headings of a crosstab are frozen. * This template includes regions for the side headings, main output and bottom scrollbar */ PinFreezeContainer.prototype.loadFreezeBothTemplateHTML = function() { var sPFOutput = '' + '' + '' + '' + '' + '' + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
 
' + '
' + '
'; return sPFOutput; }; /** * Apply a template used when the side headings of a crosstab are frozen. * This template includes regions for the side headings, main output and bottom scrollbar */ PinFreezeContainer.prototype.loadFreezeSideTemplateHTML = function() { var sPFOutput = '' + '' + '' + '' + '
' + '
' + '
' + '
 
' + '
' + '
'; return sPFOutput; }; /** * Apply a template used when the top headings of a crosstab are frozen. * This template includes regions for the top headings, main output and side scrollbar */ PinFreezeContainer.prototype.loadFreezeTopTemplateHTML = function() { var sPFOutput = '' + '' + '' + '' + '
' + '
' + '
' + '
' + '
'; return sPFOutput; }; /** * Apply the appropriate template to the supplied html for a container based. */ PinFreezeContainer.prototype.loadTemplateHTML = function() { if (this.m_freezeSide && this.m_freezeTop) { return this.loadFreezeBothTemplateHTML(); } else if (this.m_freezeSide) { return this.loadFreezeSideTemplateHTML(); } else if (this.m_freezeTop) { return this.loadFreezeTopTemplateHTML(); } return null; }; /** * Clone the main report DOM for a crosstab into the frozen side headings area. * The table element, the home cell and, after the labels after the column heading rows are visible. */ PinFreezeContainer.prototype.createSideHeadings = function(oxTab) { var pfMainOutput = this.getSection("pfMainOutput"); var mainSlid = pfMainOutput.getAttribute("pfslid"); var pfSideHeadings = this.getSection("pfSideHeadings"); var sideSlid = pfSideHeadings.getAttribute("pfslid"); var oHomeCell = this.getMainOutputHomeCell(); if (!oHomeCell) { return; } var oSourceTable=oxTab; var oTargetSideHeadings=pfSideHeadings; var bRemoveIds = this.isA11yEnabled(oSourceTable); //Clone The table element var oTargetTable=this.m_pinFreezeManager.deepCloneNode(oSourceTable); oTargetSideHeadings.appendChild(oTargetTable); var oTargetHomeCell = this.getSectionHomeCell(pfSideHeadings); if (!oTargetHomeCell) { return; } var oSourceTBodyArray=oSourceTable.getElementsByTagName("tbody"); var oTargetTBodyArray=oTargetTable.getElementsByTagName("tbody"); if (oSourceTBodyArray.length > 0 && oTargetTBodyArray.length > 0) { var oSourceTBody=oSourceTBodyArray[0]; var oTargetTBody=oTargetTBodyArray[0]; //Home cell var oSourceHomeCellTR=oSourceTBody.firstChild; var oTargetHomeCellTR=oTargetTBody.firstChild; var homeCellRowSpan=oHomeCell.rowSpan; this.markAsCopy(oHomeCell, oTargetHomeCell, mainSlid, sideSlid); for (var r=0; r 1) { if (this.m_freezeSide) { var nextCellBorder=this.getBorderInfo(oSourceRows[0].cells[1], "right"); if (nextCellBorder) { oTargetHomeCell.style.borderRightWidth=nextCellBorder.borderRightWidth; oTargetHomeCell.style.borderRightStyle=nextCellBorder.borderRightStyle; oTargetHomeCell.style.borderRightColor=nextCellBorder.borderRightColor; } } if (this.m_freezeTop) { var nextCellBorder=this.getBorderInfo(oSourceRows[0].cells[1], "bottom"); if (nextCellBorder) { oTargetHomeCell.style.borderBottomWidth=nextCellBorder.borderBottomWidth; oTargetHomeCell.style.borderBottomStyle=nextCellBorder.borderBottomStyle; oTargetHomeCell.style.borderBottomColor=nextCellBorder.borderBottomColor; } } } } }; /** * Clone the main report DOM for a crosstab into the frozen top headings area. * The table element, the home cell and the first few rows above the data cells are visible. */ PinFreezeContainer.prototype.createTopHeadings = function(oxTab) { var pfMain = this.getSection("pfMainOutput"); var mainSlid = pfMain.getAttribute("pfslid"); var pfTopHeadings = this.getSection("pfTopHeadings"); var topSlid = pfTopHeadings.getAttribute("pfslid"); var oHomeCell = this.getMainOutputHomeCell(); if (!oHomeCell) { return; } var oSourceTable=oxTab; var oTargetTopHeadings=pfTopHeadings; var bRemoveIds = this.isA11yEnabled(oSourceTable); //Clone The table element var oTargetTable=this.m_pinFreezeManager.deepCloneNode(oSourceTable); oTargetTable.setAttribute("clonednode", "true"); oTargetTopHeadings.appendChild(oTargetTable); var oSourceTBodyArray=oSourceTable.getElementsByTagName("tbody"); var oTargetTBodyArray=oTargetTable.getElementsByTagName("tbody"); if (oSourceTBodyArray.length > 0 && oTargetTBodyArray.length > 0) { var oSourceTBody=oSourceTBodyArray[0]; var oTargetTBody=oTargetTBodyArray[0]; //clear attributes from datacells and mark heading cells as copies. var homeCellRowSpan=oHomeCell.rowSpan; for (var r=0; r homeCellRowSpan || oTargetTD.getAttribute("type") == "datavalue") { oTargetTD.removeAttribute("ctx"); oTargetTD.removeAttribute("uid"); oTargetTD.removeAttribute("name"); } else { var oSourceTD=oSourceTR.cells[c]; this.markAsCopy(oSourceTD, oTargetTD, mainSlid, topSlid); if(oSourceTD === oHomeCell) { this.initializeHomeCellTabIndex(oTargetTD); this.applyNeighbouringBorderStylesToHomeCell(oSourceTBody.rows, oTargetTD); } } } oTargetTR.style.visibility='visible'; //WARNING: Do not remove this without testing IE and calculations } } }; PinFreezeContainer.prototype.createHomeCellHeading = function() { var pfMainOutput = this.getSection("pfMainOutput"); var mainSlid = pfMainOutput.getAttribute("pfslid"); var pfHomeCellDiv = this.getSection("pfHomeCell"); var oHomeCellWrapperTd = pfHomeCellDiv.parentNode; var pfHomeSlid = pfHomeCellDiv.getAttribute("pfslid"); var oSourceHomeCell = this.getMainOutputHomeCell(); if (!oSourceHomeCell) { return; } // Make sure the new home cell has the same height of the original home cell oHomeCellWrapperTd.style.height = "100%"; var topHeadingSectionHeight=this.getTopHeadingSectionHeight(oSourceHomeCell); // The div is the one we'll use to put the left and bottom border on. Make sure the width // and height set take the margin of the container into account pfHomeCellDiv.style.height = topHeadingSectionHeight - this.m_containerMargin.top + "px"; pfHomeCellDiv.style.width = this.getSideHeadingSectionWidth(oSourceHomeCell) - this.m_containerMargin.left + "px"; pfHomeCellDiv.style.marginTop = this.m_containerMargin.top + "px"; pfHomeCellDiv.style.marginLeft = this.m_containerMargin.left + "px"; var oSourceTR = oSourceHomeCell.parentNode; var oTargetTR = oSourceTR.cloneNode(false); var bestGuessHomeCell = this._findBestGuessHomeCell(oSourceHomeCell); var sizeCalculatingDiv = document.createElement("div"); sizeCalculatingDiv.style.width = "100%"; sizeCalculatingDiv.style.height = "100%"; /** * The home cell could be made up of a single TD (normal case), or it can be a bunch * of crosstab spacers. Go through all the TDs until the offsetLeft gets beyond the 'home cell' boundary */ while (oSourceHomeCell.offsetLeft <= bestGuessHomeCell.offsetLeft) { oTargetHomeCell = this.m_pinFreezeManager.deepCloneNode(oSourceHomeCell); // For Firefox and IE we can't simply take the clientWidth, since that includes the padding. Use a hidden // 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 if (isFF() || isIE()) { oSourceHomeCell.appendChild(sizeCalculatingDiv); oTargetHomeCell.style.width = sizeCalculatingDiv.clientWidth + "px"; oSourceHomeCell.removeChild(sizeCalculatingDiv); } else { oTargetHomeCell.style.width = oSourceHomeCell.clientWidth + 1 + "px"; } // Make sure there's no bottom border on the TD. We'll have a bottom border on the wrapper TD oTargetHomeCell.style.borderBottomWidth="0px"; oTargetTR.appendChild(oTargetHomeCell); this.markAsCopy(oSourceHomeCell, oTargetHomeCell, mainSlid, pfHomeSlid); if (oSourceHomeCell.nextSibling) { oSourceHomeCell = oSourceHomeCell.nextSibling; } else { break; } } // Don't need a right border on the last TD, we'll have that on the wrapper TD if (oTargetHomeCell) { oTargetHomeCell.style.borderRightWidth="0px"; } var oSourceTBody = oSourceTR.parentNode; var oTargetTBody = oSourceTBody.cloneNode(false); oTargetTBody.appendChild(oTargetTR); var oSourceTable = oSourceTBody.parentNode; var oTargetTable = oSourceTable.cloneNode(false); oTargetTable.appendChild(oTargetTBody); oTargetTable.style.width = "100%"; oTargetTable.style.height = "100%"; // Since the margins have been set on the containing div, clear them on the table oTargetTable.style.marginLeft = ""; oTargetTable.style.marginTop = ""; pfHomeCellDiv.appendChild(oTargetTable); this.initializeHomeCellTabIndex(oTargetHomeCell); this.applyNeighbouringBorderStylesToHomeCell(pfMainOutput.firstChild.rows, pfHomeCellDiv); }; /** * Given an element and its copy, add pointers between them and mark their slid. */ PinFreezeContainer.prototype.markAsCopy = function(main, copy, mainSlid, copySlid) { if(!main.pfCopy) { main.setAttribute("pfslid", mainSlid); main.pfCopy = []; } main.pfCopy.push(copy); copy.pfMain = main; copy.setAttribute("pfslid", copySlid); }; /** * Given an element, the visible copied version of it, if one exists */ PinFreezeContainer.prototype.getCopy = function(element) { if(element.pfCopy) { var copies = {}; for(var i in element.pfCopy) { var copy = element.pfCopy[i]; if (copy.getAttribute) { var copySlid = copy.getAttribute("pfslid"); if(copySlid) { var sectionName = PinFreezeContainer.getSectionNameFromSlid(copySlid); var copySection = this.getSection(sectionName); if(copySection && PinFreezeContainer.isSectionVisible(copySection)) { copies[sectionName] = copy; } } } } //Favour the home cell if(copies["pfHomeCell"]) { return copies["pfHomeCell"]; } //If no home cell, return any copy arbitrarily for(i in copies) { return copies[i]; } } return null; }; /** * Given an element, return the original it is a copy of, if it is a copy */ PinFreezeContainer.prototype.getMain = function(element) { if(element.pfMain) { return element.pfMain; } return null; }; /** * Return whether the given section is visible to the user */ PinFreezeContainer.isSectionVisible = function(section) { var node = section; if (!node) { return false; //A section that doesn't exist isn't visible. } //Search for a parent with display: none. Limit the search //to within the container. //This is necessary for times when the row a section is in //is hidden, rather than the section itself. while(node.parentNode && !node.getAttribute("pfclid")) { if(node.style && node.style.display === "none") { return false; } node = node.parentNode; } return (!node.style || node.style.display !== "none"); }; /** * Returns an object representing whether side and top headings * are actually frozen right now. */ PinFreezeContainer.prototype.getSectionStructure = function() { var result = { isSideFrozen: false, isTopFrozen: false }; if(this.m_freezeSide) { var side = this.getSection("pfSideHeadings"); if(side) { result.isSideFrozen = PinFreezeContainer.isSectionVisible(side); } } if(this.m_freezeTop) { var top = this.getSection("pfTopHeadings"); if(top) { result.isTopFrozen = PinFreezeContainer.isSectionVisible(top); } } return result; }; /** * Compares the before and after section structures (returned from getSectionStructure) * and if there has been a change, informs the Viewer Widget. */ PinFreezeContainer.prototype.checkSectionStructureChange = function(before, after) { if(before.isSideFrozen !== after.isSideFrozen || before.isTopFrozen !== after.isTopFrozen) { this.m_pinFreezeManager.sectionStructureChange(); } }; /** * Given an object representing the full report, find the specific container * and complete sizing and layout to cause it to freeze. */ PinFreezeContainer.prototype.freezeContainerInReport = function(oReport) { this.cacheContainerAndSections(this.getContainerByLID(oReport)); this.m_homeCellNodes = {}; this.updateContainer(); }; /** * return true if frozen sections are actually required... * They are required when the width/height of the container is not large enough to present all the data */ PinFreezeContainer.prototype.frozenSectionsRequired = function() { return (this.frozenSideHeadingsRequired() || this.frozenTopHeadingsRequired()); }; PinFreezeContainer.prototype.frozenSideHeadingsRequired = function() { var pfMainOutput=this.getSection("pfMainOutput"); if (pfMainOutput) { if (this.m_freezeSide) { var mainScrollWidth=pfMainOutput.scrollWidth; return ((this.m_clientWidth < mainScrollWidth) || mainScrollWidth==0); } } return false; }; PinFreezeContainer.prototype.frozenTopHeadingsRequired = function() { var pfMainOutput=this.getSection("pfMainOutput"); if (pfMainOutput) { if (this.m_freezeTop) { var mainScrollHeight=pfMainOutput.scrollHeight; return ((this.m_clientHeight < mainScrollHeight) || mainScrollHeight==0); } } return false; }; /** * set the display state (true=visible, false=hidden) to the specified template part */ PinFreezeContainer.prototype.showTemplatePart = function(templatePartToChange, valueToSet) { var oTemplateRows=this.getContainer().rows; for (var r=0; r