March 2007 - Posts

API lecture
Fri, Mar 30 2007 18:08

More than 3 hours on how to design an API by Krzysztof Cwalina. Already on my "listen/watch" queue when I return from vacantions...

by luisabreu | with no comments
Filed under:
Open Source projects in .NET
Fri, Mar 30 2007 7:36

An open letter from D. Starr to Scott G. resulted in a cool discussion about open source projects in the .NET platform. If you have the time, then go read it!

[I know this is old, but I'm on vacations:) ]

by luisabreu | with no comments
Filed under: , ,
Printing accented words on a Zebra
Wed, Mar 28 2007 16:56

Some time ago, I've written a small sample that abstracts some of the details associated with printing to the Zebra. This post explains what you need to do in order to print accented chars to those printers. By default, Zebras use a  codepage which don't print accents. Fortunately, setting the codepage is easy: You need to send an I command  with the codepage you want to use and that's it!

If you look at the code, you'll find a class called Label, which is used to represent a label that you want to print. All you need to do is add a line to the GetCommandString method (you can put it right after the _referencePoint if test) with the I command and the codepage you want to use. Zebra's EPL II manual has all the info you need in order to get the correct codepage. Just don't forget to end the command with a new line (\n) or it won't work.

by luisabreu | 4 comment(s)
Filed under: ,
Javascript free programming books and resources
Thu, Mar 22 2007 16:51

Today I've found this on one of the blogs I read. Unfortunatelly, I've opened the link in one of my opera tabs and started doing other things at the time. That's why I don't recall where I've gotten this link :( So, If by any chance you were the one that posted about this, please let me know so that I can give you credit for it.

by luisabreu | 2 comment(s)
Filed under:
Getting your intervals right with the AJAXToolkit timespans
Wed, Mar 21 2007 17:44

The AJAXToolkit introduces a Timespan class that lets you do some interesting things with time intervals. After looking at the class (which is defined on the datetime.js file, inside the common folder), it's obvious that it tries to mimic the Timestamp class of the .NET platform. The good news is that you'll be right at home if you have any experience with that class :)

You can create a new timespan in several ways. Lets start with the constructor. Internally, the class defines several auxiliary methods which are responsible for initializing the class' internal state (the number of ticks).  When you call the constructor with one parameter, you're setting the number of ticks:

var timespan = new AjaxControlToolkit.TimeSpan( 260 ); //260 ticks

When you use 3 parameters, you're defining the number of hours, minutes and settings stored in the timespan:

var timespance = new AjaxControlToolkit.TimeSpan(
                                                                       1, //hour
                                                                       20, //minutes
                                                                       30 //seconds
                                                                    );

Four parameter, means you're also interested in setting the number of days:

var timespance = new AjaxControlToolkit.TimeSpan(
                                                                       3, //days
                                                                       1, //hour
                                                                       20, //minutes
                                                                       30 //seconds
                                                                    );

Finally, you can also specify the number of milliseconds by using a 5th parameter:

var timespance = new AjaxControlToolkit.TimeSpan(
                                                                       3, //days
                                                                       1, //hour
                                                                       20, //minutes
                                                                       30, //seconds
                                                                       10 //milliseconds
                                                                    );

And yes, you can also simply call the constructor without parameters. This will automatically set the internal tick count to zero. As you'll see by the end of this post, the class has several "static" methods and the Date Object has also been enhanced with some new cool methods that help you in creating new timespans.

After getting a timespan, you have access to several methods that let you get more info on the current interval:

  • getDays: returns the number of days in the current time interval. This is the largest unit you'll get in a timespan object;
  • getHours: returns the number of hours in the timespan;
  • getMinutes: returns the number of minutes in the current interval;
  • getSeconds: surprise! returns the number of seconds in the timespan (yep, I bet you didn't see that one coming! :) );
  • getMilliseconds: gives you the number of milliseconds in the timespan.

for instance, if you use the last constructor I've presented, and run the following code:

$get("info").innerHTML += "ticks: " + timespan.getTicks() +"<br/>";
$get("info").innerHTML += "days: " + timespan.getDays() +"<br/>";
$get("info").innerHTML += "hours: " + timespan.getHours() +"<br/>";
$get("info").innerHTML += "mins: " + timespan.getMinutes() +"<br/>";
$get("info").innerHTML += "secs: " + timespan.getSeconds() +"<br/>";
$get("info").innerHTML += "millisecs: " + timespan.getMilliseconds() +"<br/>";

where info is the ID of a div that exists on the page, you'll get the following:

ticks: 2640300100000
days: 3
hours: 1
mins: 20
secs: 30
millisecs: 10

Simple, right? Well, there's more! You can add and subtract two timespans:

var timespan = new AjaxControlToolkit.TimeSpan(
                                                                  3, //days
                                                                  1, //hour
                                                                  20, //minutes
                                                                  30, //seconds
                                                                  10 //miliseconds
);

var timespan2 = new AjaxControlToolkit.TimeSpan(
                                                                   1, //days
                                                                   1, //hour
                                                                   0, //minutes
                                                                   0, //seconds
                                                                   0 //miliseconds
  );

var subTimespan = timespan.subtract(timespan2)
$get("info").innerHTML += "ticks: " + subTimespan.getTicks() +"<br/>";
$get("info").innerHTML += "days: " + subTimespan.getDays() +"<br/>";
$get("info").innerHTML += "hours: " + subTimespan.getHours() +"<br/>";
$get("info").innerHTML += "mins: " + subTimespan.getMinutes() +"<br/>";
$get("info").innerHTML += "secs: " + subTimespan.getSeconds() +"<br/>";
$get("info").innerHTML += "ticks: " + subTimespan.getMilliseconds() +"<br/>";

Comparing is also possible: you can see if two timespanes are equal by calling (yes, you've guessed :) ) the equals method. There's also a compareTo method which returns an integer (positive means the current timespan is bigger than the one passed as an argument to the method). Finally, you can also use a format method (which you can use to get a custom string that represents the timespan)  and a parse method (which tries to build a timespan object from string). The following method shows how you're supposed to use these methods (btw, pay attention to the string I've used in the format method: i've used that string because that's the format the parse method understands when you want to parse a "complete" timespan - ie, a timespan that has days, hours, minutes, seconds and milliseconds):

var timespan = new AjaxControlToolkit.TimeSpan(
                                                                  3, //days
                                                                  1, //hour
                                                                  20, //minutes
                                                                  30, //seconds
                                                                  10 //miliseconds
);

var str = timespan.format("dd:hh:mm:ss.nnnn");
alert( str );

var timespan2 = AjaxControlToolkit.TimeSpan.parse( str );
alert( timespan2.getHours() );

Even though you can use the constructor to create a new instance of a timespan, the truth is that you'll end up using one of the "static" auxiliary methods introduced by the class: fromTicks, fromHours, fromMinutes, fromSeconds and fromMilliseconds. These methods behave exactly like you'd expect (ex.: fromTicks creates a new instance of a timespan from the number of ticks you send to the method).

As I've said in the beginning of this post, the Date object has been enhanced with several new methods that will give you a timespan. You can get the current time of the day as timespan (getTimeOfDay), get the date (only - ie, stripped off of time) as timespan (getDateOnly), add or subtract a timespan (add/subtract methods) and count the number of ticks in a date (getTicks). Note that these methods have been added to the prototype  property of the Date Object and you can call them on instances of that type!

And that's all for today! I'll come back tomorrow with more info on the toolkit classe!

by luisabreu | with no comments
Filed under:
My book is out!
Tue, Mar 20 2007 13:08

As I've said before, I've written a book (portuguese only) about ASP.NET AJAX. After waiting for a long time, it seems like the book is out! (unfortunately, there's no direct link for a complete page because they keep on insisting on using iframes. if you want to get all the info about it, you'll have to go to their main site and search for the book).

I'd like to thank all that have contributed to the book (specially Joao Cardoso, which did a lot of reviewing and gave me lot of feedback). btw, do keep an eye on the FCA web site because they will be updating the site with the code and errata (yes, unfortunately there are one or two things which changed after I've delivered the final draft).

by luisabreu | 4 comment(s)
Filed under: , ,
Book review: PPK on Javascript
Sun, Mar 18 2007 17:36

During this weekend I've read PPK On Javascript. Petter-Paul Koch, who is responsible for the great quirksmode site, has written a good intro Javascript book. Even though I think some topics deserved better coverage (ex.: debugging - here the author really surprised me by saying that he has no experience with the current debugging  for IE and Firefox), I'd recommend this book to anyone that wants to start using Javascript on web pages. Overall, I'd give it 7/10.

by luisabreu | with no comments
Filed under:
Does having a CS degree matter?
Sun, Mar 18 2007 5:57

Yesterday, I talked with an old friend (that I haven't seen for a while) and we ended up talking about CS degrees. Are they really important? Well, I'd say yes...at least, that's what I would say 5 years ago. Why 5 years ago and not now? Simply because I think Joel is absolutely correct: as he says in this great article, you cannot use Java in CS courses (which seems to be the standard nowadays)! If you want to know why, just read the article and you'll see what I mean.

by luisabreu | 3 comment(s)
Filed under:
Getting along with the _CommonToolkitScripts - part III
Fri, Mar 16 2007 18:04

As we've seen in the last part, the _CommonToolkitScripts class has several utility methods that makes our lifes easier when we're doing "multi-browser" programmer (I'm not sure if this word even exists, but i do like it :) ). Today I'm going to write the last post dedicated to this class and I'll present the remaining methods. In my previous post, I've said that the class can help you if you need to parse units. Currently, the class exposes at least three methods: parseBorderWidth, parsePadding and parseUnit. The last is the most important and it's used by the first two. It simply returns an anonymous object whith two fields: size and type. For instance, if you run the following line:

var unitParsed = $common.parseUnit( "10px" );

You'll get 10 on the unitParsed.size property and px for the unitParsed.type field. Notice that if you send a value that doesn't have a unit, you'll get px on the type field.

Getting and setting the opacity of an element can also be easily accomplished by the getElementOpacity/setElementOpacity methods. Both methods receive the element you're interested in and the set method receives a value between 0 and 1 (where 1 means opaque). The class also lets you add/remove several css classes to/from  a DOM element by calling the addCssClasses and removeCssClasses methods (internaly, it reuses the Sys.UI.DomElement.addCssClass/removeCssClass defined by the main AJAX client file).

The class also exposes a couple of interesting methods related with events: tryFireRawEvent and tryFireEvent. The first receives a reference to the element that is supposed to fire the event and another to the raw event object. Here's a really simple example of how you might use this method:

<script type="text/javascript">
  function h( evt ){
       if( document.createEvent ){
            var templateEvent = $common.__DOMEvents["click"];
            var newEvt = document.createEvent( templateEvent.eventGroup );
            templateEvent.init( newEvt, evt );
            $common.tryFireRawEvent( $get("top"), newEvt);
       }else{
            $common.tryFireRawEvent( $get("top"), evt); 
       }
  }
  function print(){
     $get("info").innerText += "Yey"
  }
</script>
<div id="top" onclick="print()">
      Top div
</div>
<div id="bottom" onclick="h(event)">
     Bottom div
</div>
<div id="info">
</div>

The idea is to add the word yey to the inner text property of the #info div. Note that even if I click over the #bottom div, I'll end up firing the #top div click's event. As always, IE is more forgiving than Firefox. That's why I only need to make a simple method call to generate the event. To make it work with Firefox and Opera, you must create a new event object and fill its property. __DOMEvents helps us with this and it'll automatically copy the props of the current event to the new event which is going to be fired.

You could simplify the previous code by using the second event method I've presented before:

function h( evt ){
  if( document.createEvent ){
     $common.tryFireEvent( $get("top"), "click", evt );
  }else{
     $common.tryFireRawEvent( $get("top"), evt);
  }
}

Since this is the last post about this class, I couldn't end it without talking about the createElementFromTemplate method (note that the class has other methods, but this one really caught my attention :) ). It lets you create and append a new DOM element based on a template you specify. Let's start with a simple example:

<div id="container">
</div>
<script type="text/javascript">
   var table = {};
   $common.createElementFromTemplate(
         {
            nodeName: "div",
            name: "div1",
            properties: {
                 innerHTML: "1",
                 id:"prim"
           }
       },
       $get("container"),
       table );
 $common.createElementFromTemplate(
      {
          nodeName: "div",
          name: "div2",
          properties: {
              innerHTML: "2",
              id:"sec"
         }
     },
     $get("container"),
     table );
</script>

The previous example creates 2 new divs and adds them  to the #container div that already exists on the page. Since I'm passing three parameters and setting the name property on the anonymous object that I'm passing as the 1st parameter, the global table object will point to those objects. There are several things you can do with this method (just take a look at the source code to see the specified properties you can set on the template object that you pass!), but I'm pretty sure that you'll only use it 99% of the time for something like I've shown in the previous example.

by luisabreu | with no comments
Filed under:
Techdays gadget update
Fri, Mar 16 2007 16:19

Today I've done a small change to the Techdays gadget I've talked about here. You can get it here.

by luisabreu | with no comments
Filed under: ,
Bertrand on the $ used on methods on the AJAX client library
Fri, Mar 16 2007 16:01

You can read it here.

by luisabreu | with no comments
Filed under:
Getting serious about sidebar gadgets?
Thu, Mar 15 2007 4:46

Well, then check Jonathan Abbot's blog. It has already saved me the time to code for automatically registering an activex dll :)

by luisabreu | with no comments
Filed under:
GadgetChecker: a proposal for updating gadgets automatically
Wed, Mar 14 2007 11:24

I've been playing with SideBar gadgets for some time now. Besides some quirks (ok, they're really bugs :) ), I've been please with how easy it is to build a gadget and deploy it. One of the things I've been missing though, is an auto-update feature that automatically checks a web site and downloads a new version (when it's available). Since gadgets run at full trust, you can do several interesting things, like accessing the installation folder and making XML HTTP requests that (in theory, this looks like a security hole). After having played for some hours, I've built a small class that tries to do just that: checks a server and downloads a new version if it's available. I've called it GadgetChecker.

Before going on, you should note that I didn't run exaustive tests on it and I'm not giving any support. So, if you decide to use it and find bugs, I don't give you any garantees of fixes. You can, however,  tweek its code and use it as you like. Now that we've got this straight, lets proceed...

The idea is to have a web site (or virtual dir) which has the manifest file with the latest version available and a zip file with the contents of the gadget. GadgetChecker will always compare the version number of the gadget.xml file that exists on the installation folder with the one that you've got online (currently, the online file must be called gadget.xml - to change that nameyou'll have to change the code). If the online version is newer (ie, has a bigger number), it'll download the zip file and copy its contents to the installation folder of the gadget. After doing that, it'll call a callback method (if is is set).

The first thing we need is to get the current version number of the gadget, the url of the online gadget and the url of the possible zip file with the new version:

function loadGadgetXmlFile(){
  var path = System.Gadget.path + "\\gadget.xml"
  var gadgetDoc = null;
  try{
       gadgetDoc = new ActiveXObject( "Msxml2.DOMDocument.3.0" );
       gadgetDoc.load( path );
       var versionNode = gadgetDoc.selectSingleNode( "/gadget/version" );
       _version = versionNode.text;
       var urlNode = gadgetDoc.selectSingleNode( "/gadget/hosts/host[0]/site/@url");
       _url = urlNode.text;
       var fileNode = gadgetDoc.selectSingleNode( "/gadget/hosts/host[0]/site/@file" );
      _urlFile = fileNode.text;
  }
  catch(ex){
     System.Debug.outputString( ex.message );
  }
  finally{
     gadgetDoc = null;
  }
}

As you can see, I'm just using the MS XML DOMDocument to load the xml contained in the manifest. Btw, note that I've added a custom element (called site) to the 1st host element of the gadget file. This element is supposed to have 2 attributes (url and file) which point to the vdir where the manifest and zip file are (the url of the zip file is obtained by concatenating the url and fle attributes).

After getting these values, we must get the online gadget and compare its version with the one we've just got from the installation file manifest:

function _checkForUpdates(){
  var request = new XMLHttpRequest(); 
  request.onreadystatechange = function(){
    if( request && request.readyState == 4 ){   
       if( request.status != 200 ) {
          return;
       }
       var remoteGadgetManifest = request.responseXML;
       if( remoteGadgetManifest ){
          var newVersionNode = remoteGadgetManifest.selectSingleNode( "/gadget/version" );
          var newVersion = newVersionNode.text;
          var existsNewerVersion = _compareVersions( _version, newVersion );
          if( existsNewerVersion ){
              //download newer version
              var newFilePath = _url;
              if( newFilePath.lastIndexOf( "/" ) !== newFilePath.length - 1){
                 newFilePath += "/"
              }
              var urlFile = newFilePath + _urlFile;
              _donwloadFile( urlFile );
          }
       }
    remoteGadgetManifest = null;
   } 
  }//end of callback function
  var remoteGadgetPath = _url;
  if( remoteGadgetPath.lastIndexOf( "/" ) !== remoteGadgetPath.length - 1){
        remoteGadgetPath += "/"
   }
   remoteGadgetPath += "gadget.xml"
   request.open( "GET", remoteGadgetPath, true );
   request.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
   request.send();
}

Several things happen in this method:

  • The gadget is obtained through an async XMLHttpRequest method call;
  • The If-Modified-Since header is used to avoid caching (I didn't try it, but I'm assuming it works);
  • The _compareVersions auxiliary method is responsible for comparing the gadgets' versions;
  • When the online gadget has a newer version that the one we've got from the installation dir, it's time to download the new zip file and copy its contents to the gadget installation folder. That's what the _donwloadFile method does.

Let's take a quick peek at the _compareVersions method. It will only perform a comparison if both versions have the same "version parts" and it'll only return true if the second version is newer:

function _compareVersions( version1, version2 ){
  var parts1 = version1.split( "." );
  var parts2 = version2.split( "." );
  if( parts1.length != parts2.length ){
         return false;
  }
  for( var i = 0; i < parts1.length; i++ ){
      var v1 = parseInt( parts1[ i ] );
      var v2 = parseInt( parts2[ i ] );
     if( v1 < v2 ){
         return true;
     }
  }
  return false;
}

The _downloadFile isn't really difficult, as you can see:

function _donwloadFile( fileName ){
    var requestZip = new XMLHttpRequest();
    requestZip.onreadystatechange = function(){
         if( requestZip && requestZip.readyState == 4 ){
             if( requestZip.status != 200 ){
               return;
            }
            var fileName = System.Gadget.path;
            if( fileName.lastIndexOf( "\\" ) < fileName.length ){
                 fileName += "\\"
            }
            fileName += _urlFile;
           _saveFile( fileName, requestZip.responseBody );
      }
    }
    requestZip.open( "GET", fileName, true );
    requestZip.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
    requestZip.send();
}

The responseBody property is great: it lets you get an array of bytes with the response returned from the server. Saving it to the disk is the tricky part. As the great Eric Lippert said, Binary files and the File System Objects do not mix! Yep, that means that you CANNOT use FSO to save the bytes to disk. Looking at the gadget API, it really looks like you can only read and get items, but can't really create new files. The solution I've used (which, btw, is also presented in the comments of the Eric's post) is to use the ADODB.Stream object:

function _saveFile( fileName, bytes ){
   var fso = null;
   try{
     fso = new ActiveXObject( "ADODB.Stream" ); //garantee that binary data is saved correctly
     fso.Type = 1; //binary
     fso.Open();
     fso.Write( bytes );
     fso.SaveToFile( fileName, 2 );
     //unpack
     _unpackFiles( fileName );
     _deleteFile( fileName );
     if( _cb ){
       _cb();
     }
   }
   catch( ex ){
       System.Debug.outputString( ex.message );
   }
   finally{
      fso = null;
   }
}

Hurray! now, the only thing left is getting the contents of the zip file. As I've said before, the files must be zipped (and not rared, for instance). Why? well, because i can treat zip files like folders, making it get its content a child's play. As you can see from the previous code excerpt, after unpacking the zip contents, the downloaded file is deleted and a callback method (which can be  passed as an argument to the constructor) is called.

The _unpackMethod is simple and it uses the Gadgets API to copy the items to the current gadget installation folder. It'll overwrite all the items (including folders). You do need to garantee that none of the files is "locked" by another process (don't worry about the html file used as the gadget entry point because the sidebar doesn't lock it -at least it didn't in my pc). Here's that method:

function _unpackFiles(fileName){
  var currentFolder = System.Shell.itemFromPath( System.Gadget.path );
  var pathToZip = fileName;
  var folder = System.Shell.itemFromPath( pathToZip );
  for( var i = 0; i < folder.SHFolder.Items.count; i++ ){
     var item = folder.SHFolder.Items.item(i);
     currentFolder.SHFolder.copyHere( item, 16 + 256 + 512 );
  }
}

I guess I could also move the items, instead of copying them. The only thing that you need to know is that the whole process I've described here starts when you create a new GadgetChecker object and call its initialize method.

The demo code has a very simple (almost too simple) gadget that you can use to test the code. To do that, you only need to create or use an existing vdir and, for example, copy the gadget manifest and the updatable.htm file (which is used as the entry point of the gadget) to that folder. Then, change the version number of that manifest file (for instance, set it 1.0.2) and change the message of the html file for something like version 2 (or whatever you want). Then compress both files into a zip file (which you can call test.zip). Note that you'll have to change the url attribute on the gadget.xml file so that it points to the folder url where the gadget file is (another gotcha: using localhost won't work - you need to use the name of the machine if you're hosting the site on the same machine where the gadget is running).

After doing that, install the original gadget (by copying it to the gadgets folrder or changing its extension and double clicking it) and add it to the sidebar. You should get a message saying that a newer version has been downloaded and that you need to restart the sidebar.

Before ending this long post, I must say that I've tried restarting the gadget, but I didn't manage to do it. Calling window.location.reload( true ) resulted in a system crash because the System object went dead while reloading the html page. That's why the simple gadget I've built uses a flyout to alert the user about the new version. Btw, note that you need to exit and restart the gadget in order to get the new version. Closing it and reopening isn't enought to get a refresh.

wow! I may never used, but I can assure you that the 3 hours I've need to build this small class were lots of fun :)

by luisabreu | 6 comment(s)
Filed under:
First rant about VS orcas
Wed, Mar 14 2007 5:34

Yes, we've got better support for Javascript and debugging, but  where's the so much needed auto-format option for JS code? please, please, put it in the final release!

by luisabreu | with no comments
Filed under: ,
Sidebar Gadget docs broken?
Tue, Mar 13 2007 18:37

Today I've just lost 30 mins trying to enumerate the items that exist inside a folder with the Gadget API. Guess what? The docs aren't correct (I'm not sure why, since Gadgets exist for a long time now). Several things I've found today:

  • I couldn't manage to get the System.Shell.Folder.parse method to work;
  • There isn't an Items property on a System.Shell.Folder object
  • The same thing goes for the methods of that class (at least, copyHere and moveHere - I tried these)

Solutions: for the parse method,  I've manage to use the System.Shell.itemFromPath to get a reference to a folder object (I'm still not sure on why the parse method didn't work with me - I'm probably doing something wrong). If you need to get the items that exist inside a folder, you must use the SHFolder property (which the docs don't mention) and only then you'll get references to those elements. Here's a quick example of how you can use this info (the sample shows how to copy items from one folder to another):

var currentFolder = System.Shell.itemFromPath( "some path here" );
var folder = System.Shell.itemFromPath( "some path here" );
for( var i = 0; i < folder.SHFolder.Items.count; i++ ){
   var item = folder.SHFolder.Items.item(i);
   currentFolder.SHFolder.copyHere( item, 16 + 256 + 512 );
}

btw, I've just opened a new thread on the gadget forums. If you know the answer, please add it to the comments or as reply to that thread.

by luisabreu | 2 comment(s)
Filed under:
Book review: Essential Windows Workflow Foundation
Tue, Mar 13 2007 4:38

Let me start by saying that the book's title is wrong: it should be called something like WF bible :) why? well, because it's really the best book I've seen about WF and, in my opinion, it has most of the things (if not all) you need to know when you're building WF apps. I'm not sure if this book will be for everyone. If you want to know what each of the built in activities does, don't buy it...on the other hand, if you really want to understand how WF works, then don't walk, run to the nearest bookstore and buy it (ok, running isn't really needed: you can simply buy it over amazon :) ). I can assure you one thing: it's a GREAT BOOK! It really tells you the story and explains why things work like that.

In fact, I think that it's one of the best essentials book I've read in quite some time now... in fact, i think that the closest match is essential COM, which i've read a long time ago (yes, there are other cool books in the AW essential series, but these 2 are my 2 favourites of all times!)

by luisabreu | with no comments
Filed under:
Getting along with the _CommonToolkitScripts class - part II
Sun, Mar 11 2007 17:13

In part I, we saw how the getLocation/setLocation simplifies getting or setting the position of an HTML element on a page. In this post, I'll talk about several methods which help you getting the size of a specific object. I'll start with getContentSize. This method will return an object which gives you the width and height occupied by the contents of an HTML element. Note that this does not include the borders of the element. As you may be expecting, the content size will depend directly on the type of element, its content and on the setting of the width and height CSS styles.

If you want to get the "full dimensions" of the element (ie, you want to include its border and  padding - still no margins here!), then you can call the getSize method. On the other hand, you might only be interested in getting the bounds of the element. In this case, you'll receive an object with the top-left coords of its top-left corner and its width and height (yep, it's the same as calling getLocation and getBounds). Since I'm talking about bounds, there's also another method which you might find usefull: getClientBounds. It will give you the width and height of the browser window (the cool think about this method is that it'll work with several browsers!)

The getBorderBox method receives an element and returns a peculiar object. It'll let you access the width of the top (top property), left (left), bottom (bottom) and right (right) borders. Note that this property returns the width without the unit. Besides those four properties, you can still access the horizontal and vertical properties: these let you get the total width occupied by the border on the horizontal (sum of left and right) and vertical (sum of top and border). Similarly, there's a getPaddingBox method which returns the an object with info regarding padding.

There are other utility methods, like isBorderVisible (which indicates if the current border is visible or not), getMargin (which calculates the width of a specific margin), getBorderWidth (which returns the width of a specific border) and getPadding (returns padding associated with an element). The _CommonToolkitScripts class can also help you when you need to parse a unit (through it's parsePadding and parseUnit methods).

Lets end this post with a simple example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
 <head>
    <title>Untitled Page</title>
    <script type="text/javascript" src="MicrosoftAjax.debug.js">
    </script> 
    <style type="text/css">
         #test {
          border: solid 2px red;
          margin: 2px;
          width: 200px;
          height: 100px;
          padding: 2px;
       }
   </style>
  </head>
<body>
   <script type="text/javascript" src="common.js">
   </script>
   <div id="test">
       Another simple test
   </div>
   <div id="info">
   </div>
  </body>
<script type="text/javascript">
function getInfo(){
  var contentSize = $common.getContentSize( $get("test") );
  var size = $common.getSize( $get("test") );
  var bounds = $common.getBounds( $get("test") );
  var marginBox = $common.getMarginBox( $get("test") );
  var borderBox = $common.getBorderBox( $get("test") );
  var paddingBox = $common.getPaddingBox( $get("test") );

  $get("info").innerHTML = [
       "content: " + contentSize.width + ":" + contentSize.height,
       "size:" + size.width + ":" + size.height,
       "bounds: " + bounds.x + ":" + bounds.y + ":" + bounds.width + ":" + bounds.height,
       "margin box: " + marginBox.top + ":" + marginBox.left + ":" + marginBox.bottom + ":" + marginBox.right,
       "border box: " + borderBox.top + ":" + borderBox.left + ":" + borderBox.bottom + ":" + borderBox.right + ":" + borderBox.horizontal + ":" + borderBox.vertical,
      "padding box:" + paddingBox.top + ":" + paddingBox.left + ":" + paddingBox.bottom + ":" + paddingBox.right + ":" +   paddingBox.horizontal + ":" + paddingBox.vertical
       ].join( "<br />" );
   }
   getInfo();
</script>
</html>

As you can see if you run this example, those methods are really cool and they will really help you when you need to get info about HTML elements (and remember: they work with several browsers too!).

Now, before you run the previous page, and come back adding some comments on how it doesn't work, you  must take into account that the Toolkit suffers from a "use it from ASP.NET pages only" disease. What? yep, like the AJAX ASP.NET, it also uses resources for customizing the error messages of the client class. Unfortunately, and unlike the AJAX ASP.NET client library, there's no download for js client files that introduce those global error variables (at least, i didn't find it; please tell me I'm wrong!!!). So, if you want to use only the client portion of the toolkit, you'll have to build those classes and its members or you'll have to go through the code and change it so that it doesn't use those objects.

If you know me, you already know that this thing (using resources for localized error messages) doesn't make me happy...

by luisabreu | 1 comment(s)
Filed under:
Getting along with the _CommonToolkitScripts class
Sun, Mar 11 2007 11:08

The _CommonToolkitScripts client class is defined on the common.js file and contains several helpers which perform several important operations that might come handy when you need to write client components that shoudl work in several browsers. The common.js file also creates a new object of this type ($common) so that you don't need to instantiate a component when you need to use the methods exposed by the class. Let's see what we can do with this class...

let's start with something interesting: positioning! Not sure about you, but to me, position is one of those things i never get right at first. Well, I know the basics, but when you start adding iframes, relative and absolute positions, everything gets in a huge mess rather quickly. The AJAX client framework already introduced 2 cool methods which let you get or set the correct position of an HTML element: getLocation/setLocation static methods exposed by the Sys.UI.DomElement helper class. The _CommonToolkit Scripts class extends the getLocation method with aditional code that makes sure you're getting the correct positioning in IE 6 for some specific cases. Note that this method will return a point relative to the the top-left corner of the window and you'll get the current scrolling taken into account. For instance, if you have the following page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head>
      <title>Untitled Page</title>
      <style type="text/css">
          #demo {
              position: relative;
              top: 20px;
              left: 30px;
              border: solid 1px red;
         } 
         #parent{
               height: 100px;
               position: absolute;
               top: 10px;
               left: 10px;
               border: solid 1px blue;
         }
         #top{
            position: absolute;
            top: 10px;
            left: 10px;
        } 
        #info{
             position: absolute;
             top: 300px;
             left: 0px;
         }
     </style>
     <script type="text/javascript" src="MicrosoftAjax.debug.js"></script> 
  </head>
  <body>
        <div id="top">
               <div id="parent">
                    <div id="demo">A simple div used for testing purposes</div>
              </div>
       </div>
      <span id="info" />
   </body>
   <script type="text/javascript" src="Common.js">
   </script>
   <script type="text/javascript">
       function getLocation(){
          var pt1 = Sys.UI.DomElement.getLocation( $get("demo") );
          var pt2 = $common.getLocation( $get("demo") );
          $get("info").innerHTML = [ pt1.x + ":" + pt1.y, pt2.x + ":" + pt2.y].join("<br />");
      }
      getLocation();
    </script>
</html>

Will give you a point with coords (51, 41) which also take the current borders size into account (notice that this is due to the border introduced by the parent div which has its border set; if you remove the border set to element demo, you'll still get the same coords for the top left point).

Besides getting the location of an element, you can also set its location. Do note that this method might not do what you expect. When you use this method, you're setting the element in relation to its relative or absolute parent. For instance, suppose you've got the following:

<div id="top">
  top
  <div id="parent">parent
      <div id="demo">A simple div used for testing purposes</div>
   </div>
</div>

If you call setLocation like this:

$common.setLocation( $get("demo"), new Sys.UI.Point( 10, 20 ) );

what will happen? Well, simple: the demo div will de positioned according to its first absolute or relative positioned parent. So, if you set #parent's positioning to relative or absolute, then that  div will be used as the axis from which the demo div is going to be positioned. Again, do notice that if you call getLocation and the expect to pass the coordinates you've got to a setLocation method call, you will not get the expected results.

In the next post i'll keep presenting the _CommonToolkitScriptMethods and I'll talk about (what I call) its size methods.

by luisabreu | 1 comment(s)
Filed under:
SQL Server Express SP2
Wed, Mar 7 2007 16:15

More info here.

by luisabreu | with no comments
Filed under:
This is what happens when you don't have fair play
Wed, Mar 7 2007 12:52

Valencia isn't really one of my favourite teams, but I must say that after seeing this video, I was really shocked with Inter. That really isn't fair play! I've never seen something like that...In my opinion, Inter's 10 (I think his name is Burdisso) should be banned from football since he was the main responsible for what happened there. Look at what he's doing after the game finished...he started pushing Valencia's players. What did he expect? that they lowered their arms and offered the m the other cheek?

According to Publico, Mancini (Inter's coach) said that Navarro (Valencia's player which broke Burdisso's noise - yeah, he did'nt really behaved well too) is a coward. hello, anybody home???? And I ask: Inter players are what? heroes? btw, I'm really interested in seeing how this is going to end...I want to see if UEFA is going to do the appropriate thing (though i really doubt it!)

by luisabreu | with no comments
Filed under:
More Posts Next page »

Search

This Blog

Tags

Community

Archives

Syndication

Email Notifications

News




  • View Luis Abreu's profile on LinkedIn


    Follow me at Twitter

    My books

    Silverlight 4.0: Curso Completo

    ASP.NET 4.0: Curso Completo

    Portuguese LINQ book cover

    Portuguese ASP.NET 3.5 book cover

    Portuguese ASP.NET AJAX book cover

    Portuguese ASP.NET AJAX book cover