/**
 * Squarespace Jetpack - see your squarespace stats in a little overlay that you can bring up by clicking the statusbar icon or alt-s
 *
 * This is my first Jetpack... I put it together over a 24 hour work session.. it has a bunch of rough edges and is in dire need of refactoring.
 *
 * you will need to be already be authenticated on squarespace with access to your stats for this to work.
 *
 * info@bustedloop.com
 * http://bustedloop.com/squarespace-jetpack
 *
 * 2009-07-09 - initial release
 * 2009-07-09 - fix major bugs
 * 2009-07-10 - fix referrer time
 * 2009-07-10 - fix initial save problem, clean up intervals, fix typos, 
 */

// base64 encoded animated loading gif
var loadingIcon = '<img alt="loading" src="data:image/x-icon;base64,R0lGODlhEAAQAPMAAMzMzLu7u7u7u8PDw8LCwsXFxcbGxsjIyMnJycTExMrKysHBwQAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAEKxDISau9OE/Bu//cQBTGgWDhWJ5XSpqoIL6s5a7xjLeyCvOgIEdDLBqPlAgAIfkECQoAAAAsAAAAABAAEAAABCsQyEmrvThPwbv/XJEMxIFg4VieV0qaqCC+rOWu8Yy3sgrzoCBHQywaj5QIACH5BAkKAAAALAAAAAAQABAAAAQrEMhJq704T8G7/9xhFMlAYOFYnldKmqggvqzlrvGMt7IK86AgR0MsGo+UCAAh+QQJCgAAACwAAAAAEAAQAAAEMRDISau9OE/Bu/+cghxGkQyEFY7lmVYraaKqIMpufbc0bLOzFyXGE25AyI5myWw6KREAIfkECQoAAAAsAAAAABAAEAAABDYQyEmrvThPwbv/nKQgh1EkA0GFwFie6SqIpImq29zWMC6xLlssR3vdZEWhDwBqejTQqHRKiQAAIfkECQoAAAAsAAAAABAAEAAABDYQyEmrvThPwbv/HKUgh1EkAyGF01ie6SqIpImqACu5dpzPrRoMpwPwhjLa6yYDOYuaqHRKjQAAIfkECQoAAAAsAAAAABAAEAAABDEQyEmrvThPwbv/nKUgh1EkAxFWY3mmK9WaqCqIJA3fbP7aOFctNpn9QEiPZslsOikRACH5BAkKAAAALAAAAAAQABAAAAQrEMhJq704T8G7/xymIIexEOE1lmdqrSYqiGTsVnA7q7VOszKQ8KYpGo/ICAAh+QQJCgAAACwAAAAAEAAQAAAEJhDISau9OE/Bu/+cthBDEmZjeWKpKYikC6svGq9XC+6e5v/AICUCACH5BAkKAAAALAAAAAAQABAAAAQrEMhJq704T8G7/xy2EENSGOE1lmdqrSYqiGTsVnA7q7VOszKQ8KYpGo/ICAAh+QQJCgAAACwAAAAAEAAQAAAEMRDISau9OE/Bu/+ctRBDUhgHElZjeaYr1ZqoKogkDd9s/to4Vy02mf1ASI9myWw6KREAIfkECQoAAAAsAAAAABAAEAAABDYQyEmrvThPwbv/HLUQQ1IYByKF01ie6SqIpImqACu5dpzPrRoMpwPwhjLa6yYDOYuaqHRKjQAAIfkECQoAAAAsAAAAABAAEAAABDYQyEmrvThPwbv/nLQQQ1IYB0KFwFie6SqIpImq29zWMC6xLlssR3vdZEWhDwBqejTQqHRKiQAAIfkECQoAAAAsAAAAABAAEAAABDEQyEmrvThPwbv/3EIMSWEciBWO5ZlWK2miqiDKbn23NGyzsxclxhNuQMiOZslsOikRADsAAAAAAAAAAAA=" />';

// big chunk of CSS and HTML to use as a template... kinda ugly
// TODO - this template sucks more and more every second... I'm loathe to move it to a remote locations.. but I may have to...
var xmlTemplate = '<div><style id="bl_ss_overlay_style" type="text/css">\
            .bl_ss_overlay {\
	            font-family:Arial;\
	            display:none;\
	            padding-top:20px; \
	            margin-left:10px; \
	            width:200px;   \
	            height:400px;   \
	            opacity:.9;      \
	            background-color:white;\
	            position: fixed;\
	            bottom: 0px;\
	            right: 0px;\
                font-size:11px;\
                line-height:1;\
	            z-index: 999999999;\
	            border: 1px solid black;\
	            -moz-border-radius-topleft:2em;\
	            -moz-border-radius-topright:2em;\
            }\
            .bl_ss_overlay a {\
	            text-decoration:none;\
	            font-weight:bolder; \
	            color:black; \
            }\
            .ss_referrers {\
            }\
            #bl_ss_prefs {\
                padding:10px;\
            }\
            .ss_ref {\
                font-weight:bold;\
                text-align:left;\
                padding-top:3px;\
	            color:black; \
            }\
            .ss_ref_date {\
	            color:black; \
                padding-left:5px;\
                font-weight:bold;\
                font-size:10px;\
            }\
            .ss_ref_link {\
	            color:black; \
                padding-left:7px;\
                overflow:hidden;\
                display:inline;\
                font-size:11px;\
                line-height:1;\
                height:13px;\
                width:150px;\
            }\
            .ss_stat_row {\
                float:left;\
                width:100%;\
                padding-top:5px;\
                text-align:right;\
                vertical-align:top;\
            }\
            .ss_stat_label {\
                padding-left:5px;\
                width:20px;\
                height:20px;\
                float:left;\
                font-size:8px;\
                color:black;\
                text-align:right;\
                vertical-align:top;\
            }\
            .ss_stat_value {\
                width:40px;\
                height:20px;\
                float:left;\
                font-size:13px;\
                color:green;\
                font-weight:bolder;\
                text-align:right;\
                vertical-align:top;\
            }\
            .ss_section_head_time {\
                font-size:10px;\
                vertical-align:middle;\
                float:right;\
                padding-right:5px;\
                color:#706D5F;\
            }\
            .ss_section_head_time img {\
                vertical-align:top;\
                float:right;\
                padding-right:5px;\
            }\
            .ss_section_head {\
                padding:3px;\
                clear:both;\
                background-color:#333333;\
                vertical-align:middle;\
                text-align:left;\
            }\
            .ss_section_head_label {\
                height:20px;\
                cursor:pointer;\
                font-weight:bold;\
                color:#E7E7E7;\
                font-size:12px;\
                vertical-align:middle;\
            }\
            #bl_ss_prefs_link {\
                height:20px;\
                cursor:pointer;\
                font-weight:bold;\
                color:#E7E7E7;\
                font-size:12px;\
                vertical-align:middle;\
            }\
            #ss_head {\
                position:absolute;\
                top:0;\
                width:100%;\
                height:20px;\
                color:#E7E7E7;\
                background-color:#101010;\
                font-weight:bold;\
                font-size:10px;\
                vertical-align:middle;\
                text-align:center;\
	            border: 1px solid black;\
	            -moz-border-radius-topleft:2em;\
	            -moz-border-radius-topright:2em;\
            }\
            #ss_head a, #ss_head img {\
                color:#E7E7E7;\
                border:0px;\
                vertical-align:middle;\
            }\
            #ss_footer {\
                position:absolute;\
                bottom:0;\
                width:100%;\
                color:#E7E7E7;\
                background-color:#333333;\
                font-weight:bold;\
                font-size:10px;\
                vertical-align:middle;\
                text-align:center;\
            }\
            #ss_footer a {\
                color:white;\
                font-weight:bold;\
                font-size:10px;\
                vertical-align:middle;\
                text-align:center;\
            }\
        </style>\
        <div class="bl_ss_statusbar">\
            <div class="bl_ss_statusbarpanel">\
                <img src="http://bustedloop.com/universal/favicon.ico"/> \
            </div>\
        </div>\
        <div class="bl_ss_overlay">\
            <div id="ss_head"><a href="http://www.squarespace.com/?associateTag=bustedloop"><img src="http://bustedloop.com/universal/favicon.ico"/> squarespace</a></div>\
            <div class="ss_section_head"><span class="ss_section_head_label">this hour</span> <span class="ss_section_head_time" id="ss_this_hour_time"></span></div>\
        	<div class="ss_stat_row">\
                <div class="ss_stat_label">views:</div>  <div id="ss_this_hour_views"  class="ss_stat_value"></div>\
                <div class="ss_stat_label">unique:</div> <div id="ss_this_hour_unique" class="ss_stat_value"></div>\
                <div class="ss_stat_label">bots:</div>   <div id="ss_this_hour_bots"   class="ss_stat_value"></div>\
            </div>\
            <div class="ss_section_head"><span class="ss_section_head_label">last hour</span> <span class="ss_section_head_time" id="ss_last_hour_time"></span></div>\
            <div class="ss_stat_row">\
                <div class="ss_stat_label">views:</div>  <div id="ss_last_hour_views"  class="ss_stat_value"></div>\
                <div class="ss_stat_label">unique:</div> <div id="ss_last_hour_unique" class="ss_stat_value"></div>\
                <div class="ss_stat_label">bots:</div>   <div id="ss_last_hour_bots"   class="ss_stat_value"></div>\
            </div>\
            <div class="ss_section_head"><span class="ss_section_head_label">this day</span> <span class="ss_section_head_time" id="ss_this_day_time"></span></div>\
            <div class="ss_stat_row">\
                <div class="ss_stat_label">views:</div>  <div id="ss_this_day_views"  class="ss_stat_value"></div>\
                <div class="ss_stat_label">unique:</div> <div id="ss_this_day_unique" class="ss_stat_value"></div>\
                <div class="ss_stat_label">bots:</div>   <div id="ss_this_day_bots"   class="ss_stat_value"></div>\
            </div>\
            <div class="ss_section_head"><span class="ss_section_head_label">last day</span> <span class="ss_section_head_time" id="ss_last_day_time"></span></div>\
            <div class="ss_stat_row">\
                <div class="ss_stat_label">views:</div>  <div id="ss_last_day_views"  class="ss_stat_value"></div>\
                <div class="ss_stat_label">unique:</div> <div id="ss_last_day_unique" class="ss_stat_value"></div>\
                <div class="ss_stat_label">bots:</div>   <div id="ss_last_day_bots"   class="ss_stat_value"></div>\
            </div>\
            <br/><br/>\
            <div class="ss_section_head"><span class="ss_section_head_label">referrers</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<span id="bl_ss_prefs_link">prefs</span>] <span class="ss_section_head_time" id="ss_referrer_time"></span></div>\
            <div id="ss_referrers"></div>\
            <div id="ss_footer">Copyright 2009 - <a href="http://bustedloop.com">bustedloop.com</a></div>\
        </div></div>';

jetpack.future.import('storage.simple')
// class that kinda sorta encapsulates access to squarespace space
function SSOverlay() {
    // the overlay jquery object
    this.overlay   = null;
    // the status bar widget jquery object
    this.statusBar = null;
    this.threadLauncher = null;
}
SSOverlay.prototype = {
    overlay:    null,
    statusBar:  null,
    threadLauncher: null,
    baseUrl:null,

    init: function(overlay, statusBar) {
        this.overlay    = $(overlay);
        this.statusBar  = $(statusBar);
        this.threadLauncher  = new SimpleThreads();

        var self = this;

        // when headers are clicked in the overlay, fire off data refresh.
        this.overlay.find('.ss_section_head_label').bind('click', function() {
            self.updateAll();
        });
        this.overlay.find('#bl_ss_prefs_link').bind('click', function() {
            self.showPrefs();
        });
    },


    // make sure the URL is set!
    setup: function(callback) {
        var self = this;
        jetpack.storage.simple.get('ss_url', {
            onResult:function(key, value) {
                if(!value) {
                    self.showPrefs();
                } else {
                    self.baseUrl = value;
                    self.updateAll();
                }
            },
            onError:function(msg, key, val) {
                conosle.log(msg + ' : ' + key + ' = ' + value);
        }});
    },
    showPrefs: function() {
        var self = this;
        self.overlay.find('#ss_referrers').html('<div id="bl_ss_prefs">squarespace url:<br/><input id="bl_ss_url"/><br/><input type="submit" value="cancel" id="bl_ss_pref_cancel"/><input type="submit" value="save" id="bl_ss_pref_save"/><br/><br/>The url looks like this: http://bustedloop.com</div>');

        self.overlay.find('#bl_ss_url').val(self.baseUrl);
        self.overlay.find('#bl_ss_pref_save').click(function() {
            var ssUrlInput = self.overlay.find('#bl_ss_url').val();
            self.baseUrl = ssUrlInput;
            jetpack.storage.simple.set('ss_url', ssUrlInput, function() {
                if (self.baseUrl) {
                    self.updateAll();
                }
            });
        });
        self.overlay.find('#bl_ss_pref_cancel').click(function() {
            if (self.baseUrl) {
                self.updateAll();
            }
        });
    },

    // add overlay to current document (moves it to this tab if it's on another one already)
    // also makes sure the styles are in place on the page.
    setupOverlay:function() {
        var self = this;

        var doc = $(jetpack.tabs.focused.contentDocument);
        doc.bind('keypress', keyboardHandler);
        // make sure the style is on the tab
        if(doc.find('#bl_ss_overlay_style').size() == 0) {
            doc.find('body').append($(xmlTemplate).find('#bl_ss_overlay_style'));
        }

        doc.find('body').append(self.overlay);
    },
    toggleOverlay: function() {
        self = this;
        // hide or show it.
        if(self.overlay.is(':visible')) {
            self.overlay.hide('slow');
        } else {
            // always refresh referrers when the overlay is shown.
            if(self.baseUrl) {
                self.updateAll();
            }
            self.overlay.show('slow');
        }
    },
    hideOverlay: function() {
        this.overlay.hide('slow');
    },
    updateAll: function() {
        this.updateHourly();
        this.updateDaily();
        this.updateReferrers();
    },

    // update the hourly stats...
    updateHourly: function() {
        self = this;
        if (self.baseUrl) {
            var requestTime = getNowString();
            self.overlay.find('#ss_this_hour_time,#ss_last_hour_time').html(loadingIcon);

            // async request for the hourly traffic
            $.ajax({
                type: "GET",
                url: self.baseUrl + '/display/configuration/micro/MicroTrafficChart?contentType=traffic&width=200&height=160&verbose=true&segmentName=day',
                dataType: "text",
                async: true,
                error: errorHandler,
                success: function(xml) {
                    // on sucess we're going to spawn a thread to regex the rather large javascript chunk in the middle of the html
                    // we're looking for the table rows... then the second closure is executed on the main thread and can modify the UI
                    self.threadLauncher.launchThread(createTrafficWorker(xml),
                        // update all the places the hourly data appears
                                function(hourRows) {
                                    // get the this hour stats
                                    var columns = $(hourRows.lines.pop()).find('td');
                                    var time = columns.eq(0).text();
                                    var views = columns.eq(1).text();
                                    var unique = columns.eq(2).text();
                                    var robots = columns.eq(3).text();

                                    // set the overlay values
                                    self.overlay.find('#ss_this_hour_views').html(views);
                                    self.overlay.find('#ss_this_hour_unique').html(unique);
                                    self.overlay.find('#ss_this_hour_bots').html(robots);

                                    // set the last update time
                                    self.overlay.find('#ss_this_hour_time').html(requestTime);

                                    // get the last hour stats
                                    columns = $(hourRows.lines.pop()).find('td');
                                    time = columns.eq(0).text();
                                    views = columns.eq(1).text();
                                    unique = columns.eq(2).text();
                                    robots = columns.eq(3).text();
                                    // set overlay values
                                    self.overlay.find('#ss_last_hour_views').html(views);
                                    self.overlay.find('#ss_last_hour_unique').html(unique);
                                    self.overlay.find('#ss_last_hour_bots').html(robots);

                                    // set the last update time
                                    self.overlay.find('#ss_last_hour_time').html(requestTime);
                                });
                }
            });
        }

    },
    // update the hourly stats...
    updateDaily: function() {
        self = this;
        if (self.baseUrl) {
            var requestTime = getNowString();
            self.overlay.find('#ss_this_day_time,#ss_last_day_time').html(loadingIcon);

            // async request for the daily traffic
            $.ajax({
                type: "GET",
                url: self.baseUrl + '/display/configuration/micro/MicroTrafficChart?contentType=traffic&width=780&height=260&verbose=true&segmentName=month',
                dataType: "text",
                async: true,
                error: errorHandler,
                success: function(xml) {
                    // on sucess we're going to spawn a thread to regex the rather large javascript chunk in the middle of the html
                    // we're looking for the table rows... then the second closure is executed on the main thread and can modify the UI
                    self.threadLauncher.launchThread(createTrafficWorker(xml),
                        // update all the places the daily data appears
                                function(dayRows) {
                                    // get the this hour stats
                                    var columns = $(dayRows.lines.pop()).find('td');
                                    var time = columns.eq(0).text();
                                    var views = columns.eq(1).text();
                                    var unique = columns.eq(2).text();
                                    var robots = columns.eq(3).text();

                                    // set the overlay values
                                    self.overlay.find('#ss_this_day_views').html(views);
                                    self.overlay.find('#ss_this_day_unique').html(unique);
                                    self.overlay.find('#ss_this_day_bots').html(robots);

                                    self.overlay.find('#ss_this_day_time').html(requestTime);

                                    // get the last day stats
                                    columns = $(dayRows.lines.pop()).find('td');
                                    time = columns.eq(0).text();
                                    views = columns.eq(1).text();
                                    unique = columns.eq(2).text();
                                    robots = columns.eq(3).text();
                                    // set overlay values
                                    self.overlay.find('#ss_last_day_views').html(views);
                                    self.overlay.find('#ss_last_day_unique').html(unique);
                                    self.overlay.find('#ss_last_day_bots').html(robots);

                                    // set the last update time
                                    self.overlay.find('#ss_last_day_time').html(requestTime);
                                });
                }
            });
        }

    },
    updateReferrers: function() {
        self = this;
        if (self.baseUrl) {
            var requestTime = getNowString();
            self.overlay.find('#ss_referrer_time').html(loadingIcon);
            // fire off ajax request for detailed referrer data
            $.ajax({
                type: "GET",
                url: self.baseUrl + '/display/configuration/micro/MicroTrafficDetails?contentType=referrer&currentPage=1',
                dataType: "text",
                async: true,
                error: errorHandler,
                success: function(xml) {
                    // regex out the date and referrer links in a background thread, then add them to the overlay in the main thread.
                    self.threadLauncher.launchThread(function() {
                        var linkReg = /.*?tableCode\s.*?\<a.*?\>(.*?)\<.*/;
                        var dateReg = /.*?tableCode\s.*?..\/.. at (..:.. .M).*/;
                        var lines = xml.split("\r\n");

                        var referrers = [];
                        var theDate = null;
                        var theLink = null;
                        for (var i in lines) {
                            var line = lines[i];
                            if (line.indexOf('tableCode') > -1) {
                                var matches = linkReg.exec(line);
                                if (matches && matches.length > 0) {
                                    theLink = matches[1].trim();
                                } else {
                                    matches = dateReg.exec(line);

                                    if (matches && matches.length > 0) theDate = matches[1].trim();
                                }
                            }

                            if (theDate && theLink) {
                                referrers.push({theDate:theDate, link:theLink});
                                theDate = null;
                                theLink = null;
                            }
                        }
                        return referrers;
                    }
                    , function(referrers) {
                        var referrerHtml = '';
                        var num = Math.min(20, referrers.length);
                        for (var i = 0; i < num; i++) {
                            ref = referrers[i];
                            referrerHtml += '<div class="ss_ref"><span class="ss_ref_date">' + ref.theDate + '</span><a class="ss_ref_link" href="' + ref.link + '" title="' + ref.link + '">' + ref.link.replace('http://', '').substring(0, 20) + '</a></div>';
                        }
                        self.overlay.find('#ss_referrers').html(referrerHtml);
                        self.overlay.find('#ss_referrer_time').html(requestTime);
                    });
                }
            });
        }

    }
};

// alt-s is bound to this plugin right now...
function keyboardHandler(event) {
    if(event.altKey && event.which == 223) {
        ss.toggleOverlay();
    }
}

function errorHandler(XMLHttpRequest, textStatus, errorThrown) {
    console.log('ERROR!');
    console.log(textStatus);
    console.log(errorThrown);
}

// creates a closure that will parse the passed in xml when called.
// returns table rows from squarespace traffic reports.
function createTrafficWorker(xml) {
   return  function() {
        var myregexp = /.*?row\s.*?\s\"(.*)\".*?/;
        var lines = xml.split("\r\n");
        var row = '';
        var hourRows = [];
        for (var i in lines) {
            var line = lines[i];
            if (line.indexOf('row')>-1) {
                var matches = myregexp.exec(line);
                if (matches && matches.length > 0) {
                    row += matches[1].trim();
                    if (row.indexOf('</tr>') > 0) {
                        hourRows.push(row);
                        row = '';
                    }
                }
            }
        }
       if(row.indexOf('</tr>') > 0) {
           hourRows.push(row);
       }

        return {lines:hourRows};
    };
}

// not using storage for anything yet... but when I need prefs, this will be awesome.
//jetpack.future.import("storage.simple");
var ss = new SSOverlay();

// append the status bar widget and set up events
jetpack.statusBar.append({
    // jetpack turns this html into the statusbar widget.
    html: xmlTemplate,
    intervals: [],
    onReady: function(wdgt) {
        // set up the overlay div... the template hidden in the statusbar widget html
        var ovrly = $(wdgt).find('.bl_ss_overlay');

        // init the squarespace object and show it.
        ss.init(ovrly, wdgt);
        ss.setupOverlay();
        ss.hideOverlay();

        // when the status widget is clicked we'll toggle the visibility of the overlay
        $(wdgt).click(function(){
            ss.setupOverlay();
            ss.toggleOverlay();
            return false;
        });

        // when focus changes tabs, we'll move the overlay along for the ride.
        jetpack.tabs.onFocus(function(){ss.setupOverlay();});
        jetpack.tabs.onReady(function(){ss.setupOverlay();});

        ss.setup();


        this.intervals.push(setInterval(function() { ss.updateHourly(); }, 5 * 60 * 1000));
        this.intervals.push(setInterval(function() { ss.updateDaily(); }, 11 * 60 * 1000));
        this.intervals.push(setInterval(function() { ss.updateReferrers(); }, 12 * 60 * 1000));
    },
    onUnload: function(wdgt) {
        $.each(this.intervals, function() {
            clearInterval(this);
            ss.hideOverlay();
        });
    },
    width: 20
});


function getNowString() {
    var now = new Date();
    var hour        = now.getHours();
    var minute      = now.getMinutes();
    var monthnumber = now.getMonth()+1;
    var monthday    = now.getDate();
    return monthnumber +'/'+monthday+' '+(hour < 10 ? '0'+hour : hour)+':'+(minute < 10 ? '0'+minute : minute);
}

function SimpleThreads() {}
SimpleThreads.prototype = {
    /**
     * this launches a background thread and calls the "worker" closure... when that thread is done, its results are passed to callback which is executed in the main thread.
     *
     * launchThread(function() {return 'did some work in background';},
     *              function(result) {console.log('in the main thread I print the result from the background thread: '+result);}
     * );
     *
     */
    numThreads: 0,
    launchThread: function(worker, callback) {
        let threadManager = Components.classes["@mozilla.org/thread-manager;1"].getService();
        let background = threadManager.newThread(++this.numThreads);
        background.dispatch(new SimpleWorkingThread(++this.numThreads, threadManager.mainThread, worker, callback), background.DISPATCH_NORMAL);
    }
}
// create a worker thread... it needs an ID, a thread bound to the main thread, and two closures.
// The first closure is to be executed in the background and should NOT modify any UI elements or even log to firebug console.
// The second closure is executed on the main thread and can do whatever you want.
var SimpleWorkingThread = function(threadID, mainThread, workClosure, cback) {
  this.threadID = threadID;
  this.workClosure = workClosure;
  this.cback = cback;
  this.mThread = mainThread;
  this.result = null;
};
SimpleWorkingThread.prototype = {
    QueryInterface: qInterface,
    run: function() {
        try {
          this.result = this.workClosure.call();
          // When it's done, call back to the main thread to let it know
          // we're finished.
          this.mThread.dispatch(new SimpleMainThread(this.threadID, this.result, this.cback), this.mThread.DISPATCH_NORMAL);
        } catch(err) {
            Components.utils.reportError(err);
        }
    }
};

var SimpleMainThread = function(threadID, result, cback) {
  this.threadID = threadID;
  this.result = result;
  this.cback = cback;
};
SimpleMainThread.prototype = {
    QueryInterface: qInterface,
    run: function() {
        try {
            this.cback(this.result);
        } catch(err) {
            Components.utils.reportError(err);
        }
    }
};

// make sure these threads claim to be the right kind.
function qInterface(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) || iid.equals(Components.interfaces.nsISupports)) return this;
    throw Components.results.NS_ERROR_NO_INTERFACE;
};

