/**
 * Quick & dirty top ten Fx3 download countries widget
 * l.m.orchard <lorchard@mozilla.com> http://decafbad.com
 *
 * see also: 
 *      http://decafbad.com/blog/2008/06/16/firefox-3-download-day-mega-widget
 *      http://kentbrewster.com/case-hardened-javascript/
 *
 * Share and Enjoy!
 */
(function() {

    var true_name = 'moz_dd_';
    for (var i = 0; i < 16; i++) {
        true_name += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
    }

    var options = {
        script_re: /\/download-day-top-ten.js/,
        id:        'moz_download_day_top_ten_' + true_name,
        css_id:    'moz_download_day_top_ten_css_' + true_name,
        css:       'http://decafbad.com/2008/download-day-top-ten.css',
        
        pledge_data_url:    'http://www.spreadfirefox.com/en-US/worldrecord/mapdata?format=json',
        downloads_data_url: 'http://www.spreadfirefox.com/en-US/worldrecord/mapdata?format=json',
        // downloads_data_url: 'http://decafbad.com/2008/fx3-downloads.php?format=json',

        dday:      new Date('June 17, 2008 10:00:00 am PDT')
    }

    var $ = window[true_name] = function() {

        return {

            buildWidget : function() {
                var is_dday = ( new Date() > this.options.dday );

                return $.e('div', {'id':this.options.id, 'class':'moz_download_day_top_ten'}, [
                    $.e('h3', {}, [ 
                        'Top Ten Countries by ',
                        $.e(
                            'a', 
                            {
                                'href': (is_dday) ? 
                                    'http://www.mozilla.com/en-US/firefox/' :
                                    'http://www.spreadfirefox.com/en-US/worldrecord/pledge#pledge_form'
                            }, 
                            (is_dday) ? 
                                'Firefox 3 Downloads' : 
                                'Firefox 3 Download Pledges'
                        )
                    ]),
                    ( this.widget_list = $.e('div', {}, [ $.e('p', {'class':'loading'}, 'Loading...') ]) ),
                    $.e('div', {'class':'moz_footer'}, [ 
                        $.e('a', {'href':'http://www.spreadfirefox.com/en-US/worldrecord/'}, 'Help Us Set a Guinness World Record!'),
                        $.e('br', {}, []),
                        'Download Firefox 3 on June 17, 2008!'
                    ]),
                ])
            },

            renderResult: function(data) {
                data.sort(function(a,b) {
                    return b['ct'] - a['ct']
                });
                
                var top_ten = data.slice(0,10);

                this.replaceChildNodes(this.widget_list, [
                    $.e('dl', {}, top_ten.map(
                        function(item) {
                            return [
                                $.e('dt', {}, item.nm),
                                $.e('dd', {}, ''+item.ct)
                            ]
                        }
                    ))
                ]);

                delete this.widget_list;
            },

            /************************************************************/

            defaults: options,
            options:  options,

            init: function() {
                var target = this.options.script_re;

                // Search through scripts on the page for the first one
                // matching the URL from which this script was expected to have
                // been loaded.
                var theScripts = document.getElementsByTagName('SCRIPT');
                for (var i = 0; i < theScripts.length; i++) {
                    if (theScripts[i].src.match(target)) {
                        
                        // Attempt to parse inline config options from <script> tag.
                        var ext_options;
                        if (theScripts[i].innerHTML) {
                            ext_options = this.evalJSON(theScripts[i].innerHTML);
                        }
                        if (typeof ext_options !== 'object') ext_options = {};

                        // Now, merge the inline config options into the defaults.
                        for (k in ext_options) {
                            if (!ext_options.hasOwnProperty(k)) continue;
                            this.options[k] = ext_options[k];
                        }

                        // Inject a stylesheet for the widget from options.
                        if (!document.getElementById(this.options.css_id)) {
                            this.addCSS(this.options.css, this.options.css_id);
                        }

                        // Replace the script element with the initial state of
                        // the widget.
                        this.widget = this.buildWidget();
                        theScripts[i].parentNode.insertBefore(this.widget, theScripts[i]);
                        theScripts[i].parentNode.removeChild(theScripts[i]);

                        // this.onload(); 

                        // Attach the onload handler to finish the job of
                        // loading the widget data.
                        if(typeof window.addEventListener !== 'undefined') {
                            window.addEventListener('load', this.rescope(function() { 
                                this.onload(); 
                            }), false);
                        } else if(typeof window.attachEvent !== 'undefined') {
                            window.attachEvent('onload', this.rescope(function() { 
                                this.onload(); 
                            }));
                        }

                        // Stop after finding the first script tag.
                        break;
                    }
                }

                return this;
            },

            onload: function() {
                this.loadData();
            },

            run_function : [],

            loadData : function() {
                var is_dday = ( new Date() > this.options.dday );
                var n = this.run_function.length;
                var id = true_name + '.run_function[' + n + ']';
                this.run_function[n] = this.rescope(function(r) {
                    delete(this.run_function[n]);
                    this.removeScript(id);
                    this.renderResult(r);
                    delete this.widget;
                });
                var url = (is_dday) ? 
                    this.options.downloads_data_url : this.options.pledge_data_url;
                url += '&callback=' + id;
                this.runScript(url, id);
            },

            addCSS: function(url, id) {
                var l = document.createElement('link');
                l.id = id;
                l.type ='text/css';
                l.rel = 'stylesheet';
                l.href = url;
                document.getElementsByTagName('head')[0].appendChild(l);
            },

            evalJSON: function(data) {
                if (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]+$/.test(data.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) {
                    try { return eval( '(' + data + ')' ); }
                    catch(err) { }
                }
                return {};
            },

            runScript : function(url, id) {
                var s = document.createElement('script');
                s.id = id;
                s.type ='text/javascript';
                s.src = url;
                document.getElementsByTagName('body')[0].appendChild(s);
            },

            removeScript : function(id) {
                var s = '';
                if (s = document.getElementById(id)) {
                    s.parentNode.removeChild(s);
                }
            },

            e: function(name, attrs, nodes) {
                var elem = document.createElement(name);
                if (attrs) for (k in attrs) {
                    if (!attrs.hasOwnProperty(k)) continue;
                    var v = attrs[k];
                    if (k.substring(0, 2) == "on") {
                        if (typeof(v) == "string") {
                            v = new Function(v);
                        }
                        elem[k] = v;
                    } else {
                        elem.setAttribute(k, v);
                    }
                    switch(k) {
                        case 'class': elem.className = v; break;
                    }
                }
                if (nodes) this.appendChildNodes(elem, nodes);
                return elem;
            },

            replaceChildNodes: function(parent, nodes) {
                while (parent.firstChild) 
                    parent.removeChild(parent.firstChild);
                return this.appendChildNodes(parent, nodes);
            },

            appendChildNodes: function(parent, nodes) {
                if (!nodes || !nodes.length) return;
                for (var i=0; i<nodes.length; i++) {
                    var node = nodes[i];
                    if (!node) continue;
                    if (node.nodeType) 
                        parent.appendChild(node);
                    else if ( (typeof(node) == 'object') && node.length)
                        this.appendChildNodes(parent, node);
                    else
                        parent.appendChild(document.createTextNode(''+node));
                }
            },

            rescope: function(fn) {
                var obj = this;
                return function() {
                    var args = []; // [this];
                    for (var i = 0, ix = arguments.length; i < ix; i++) {
                        args.push(arguments[i]);
                    }
                    return fn.apply(obj, args);
                };
            }

        };

    }();

    $.init();

})();
