Add onclick event to webcharts3d chart? - coldfusion

I am using webcharts3d to create a gauge graph for my webpage. I want to add an onclick event to the chart- any idea how to do that?
Here is my code:
<cfsavecontent variable="chartStyle">
<!--- xml chart style --->
</cfsavecontent>
<cfsavecontent variable="chartModel"><?xml version="1.0" encoding="UTF-8"?>
<XML type="default">
<COL>2000</COL>
<cfoutput><ROW col0="#url.rating#">Sample 0:</ROW></cfoutput>
</XML></cfsavecontent>
<cfscript>
oMyWebChart = createObject("Java","com.gp.api.jsp.MxServerComponent");
oMyApp = getPageContext().getServletContext();
oSvr = oMyWebChart.getDefaultInstance(oMyApp);
oMyChart2 = oSvr.newImageSpec();
oMyChart2.width = 120;
oMyChart2.height= 120;
oMyChart2.type = "swf";
oMyChart2.style = "#chartStyle#";
oMyChart2.model = "#chartModel#";
</cfscript>
<cfsavecontent variable="chartImgTag">
<cfoutput>#oSvr.getImageTag(oMyChart2,"http://myWebsite.com/CFIDE/GraphData.cfm?graphCache=wc50&graphID=")#</cfoutput>
</cfsavecontent>
<cfoutput>
#chartimgtag#
</cfoutput>

There is URL event in cfchart. You can check it out.

What I ended up doing was setting the chart type to png instead of swf. Then I was able to surround it with an anchor tag.
oMyChart2.type = "png";
instead of
oMyChart2.type = "swf";

This is what worked for me, keeping the oMyChart2.type = "swf";
<!--- Extract the style from the WCP file --->
<cfset wcp = XMLParse( ExpandPath("reports/pie.wcp") )>
<cfset wcp.project.style.pieChart.elements.XmlAttributes.action = "javascript:alert('$(colLabel)')">
<cfset chartStyle = ToString(wcp.project.style.pieChart)>

Related

How to use ZingCharts utility with my existing code in ColdFusion 11?

I recently migrated my application from ColdFusion 10 to ColdFusion 11. Though the migration was successful, the charts have been destroyed.
Up until ColdFusion 10 there was a WebCharts JAR file, called wc50.jar, in the \lib directory. It contained, amongst others, chart modules such as com.gp.api.jsp.MxServerComponent. That JAR file is absent from ColdFusion 11. Hence we have decided to update the our "charting code" and decided to use the new ZingChart.
ZingChart is JSON based. I analysed and found there is a utility in ColdFusion 11 for us to convert the XML stuff to JSON. The name of the utility is cfchart_xmltojson.bat. So this is my analysis by far. My question is - how to run/use this utility to do conversion and then modify my charting code?
My current charting code look something like this:
<cfsaveContent variable="chartStyle">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<frameChart isMultiline="false" is3D="#is3D#">
<frame outline="##666666"/>
<yAxis scaleMin="0" scaleMax="100"/>
<legend allowSpan="true" equalCols="false" placement="Right" isMultiline="true">
<decoration style="None"/>
</legend>
<cfif ShowPyramidNums>
<dataLabels style="value" placement="Inside" font="Arial-12-bold" >
</dataLabels>
</cfif>
<elements place="Stacked" shape="PyramidCut" shapeSize="100">
<movie framesPerSecond="2"/>
<cfset sPerfCtr = 0>
<cfloop index="i" from="#q_PerformanceDetailsByTemp.RecordCount#" to="1" step="-1">
<series index="#Evaluate(sPerfCtr)#">
<paint color="###q_PerformanceDetailsByTemp.ChartColor[i]#"/>
</series>
<cfset sPerfCtr = sPerfCtr+1>
</cfloop>
</elements>
<table>
<decoration style="Shadow"/>
</table>
<background maxColor="white"/>
<decoration foreColor="white" backColor="##90FFFF"/>
<popup showOn="Disabled" isAntialiased="true"/>
<paint palette="Transluent" paint="Plain" max="52"/>
</frameChart>
</cfoutput>
</cfsaveContent>
<cfsavecontent variable="chartModel">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<XML type="default">
<COL>Fall</COL>
<COL>Winter</COL>
<COL>Spring</COL>
<cfset over100FallFixed = false>
<cfset over100WinterFixed = false>
<cfset over100SpringFixed = false>
<cfloop index="i" from="#ArrayLen(PerfStats)#" to="1" step="-1">
<cfset rowVal = Evaluate((1/q_PerformanceDetailsByTemp.recordCount)*100)>
<cfif isDefined("over100Alert")>
<!--- This set of if statements is to modify value to show triangle correctly --->
<!--- Without this modification, the total ia 100.1 which cuts off the top of the triangle --->
<cfif right(PerfStats[i].FallPercentage, 1) GT 0 AND NOT over100FallFixed>
<cfset PerfStats[i].FallPercentage = PerfStats[i].FallPercentage - .1>
</cfif>
<cfif right(PerfStats[i].WinterPercentage, 1) GT 0 AND NOT over100WinterFixed>
<cfset PerfStats[i].WinterPercentage = PerfStats[i].WinterPercentage - .1>
</cfif>
<cfif right(PerfStats[i].SpringPercentage, 1) GT 0 AND NOT over100SpringFixed>
<cfset PerfStats[i].SpringPercentage = PerfStats[i].SpringPercentage - .1>
</cfif>
<ROW col0="#PerfStats[i].FallPercentage#" col1="#PerfStats[i].WinterPercentage#" col2="#PerfStats[i].SpringPercentage#">% #PerfStats[i].Name#</ROW>
<cfelse>
<ROW col0="#round(PerfStats[i].FallPercentage)#" col1="#round(PerfStats[i].WinterPercentage)#" col2="#round(PerfStats[i].SpringPercentage)#">% #PerfStats[i].Name#</ROW>
</cfif>
</cfloop>
</XML>
</cfoutput>
</cfsavecontent>
<cfscript>
ImageName = "TierTrans_" & CreateUUID() & ".png";
oMyWebChart = createObject("Java","com.gp.api.jsp.MxServerComponent");
oMyApp = getPageContext().getServletContext();
oSvr = oMyWebChart.getDefaultInstance(oMyApp);
oMyChart2 = oSvr.newImageSpec();
if(NOT Session.PDFIng)
{
oMyChart2.width = 650;
} else
{
oMyChart2.width = 550;
}
oMyChart2.height= 230;
oMyChart2.type = "png";
oMyChart2.style = "#chartStyle#";
oMyChart2.model = "#chartModel#";
</cfscript>
<cfif NOT DirectoryExists(ImagesDir)>
<CFDIRECTORY ACTION="CREATE" DIRECTORY="#ImagesDir#">
</cfif>
<!--- Save image to a different location --->
<cfset oSvr.saveBytesTo(oMyChart2,"#ImagesDir##ImageName#")>
<cfoutput><img src="Images/Reports/#ImageName#" border="0"/></cfoutput>
It used to draw a chart something like the image below. (The watermark you see is because I copied the wc50.jar from ColdFusion 10 directory to ColdFusion 11 lib directory):

Randomize cfoutput

The following code changes the body BG dependent on if the /home url is active else use a different BG. We have the pageID = 206 containing the /home BG image and pageID = 207 containing the else BG image. We currently have it setup this way so the client can go into the CMS and change the BG image without an issue.
However, the client would like the ability to add additional backgrounds and have them randomize. In theory I would add a page to the CMS and include the pageID and add randomize. I'm familiar with ColdFusion but this is a little over my head. Any input or direction would be greatly appreciated.
<cfsavecontent variable="HOME_BG">
<cfoutput>
<cfset includeID = '206'><cfinclude emplate='/PageInclude/PageInclude.cfm'/>
</cfoutput>
</cfsavecontent>
<cfset BG1 = getToken(HOME_BG,3, """") >
<cfsavecontent variable="COMM_BG">
<cfoutput>
<cfset includeID = '207'><cfinclude template='/PageInclude/PageInclude.cfm'/>
</cfoutput>
</cfsavecontent>
<cfset BG2 = getToken(COMM_BG,3, """") >
<body onload="checkCookie()" style="background-image: url(<cfoutput><cfif fpath EQ "home">#BG1#<cfelse>#BG2#</cfif></cfoutput>);">
I was looking for the ability to randomize between two different < cfset > but didn't find much luck.
Solution using an array and randrange
<cfset bgHomeIDArray = ArrayNew(1)> <!--- Create the array --->
<cfset ArrayAppend(bgHomeIDArray, 214)> <!--- Adds an array value --->
<cfset ArrayAppend(bgHomeIDArray, 215)> <!--- Adds an array value --->
<cfsavecontent variable="HOME_BG">
<cfoutput>
<cfset includeID = bgHomeIDArray[randRange(1, len(bgHomeIDArray))]> <cfinclude template='/PageInclude/PageInclude.cfm'/>
</cfoutput>
</cfsavecontent>
<cfset BG1 = getToken(HOME_BG,3, """") >
<cfset bgCommIDArray = ArrayNew(1)> <!--- Create the array --->
<cfset ArrayAppend(bgCommIDArray, 216)> <!--- Adds an array value --->
<cfset ArrayAppend(bgCommIDArray, 217)> <!--- Adds an array value --->
<cfsavecontent variable="COMM_BG">
<cfoutput>
<cfset includeID = bgCommIDArray[randRange(1, len(bgCommIDArray))]><cfinclude template='/PageInclude/PageInclude.cfm'/>
</cfoutput>
</cfsavecontent>
<cfset BG2 = getToken(COMM_BG,3, """") >
<body onLoad="checkCookie()" style="background-image: url(<cfoutput><cfif fpath EQ "home">#BG1#<cfelse>#BG2#</cfif></cfoutput>?03202014);">

How to extract slide notes from a PowerPoint file with ColdFusion

I have a .PPT (PowerPoint, transferrable to ODP or PPTX) file with speaker notes on every slide. I want to extract the entire presentation into something dynamic so I can create a speaker cheat sheet for running on a phone or table while I talk (thumbnail of the slide with speaker notes). I do this just often enough to HATE doing it by hand.
This is almost easy enough with <cfpresentation format="html" showNotes="yes"> which splits the PPT up into HTML pages and creates an image for every slide. cfpresentation, however, does not transfer the speaker notes, they are lost in translation.
I have also tried <cfdocument> which has no options for preserving slide notes once it converts to PDF.
Is there a way to get the notes out of the PowerPoint file from within ColdFusion?
The simplest solution:
Convert the PowerPoint presentation to OpenOffice ODP format. That's a ZIP file. CFML can unzip it and inside there's a content.xml file which contains the slides and the notes, so CFML can extract the notes from that format.
Given the CFDOCUMENT functionality, perhaps ColdFusion can even convert the PPT to ODP for you?
There's no way to do this directly in CF. You can do this by dropping to the underlying Java. I stand corrected. Using the showNotes attribute on the <cfpresentation> tag, should add the notes to the HTML.
As an alternative, or if that doesn't work for some reason, you should be able to use Apache POI to do this, although you may need to use a more recent version of poi than shipped with your version of coldfusion, which may require some additional work.
public static LinkedList<String> getNotes(String filePath) {
LinkedList<String> results = new LinkedList<String>();
// read the powerpoint
FileInputStream fis = new FileInputStream(filePath);
SlideShow slideShow = new SlideShow(is);
fis.close();
// get the slides
Slide[] slides = ppt.getSlides();
// loop over the slides
for (Slide slide : slides) {
// get the notes for this slide.
Notes notes = slide.getNotesSheet();
// get the "text runs" that are part of this slide.
TextRun[] textRuns = notes.getTextRuns();
// build a string with the text from all the runs in the slide.
StringBuilder sb = new StringBuilder();
for (TextRun textRun : textRuns) {
sb.append(textRun.getRawText());
}
// add the resulting string to the results.
results.add(sb.toString());
}
return results;
}
Carrying over complex formatting may be a challenge (bulleted lists, bold, italics, links, colors, etc.), as you'll have to dig much deeper into TextRuns, and the related API's and figure how to generate HTML.
CFPRESENTATION (at least as of version 9) does have a showNotes attribute, but you'd still have to parse the output. Depending on the markup of the output, jQuery would make short work of grabbing what you want.
Felt bad that my above answer didn't work out so I dug a little bit. It's a little dated, but it works. PPTUtils, which is based on the apache library that #Antony suggested. I updated this one function to do what you want. You may have to tweak it a bit to do exactly what you want, but I like the fact that this utility returns the data to you in data format rather than in HTML which you'd have to parse.
And just in case, here is the POI API reference I used to find the "getNotes()" function.
<cffunction name="extractText" access="public" returntype="array" output="true" hint="i extract text from a PPT by means of an array of structs containing an array element for each slide in the PowerPoint">
<cfargument name="pathToPPT" required="true" hint="the full path to the powerpoint to convert" />
<cfset var hslf = instance.loader.create("org.apache.poi.hslf.HSLFSlideShow").init(arguments.pathToPPT) />
<cfset var slideshow = instance.loader.create("org.apache.poi.hslf.usermodel.SlideShow").init(hslf) />
<cfset var slides = slideshow.getSlides() />
<cfset var notes = slideshow.getNotes() />
<cfset var retArr = arrayNew(1) />
<cfset var slide = structNew() />
<cfset var i = "" />
<cfset var j = "" />
<cfset var k = "" />
<cfset var thisSlide = "" />
<cfset var thisSlideText = "" />
<cfset var thisSlideRichText = "" />
<cfset var rawText = "" />
<cfset var slideText = "" />
<cfloop from="1" to="#arrayLen(slides)#" index="i">
<cfset slide.slideText = structNew() />
<cfif arrayLen(notes)>
<cfset slide.notes = notes[i].getTextRuns()[1].getRawText() />
<cfelse>
<cfset slide.notes = "" />
</cfif>
<cfset thisSlide = slides[i] />
<cfset slide.slideTitle = thisSlide.getTitle() />
<cfset thisSlideText = thisSlide.getTextRuns() />
<cfset slideText = "" />
<cfloop from="1" to="#arrayLen(thisSlideText)#" index="j">
<cfset thisSlideRichText = thisSlideText[j].getRichTextRuns() />
<cfloop from="1" to="#arrayLen(thisSlideRichText)#" index="k">
<cfset rawText = thisSlideRichText[k].getText() />
<cfset slideText = slideText & rawText />
</cfloop>
</cfloop>
<cfset slide.slideText = duplicate(slideText) />
<cfset arrayAppend(retArr, duplicate(slide)) />
</cfloop>
<cfreturn retArr />
</cffunction>

Populate object with multidimensional menu

I'm wondering if there is an effective way to put a menu into an array or any other data type.
With php I would do something like this:
$menu[1] = "home";
$menu[2] = "news";
$menu[3]["item"] = "products";
$menu[3]["subMenu"][1] = "jackets";
$menu[3]["subMenu"][2] = "T-shirts";
$menu[4] = "contact";
However I have no clue how one would do this in coldfusion.
I want to grab this data from the DB and push it into an object, this will allow me to generate the html from the array.
To take Ciaran's answer a step farther, you can do it completely with object literals in CF 9:
<cfset menu = ["home",
"news",
{"item"="products",
"subMenu"= ["jackets",
"T-shirts"]},
"contact"]>
<cfdump var="#menu#" /> <!--- Output --->
It's actually very similar. This presumes ColdFusion 8 (or higher) for array ([]) and struct ({}) literals:
<cfset menu = [] /> <!--- Create initial array --->
<cfset menu[1] = "home" />
<cfset menu[2] = "news" />
<cfset menu[3] = {} /> <!--- Create structure --->
<cfset menu[3]["item"] = "products" /> <!--- Address structure by key --->
<cfset menu[3]["subMenu"] = [] />
<cfset menu[3]["subMenu"][1] = "jackets" />
<cfset menu[3]["subMenu"][2] = "T-shirts" />
<cfset menu[4] = "contact" />
<cfdump var="#menu#" /> <!--- Output --->
Hope that helps!

Breadcrumbs in Fusebox 4/5

I'm wondering if anyone has come up with a clean way to generate a breadcrumbs trail in Fusebox. Specifically, is there a way of keeping track of "where you are" and having that somehow generate the breadcrumbs for you? So, for example, if you're executing
/index.cfm?fuseaction=Widgets.ViewWidget&widget=1
and the circuit structure is something like /foo/bar/widgets/ then somehow the system automatically creates an array like:
[
{ title: 'Foo', url: '#self#?fuseaction=Foo.Main' },
{ title: 'Bar', url: '#self#?fuseaction=Bar.Main' },
{ title: 'Widgets', url: '#self#?fuseaction=Widgets.Main' },
{ title: 'Awesome Widget', url: '' }
]
Which can then be rendered as
Foo > Bar > Widgets > Awesome Widget
Right now it seems the only way to really do this is to create the structure for each fuseaction in a fuse of some kind (either the display fuse or a fuse dedicated to creating the crumbtrail).
I'm working with Fusebox for a long time, but still can't understand this part:
circuit structure is something like /foo/bar/widgets/
Any way, once my idea was to use the custom lexicon called "parent" (or anything) for each controller fuseaction, where you put the name of previous level fuseaction.
But as I remember, this method was applicable only when using XML-style circuits, where you can always get any fuseaction info from the global container -- so I didn't make it because of intensive use of no-XMl style.
EDIT: example with lexicon
This will work only with Fusebox 5 traditional.
Let's say we have created following lexicon definition /lexicon/bc/parent.cfm:
<cfscript>
if (fb_.verbInfo.executionMode is "start") {
// validate fb_.verbInfo.attributes contents
if (not structKeyExists(fb_.verbInfo.attributes,"value")) {
fb_throw("fusebox.badGrammar.requiredAttributeMissing",
"Required attribute is missing",
"The attribute 'value' is required, for a 'parent' verb in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
}
// compile start tag CFML code
circuit = fb_.verbInfo.action.getCircuit().getName();
fa = fb_.verbInfo.action.getCircuit().getFuseactions();
fa[#fb_.verbInfo.fuseaction#].parent = circuit & "." & fb_.verbInfo.attributes.value;
} else {
// compile end tag CFML code
}
</cfscript>
Basically this is copy-pasted standard lexicon tag specially for lexicon parent.
Assuming we're using Fusebox 5 skeleton example, contoller can look like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE circuit>
<circuit access="public" xmlns:bc="bc/">
<postfuseaction>
<do action="layout.mainLayout" />
</postfuseaction>
<fuseaction name="welcome" bc:parent="">
<do action="time.getTime" />
<do action="display.sayHello" />
</fuseaction>
<fuseaction name="widgets" bc:parent="app.welcome">
<do action="display.showWidgets" />
</fuseaction>
<fuseaction name="widget" bc:parent="app.widgets">
<do action="display.showWidget" />
</fuseaction>
</circuit>
It shows how the lexicon used for each fuseaction. Please note that if you wont define the attribute bc:parent it wont appear in custom attributes structure later.
It is possible to use only fuseaction name as parent, if you have all chain within same circuit, it can be easier to use later.
Finally, some quick code to build the stuff. Please see comments, they should be helpful enough.
<!--- path data container with current fuseaction saved --->
<cfset arrBreadcrumbs = [] />
<cfset ArrayAppend(arrBreadcrumbs, attributes.fuseaction) />
<!--- pull the current circuit fuseactions --->
<cfset fuseactions = myFusebox.getApplication().circuits[ListFirst(attributes.fuseaction,'.')].getFuseactions() />
<!--- OR <cfset fuseactions = application.fusebox.circuits[ListFirst(attributes.fuseaction,'.')].getFuseactions()> --->
<!--- pull the current fuseaction custom attributes --->
<cfset fa = ListLast(attributes.fuseaction,'.') />
<cfset customAttributes = fuseactions[fa].getCustomAttributes('bc') />
<!--- save the parent fuseaction name if present -- KEY CHECK IS RECOMMENDED --->
<cfif StructKeyExists(customAttributes, "parent")>
<cfset ArrayPrepend(arrBreadcrumbs, customAttributes.parent) />
</cfif>
<!--- let's say we know that parent is there... --->
<!--- pull the found fuseaction custom attributes --->
<cfset fa = ListLast(customAttributes.parent,'.') />
<cfset customAttributes = fuseactions[fa].getCustomAttributes('bc') />
<!--- save the parent fuseaction name if present --->
<cfif StructKeyExists(customAttributes, "parent")>
<cfset ArrayPrepend(arrBreadcrumbs, customAttributes.parent) />
</cfif>
<!--- render the collected path --->
<cfoutput>
<cfloop index="crumb" from="1" to="#ArrayLen(arrBreadcrumbs)#">
<!--- to have a nice labels you can use another lexicon --->
#arrBreadcrumbs[crumb]# <cfif crumb LT ArrayLen(arrBreadcrumbs)>></cfif>
</cfloop>
</cfoutput>
So the output should look like this: app.welcome > app.widgets > app.widget
Here's something I use...
act_breadcrum.cfm
=============================
<cfscript>
if (NOT structKeyExists(session, 'clickstream'))
{
session.clickstream = arrayNew(1);
}
</cfscript>
<cflock name="addNewPage" type="exclusive" timeout="10">
<cfscript>
if ((arrayIsEmpty(session.clickstream))
OR (compare(myFusebox.originalCircuit, session.clickstream[arrayLen(session.clickstream)].Circuit))
OR (compare(myFusebox.originalFuseaction, session.clickstream[arrayLen(session.clickstream)].Fuseaction))
)
{
if (arrayLen(session.clickstream) EQ 10)
{
temp = arrayDeleteAt(session.clickstream, 1);
}
temp = arrayAppend(session.clickstream, structNew());
session.clickstream[arrayLen(session.clickstream)].Fuseaction = myFusebox.originalFuseaction;
session.clickstream[arrayLen(session.clickstream)].Circuit = myFusebox.originalCircuit;
}
</cfscript>
</cflock>
dsp_Breadcrum.cfm
==========================
<cfoutput>
<center>
<b><u>Last Clicked</u></b><BR>
<cfloop from="#arrayLen(session.clickstream)#" to="1" index="i" step="-1">
<cfset Opaque=i*.2>
<a href="#Myself##session.clickstream[i].Circuit#.#session.clickstream[i].Fuseaction#" style=opacity:#Opaque#>
#session.clickstream[i].Circuit#
</a><BR>
</cfloop>
</center>
</cfoutput>