The source code

    /*!
     * Ext JS Library 3.4.0
     * Copyright(c) 2006-2011 Sencha Inc.
     * licensing@sencha.com
     * http://www.sencha.com/license
     */
    /**
     * @class Ext
     */

    Ext.ns("Ext.grid", "Ext.list", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu",
           "Ext.state", "Ext.layout.boxOverflow", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct", "Ext.slider");
        /**
         * Namespace alloted for extensions to the framework.
         * @property ux
         * @type Object
         */

    Ext.apply(Ext, function(){
        var E = Ext,
            idSeed = 0,
            scrollWidth = null;

        return {
            /**
            * A reusable empty function
            * @property
            * @type Function
            */
            emptyFn : function(){},

            /**
             * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
             * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server.
             * For other browsers it uses an inline data URL.
             * @type String
             */
            BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 || Ext.isAir ?
                                'http:/' + '/www.extjs.com/s.gif' :
                                'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',

            extendX : function(supr, fn){
                return Ext.extend(supr, fn(supr.prototype));
            },

            /**
             * Returns the current HTML document object as an {@link Ext.Element}.
             * @return Ext.Element The document
             */
            getDoc : function(){
                return Ext.get(document);
            },

            /**
             * Utility method for validating that a value is numeric, returning the specified default value if it is not.
             * @param {Mixed} value Should be a number, but any type will be handled appropriately
             * @param {Number} defaultValue The value to return if the original value is non-numeric
             * @return {Number} Value, if numeric, else defaultValue
             */
            num : function(v, defaultValue){
                v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && v.trim().length == 0) ? NaN : v);
                return isNaN(v) ? defaultValue : v;
            },

            /**
             * Utility method for returning a default value if the passed value is empty.
             * The value is deemed to be empty if it is
             * null
             * undefined
             * an empty array
             * a zero length string (Unless the allowBlank parameter is true)
             *
             * @param {Mixed} value The value to test
             * @param {Mixed} defaultValue The value to return if the original value is empty
             * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
             * @return {Mixed} value, if non-empty, else defaultValue
             */
            value : function(v, defaultValue, allowBlank){
                return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
            },

            /**
             * Escapes the passed string for use in a regular expression
             * @param {String} str
             * @return {String}
             */
            escapeRe : function(s) {
                return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
            },

            sequence : function(o, name, fn, scope){
                o[name] = o[name].createSequence(fn, scope);
            },

            /**
             * Applies event listeners to elements by selectors when the document is ready.
             * The event name is specified with an @ suffix.
             *
    Ext.addBehaviors({
        // add a listener for click on all anchors in element with id foo
        '#foo a@click' : function(e, t){
            // do something
        },

        // add the same listener to multiple selectors (separated by comma BEFORE the @)
        '#foo a, #bar span.some-class@mouseover' : function(){
            // do something
        }
    });
             *
             * @param {Object} obj The list of behaviors to apply
             */
            addBehaviors : function(o){
                if(!Ext.isReady){
                    Ext.onReady(function(){
                        Ext.addBehaviors(o);
                    });
                } else {
                    var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
                        parts,
                        b,
                        s;
                    for (b in o) {
                        if ((parts = b.split('@'))[1]) { // for Object prototype breakers
                            s = parts[0];
                            if(!cache[s]){
                                cache[s] = Ext.select(s);
                            }
                            cache[s].on(parts[1], o[b]);
                        }
                    }
                    cache = null;
                }
            },

            /**
             * Utility method for getting the width of the browser scrollbar. This can differ depending on
             * operating system settings, such as the theme or font size.
             * @param {Boolean} force (optional) true to force a recalculation of the value.
             * @return {Number} The width of the scrollbar.
             */
            getScrollBarWidth: function(force){
                if(!Ext.isReady){
                    return 0;
                }

                if(force === true || scrollWidth === null){
                        // Append our div, do our calculation and then remove it
                    var div = Ext.getBody().createChild(''),
                        child = div.child('div', true);
                    var w1 = child.offsetWidth;
                    div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
                    var w2 = child.offsetWidth;
                    div.remove();
                    // Need to add 2 to ensure we leave enough space
                    scrollWidth = w1 - w2 + 2;
                }
                return scrollWidth;
            },

            // deprecated
            combine : function(){
                var as = arguments, l = as.length, r = [];
                for(var i = 0; i < l; i++){
                    var a = as[i];
                    if(Ext.isArray(a)){
                        r = r.concat(a);
                    }else if(a.length !== undefined && !a.substr){
                        r = r.concat(Array.prototype.slice.call(a, 0));
                    }else{
                        r.push(a);
                    }
                }
                return r;
            },

            /**
             * Copies a set of named properties fom the source object to the destination object.
             * example:
    ImageComponent = Ext.extend(Ext.BoxComponent, {
        initComponent: function() {
            this.autoEl = { tag: 'img' };
            MyComponent.superclass.initComponent.apply(this, arguments);
            this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
        }
    });
             *
             * @param {Object} dest The destination object.
             * @param {Object} source The source object.
             * @param {Array/String} names Either an Array of property names, or a comma-delimited list
             * of property names to copy.
             * @return {Object} The modified object.
            */
            copyTo : function(dest, source, names){
                if(typeof names == 'string'){
                    names = names.split(/[,;\s]/);
                }
                Ext.each(names, function(name){
                    if(source.hasOwnProperty(name)){
                        dest[name] = source[name];
                    }
                }, this);
                return dest;
            },

            /**
             * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
             * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
             * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
             * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
             * passed into this function in a single call as separate arguments.
             * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
             * @param {Mixed} arg2 (optional)
             * @param {Mixed} etc... (optional)
             */
            destroy : function(){
                Ext.each(arguments, function(arg){
                    if(arg){
                        if(Ext.isArray(arg)){
                            this.destroy.apply(this, arg);
                        }else if(typeof arg.destroy == 'function'){
                            arg.destroy();
                        }else if(arg.dom){
                            arg.remove();
                        }
                    }
                }, this);
            },

            /**
             * Attempts to destroy and then remove a set of named properties of the passed object.
             * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
             * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
             * @param {Mixed} etc... More property names to destroy and remove.
             */
            destroyMembers : function(o, arg1, arg2, etc){
                for(var i = 1, a = arguments, len = a.length; i < len; i++) {
                    Ext.destroy(o[a[i]]);
                    delete o[a[i]];
                }
            },

            /**
             * Creates a copy of the passed Array with falsy values removed.
             * @param {Array/NodeList} arr The Array from which to remove falsy values.
             * @return {Array} The new, compressed Array.
             */
            clean : function(arr){
                var ret = [];
                Ext.each(arr, function(v){
                    if(!!v){
                        ret.push(v);
                    }
                });
                return ret;
            },

            /**
             * Creates a copy of the passed Array, filtered to contain only unique values.
             * @param {Array} arr The Array to filter
             * @return {Array} The new Array containing unique values.
             */
            unique : function(arr){
                var ret = [],
                    collect = {};

                Ext.each(arr, function(v) {
                    if(!collect[v]){
                        ret.push(v);
                    }
                    collect[v] = true;
                });
                return ret;
            },

            /**
             * Recursively flattens into 1-d Array. Injects Arrays inline.
             * @param {Array} arr The array to flatten
             * @return {Array} The new, flattened array.
             */
            flatten : function(arr){
                var worker = [];
                function rFlatten(a) {
                    Ext.each(a, function(v) {
                        if(Ext.isArray(v)){
                            rFlatten(v);
                        }else{
                            worker.push(v);
                        }
                    });
                    return worker;
                }
                return rFlatten(arr);
            },

            /**
             * Returns the minimum value in the Array.
             * @param {Array|NodeList} arr The Array from which to select the minimum value.
             * @param {Function} comp (optional) a function to perform the comparision which determines minimization.
             *                   If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
             * @return {Object} The minimum value in the Array.
             */
            min : function(arr, comp){
                var ret = arr[0];
                comp = comp || function(a,b){ return a < b ? -1 : 1; };
                Ext.each(arr, function(v) {
                    ret = comp(ret, v) == -1 ? ret : v;
                });
                return ret;
            },

            /**
             * Returns the maximum value in the Array
             * @param {Array|NodeList} arr The Array from which to select the maximum value.
             * @param {Function} comp (optional) a function to perform the comparision which determines maximization.
             *                   If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
             * @return {Object} The maximum value in the Array.
             */
            max : function(arr, comp){
                var ret = arr[0];
                comp = comp || function(a,b){ return a > b ? 1 : -1; };
                Ext.each(arr, function(v) {
                    ret = comp(ret, v) == 1 ? ret : v;
                });
                return ret;
            },

            /**
             * Calculates the mean of the Array
             * @param {Array} arr The Array to calculate the mean value of.
             * @return {Number} The mean.
             */
            mean : function(arr){
               return arr.length > 0 ? Ext.sum(arr) / arr.length : undefined;
            },

            /**
             * Calculates the sum of the Array
             * @param {Array} arr The Array to calculate the sum value of.
             * @return {Number} The sum.
             */
            sum : function(arr){
               var ret = 0;
               Ext.each(arr, function(v) {
                   ret += v;
               });
               return ret;
            },

            /**
             * Partitions the set into two sets: a true set and a false set.
             * Example:
             * Example2:
             *
    // Example 1:
    Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]

    // Example 2:
    Ext.partition(
        Ext.query("p"),
        function(val){
            return val.className == "class1"
        }
    );
    // true are those paragraph elements with a className of "class1",
    // false set are those that do not have that className.
             *
             * @param {Array|NodeList} arr The array to partition
             * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
             *                   itself must be able to be evaluated for its truthfulness.
             * @return {Array} [true,false]
             */
            partition : function(arr, truth){
                var ret = [[],[]];
                Ext.each(arr, function(v, i, a) {
                    ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
                });
                return ret;
            },

            /**
             * Invokes a method on each item in an Array.
             *
    // Example:
    Ext.invoke(Ext.query("p"), "getAttribute", "id");
    // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
             *
             * @param {Array|NodeList} arr The Array of items to invoke the method on.
             * @param {String} methodName The method name to invoke.
             * @param {...*} args Arguments to send into the method invocation.
             * @return {Array} The results of invoking the method on each item in the array.
             */
            invoke : function(arr, methodName){
                var ret = [],
                    args = Array.prototype.slice.call(arguments, 2);
                Ext.each(arr, function(v,i) {
                    if (v && typeof v[methodName] == 'function') {
                        ret.push(v[methodName].apply(v, args));
                    } else {
                        ret.push(undefined);
                    }
                });
                return ret;
            },

            /**
             * Plucks the value of a property from each item in the Array
             *
    // Example:
    Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
             *
             * @param {Array|NodeList} arr The Array of items to pluck the value from.
             * @param {String} prop The property name to pluck from each element.
             * @return {Array} The value from each item in the Array.
             */
            pluck : function(arr, prop){
                var ret = [];
                Ext.each(arr, function(v) {
                    ret.push( v[prop] );
                });
                return ret;
            },

            /**
             * Zips N sets together.
             *
    // Example 1:
    Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
    // Example 2:
    Ext.zip(
        [ "+", "-", "+"],
        [  12,  10,  22],
        [  43,  15,  96],
        function(a, b, c){
            return "$" + a + "" + b + "." + c
        }
    ); // ["$+12.43", "$-10.15", "$+22.96"]
             *
             * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
             * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
             * @return {Array} The zipped set.
             */
            zip : function(){
                var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
                    arrs = parts[0],
                    fn = parts[1][0],
                    len = Ext.max(Ext.pluck(arrs, "length")),
                    ret = [];

                for (var i = 0; i < len; i++) {
                    ret[i] = [];
                    if(fn){
                        ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
                    }else{
                        for (var j = 0, aLen = arrs.length; j < aLen; j++){
                            ret[i].push( arrs[j][i] );
                        }
                    }
                }
                return ret;
            },

            /**
             * This is shorthand reference to {@link Ext.ComponentMgr#get}.
             * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
             * @param {String} id The component {@link Ext.Component#id id}
             * @return Ext.Component The Component, undefined if not found, or null if a
             * Class was found.
            */
            getCmp : function(id){
                return Ext.ComponentMgr.get(id);
            },

            /**
             * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
             * you may want to set this to true.
             * @type Boolean
             */
            useShims: E.isIE6 || (E.isMac && E.isGecko2),

            // inpired by a similar function in mootools library
            /**
             * Returns the type of object that is passed in. If the object passed in is null or undefined it
             * return false otherwise it returns one of the following values:
             * string: If the object passed is a string
             * number: If the object passed is a number
             * boolean: If the object passed is a boolean value
             * date: If the object passed is a Date object
             * function: If the object passed is a function reference
             * object: If the object passed is an object
             * array: If the object passed is an array
             * regexp: If the object passed is a regular expression
             * element: If the object passed is a DOM Element
             * nodelist: If the object passed is a DOM NodeList
             * textnode: If the object passed is a DOM text node and contains something other than whitespace
             * whitespace: If the object passed is a DOM text node and contains only whitespace
             *
             * @param {Mixed} object
             * @return {String}
             */
            type : function(o){
                if(o === undefined || o === null){
                    return false;
                }
                if(o.htmlElement){
                    return 'element';
                }
                var t = typeof o;
                if(t == 'object' && o.nodeName) {
                    switch(o.nodeType) {
                        case 1: return 'element';
                        case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
                    }
                }
                if(t == 'object' || t == 'function') {
                    switch(o.constructor) {
                        case Array: return 'array';
                        case RegExp: return 'regexp';
                        case Date: return 'date';
                    }
                    if(typeof o.length == 'number' && typeof o.item == 'function') {
                        return 'nodelist';
                    }
                }
                return t;
            },

            intercept : function(o, name, fn, scope){
                o[name] = o[name].createInterceptor(fn, scope);
            },

            // internal
            callback : function(cb, scope, args, delay){
                if(typeof cb == 'function'){
                    if(delay){
                        cb.defer(delay, scope, args || []);
                    }else{
                        cb.apply(scope, args || []);
                    }
                }
            }
        };
    }());

    /**
     * @class Function
     * These functions are available on every Function object (any JavaScript function).
     */
    Ext.apply(Function.prototype, {
        /**
         * Create a combined function call sequence of the original function + the passed function.
         * The resulting function returns the results of the original function.
         * The passed fcn is called with the parameters of the original function. Example usage:
         *
    var sayHi = function(name){
        alert('Hi, ' + name);
    }

    sayHi('Fred'); // alerts "Hi, Fred"

    var sayGoodbye = sayHi.createSequence(function(name){
        alert('Bye, ' + name);
    });

    sayGoodbye('Fred'); // both alerts show

         * @param {Function} fcn The function to sequence
         * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
         * If omitted, defaults to the scope in which the original function is called or the browser window.
         * @return {Function} The new function
         */
        createSequence : function(fcn, scope){
            var method = this;
            return (typeof fcn != 'function') ?
                    this :
                    function(){
                        var retval = method.apply(this || window, arguments);
                        fcn.apply(scope || this || window, arguments);
                        return retval;
                    };
        }
    });

    /**
     * @class String
     * These functions are available as static methods on the JavaScript String object.
     */
    Ext.applyIf(String, {

        /**
         * Escapes the passed string for ' and \
         * @param {String} string The string to escape
         * @return {String} The escaped string
         * @static
         */
        escape : function(string) {
            return string.replace(/('|\\)/g, "\\$1");
        },

        /**
         * Pads the left side of a string with a specified character.  This is especially useful
         * for normalizing number and date strings.  Example usage:
         *
    var s = String.leftPad('123', 5, '0');
    // s now contains the string: '00123'
         *
         * @param {String} string The original string
         * @param {Number} size The total length of the output string
         * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
         * @return {String} The padded string
         * @static
         */
        leftPad : function (val, size, ch) {
            var result = String(val);
            if(!ch) {
                ch = " ";
            }
            while (result.length < size) {
                result = ch + result;
            }
            return result;
        }
    });

    /**
     * Utility function that allows you to easily switch a string between two alternating values.  The passed value
     * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
     * they are already different, the first value passed in is returned.  Note that this method returns the new value
     * but does not change the current string.
     *
    // alternate sort directions
    sort = sort.toggle('ASC', 'DESC');

    // instead of conditional logic:
    sort = (sort == 'ASC' ? 'DESC' : 'ASC');

     * @param {String} value The value to compare to the current string
     * @param {String} other The new value to use if the string already equals the first value passed in
     * @return {String} The new value
     */
    String.prototype.toggle = function(value, other){
        return this == value ? other : value;
    };

    /**
     * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
     *
    var s = '  foo bar  ';
    alert('-' + s + '-');         //alerts "- foo bar -"
    alert('-' + s.trim() + '-');  //alerts "-foo bar-"

     * @return {String} The trimmed string
     */
    String.prototype.trim = function(){
        var re = /^\s+|\s+$/g;
        return function(){ return this.replace(re, ""); };
    }();

    // here to prevent dependency on Date.js
    /**
     Returns the number of milliseconds between this date and date
     @param {Date} date (optional) Defaults to now
     @return {Number} The diff in milliseconds
     @member Date getElapsed
     */
    Date.prototype.getElapsed = function(date) {
        return Math.abs((date || new Date()).getTime()-this.getTime());
    };

    /**
     * @class Number
     */
    Ext.applyIf(Number.prototype, {
        /**
         * Checks whether or not the current number is within a desired range.  If the number is already within the
         * range it is returned, otherwise the min or max value is returned depending on which side of the range is
         * exceeded.  Note that this method returns the constrained value but does not change the current number.
         * @param {Number} min The minimum number in the range
         * @param {Number} max The maximum number in the range
         * @return {Number} The constrained value if outside the range, otherwise the current value
         */
        constrain : function(min, max){
            return Math.min(Math.max(this, min), max);
        }
    });