27 May 2010

Jquery gradual loading page

Ever wanted to do a google mail, gmail style gradual loading type page. Where the longer something takes the more messages you get.

So for example something taking ages to load would say:

"loading..."

"still loading....."

"sorry this is taking ages, trying to fix it now...."

gradually, over time. Well i worked that out recently with jquery, it's not rocket science but it's a cute bit of code, made all the easier with jquery. Now before i go on, i'm no javascript expert and definitely i'm no jquery expert. In fact i'm only a few weeks into my jquery life. So if you know a better way about this, then do let me know.

Apologies once again for the poor code sampling, i simply can't work well with google blogger and code samples.

<html>
<head>
 <script language="javascript" src="jquery.js"></script>
 <title>Jay loadings</title>
 <script>
  var loadComplete = false;
  
  //straight away load the loading page
  $.ajax({
   url: "loading.htm",
   cache: false,
   success: function(html){
    $("#mydiv").html(html);
    //if thats done and if page still not completed, do a still loading page
    var t1=setTimeout("if(loadComplete == false){$.ajax({url: 'loading2.htm',cache: false,success: function(html){$('#mydiv').html(html);}});}",2000);
    var t1=setTimeout("if(loadComplete == false){$.ajax({url: 'loading3.htm',cache: false,success: function(html){$('#mydiv').html(html);}});}",6000);
   }
  });
  
  //when target.cfm is ready, load it.
  $(document).ready(function(){
   $.get("target.cfm",
    function(data){
    $("#mydiv").html(data);
    loadComplete = true;
   });  
  });  
 </script>
</head>



<div id="mydiv"></div>  

</html>



26 May 2010

HTML5 drag and drop example

Super quick post here, i was at SOTR recently and all excited about trying out me some new HTML 5. Something i've been meaning to do for ages and ages. Anyway i thought the easiest of which looked like the drag and drop feature. Most fun too, and i had no idea that almost all modern up-to-date browsers support most of the new HTML 5 features already.

I just hope they can get things in gear and punch out some standards sooner rather than the 10 year joke that seems to be circulating.



By the way, if you're thinking of checking out HTML 5, this is really cool:
http://html5readiness.com/

Anyway without further a-do here it is:




Super quick post here



<DOCTYPE HTML>
<html>
<head>
<title>Jay JS</title>
<style type="text/css">
.jayzclass{back9round-color:#ff0000; margin: l2px llO2px l2px l2px; padding: 3px 3px 3px 3px;}
.jayzhidden{visibility:hidden;}
</style>

<script language="javascript">
function dragoverHandler(event) {
if (event.dataTransfer.getData('text/plain').indexOf('monster') != -1){
event. preventDefault();
}
}

function dropHandler(event) {
// remove the dragged element
if (event.dataTransfer.getData('text/plain').indexof('monsterl') != -1){
document.getElementById('1').style.visibility = "hidden";
document.getElementsyId('4').style.visibility = "visible";
}
else if (event. dataTransfer.getData('text/plain'). indexof('monster2') != -1){
document.getElementById('2').style.visibility = "hidden";
document.getElementById('5').style.visibility = "visible";
}
else if (event.dataTransfer.getData('text/plain').indexof('monster3') != -1){
document.getElementById('3').style.visibility = "hidden";
document.getElementById('6').style.visibility = "visible";
}
</script>

</head>

<body>
<p>what monsters do you like?</p>
<table border="O">
<tr>
<td>
<div id="1"><img src="monster1.jpg" draggable /></div><br/>
<div id="2"><img src="monster2.jpg" draggable /></div><br/>
<dlv id="3"><img src="monster3.jpg" draggable /></div><br/>
</td>

<td width="300">&nbsp;</td>
<td>
<div id="4" class="jayzhidden"><img src="monsterl.jpg" draggable /></div><br/>
<div id="5" class="jayzhidden"><img src="monster2.jpg" draggable /></div><br/>
<div id="6" class="jayzhidden"><img src="monster 3.jpg" draggable /></div><br/>
</td>
</tr>
</table>

<div class="jayzclass" id="dropzone" ondragover="dragoverhandler(event); return false;" ondrop="dropHandler(event); return false;" 7> Give me your monsters, i will eat them<br/><br/>
</div>
</body>
</html>

26 March 2010

Google App Engine and Flex

OK so I've previously done a few projects where Flex talks to web-services which is both cool and fun. I started to think i wonder if i can get Google App Engine (GAE) to host my webservice and then Flex to do my front end. Well I'm a big fan of Open BlueDragon (OBD) and their fantastic work with GAE, so i thought I'd give it a spin.

First up the Adobe coldFusion method doesn't yet work, .cfc?wsdl was a no go, not surprising i dread to think what complexity is involved in that. There's something of a discussion of that problem here:

http://groups.google.com/group/openbd/browse_thread/thread/eec7f5664d76f47f?hl=en

However i discovered you can create and host a standard cfc and then call methods in that cfc from a standard cf template.

As the Google groups article suggested, it should be possible to just use OBD to output JSON or wxml or something to output data on a standard webpage minus the HTML. So i thought I'd give that a go.

So on my new GAE app I created a cfc with one function taking one input param and a basic case statement which returned a hardcoded string. Not rocket science. Then i created a simple cfm page to invoke that function and create a json string based on the return.

Something like this:

<cfset jayrox = structNew() />
<cfset jayrox.compName = variables.compName />
<cfset jayrox.compDesc = variables.hello1 />
<cfoutput>#SerializeJSON(jayrox)#</cfoutput>


I committed the above to GAE and then started working on the Flex app. The crux here is two fold.

First the HTTPService


<mx:HTTPService id="personRequest" url="
http://mygaelocation.appspot.com/jsonget.cfm"
requestTimeout="15" useProxy="false"
method="POST" resultFormat="object"
result="personJSON(event)"
fault="server_fault(event);"></mx:HTTPService>


Point that url to your gae cfm page and create a bindable string and a personJSON event handler:


[Bindable]
private var myJSON: String;



private function personJSON(event:ResultEvent):void
{
myJSON = String(event.result);
}


You're almost done, on application initialize run personRequest.send(); and bind a label or textarea to myJSON and you're set.

Second The crossdomain.xml

This is really really important. This took me ages to figure out, Flex and Flash when they're doing remote URL calls are trying to be security conscious. So they don't allow them unless the host (the GAE webroot) has a crossdomain.xml file. So in your war directory in GAE you need to create a crossdomain.xml file and put something like this in there:


<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>


More on this issue here:

http://www.wombatnation.com/2008/04/security-error-accessing-url

And you're done. You can drag and drop your completed .swf into the GAE war directory and there you go, Google App Engine is now hosting a complete end to end data driven Flex app.

When i get time i'll post something about making this a bit more dynamic using GAE data services.

29 January 2010

Link Adobe Flex 3.5 Chart to ColdFusion webservice

As promised, here is a (not very) short blog about how to get Adobe Flex 3.5 to consume a coldfusion cfc based webservice and display it as a chart.

As mentioned in my previous post it's EPIC that Adobe decided to put charting in the SDK. I use the SDK (coz I'm poor) and with it and a little patience you can achieve some amazing Flex apps.

So to do this you'll need the Flex 3.5 SDK, you'll need the Flex data visualization pack and obviously you'll need coldFusion.

I'm sorry for the code here, Google blogger doesn't make posting code easy.

1)The cfc:


<cfcomponent>

<cffunction name="getPersonByDept" access="remote" returntype="query" output="false">
<cfargument name="deptname1" type="string" required="true" />
<cfargument name="deptname2" type="string" required="true" />
<cfargument name="deptname3" type="string" required="true" />
<cfset var myQuery = queryNew('counttt,deptName') />

<cfquery name="myQuery" datasource="jayLocal">
select count(personid) * 100 as [counttt], [deptName]
from [person]
group by [deptName]
having
[deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname1#" />
or [deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname2#" />
or [deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname3#" />
</cfquery>

<cfreturn myQuery />
</cffunction>

</cfcomponent>



I know, I know, the function isn't great, i don't need to pass in 3 separate params, but this is just for illustration purposes. Take this cfc and save it as latestone.cfc in your coldfusion webroot or in a webservices folder. Whatever you fancy. You're going to want to be careful of Application.cfc because it can eat your webservices. You should be able to then open up the wsdl in your browser.
http://localhost/webserrvices/latestone.cfc?wsdl
You should see a bunch of xml data. This is coldfusion doing the hard work for you and describing the webservices offered at this wsdl.
The datasource and query are linked to a ms sql server, a single table called person. It should all be pretty straightforward.



2)The test.cfm:

OK so far so good, lets create a quick test.cfm and make sure the webservice works so far. Create the cfm file and paste in the following code:


<cfobject type="JAVA"
action="Create"
name="factory"
class="coldfusion.server.ServiceFactory">
<cfset RpcService = factory.XmlRpcService />
<cfset RpcService.refreshWebService("http://localhost/webserrvices/latestone.cfc?wsdl")>


<cfinvoke webservice="http://localhost/webserrvices/latestone.cfc?wsdl" method="getPersonByDept" returnvariable="hello2">
<cfinvokeargument name="deptname1" value="development" />
<cfinvokeargument name="deptname2" value="sales" />
<cfinvokeargument name="deptname3" value="marketing" />
</cfinvoke>



<cfdump var="#hello2#" />





OK quick bit of explaining. First and foremost the first paragraph, the JAVA stuff may be the single most useful piece of code when working with webservices in CF. CF does some aggressive caching when it comes to webservices. So if you're in dev mode this code forces CF to keep re-looking. DO NOT leave this code on a production server as it'll slow it right down. But for now it's useful. The second bit of code is a simple cfinvoke based on a webservice consumption.


3)The mxml:

Finally the Flex part. OK So assuming you've downloaded Flex, and tried a helloWorld already you should be able to take the following code and paste it into a .mxml file. Then compile it using the mxmlc command as before. Here's the mxml Flex code:




<?xml version="1.0" encoding="utf-8"?>

<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
viewSourceURL="src/HelloWorld/index.html"
horizontalAlign="center" verticalAlign="middle" initialize="initApp()" backgroundColor="white">


<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.utils.ObjectUtil;
import mx.core.Application;
import mx.validators.Validator;
import mx.managers.CursorManager;
import mx.events.ListEvent;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridListData;
import mx.utils.ArrayUtil;


[Bindable]
private var myDebpts: ArrayCollection;



public function initApp():void {
//onload send webservice
WSgetTasks.getPersonByDept.send();
}


private function server_fault(event:FaultEvent):void{
//on error, show...an error message!
Alert.show(ObjectUtil.toString(event.fault));
}


private function returnLoadTasks(evt:ResultEvent):void {
//when webservice returns, we grab the results and stick it in the bindable myDebpts
myDebpts = WSgetTasks.getPersonByDept.lastResult;
}


]]>
</mx:Script>

//This is the crus of the app, the webservice call. the operation tells it what method to call and the request stuff is the parameters, that simple!
<mx:WebService id="WSgetTasks" wsdl="http://localhost/webserrvices/latestone.cfc?wsdl" fault="server_fault(event);" result="returnLoadTasks(event)">
<mx:operation name="getPersonByDept">
<mx:request>
<deptname1>development</deptname1>
<deptname2>sales</deptname2>
<deptname3>marketing</deptname3>
</mx:request>
</mx:operation>
</mx:WebService>


<mx:Panel title="Users By Department" height="300" width="400">
//the chart
<mx:ColumnChart id="myChart"
dataProvider="{myDebpts}"
height="100%"
width="100%"
showDataTips="true">


//this bit is confusing but it works!
<mx:horizontalAxis>
<mx:CategoryAxis dataProvider="{myDebpts}" categoryField="DEPTNAME" />
</mx:horizontalAxis>

<mx:series>
<mx:ColumnSeries yField="COUNTTT" xField="DEPTNAME" displayName="Department Name" />
</mx:series>
</mx:ColumnChart>


<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>


</mx:Application>





There we go. Hopefully the comments provide some clarity, again I'm sorry Google blogger does such a poor job of showing the code properly. To talk you through it briefly, on app initialize the initApp() is called, this triggers the webservice. The mx:WebService calls the cfc which we defined as a webservice. It uses the mx:operation and the mx:request as parameters, obviously you can bind these to a text field or something. Then on completion the result runs the returnLoadTasks() function which loads the lastresult into the bindable arraycollection. This arraycollection is bound to the chart with the appropriate x and y axis names.

4)Display the generated flash file:

Done. Compile this and flex will spit out a lovely flash file. Copy this to your web directory somewhere and add the following to output the flash file:



<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="100%" height="100%">
<param name="movie" value="wschart.swf">
<param name="quality" value="high">
<embed src="wschart.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="100%" height="100%"></embed>
</object>




21 January 2010

Update: Charting available in Flex

A while ago i posted an entry saying charting was not available to Flex users of the SDK. While i was upset (charting is cool) I understood that Adobe wanted to attract users to their Flex Builder (purchased) product over the free SDK. Well in the latest version i discovered to my surprise the SDK can now support charting. This is awesome, hats of to Adobe, it works great and looks amazing, and all for free, grata, no money!

Here's a very brief example:

1) Download the SDK and the data visualization pack (currently flex version 3.5):
http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3sdk

2) Unzip install, etc, rtfm.

3) Create a blank text file with the extension .mxml

4) type the following code:



5) type something along the lines of this in a cmd window

C:\Adobe\Flex3\bin>mxmlc --strict=true --file-specs C:\flexie\chartTest.mxml

**should be obvious the first bit is cmd window telling me what dir i'm in, then the mxmlc is the executable to use for compiling and the rest is jarg, then the last bit is the location of my mxml code file.

6) This should deploy a .swf file to your code location. The swf file is then your flex app with some pretty little charts available!

I'll blog later with how to link these charts to something useful like a cf webservice.

Congrats Adobe on putting charts in the sdk, nice one.

06 November 2009

Javascript display time in browser local format

This is something i found useful and I'm sure I will again in the future.

Use coldfusion to set the UTC date and time. Then we'll have JS convert that time to the users local time.

Obviously it's preferable to have the user create an account and have them select their own, but in this case that wasn't an option.


function getDstOffset(myInputDate)
{
//get clients timezone offset so we can display in local format (60000 forces to use milliseconds)
var clientOffset = -myInputDate.getTimezoneOffset() * 60000;
return clientOffset;
}
function displayMyLocalTime(divname)
{
//take the passed in datetime(handled by server) and convert it to browser local
//this function takes the name of the dov to update with the datetime
var bigBenTime = new Date();

//set the js time to match that given to us by server
bigBenTime.setDate(#day(futuredate)#);
bigBenTime.setMonth(#month(futuredate)-1#);
bigBenTime.setYear(#year(futuredate)#);
bigBenTime.setHours(#hour(futuredate)#);
bigBenTime.setMinutes(#minute(futuredate)#);
bigBenTime.setSeconds(#second(futuredate)#);

cfif LEN(futureTime)
//time is defined, set it
bigBenTime.setHours(#hour(futuretime)#);
bigBenTime.setMinutes(#minute(futuretime)#);
bigBenTime.setSeconds(#second(futuretime)#);
/cfif

///get milliseconds since epoc and milliseconds of timezoneoffset
localBenTime = bigBenTime.getTime() + (getDstOffset(bigBenTime));

//put milliseconds back into useful format
benTime = new Date(localBenTime);

//set div to time
$(divname).innerHTML = benTime;
}

20 October 2009

Odd bug in Query of Queries

Not sure if this is a known bug or if it will really help anyone but it stumped me for a bit. As you can see ordering of uppercase values is handled completely differently than lowercase values! Frustrating.


cfscript
hello = queryNew('lastname,firstname');
queryAddRow(hello);
querySetcell(hello,'lastname','YANKEE');
querySetcell(hello,'firstname','DOODLE');
queryAddRow(hello);
querySetcell(hello,'lastname','ableson');
querySetcell(hello,'firstname','abe');
/cfscript
cfdump var="#hello#"



cfquery name="qhello" dbtype="query"
select lastname, firstname from hello
order by lastname,firstname
/cfquery
cfdump var="#qhello#"






Obviously the solution is "select lower(lastname), lower(firstname) from"....

I'd love to see CFML improve query of queries, it is a useful tool.