The source code

    /*!
     * Ext JS Library 3.2.1
     * Copyright(c) 2006-2010 Ext JS, Inc.
     * licensing@extjs.com
     * http://www.extjs.com/license
     */
    Ext.ns('Ext.ux');

    Ext.ux.Rating = Ext.extend(Ext.util.Observable, {
    	// Configuration options
        starWidth: 16,
        split: 1,
        resetValue: '',
    	defaultSelected: -1,
        selected: -1,
        showTitles: true,

    	// Our class constructor
        constructor : function(element, config) {
            Ext.apply(this, config);
            Ext.ux.Rating.superclass.constructor.call(this);

            this.addEvents(
                'change',
                'reset'
            );

            this.el = Ext.get(element);
            this.init();
        },

        init : function() {
            var me = this;

    		// Some arrays we are going to store data in
            this.values = [];
            this.titles = [];
            this.stars = [];

    		// We create a container to put all our stars into
            this.container = this.el.createChild({
                cls: 'ux-rating-container ux-rating-clearfix'
            });

            if(this.canReset) {
                this.resetEl = this.container.createChild({
                    cls: 'ux-rating-reset',
                    cn: [{
                        tag: 'a',
                        title: this.showTitles ? (this.resetTitle || 'Reset your vote') : '',
                        html: 'Reset'
                    }]
                });
                this.resetEl.visibilityMode = Ext.Element.DISPLAY;

                this.resetEl.hover(function() {
                    Ext.fly(this).addClass('ux-rating-reset-hover');
                }, function() {
                    Ext.fly(this).removeClass('ux-rating-reset-hover');
                });

                this.resetEl.on('click', this.reset, this);
            }

    		// We use DomQuery to select the radio buttons
    		this.radioBoxes = this.el.select('input[type=radio]');
    		// Then we can loop over the CompositeElement using each
            this.radioBoxes.each(this.initStar, this);

    		// We use DomHelper to create our hidden input
    		this.input = this.container.createChild({
    			tag: 'input',
    			type: 'hidden',
    			name: this.name,
    			value: this.values[this.defaultSelected] || this.resetValue
    		});

    		// Lets remove all the radio buttons from the DOM
    		this.radioBoxes.remove();

            this.select(this.defaultSelected === undefined ? false : this.defaultSelected)

            if(this.disabled) {
                this.disable();
            } else {
    			// Enable will set up our event listeners
                this.enable();
            }
        },

        initStar : function(item, all, i) {
            var sw = Math.floor(this.starWidth / this.split);

    		// We use the name and disabled attributes of the first radio button
    		if(i == 0) {
    	        this.name = item.dom.name;
    	        this.disabled = item.dom.disabled;
    		}

    		// Saving the value and title for this star
            this.values[i] = item.dom.value;
            this.titles[i] = item.dom.title;

            if(item.dom.checked) {
                this.defaultSelected = i;
            }

    		// Now actually create the star!
            var star = this.container.createChild({
                cls: 'ux-rating-star'
            });

            var starLink = star.createChild({
                tag: 'a',
                html: this.values[i],
                title: this.showTitles ? this.titles[i] : ''
            });

            // Prepare division settings
            if(this.split) {
              var odd = (i % this.split);
              star.setWidth(sw);
              starLink.setStyle('margin-left', '-' + (odd * sw) + 'px');
            }

    		// Save the reference to this star so we can easily access it later
            this.stars.push(star.dom);
        },

        onStarClick : function(ev, t) {
            if(!this.disabled) {
                this.select(this.stars.indexOf(t));
            }
        },

        onStarOver : function(ev, t) {
            if(!this.disabled) {
                this.fillTo(this.stars.indexOf(t), true);
            }
        },

        onStarOut : function(ev, t) {
            if(!this.disabled) {
                this.fillTo(this.selected, false);
            }
        },

        reset : function(ev, t) {
            this.select(-1);
        },

        select : function(index) {
            if(index === false || index === -1) {
                // remove current selection in el
                this.value = this.resetValue;
                this.title = "";
    			this.input.dom.value = '';

                if(this.canReset) {
                    this.resetEl.setOpacity(0.5);
                }

                this.fillNone();

                if(this.selected !== -1) {
                    this.fireEvent('change', this, this.values[index], this.stars[index]);
                }
                this.selected = -1;
            }
            else if(index !== this.selected) {
    			// Update some properties
                this.selected = index;
                this.value = this.values[index];
                this.title = this.titles[index];

    			// Set the value of our hidden input so the rating can be submitted
    			this.input.dom.value = this.value;

                if(this.canReset) {
                    this.resetEl.setOpacity(0.99);
                }

    			// the fillTo() method will fill the stars up until the selected one
                this.fillTo(index, false);

    			// Lets also not forget to fire our custom event!
                this.fireEvent('change', this, this.values[index], this.stars[index]);
            }
        },

        fillTo : function(index, hover) {
            if (index != -1) {
                var addClass = hover ? 'ux-rating-star-hover' : 'ux-rating-star-on';
                var removeClass = hover ? 'ux-rating-star-on' : 'ux-rating-star-hover';

    			// We add a css class to each star up until the selected one
                Ext.each(this.stars.slice(0, index+1), function() {
                    Ext.fly(this).removeClass(removeClass).addClass(addClass);
                });

    			// And then remove the same class from all the stars after this one
                Ext.each(this.stars.slice(index+1), function() {
                    Ext.fly(this).removeClass([removeClass, addClass]);
                });
            }
            else {
                this.fillNone();
            }
        },

        fillNone : function() {
            this.container.select('.ux-rating-star').removeClass(['ux-rating-star-hover', 'ux-rating-star-on']);
        },

        enable : function() {
            if(this.canReset) {
                this.resetEl.show();
            }

            this.input.dom.disabled = null;
            this.disabled = false;

            this.container.removeClass('ux-rating-disabled');

    		// We will be using the technique of event delegation by listening
        	// for bubbled up events on the container
            this.container.on({
                click: this.onStarClick,
                mouseover: this.onStarOver,
                mouseout: this.onStarOut,
                scope: this,
                delegate: 'div.ux-rating-star'
            });
        },

        disable : function() {
            if(this.canReset) {
                this.resetEl.hide();
            }

            this.input.dom.disabled = true;
            this.disabled = true;

            this.container.addClass('ux-rating-disabled');

            this.container.un({
                click: this.onStarClick,
                mouseover: this.onStarOver,
                mouseout: this.onStarOut,
                scope: this,
                delegate: 'div.ux-rating-star'
            });
        },

        getValue : function() {
            return this.values[this.selected] || this.resetValue;
        },

    	destroy : function() {
    		this.disable();
    		this.container.remove();
    		this.radioBoxes.appendTo(this.el);
    		if(this.selected !== -1) {
    			this.radioBoxes.elements[this.selected].checked = true;
    		}
    	}
    });