Put your message here! Contact me for more information
 
 







 

Archive for the ‘Web Technolgy’ Category


 

Based on an article on detection connection speed using JavaScript, I implemented a more completed speed detection solution which you can drop into your project with minimal fuss. An example would be to detect the user’s connection speed to show either high or low bandwidth version of the website. All the ingredients you need are an image, a small JavaScript snippet, and some imagination. There is no dependency and no other 3rd party library needed.

The main idea is that we load an image programmatically and measure the time it takes the browser to load such an file. Once an image is done loading, the browser will fire the onLoad event and we can capture the end time. I included two different versions of the code. The first one is quite simple with the image being loaded only once. The second one is more sophisticated, loading the image multiple times and taking the average loading time to arrive at a more accurate answer.

The key technique being used here are closure and passing functions as arguments. These two techniques are very commonly used in modern JavaScript frameworks such as Prototype, Scriptaculous, jQuery, Ext, YUI, etc.

In order for this technique to work, we need to have an image with an average size. Too small of an image won’t give us a very good result while too big of an image will cause more lags and affect the user’s experience. For your convenience, I included the a JPG image around 58.5KB in size as our testing image. We will need the exact size of the image in Bytes in order to calculate the bitrates or byte-rates.

Simple Javascript Speed Detection
Insert this code snippet into the page where you want to conduct the speed test

var SpeedTest = function() {
  /*
  From:  http://techallica.com/kilo-bytes-per-second-vs-kilo-bits-per-second-kbps-vs-kbps/
  256 kbps            31.3 KBps
  384 kbps            46.9 KBps
  512 kbps            62.5 KBps
  768 kbps            93.8 KBps
  1 mbps ~ 1000kbps   122.1 KBps
  */
};
SpeedTest.prototype = {
  imgUrl: "speedtest.jpg"    // Where the image is located at
  ,size: 59917                // bytes
  ,run: function( options ) {

    if( options && options.onStart )
      options.onStart();

    var imgUrl = this.imgUrl + "?r=" + Math.random();
    this.startTime = (new Date()).getTime() ;

    var testImage = new Image();
    var me = this;
    testImage.onload = function() {
      me.endTime = (new Date()).getTime();
      me.runTime = me.endTime - me.startTime;

      if( options && options.onEnd )
        options.onEnd( me.getResults() );
    };
    testImage.src = imgUrl;
  }

  ,getResults: function() {
    if( !this.runTime )
      return null;

    return {
      runTime: this.runTime
      ,Kbps: ( this.size * 8 / 1024 / ( this.runTime / 1000 ) )
      ,KBps: ( this.size / 1024 / ( this.runTime / 1000 ) )
    };
  }
}

First, we need to specify where we would load the image from (in this case, the relative path to the page) and the exact size of that image in Kilobytes. Since we don’t want the browser to cache the image, which short-circuits our test, we append a random number to the original image’s url (line 19). Before we run the test, we check to see if we need to run the onStart() functions being passed in. The idea is that you can show on the page with some messages or pop-up to notify about the test is about to run, simply by passing in an inline function.

The crux of the detection is from line 23 to line 30. If you are new to JavaScript or you are not comfortable with Closure, you should investigate a bit more since Closure is a critical and extremely powerful part of Object-Oriented JavaScript. In its simplest form of explanation, Closure means an anonymous function from a different execution context can gain access to another function’s internal properties.

In this case, after downloading the image, the browser fires the image’s onLoad event at a later time in a different execution context (the context of the current browser) other than context of our SpeedTest object. Without a closure reference via the variable “me” (line 23), which holds the reference to the SpeedTest object, we can no longer access to the startTime and endTime properties of the current SpeedTest object.

Now to run the test and obtain the results, all you have to do is to initialize a new SpeedTest object, execute the run() method, and pass in your custom actions. Currently 2 custom events onStart() and onEnd() are supported:

var st = new SpeedTest();
st.run({
  onStart: function() {
    alert('Before Running Speed Test');
  }

  ,onEnd: function(speed) {
    alert( 'Speed test complete:  ' + speed.Kbps + ' Kbps');
    // put your logic here
    if( speed.Kbps < 200 )
    {
      alert('Your connection is too slow');
    }
  }
});

The Multi-trial speed test

As with any kind of measurements, one single measure is subjected to certain errors and inaccuracy. In our case, the degree of inaccuracy is much higher since connection speed fluctuates constantly. Other factors would affect the single-pass test is the initial DNS look-up speed and overhead, temporal network latency, slow browser, etc. Hence we can further improve our answers by taking multiple tests and average the results to get a more accurate number.

Below is a more sophisticated speed-detection code, which we can specify how many times the test is run. Typically 3 runs is good enough and it will not impact on the user’s experience.

var SpeedTest = function() {
  /*
  From:  http://techallica.com/kilo-bytes-per-second-vs-kilo-bits-per-second-kbps-vs-kbps/
  256 kbps            31.3 KBps
  384 kbps            46.9 KBps
  512 kbps            62.5 KBps
  768 kbps            93.8 KBps
  1 mbps ~ 1000kbps   122.1 KBps
  */
};
SpeedTest.prototype = {
  runCount: 3                 // how many times we want to run the test for
  ,imgUrl: "speedtest.jpg"    // Where the image is located at
  ,size: 59917                // bytes
  ,run: function( options ) {
    this.results = []; // reset the results
    this.callback = ( options && options.onEnd ) ? options.onEnd : null;
    this.runTrial(0, options);
  }

  ,runTrial: function(i, options ) {
    var imgUrl = this.imgUrl + "?r=" + Math.random();
    var me = this;
    var testImage = new Image();
    testImage.onload = function() {
      me.results[i].endTime = ( new Date() ).getTime();
      me.results[i].runTime = me.results[i].endTime - me.results[i].startTime;

      if ( i < me.runCount - 1 )
        me.runTrial( i + 1 ); // run the next trial
      else
      {
        // Execute the callback
        if( me.callback )
          me.callback( me.getResults() );
      }
    };
    this.results[i] = { startTime: ( new Date() ).getTime() };
    testImage.src = imgUrl;
  }

  ,getResults: function() {
    var totalRunTime = 0;
    for( var i = 0; i < this.runCount; i++ )
    {
      if( !this.results || !this.results[i].endTime )
        return null; // exit if we found no endTime.  --> test’s not done yet
      else
        totalRunTime += this.results[i].runTime;
    }

    var avgRunTime = totalRunTime / this.runCount;

    return {
      avgRunTime: avgRunTime
      ,Kbps: ( this.size * 8 / 1024 / ( avgRunTime / 1000 ) )
      ,KBps: ( this.size / 1024 / ( avgRunTime / 1000 ) )
    };
  }
}

Even though our test method has changed, you can still use the same exact code as above to run the speed-test. The answer will be much more accurate this time. Basically we run multiple trials back-to-back, each time measuring the runtime and store it into an array, then average and get the final result.

Another small difference is that we have to store the reference to the user’s custom onEnd() method as a property of the SpeedTest object so that at the end of the test, we can execute it and return the answer to the user.

Final words

I hope you enjoy this article and make good use of the code. I packaged the 2 versions and the image into a zip file so just go ahead and have some fun. Go speed-racer!

Download

view comments
 

I was implementing an Ajax feature for a project and felt the need to display messages (progressing, savings prompt, etc.) to the users visibly and unobtrusively. Usually, to indicate a background activity, I tend to hide the link/button/checkbox and show a spinning indicator when the Ajax request was created, then return the link when the request has completed. In this case, there is no user-controllable submit button. It is a photo album page which allows users to rearrange their photos by simply dragging-and-dropping the photos into the correct positions. The positions are saved automatically on the fly. If I am to implement a typical spinning indicator, I have to make sure that it is visible enough for the user to know that the positions are being saved. However, if the user is at mid-page, any indicator at the top section of the page won’t be visible. If I place the indicator at the bottom of the page, I still run into the same situation. My solution is to implement a smart “Prompt” bar that stays affixed to the screen. With Prototype and Scriptaculous, implementation is a breeze.

Show, don’t just tell
Here is a demo which you can try out.

Details
This is made possible by using the “position:fixed” feature of CSS. According to W3Schools,

position: fixed
An element with position: fixed is positioned at the specified coordinates relative to the browser window. The element’s position is specified with the “left”, “top”, “right”, and “bottom” properties. The element remains at that position regardless of scrolling. Works in IE7 (strict mode)

The markup
Here is the HTML for the prompt bar. You can place this HTML anywhere in the page. Ideally it should be outside any container.

Some Prompt Here
Cross

CSS

#prompt
{ position: fixed; width: 400px; height: 20px; border: 1px solid #7F7F7F;z-index: 9999; background-color: #F3FF8F; }

#prompt_message
{ float: left; text-align: center; width: 380px; font-weight: bold;}
#prompt_close
{ float: right; width:16px;}

We have to set the prompt bar’s position to be fixed so that it will stay in place when we scroll the browser window.

The script
The javascript code is fairly simple, since Prototype and Scriptaculous already do the all the heavy lifting (e.g. positioning, animation). The only catch with position:fixed div is that we cannot center the prompt div using CSS. We have to explicitly set the left coordinate before we display the bar (line 16, _center() method). This is to also useful when the user resizes the browser window, we recalculate the position and center the div accordingly.

    var Prompt = {
      autoHideTime: 5000

      ,display: function( msg, options ) {
        $('prompt_message').innerHTML = msg;
        Prompt.show();
        if( options && options.autoHide )
          setTimeout('Prompt.hide()', options.autoHideTime || Prompt.autoHideTime );
      }

      ,working: function(msg, options) {
        msg = '  ’ + msg;
        Prompt.display( msg, options );
      }

      , _center: function(){
        $(’prompt’).style.left = ( ( document.viewport.getWidth() - parseInt( $(’prompt’).offsetWidth ) ) / 2 ) + ‘px’ ;
      }

      ,show: function(){
        Prompt._center();
        new Effect.Move ($(’prompt’),{ x: parseInt( $(’prompt’).style.left ), y: 0, mode: ‘absolute’, duration: 0.2 });
      }
      ,hide: function( msg) {
        new Effect.Move ($(’prompt’),{ x: parseInt( $(’prompt’).style.left ), y: -30, mode: ‘absolute’, duration: 0.1 });
      }
    }

Since the code is simple enough, I’d like to leave the details to you to work out.

Enjoy!

Download

I packaged the demo as a zip file so you can grab it and have some fun. Cheers!

view comments
 

I was hacking the acts_as_taggable_on_steroids plugin to support different categories for tags and taggings and I bumped right into the issue of how to have dynamic conditions for the has_many association. The problem with associations in general is that they are CLASS METHODs, e.g. they are run when the class declaration is being loaded into memory at runtime. However, our condition has to be evaluated at evaluation time. This means if we write (psuedo-ruby code lifted from my hack on the the acts_as_taggable_on_steroids Tag model)
class Tag
has_many :taggings, :conditions => “taggings.category_id= #{self.category_id}”
end

this will very well error out on us.  Since, at class loading time, Ruby has no idea about the particular category_id attribute of the “self” instance of the Tag object. On his blog, Dweedb proposed a solution of creating a new souped-up has_many association, e.g. has_many_with_args. I wasn’t convinced about this solution since it means I may very well end up with writing/extending every association within Rails with something else if I need to support some special dynamic conditions.

I was playing with lambda function (e.g. :conditions => Proc.new { “some_clever_dynamic_conditions_here”} ) but for some reasons I kept getting the “calling private gsub method” error for the Proc when ActiveRecord was interpolating (constructing?) the SQL.  Nonetheless, I got an important hint from the Dweedb’s post about the use of single quote on the :conditions to keep the it as string literal and delay the evaluation of the conditions till, well, evaluation time. “Excelente!”, as my friend Javier would shout in Espanol!

The solution is made possible by the use of the native Ruby #send(:action, *args) method to read the attribute of the model instance. Since if we wrap the conditions around the single quotes, when the model class is being loaded to the Ruby VM, everything still remains as-is. Only at runtime, when ActiveRecord is busy substituting the conditions string into the generated SQL statement, it inadvertently executes the code inside, which then invokes the send() method on the current instance to return the value of the attribute (You know what just popped into my mind? HOLY SMOKE! IT IS CODE-INJECTION through SQL-INJECTION!)

The updated code for the new Tag model is

has_many :taggings, :conditions => ‘#{Tagging.table_name}.category_id = #{self.send(:category_id)}’

Anyway, I was able to get all the tags and their associated taggins from the same category, dynamically and hacklessly. With this approach, any conditions for the various associations can be dynamicalized using single quotes and the (god)send method. Hurray!

view comments
 

AutoHotKeyI guess my relationship with the Lenovo 3000 N100 laptop is a complicated one: as much as I hated the Home/End/Page up/Page down combo madness, I truely missed that 2 extra buttons that my Dell D830 doesn’t have. Now instead of just flicking my finger to hit Home or End, I have to move my entire arm to reach for either End or Home. Even though now I won’t make any mistakes with Home/End jumping as with the Lenovo, I have to slow down the typing with the Dell…. NO! Only if Dell put the 2 extra home/end button next to their Arrow buttons… Why can’t we just live happily with each other? Is it really that hard to design a good working keyboard, or is it me that just being picky about laptop keyboard?

“Good news everyone…”, as the Professor Farnsworth would say in his quite-mischevious voice, I have the fix for my crave for the Home/End buttons. AutoHotKey allows me to map custom shortcut to the keyboard, especially the Windows key. I mostly use the Windows key as the … boss key to quickly hide my stuff on the screen (Window + D), or run command (Window + R), or start Explorer (Window + E), even though these days I prefer xplorer2. With AutoHotKey, I was able to map Window + A to the Home Button, and Window + S to the End button, saving me from wiggling my arm around.

So if you are not satisfied with your laptop keyboard, give AutoHotKey a try. Maybe it will save you some aggravation.

Here is the small script to remap your Home and End button: AutoHotKey.ahk

view comments
 

PrototypeJs LogoThe recent release of Prototype 1.60RC1 inadvertently breaks the drag and drop effect of Scriptaculous in IE6 (and 7). I don’t know about the exact cause of the error but according to sources around the internet, it’s because Prototype always returns false for the leftClick event in IE6. Luckily, the trunk version of Prototype has this bug patched (since revision 7954).

To fix the draggable, just patch your prototype.js file, replacing the code block “Event.method” around line 3686 with the newer code block from “event.js” in the prototype main trunk.

For your convenience, here is the patched prototype.js (release 1.60RC1) with fix from Event.js (rev 8037)

  • Download: the patched prototype.js for Scriptaculous Draggable
view comments