Source code for Zoom Maps project (Openlaszlo) March 13, 2007
Posted by julien in : RIA , trackbackSeveral developers asked me the code for this product presentation written in Openlaszlo.
Forewords
This program left from a really simple prototype, and I tried to keep it as clear as possible while it was growing up.
As I am not an Openlaszlo expert, the code can be improved in many ways I am sure. So please think this prototype as an alpha version, and feel free to comment if you wish.
LZX calls two xml files :
- zoomdata.xml which is the first file to be loaded. this file has the informations about the zooms (ratio, picture size, …) and the path to the tile url
- product.xml has the product description and the POI location and texts.
And now the code :
Code (javascript)
-
<?xml version="1.0" encoding="iso-8859-1"?>
-
-
<canvas width="515" height="614" bgcolor="#eaeaea">
-
-
<splash/>
-
-
<script>
-
<![CDATA[
-
var productName, productDescription;
-
var nbTilesX;
-
var nbTilesY;
-
var zoomLevel;
-
var ratio;
-
var currentZoomNumber;
-
var picWidth = 0;
-
var basePath, poiPath;
-
var oldPicWidth = 0;
-
var picHeight = 0;
-
var nb_pois = 0;
-
var nb_otherPics = 0;
-
var oldPicHeight = 0;
-
var tileSize;
-
var poiLoaded = false;
-
var serie = "A";
-
function replace(s, t, u) {
-
// function to replace into s the substring t by the substring u
-
i = s.indexOf(t);
-
r = "";
-
if (i == -1) return s;
-
r += s.substring(0,i) + u;
-
if ( i + t.length < s.length)
-
r += replace(s.substring(i + t.length, s.length), t, u);
-
return r;
-
}
-
// xmlPath is passed by the querystring of the swf file.
-
// it gives the place where to find the zoomdata.xml file (see below)
-
xmlPath = LzBrowser.getInitArg("xmlPath");
-
if (typeof(xmlPath) == ‘undefined’) {
-
// if no param is passed, use this default path
-
xmlPath = "http://localhost/bagages/d18182/zoomdata.xml";
-
}
-
]]>
-
</script>
-
-
<!– productData is the file where we will find informations about product :
-
Point of interest (POI), description, other views, …
-
–>
-
<dataset name="productData" type="http" request="false" />
-
<handler name="ondata" reference="productData">
-
<![CDATA[
-
var dp = productData.getPointer();
-
// grab number of POI into nb_pois variable
-
var qs = dp.xpathQuery(‘/product/pois/poi’);
-
nb_pois = qs.length;
-
poiLoaded = true;
-
// grab various product infos : name, description, …
-
productName = dp.xpathQuery(‘/product/@name’);
-
titleText.setText(productName);
-
productDescription = dp.xpathQuery(‘/product/description/text()’);
-
infoText.setText(productDescription);
-
// grap other views of the product (right side) and create thumbnail
-
thumbsView.buildThumbs();
-
draggableView.refreshThis();
-
]]>
-
</handler>
-
-
<!– first file that is loaded. This file contains informations
-
on the zoom view (the one with tiles) –>
-
-
<dataset name="zoomData" request="true" type="http" src="${xmlPath}" />
-
<handler name="ondata" reference="zoomData">
-
var dp = zoomData.getPointer();
-
basePath = dp.xpathQuery(‘/view/@basePath’);
-
// load productData xml file
-
poiPath = basePath + ‘/product.xml’;
-
productData.setSrc(poiPath);
-
productData.doRequest()
-
// grab tileSize and number of zooms for this file
-
tileSize = parseInt(dp.xpathQuery(‘/view/@tileSize’));
-
var qs = dp.xpathQuery(‘/view/zoom’);
-
nb_zooms = qs.length;
-
// first view is the last zoom, i.e the "fit to window" zoom
-
currentZoomNumber = nb_zooms;
-
</handler>
-
-
<!–
-
This class is the class for the thumbnailed view on the right side of the window
-
–>
-
<class name="otherPicThumb" extends="view" height="70" width="56">
-
<attribute type="text" name="masterOf" />
-
<handler name="onmouseover">
-
<![CDATA[
-
// masterOf if the name of the view this thumbnail commands when making a rollover
-
// ie the real size view that arrives from the left and disappears when rolling out
-
var myvar = globalCanvas.searchSubviews("name", this.masterOf);
-
if (myvar.animCome.started) {
-
myvar.animCome.stop();
-
myvar.setX(-500);
-
}
-
myvar.animCome.doStart();
-
myvar.setVisible(true);
-
// hide buttons when the full size view appears
-
btnView.fadeOut.doStart();
-
]]>
-
</handler>
-
<handler name="onmouseout">
-
<![CDATA[
-
// hides the real size view that just arrived from the left, and show the buttons back
-
var myvar = globalCanvas.searchSubviews("name", this.masterOf);
-
myvar.setVisible(false);
-
btnView.fadeIn.doStart();
-
]]>
-
</handler>
-
</class>
-
-
<!– class for the real size picture which arrive from the left, when rolling on a otherPicThumb instance –>
-
<class name="otherPic" extends="view">
-
<animator name="animCome" attribute="x" from="-500" to="15" duration="750" start="false" motion="easein" />
-
</class>
-
-
<!– poi (point of interest) class, basically a magnifier that is diplayed on some particular
-
points of the zoom view –>
-
<class name="poi" extends="multistatebutton" resource="multi_resource" overResourceNumber="6" downResourceNumber="6" normalResourceNumber="5">
-
<attribute type="text" name="commentText" />
-
<attribute name="targetX"/>
-
<attribute name="targetY"/>
-
<attribute name="whichZoom"/>
-
<method event="onclick">
-
<![CDATA[
-
isNewZoom = false;
-
if (currentZoomNumber!=whichZoom) {
-
// if zoom linked to that point of interest (whichZoom attribute) is different from the current zoom
-
// we will have to change the zoom
-
isNewZoom = true;
-
currentZoomNumber = whichZoom;
-
// hide all tiles
-
draggableView.hideAll();
-
// we use two series of tiles, to allow later effects like fading etc …
-
if (serie=="A") { serie = "B" } else { serie = "A" };
-
// refresh the zoom view at the new zoom level
-
draggableView.refreshThis();
-
}
-
// we have to seek the view center on the point this poi shows (targetX, targetY)
-
// we compute the new coordinates and sends the view to this place
-
newX = this.targetX * -1 + globalFrame.width / 2;
-
if (newX<draggableView.dragging.heldArgs.drag_min_x) { newX = draggableView.dragging.heldArgs.drag_min_x; }
-
if (newX>0) { newX = 0; }
-
draggableView.setAttribute("x",newX) ;
-
newY = this.targetY * -1 + globalFrame.height / 2;
-
if (newY<draggableView.dragging.heldArgs.drag_min_y) { newY = draggableView.dragging.heldArgs.drag_min_y; }
-
if (newY>0) { newY = 0; }
-
draggableView.setAttribute("y",newY);
-
// if we had to change to a new zoom level, we have to reload the tiles. we have to do this after positionning the
-
// zoom view, in order to load only necessary (ie visible) tiles.
-
if (isNewZoom) { draggableView.loadOrNotTile(); }
-
]]>
-
</method>
-
<method event="onmouseover">
-
// show text linked to this poi at the bottom of the window
-
infoText.setText(this.commentText);
-
</method>
-
<method event="onmouseout">
-
// show back product description when rolling out
-
infoText.setText(productDescription);
-
</method>
-
</class>
-
-
<!– the base class for a tile of picture –>
-
<class name="tile" extends="view">
-
<attribute type="text" name="imgResource" />
-
<attribute type="boolean" name="loaded" />
-
<method event="oninit">
-
loaded = false;
-
</method>
-
<animator name="anim" attribute="opacity" from="0" to="1" duration="1500" start="false" motion="easein"/>
-
<method event="onload">
-
// when picture has finished loading, start the fade in animation "anim"
-
if (this.loaded) {this.anim.doStart();}
-
</method>
-
<method name="loadOrNotThisTile">
-
<![CDATA[
-
// this method determines whether a tile must be loaded or not
-
// tile must be loaded if and only if it is visible by the user
-
if (! loaded) {
-
parentx = draggableView.x;
-
parenty = draggableView.y;
-
parentwidth = globalFrame.width;
-
parentheight = globalFrame.height;
-
realx = this.x + parentx;
-
realy = this.y + parenty;
-
isVisible = (realx<parentwidth) || (realy<parentheight);
-
if (isVisible) {
-
isVisible = ((realx+tileSize)>=0) || ((realy+tileSize)>=0);
-
}
-
if (isVisible) {
-
// if that tile must be loaded, sets his source to basePath + this.imgResource
-
// basePath is loaded from zoomdatafile
-
this.setSource(basePath + this.imgResource,"clientonly");
-
this.stretchResource();
-
this.loaded = true;
-
}
-
}
-
]]>
-
</method>
-
</class>
-
-
<simplelayout axis="y" />
-
-
<resource name="multi_resource">
-
<frame src="resources/zoomin.swf"/>
-
<frame src="resources/zoomin_over.swf"/>
-
<frame src="resources/zoomout.swf"/>
-
<frame src="resources/zoomout_over.swf"/>
-
<frame src="resources/poi.swf"/>
-
<frame src="resources/poi_over.swf"/>
-
<frame src="resources/home.swf"/>
-
<frame src="resources/home_over.swf"/>
-
</resource>
-
-
<simplelayout axis="x" />
-
-
<handler name="onclick" reference="btnHome">
-
<![CDATA[
-
// when the home button is clicked, go back to default zoom, ie the "fit window" view
-
if (currentZoomNumber!=nb_zooms) {
-
currentZoomNumber = nb_zooms;
-
draggableView.hideAll();
-
if (serie=="A") { serie = "B" } else { serie = "A" };
-
draggableView.refreshThis();
-
draggableView.setAttribute("x",0) ;
-
draggableView.setAttribute("y",0);
-
draggableView.loadOrNotTile();
-
}
-
]]>
-
</handler>
-
-
<handler name="onclick" reference="btnZoomIn">
-
<![CDATA[
-
// when button Zoom in is clicked, go to previous zoom, if applicable
-
if (currentZoomNumber>1) {
-
currentZoomNumber–;
-
// hide zoom view and change serie
-
draggableView.hideAll();
-
if (serie=="A") { serie = "B" } else { serie = "A" };
-
// refresh zoom view (= create new tiles)
-
draggableView.refreshThis();
-
if (oldPicWidth!=0) {
-
// compute new position of the zoom view, so that previous zoom and new zoom are centered
-
// on the same point … that works approximatively
-
newX = draggableView.x - (picWidth-oldPicWidth)/2;
-
if (newX<draggableView.dragging.heldArgs.drag_min_x) { newX = draggableView.dragging.heldArgs.drag_min_x; }
-
if (newX>0) { newX = 0; }
-
draggableView.setAttribute("x",newX) ;
-
newY = draggableView.y - (picHeight-oldPicHeight)/2;
-
if (newY<draggableView.dragging.heldArgs.drag_min_y) { newY = draggableView.dragging.heldArgs.drag_min_y; }
-
if (newY>0) { newY = 0; }
-
draggableView.setAttribute("y",newY);
-
}
-
// refresh zoom view by creating tiles
-
draggableView.loadOrNotTile();
-
}
-
]]>
-
</handler>
-
<handler name="onclick" reference="btnZoomOut">
-
<![CDATA[
-
// for explanations, please refer to zoom in
-
if (currentZoomNumber<nb_zooms) {
-
currentZoomNumber++;
-
draggableView.hideAll();
-
if (serie=="A") { serie = "B" } else { serie = "A" };
-
draggableView.refreshThis();
-
if (oldPicWidth!=0) {
-
newX = draggableView.x + (picWidth-oldPicWidth)/2;
-
if (newX<draggableView.dragging.heldArgs.drag_min_x) { newX = draggableView.dragging.heldArgs.drag_min_x; }
-
if (newX>0) { newX = 0; }
-
draggableView.setAttribute("x",newX) ;
-
newY = draggableView.y + (picHeight-oldPicHeight)/2;
-
if (newY<draggableView.dragging.heldArgs.drag_min_y) { newY = draggableView.dragging.heldArgs.drag_min_y; }
-
if (newY>0) { newY = 0; }
-
draggableView.setAttribute("y",newY);
-
}
-
draggableView.loadOrNotTile();
-
}
-
]]>
-
</handler>
-
-
<view id="globalCanvas" width="${parent.width}" height="${parent.height}" resource="resources/fond_bagages.jpg">
-
-
<view id="globalFrame" width="374" height="499" x="15" y="5" clip="true">
-
-
<!– draggableView is the core of the application, it is the "zoom view" that can be dragged and that is the
-
container for tiles –>
-
<view id="draggableView" x="0" y="0" clickable="true" onmousedown="dragging.apply()">
-
<method event="onmouseup">
-
// when user stops dragging the view, checks which tiles are now visible and need to be loaded
-
dragging.remove();
-
stop();
-
this.loadOrNotTile();
-
</method>
-
<method name="hideAll">
-
<![CDATA[
-
// hide all tiles from the zoom view
-
if (nbTilesX==null) { return; }
-
for (var i=0;i<nbTilesX+1;i++) {
-
for (var j=0;j<nbTilesY+1;j++) {
-
var myvar = draggableView.searchSubviews("name", "tile_A_" + i + "_" + j);
-
if (myvar != null) { myvar.destroy(); };
-
var myvar = draggableView.searchSubviews("name", "tile_B_" + i + "_" + j);
-
if (myvar != null) { myvar.destroy(); };
-
}
-
}
-
]]>
-
</method>
-
<method name="refreshThis">
-
<![CDATA[
-
// after a zoom change, this method is called to load the tiles back
-
this.hideAll();
-
pleaseWaitView.fadeIn.doStart();
-
// grab different datas from the zoomdata file : informations about the new zoom :
-
// number of tiles horizontaly and verticaly, global pic and height of the picture
-
// and ratio. Ratio is relative to the original image, that means that for a max zoom
-
// ratio is equal to 1
-
var dp = zoomData.getPointer();
-
zoomLevel = parseInt(dp.xpathQuery(‘/view/zoom[’ + currentZoomNumber + ‘]/@level’));
-
nbTilesX = parseInt(dp.xpathQuery(‘/view/zoom[’ + currentZoomNumber + ‘]/@x’));
-
nbTilesY = parseInt(dp.xpathQuery(‘/view/zoom[’ + currentZoomNumber + ‘]/@y’));
-
oldPicHeight = picHeight;
-
oldPicWidth = picWidth;
-
picWidth = parseInt(dp.xpathQuery(‘/view/zoom[’ + currentZoomNumber + ‘]/@width’)