Category Archives: Javascript

Ext.util.SpriteManager

Original post date May 1st, 2007
One of the ways of improving web page performance (especially in RIA – Rich Internet Applications) where there are lots of small images that need to be displayed, e.g. on a toolbar, is to create a compound image and then use css to select a section of the image to display. In the javascript world this is known as using sprites. In the world of C#/vb/Delphi this is well known and there have always been ImageList components that store images in this form.

So instead of creating single images we create a composite image.

To make the process of assigning the correct css style to select on of the images to display, I have created a SpriteManager addition to Ext JS as follows:

Ext.namespace('Ext', 'Ext.util');
 
Ext.util.SpriteManager = function(config){
        Ext.apply(this,config,{
                spriteSource:true,
                spriteWidth:1,
                spriteHeight:1,
                spriteColumns:1,
                spriteRows:1
                });
};
 
 
Ext.util.SpriteManager.prototype = {
 
    /**
     * Gets the CSS Style for the appropriate image
     * @param {Object...} spriteLocation Expects an object with an X and Y location of the sprite to display
     * @return {String} returns the CSS style string enclosed in braces {}
     */
    getSpriteCSS : function(spriteLocation){
            var locationX = spriteLocation.x-1 || 0;
            var locationY = spriteLocation.y-1 || 0;
            var spriteCss = ['{'];
            var spriteW = this.spriteWidth / this.spriteColumns;
            var spriteH = this.spriteHeight / this.spriteRows;
            var spriteXPos = locationX * spriteW;
            var spriteYPos = locationY * spriteH;
 
            if(!typeof this.spriteSource =="boolean")
            {            
                spriteCss.push('background:url(' + this.spriteSource +');');
            }
            spriteCss.push('height:' + spriteH + 'px;');
            spriteCss.push('width:' + spriteW + 'px;');
            spriteCss.push('background-position:-' + spriteXPos + 'px -' + spriteYPos + 'px;');
            spriteCss.push('}');
            return spriteCss.join("");
    },
 
    /**
     * Applies the CSS Style for the appropriate sprite image to an element
     * @param {element} Ext.Element to apply the sprite style to.
     * @param {Object...} spriteLocation Expects an object with an X and Y location of the sprite to display
     */
        applySpriteCss : function(el, spriteLocation)
        {
            var css = this.getSpriteCSS(spriteLocation);
            el.applyStyles(css);
        }
};

Here is an example of the usage:

<script type="text/javascript">
var spriteManager = new Ext.util.SpriteManager({
            spriteSource:"sprites.png",
            spriteWidth:64,
            spriteHeight:34,
            spriteColumns:4,
            spriteRows:2
            });
</script>
 
...
 
<img id="test" src="images/default/s.gif" /> 
<a href="javascript:spriteManager.applySpriteCss(Ext.get('test'),{x:1,y:1});">Image 1,1</a>
<a href="javascript:spriteManager.applySpriteCss(Ext.get('test'),{x:2,y:1});">Image 2,1</a>
<a href="javascript:spriteManager.applySpriteCss(Ext.get('test'),{x:1,y:2});">Image 1,2</a>
<a href="javascript:spriteManager.applySpriteCss(Ext.get('test'),{x:2,y:2});">Image 2,2</a>

Ext.Format extension for thousands separator formatting

A simple function, the basis of was in the public domain (see comments) to format a number with thousand separators.

Ext.apply(Ext.util.Format,{
    decimalSeparator : '.',
    thousandSeparator : ',',
    /* Adapted from http://www.mredkj.com/javascript/nfbasic.html, 
     * Public Domain, without copyright, and can be used without restriction: 
     * see http://www.mredkj.com/legal.html
     */    
 
    asThousands : function(value)
    {
        value = parseInt(value,10) + '';
        var x = value.split(this.decimalSeparator);
        var x1 = x[0];
        var x2 = x.length > 1 ? this.decimalSeparator + x[1] : '';
        var rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + this.thousandSeparator + '$2');
        }
        return x1 + x2;
    }
});

101 of Memory Leak causes in JavaScript

Really nice article explaining some of the gotcha’s that can cause memory leaks in JavaScript

http://www-128.ibm.com/developerworks/web/library/wa-memleak/

Javascript – Rails like pluralize function

Found this on scripts.dzone.com blogged for reference.

*
* This script depends on no outside libraries.
*/
 
Inflector = {
/*
     * The order of all these lists has been reversed from the way 
     * ActiveSupport had them to keep the correct priority.
     */
    Inflections: {
        plural: [
            [/(quiz)$/i,               "$1zes"  ],
            [/^(ox)$/i,                "$1en"   ],
            [/([m|l])ouse$/i,          "$1ice"  ],
            [/(matr|vert|ind)ix|ex$/i, "$1ices" ],
            [/(x|ch|ss|sh)$/i,         "$1es"   ],
            [/([^aeiouy]|qu)y$/i,      "$1ies"  ],
            [/(hive)$/i,               "$1s"    ],
            [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
            [/sis$/i,                  "ses"    ],
            [/([ti])um$/i,             "$1a"    ],
            [/(buffal|tomat)o$/i,      "$1oes"  ],
            [/(bu)s$/i,                "$1ses"  ],
            [/(alias|status)$/i,       "$1es"   ],
            [/(octop|vir)us$/i,        "$1i"    ],
            [/(ax|test)is$/i,          "$1es"   ],
            [/s$/i,                    "s"      ],
            [/$/,                      "s"      ]
        ],
        singular: [
            [/(quiz)zes$/i,                                                    "$1"     ],
            [/(matr)ices$/i,                                                   "$1ix"   ],
            [/(vert|ind)ices$/i,                                               "$1ex"   ],
            [/^(ox)en/i,                                                       "$1"     ],
            [/(alias|status)es$/i,                                             "$1"     ],
            [/(octop|vir)i$/i,                                                 "$1us"   ],
            [/(cris|ax|test)es$/i,                                             "$1is"   ],
            [/(shoe)s$/i,                                                      "$1"     ],
            [/(o)es$/i,                                                        "$1"     ],
            [/(bus)es$/i,                                                      "$1"     ],
            [/([m|l])ice$/i,                                                   "$1ouse" ],
            [/(x|ch|ss|sh)es$/i,                                               "$1"     ],
            [/(m)ovies$/i,                                                     "$1ovie" ],
            [/(s)eries$/i,                                                     "$1eries"],
            [/([^aeiouy]|qu)ies$/i,                                            "$1y"    ],
            [/([lr])ves$/i,                                                    "$1f"    ],
            [/(tive)s$/i,                                                      "$1"     ],
            [/(hive)s$/i,                                                      "$1"     ],
            [/([^f])ves$/i,                                                    "$1fe"   ],
            [/(^analy)ses$/i,                                                  "$1sis"  ],
            [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
            [/([ti])a$/i,                                                      "$1um"   ],
            [/(n)ews$/i,                                                       "$1ews"  ],
            [/s$/i,                                                            ""       ]
        ],
        irregular: [
            ['move',   'moves'   ],
            ['sex',    'sexes'   ],
            ['child',  'children'],
            ['man',    'men'     ],
            ['person', 'people'  ]
        ],
        uncountable: [
            "sheep",
            "fish",
            "series",
            "species",
            "money",
            "rice",
            "information",
            "equipment"
        ]
    },
    ordinalize: function(number) {
        if (11 &lt;= parseInt(number) % 100 &amp;&amp; parseInt(number) % 100 &lt;= 13) {
            return number + "th";
        } else {
            switch (parseInt(number) % 10) {
                case  1: return number + "st";
                case  2: return number + "nd";
                case  3: return number + "rd";
                default: return number + "th";
            }
        }
    },
    pluralize: function(word) {
        for (var i = 0; i &lt; Inflector.Inflections.uncountable.length; i++) {
            var uncountable = Inflector.Inflections.uncountable[i];
            if (word.toLowerCase() == uncountable) {
                return uncountable;
            }
        }
        for (var i = 0; i &lt; Inflector.Inflections.irregular.length; i++) {
            var singular = Inflector.Inflections.irregular[i][0];
            var plural   = Inflector.Inflections.irregular[i][1];
            if ((word.toLowerCase() == singular) || (word == plural)) {
                return plural;
            }
        }
        for (var i = 0; i &lt; Inflector.Inflections.plural.length; i++) {
            var regex          = Inflector.Inflections.plural[i][0];
            var replace_string = Inflector.Inflections.plural[i][1];
            if (regex.test(word)) {
                return word.replace(regex, replace_string);
            }
        }
    }
}
 
function ordinalize(number) {
    return Inflector.ordinalize(number);
}
 
/*
 * Javascript doesnt have optional parameters or overloading so I had to use
 * the ever popular pseudo options hash object technique.
 * required properties:
 *     count    Number of objects to pluralize
 *     singular Singular noun for the objects
 * optional property:
 *     plural   Plural (probably irregular) noun for the objects
 * examples:
 *      pluralize({ count: total_count, singular: "Issue" })
 *      pluralize({ count: total_count, singular: "Goose", plural: "Geese" })
 */
function pluralize(options) {
    return options.count + " " + (1 == parseInt(options.count) ?
        options.singular :
        options.plural || Inflector.pluralize(options.singular));
}

JQuery and human readable email addresses

I’ve just started playing with the power that is Jquery and came up with this nice use for the Ajax functionality in Jquery.

You want to put your email address on your web site so people can contact you, but don’t want to put a mailto: anchor in the html, because it will be spidered and added to some morons Spam list.

Here is a simple and server independent solution:

Firstly, create a file on your site, it doesn’t have to have a html extension, e.g. email.me
In the content of the file, add the html snippet that you would like to display to the user as your email address (usually an anchor tag with a mailto: href). This is the html that will be inserted into your web page when the user clicks on the Show email address text.

e.g.

<a href="mailto:someone@somewhere">someone@somewhere</a>

In your web pages, where you wish someone to be able to see your email address by clicking, enter a span with a class of email.

<span class="email">Click to see email</span>

in the ready function for your document enter the following:

$('.email').click(function(el){
    $(this).hide().load('email.me').fadeIn(1000);
});

This will add a click function to all elements assigned the email class, that when clicked, will replace the current content with the content of the email.me file (faded in :) ). In this case your email mailto anchor. This principle can be used to display any content. Maybe you want to hide the comment form for your blog, until a display has been clicked, give it a try.

JQuery and human readable email addresses

Ive just started playing with the power that is Jquery and came up with this nice use for the Ajax functionality in Jquery. You want to put your email address on your web site so people can contact you, but don’t want to put a mailto: anchor in the html, because it will be spidered and added to some morons Spam list. Here is a simple and server independent solution: Firstly, create a file on your site, it doesn’t have to have a html extension, e.g. email.me In the content of the file, add the html snippet that you would like to display to the user as your email address (usually an anchor tag with a mailto: href). This is the html that will be inserted into your web page when the user clicks on the Show email address text. e.g.
<a href="mailto:someone@somewhere">someone@somewhere</a>
In your web pages, where you wish someone to be able to see your email address by clicking, enter a span with a class of email.
<span class="email">Click to see email</span>
in the ready function for your document enter the following:
$('.email').click(function(el){
    $(this).hide().load('email.me').fadeIn(1000);
});
This will add a click function to all elements assigned the email class, that when clicked, will replace the current content with the content of the email.me file (faded in ). In this case your email mailto anchor. This principle can be used to display any content. Maybe you want to hide the comment form for your blog, until a display has been clicked, give it a try.
Demo:

Javascript Namespaces

One of the big problems as the number of toolkits that you can use in your RIA increases is variable collision.  Constructing your classes and widgets in the form of a namespace reduces the risk that the global variables you are using will have been used by someone else.  The following code demonstrates a pattern for defining a namespace utilizing objects to create an object hierarchy for your namespace.
var MyNamespace = {};
 MyNamespace.test = function(){
         alert("MyNamespace.test");
 };

 MyNamespace.common = {};
 MyNamespace.common.test = function(){
         alert("MyNamespace.common.test");
 };

 MyNamespace.utilities = {};
 MyNamespace.utilities.test = function(){
         alert("MyNamespace.utilities.test");
 };

 MyNamespace.common.dataaccess = {};
 MyNamespace.common.dataaccess.test = function(){
    alert("MyNamespace.common.dataaccess.test");
 };
Notice that before the namespace can be assigned variables and functions, it needs to be initialized to an empty object. This allows you to make calls like:
MyNamespace.common.dataaccess.test();
However, it is important that you only initialize the name space object once, and it is important that the full path of namespace objects be declared before you try and assign functions and properties to them. If you want to separate your name space implementation across separate files, then you really need to check that all the stages of the namespace are assigned and create them if not. e.g.
 if(!MyNamespace) MyNameSpace = {};
 if(!MyNamespace.common ) MyNameSpace.common = {};
 if(!MyNamespace.common.dataaccess) MyNameSpace.common.dataaccess = {};
You can imagine that if your namespaces get quite deep, you start having quite a lot of code at the start of each of your Javascript files just to ensure the namespace objects are created.  There is however a very nice solution in the Ext library which helps you with this: Ext.namespace() – this takes a variable list of namespace names and creates the objects for you if they don’t exist. e.g.
 Ext.namespace("MyNamespaces","MyNamespace.common","MyNamespace.common.dataaccess");

Creating a JavaScript Singleton object

If we want to create a singleton object, i.e. there can only every be one instance of it that is referenced by a global variable, in JavaScript it is very easy.  We simply declare the class function inline, returning a new object with the public methods and properties on, directly followed by () parenthesis and assign it to our global variable. If you want to create private methods and functions then you should be able to see the similarities of this and the JavaScript Class Patterns article.
var MySingleton = function(){
    return {
        // variables and methods 
         id : 1,
  
         test : function()
         {
             return this.id;
         }
     }
 }();