Automatically looping through charts in Active Reports

IMPORTANT NOTE! BLOG HAS RELOCATED TO: http://cognospaul.com/

Please make sure to update all links and bookmarks. The existing pages will remain, but will not be updated. Comments left here will be ignored.

This is the first of a series of articles on extending the usability of Active Reports.

Active Reports makes a wonderful addition to the Cognos suite. Locally processed interactivity (my Brio alter ego is shouting, “so what!”; just ignore him) means complex, though predefined, data exploration scenarios can be offered to ever more demanding users. Just by clicking on a row in a list, the entire report can be refreshed instantly. Standard web-based reports are simply incapable of that behavior.

Periodically I get requests for things Active Reports can’t do. For some reason, saying that something is not possible completely renders all of the other features moot. One such request was: “I want an animated chart that will loop through all of the months in the database”. The user wanted a chart that would show 12 months at a time, incrementing one month per second, looping back to the start once it hits the last month.

As a mockup, I built an example chart in a data deck that shows that behavior.
Chart in Data Deck

I stuck a Data Iterator under it. By pressing the next button you can make it look like it’s animated. Sadly, this wasn’t enough. The needed it to be automatic.

Now we can start playing with JavaScript. This presents a few interesting problems, some of which I’m still trying to solve.

Problem 1. Cognos rewrites IDs.
To demonstrate, I create a text item and an HTML item – a div with text inside it:
HTML Item In RS

But when I run it, and examine the HTML:
HTML Item Div ID Changed

That’s interesting! The ID of the div changed from myDiv to v9! This means that any JavaScript written will have to not use getElementById(). There is no guarantee that the div will be v9 tomorrow. Especially if there are data changes in the report. That’s fine though. I could do a getElementsByTagName(‘div’) and loop through them until I find one that has the correct name, or some other attribute I set. It’ll be slow, but better than nothing.

Let’s see what happens if I use JavaScript to count the number of divs. It should be a simple:

<script>alert(document.getElementsByTagName('div').length)</script>

I just add it to the existing HTML item:
script tag

And yet when I run it, I’m not getting the alert. The JS is sound, and if I copy it to the script console in IE Dev Toolbar I get back the expected value. So what is going on?

Unfortunately this is still one of the things that I’m still trying to figure out. My guess is that every element (list, chart, HTML item) is stored in an object and loaded into the page after the page has been loaded as needed. When running a report with a data deck, we can see that the individual cards aren’t loaded until another control references them. This allows the page to be significantly smaller on the first load, and far more responsive than if everything was rendered. Of course, this is just a guess and I could be completely wrong (but that hardly ever happens).

Effectively this means that onload events are out. And predefined functions are out. Everything will have to be inline events on elements hand written with HTML items.

Getting back to the original problems, the user said that he would be okay with a start/stop button. He presses it to start the “animation”, and clicks it again to stop it whenever he wants. This means we can define the function in the onclick event. And once again I’ve turned to my good friend Dan Freundel for help. And once again he turned 20 lines of spaghetti code into 5 lines of pure JS genius.

Since we can’t loop through divs that don’t yet exist, we’ll have to find a way to recreate the action of clicking on the next/previous buttons on the iterator. One way is to set the parameter directly. Any control that uses that param will be effected. Unfortunately, that way requires referencing minified JS functions. If we do it this way, any upgrade or fix pack will kill the report. Instead, I opted to literally click on the next/prev buttons in the loop.

First, the JS:

<button onclick="
if(!document.runAnim)
{

runAnim = function(buttonElement)
{
var intervalTracker, doStuff = function() {
var iter = buttonElement.previousSibling
, ibuttons = iter.getElementsByTagName(‘button’);
if(ibuttons[2].getAttribute(‘disabled’)) {ibuttons[0].setAttribute(‘disabled’,false);ibuttons[0].click();} else {ibuttons[2].click()}
}
buttonElement.onclick = function()
{
if(intervalTracker)
{
clearInterval(intervalTracker);
intervalTracker = false;
}
else
{
intervalTracker = setInterval(doStuff,1000);
}
};
intervalTracker = setInterval(doStuff,1000);

};}
runAnim(this);
this.value=’stop’;
" value="Start animation">Start</button>

First this defines the function. Since we can’t use inline HTML functions will have to be defined like this. On the first run, if the runAnim function doesn’t exist, it will create it. Next it will call that function in the scope of the button. That means the setInterval timer will exist only for that button, and it will not interfere with any other timers on the page. As mentioned before, because the IDs get rewritten, it is impossible to easily reference an element on the page. For this, I simply placed the button directly after the iterator. Next, when an iterator is first loaded, it has no value set and you can’t click any of the buttons. But if, somehow, the disabled=true was removed from the First button, clicking it would select the first item in the list.

So we have the script. The timer function is created the first time the button is clicked, that function and all associated variables are invoked in the scope of the button and it immediately starts working.
Loopy Charts

The list on the right acts both as a highlighter for the current row, and as a control to select which month you want to see in the chart.

10.2 XML (expand and double-click to select everything)

<report xmlns="http://developer.cognos.com/schemas/report/9.0/" useStyleVersion="10" expressionLocale="en-us" application="true">
				<modelPath>/content/folder[@name='Samples']/folder[@name='Cubes']/package[@name='Sales and Marketing (cube)']/model[@name='2008-07-25T15:28:38.072Z']</modelPath>
				<drillBehavior modelBasedDrillThru="true"/>
				<layouts>
					<layout>
						<reportPages>
							<page name="Page1">
								<style>
									<defaultStyles>
										<defaultStyle refStyle="pg"/>
									</defaultStyles>
								</style>
								<pageBody>
									<style>
										<defaultStyles>
											<defaultStyle refStyle="pb"/>
										</defaultStyles>
									</style>
									<contents>
						<table><style><defaultStyles><defaultStyle refStyle="tb"/></defaultStyles><CSS value="border-collapse:collapse"/></style><tableRows><tableRow><tableCells><tableCell><contents><appDataDeck name="Data Deck1" refQuery="Query2" width="0px" height="0px">
			<appDataCard>
				<contents><v2_combinationChart maxHotspots="10000" refQuery="Query1" name="Scatter Chart1">
																							<v2_combinationTypeTooltips/>
																							<v2_commonAxis>
																								<v2_ordinalAxis>
																									<v2_axisTitle refQuery="Query1">
																										<v2_chartTextContents>
																											<v2_automaticText/>
																										</v2_chartTextContents>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="at"/>
																											</defaultStyles>
																										</style>
																									</v2_axisTitle>
																									<v2_axisLine lineWeight="0" majorTickMarkLocation="none" minorTickMarkLocation="none"/>
																									<v2_axisLabels>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="al"/>
																											</defaultStyles>
																										</style>
																									</v2_axisLabels>
																								</v2_ordinalAxis>
																								<chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Month"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes></v2_commonAxis>
																							<v2_topLeftAxis>
																								<v2_combinationChartTypes>
																									<v2_line>
																										<v2_linePalette markerSize="5pt">
																											<v2_linePaletteEntries><v2_linePaletteEntry>
																							<v2_lineFill>
																								<v2_linearGradient gradientAngle="0">
																									<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																									<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																								</v2_linearGradient>
																							</v2_lineFill>
																							<v2_pointPaletteEntry markerShape="circle">
																								<v2_fillEffect>
																									<v2_linearGradient gradientAngle="0">
																										<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																										<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																									</v2_linearGradient>
																								</v2_fillEffect>
																							</v2_pointPaletteEntry>
																						</v2_linePaletteEntry>
																					</v2_linePaletteEntries></v2_linePalette>
																										<!-- v2_linePaletteRef ref="gDefaultLine"/ -->
																									<chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Revenue"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes></v2_line>
																								</v2_combinationChartTypes>
																								<v2_axis>
																									<v2_axisTitle refQuery="Query1">
																										<v2_chartTextContents>
																											<v2_automaticText/>
																										</v2_chartTextContents>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="at"/>
																											</defaultStyles>
																										</style>
																									</v2_axisTitle>
																									<v2_axisLine lineWeight="0"/>
																									<v2_axisRange>
																										<v2_automaticRange/>
																									</v2_axisRange>
																									<v2_axisLabels>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="al"/>
																											</defaultStyles>
																										</style>
																									</v2_axisLabels>
																									<v2_majorGridlines lineWeight="0" lineColor="#CCCCCC"/>
																									<v2_majorBackgroundColors>
																										<v2_firstBackgroundColor color="#D2D2D2" transparency="50"/>
																										<v2_secondBackgroundColor color="#E2E2E2" transparency="50"/>
																									</v2_majorBackgroundColors>
																								</v2_axis>
																							</v2_topLeftAxis>
																							<style>
																								<CSS value="width:600px;height:400px"/><defaultStyles><defaultStyle refStyle="ch"/></defaultStyles></style>
																							
																							<v2_chartTitle refQuery="Query1"><style><defaultStyles><defaultStyle refStyle="ct"/></defaultStyles></style><v2_chartTextItems><v2_chartTextItem><dataSource><staticValue>Revenue</staticValue></dataSource></v2_chartTextItem></v2_chartTextItems></v2_chartTitle><masterDetailLinks><masterDetailLink><masterContext><dataItemContext refDataItem="Month"/></masterContext><detailContext><parameterContext parameter="Month"/></detailContext></masterDetailLink></masterDetailLinks><v2_bottomLeftAxis><v2_axis>
													<v2_axisTitle refQuery="Query1">
														<v2_chartTextContents>
															<v2_automaticText/>
														</v2_chartTextContents>
														<style>
															<defaultStyles>
																<defaultStyle refStyle="at"/>
															</defaultStyles>
														</style>
													</v2_axisTitle>
													<v2_axisLine lineWeight="0"/>
													<v2_axisRange>
														<v2_automaticRange/>
													</v2_axisRange>
													<v2_axisLabels>
														<style>
															<defaultStyles>
																<defaultStyle refStyle="al"/>
															</defaultStyles>
														</style>
													</v2_axisLabels>
													<v2_majorGridlines lineWeight="0" lineColor="#CCCCCC"/>
													<v2_majorBackgroundColors>
														<v2_firstBackgroundColor color="#D2D2D2" transparency="50"/>
														<v2_secondBackgroundColor color="#E2E2E2" transparency="50"/>
													</v2_majorBackgroundColors>
												</v2_axis>
												<v2_combinationChartTypes><v2_line seriesType="absolute"><chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Quantity"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes><v2_linePalette><v2_linePaletteEntries><v2_linePaletteEntry>
																							<v2_lineFill>
																								<v2_linearGradient gradientAngle="0">
																									<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																									<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																								</v2_linearGradient>
																							</v2_lineFill>
																							<v2_pointPaletteEntry markerShape="circle">
																								<v2_fillEffect>
																									<v2_linearGradient gradientAngle="0">
																										<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																										<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																									</v2_linearGradient>
																								</v2_fillEffect>
																							</v2_pointPaletteEntry>
																						</v2_linePaletteEntry>
																					</v2_linePaletteEntries></v2_linePalette></v2_line></v2_combinationChartTypes></v2_bottomLeftAxis></v2_combinationChart></contents>
			</appDataCard>
		<appCardDefinition><appCardValues><appCardValue refDataItem="Month"/></appCardValues></appCardDefinition><appContainerSelect><appCondition><appConditionDataItemComparison refDataItem="Month" refAppVariable="Month" operator="in"/></appCondition></appContainerSelect></appDataDeck><HTMLItem>
			<dataSource>
				<staticValue><div style="display:none"></staticValue>
			</dataSource>
		</HTMLItem><appDataIterator name="Data Iterator1" refQuery="Query2">
			<appIteratorUI>
				<appIteratorPrevious>
					<appIteratorButton/>
				</appIteratorPrevious>
				<appIteratorLabelArea>
					
				<appIteratorDropDownList width="200px"/></appIteratorLabelArea>
				<appIteratorNext>
					<appIteratorButton/>
				</appIteratorNext>
			<appIteratorFirst><appIteratorButton/></appIteratorFirst></appIteratorUI>
		<appIteratorDefinition><appIteratorValues><appIteratorValue refDataItem="Month"/></appIteratorValues><appIteratorLabel refDataItem="Month"/></appIteratorDefinition><appOnSelectSetVariableValues><appSetVariableValueToDataItemValue refDataItem="Month" refAppVariable="Month"/></appOnSelectSetVariableValues><appContainerSelect><appCondition><appConditionDataItemComparison refDataItem="Month" refAppVariable="Month" operator="in"/></appCondition></appContainerSelect></appDataIterator><HTMLItem>
			<dataSource>
				<staticValue></div><button onclick="
  if(!document.runAnim) 
  {

runAnim = function(buttonElement)
{
 var intervalTracker, doStuff = function() { 
  var iter = buttonElement.previousSibling
  , ibuttons = iter.getElementsByTagName('button');
  if(ibuttons[2].getAttribute('disabled')) {ibuttons[0].setAttribute('disabled',false);ibuttons[0].click();} else {ibuttons[2].click()}
}
 buttonElement.onclick = function()
 {
    if(intervalTracker)
   {
     clearInterval(intervalTracker);
     intervalTracker = false;
   }
   else
   {
     intervalTracker = setInterval(doStuff,1000);
   }
 };
 intervalTracker = setInterval(doStuff,1000);

};}
runAnim(this);
this.value='stop';
" value="Start animation">Start</button></staticValue>
			</dataSource>
		</HTMLItem></contents><style><CSS value="vertical-align:top"/></style></tableCell><tableCell><contents><block>
			<contents><list horizontalPagination="true" name="List1" refQuery="Query1">
			
			
			
			<noDataHandler>
				<contents>
					<block>
						<contents>
							<textItem>
								<dataSource>
									<staticValue>No Data Available</staticValue>
								</dataSource>
								<style>
									<CSS value="padding:10px 18px;"/>
								</style>
							</textItem>
						</contents>
					</block>
				</contents>
			</noDataHandler>
			<style>
				<CSS value="border-collapse:collapse"/>
				<defaultStyles>
					<defaultStyle refStyle="ls"/>
				</defaultStyles>
			</style>
		<listColumns><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Month1"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lc"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Month1"/></dataSource></textItem></contents></listColumnBody></listColumn><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Revenue"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lm"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Revenue"/></dataSource></textItem></contents></listColumnBody></listColumn><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Quantity"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lm"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Quantity"/></dataSource></textItem></contents></listColumnBody></listColumn></listColumns><appContainerSelect><appCondition><appConditionDataItemComparison refAppVariable="Month" refDataItem="Month1" operator="in" dropIfValueIsEmpty="true"/></appCondition></appContainerSelect><appOnSelectSetVariableValues><appSetVariableValueToDataItemValue refAppVariable="Month" refDataItem="Month1"/></appOnSelectSetVariableValues></list></contents>
		<style><CSS value="height:550px;overflow:auto"/></style></block></contents></tableCell></tableCells></tableRow></tableRows></table></contents>
								</pageBody>
							</page>
						</reportPages>
					</layout>
				</layouts>
			<XMLAttributes><XMLAttribute name="RS_CreateExtendedDataItems" value="true" output="no"/><XMLAttribute name="listSeparator" value="," output="no"/><XMLAttribute name="RS_modelModificationTime" value="2008-07-25T15:28:38.133Z" output="no"/></XMLAttributes><queries><query name="Query1"><source><model/></source><selection><dataItem name="Revenue"><expression>[sales_and_marketing].[Measures].[Revenue]</expression></dataItem><dataItem name="Product line" aggregate="none" rollupAggregate="none"><expression>[sales_and_marketing].[Products].[Products].[Product line]</expression></dataItem><dataItem name="Month" aggregate="none" rollupAggregate="none"><expression>lastPeriods(12,#prompt('Month','mun','[sales_and_marketing].[Time].[Time].[Time]->:[PC].[@MEMBER].[Time]')#)</expression></dataItem><dataItem name="Quantity"><expression>[sales_and_marketing].[Measures].[Quantity]</expression></dataItem><dataItemLevelSet name="Month1"><dmLevel><LUN>[sales_and_marketing].[Time].[Time].[Month]</LUN><itemCaption>Month</itemCaption></dmLevel><dmDimension><DUN>[sales_and_marketing].[Time]</DUN><itemCaption>Time</itemCaption></dmDimension><dmHierarchy><HUN>[sales_and_marketing].[Time].[Time]</HUN><itemCaption>Time</itemCaption></dmHierarchy></dataItemLevelSet></selection></query><query name="Query2"><source><model/></source><selection><dataItem name="Month" aggregate="none" rollupAggregate="none"><expression>[sales_and_marketing].[Time].[Time].[Month]</expression></dataItem></selection><detailFilters><detailFilter><filterExpression>[sales_and_marketing].[Measures].[Revenue]>0</filterExpression></detailFilter></detailFilters></query></queries><appProperties><appVariables><appVariable name="Month"/></appVariables></appProperties><reportName>AR Start Animation</reportName></report>
Advertisements

One Response to Automatically looping through charts in Active Reports

  1. Daryl Atkins says:

    In 10.2.1 this worked perfectly and the iterator reset after the cycle every run. In 10.2.2 once running through a single cycle it stops. Is it possible you add a loop function to your JS?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: