/* Addons display js */
var gPlatform = PLATFORM_WINDOWS;
var gLatestVersionID = null; //latest version ID of any compatible addon for versions page.
var gLatestAddonVersion = null; //addon version of latest compatible addon for versions page.
var gLatestAppVersion = null; //application version of latest compatible addon for versions page.

var PLATFORM_OTHER    = 0;
var PLATFORM_WINDOWS  = 1;
var PLATFORM_LINUX    = 2;
var PLATFORM_MACOSX   = 3;
var PLATFORM_MAC      = 4;

if (navigator.platform.indexOf("Win32") != -1)
  gPlatform = PLATFORM_WINDOWS;
else if (navigator.platform.indexOf("Linux") != -1)
  gPlatform = PLATFORM_LINUX;
else if (navigator.userAgent.indexOf("Mac OS X") != -1)
  gPlatform = PLATFORM_MACOSX;
else if (navigator.userAgent.indexOf("MSIE 5.2") != -1)
  gPlatform = PLATFORM_MACOSX;
else if (navigator.platform.indexOf("Mac") != -1)
  gPlatform = PLATFORM_MAC;
else
  gPlatform = PLATFORM_OTHER;

function getPlatformName()
{
  if (gPlatform == PLATFORM_WINDOWS)
    return "Windows";
  if (gPlatform == PLATFORM_LINUX)
    return "Linux";
  if (gPlatform == PLATFORM_MACOSX)
    return "MacOSX";
  return "Unknown";
}

function getInstallURL(aEvent) {
    // The event target might be the link itself or one of its children
    var target = aEvent.target;
    while (target && !target.href)
      target = target.parentNode;
    
    return target && target.href;
}

function checkMatchUserAgentAppId() {
    var uapattern = /(?:Firefox|Minefield|Shiretoko|GranParadiso|BonEcho|Iceweasel)/;
    var ua = navigator.userAgent;
    var uamatch = uapattern.exec(ua);
	
    if( uamatch !=null && APP_ID == 1)	return true;

    uapattern = /(SeaMonkey|Iceape)/;
    ua = navigator.userAgent;
    uamatch = uapattern.exec(ua);
    if( uamatch !=null && APP_ID == 59) return true;
	
    return false;

}

/**
 * Install an add-on into the current browser-type application
 * (mostly: Firefox, SeaMonkey)
 */
function install( aEvent, extName, iconURL, extHash)  { 

    if (aEvent.altKey || !window.InstallTrigger || !checkMatchUserAgentAppId())
        return true;

    var url = getInstallURL(aEvent);

    if (url) {

        var params = new Array();

        params[extName] = {
            URL: url,
            IconURL: iconURL,
            toString: function () { return this.URL; }
        };

        // Only add the Hash param if it exists.
        //
        // We optionally add this to params[] because installTrigger
        // will still try to compare a null hash as long as the var is set.
        if (extHash) {
            params[extName].Hash = extHash;
        }

        InstallTrigger.install(params);

        return false;
    }
    return true;
}

/**
 * Install a search engine (opensearch)
 * Returns false in case of success (sic!) because that will keep the file link
 * from being followed.
 */
function addEngine(engineURL) { 	 
    if (window.external && ("AddSearchProvider" in window.external)) {
        window.external.AddSearchProvider(engineURL);
        return false;
    } else { 	 
        alert(error_opensearch_unsupported);
        return true;
    } 	 
} 	 

/**
 * Detect which install button should show, and hide the rest
 */
function fixPlatformLinks(versionID, name) {
    if (gPlatform == PLATFORM_OTHER) return true; // only hide something if we were able to detect platforms
    var platform = getPlatformName();
    var outer = $("#install-"+ versionID);
    var installs = outer.find("p.install-button");
    
    // hide incompatible installs
    var others = installs.not(".platform-ALL,.platform-"+platform);
    others.hide();
    
    if (installs.length == others.length) {
        outer.append($('<p class="not-avail"></p>').append(sprintf(addOnNotAvailableForPlatform, name, platform)));
    }

    return true;
}

/**
*   Used to select between the string Add to App and Download depending on whether Firefox is the UA
*/
function installVersusDownloadCheck(triggerID, installString, downloadString)
{
    var buttonMessage = installString;
    var uapattern = /Mozilla.*(Firefox|Minefield|Shiretoko|GranParadiso|BonEcho|SeaMonkey|Iceweasel|Iceape)\/.*$/;
    var ua = navigator.userAgent;
    var uamatch = uapattern.exec(ua);
    if (!uamatch || uamatch.length < 2 || !checkMatchUserAgentAppId()) // not a Firefox-like browser
        buttonMessage = downloadString;
    $("#" + triggerID + " strong").text(buttonMessage);
}

/**
 * Provide hints on install buttons for add-ons incompatible with the
 * currently used browser. 
 *
 * It has the side-effect that the first time it is called on a page and doesn't need to add hints, it will
 * set gLatestVersion to the passed versionID
 */
function addCompatibilityHints(addonID, versionID, fromVer, toVer, showVersionLink, versionsPage) {
    var uapattern = /Mozilla.*(?:Firefox|Minefield|Shiretoko|GranParadiso|BonEcho|Iceweasel)\/([^\s]*).*$/;
    var ua = navigator.userAgent;
    var uamatch = uapattern.exec(ua);
    if (!uamatch || uamatch.length < 2) return true;

    var outer = $("#install-"+ versionID);

    var version = uamatch[1];
    
    var vc = new VersionCompare();
    if (vc.compareVersions(version, fromVer)<0)
        var needUpgrade = true;
    else if(vc.compareVersions(version, toVer)>0)
        var needUpgrade = false;
    else { //check if this is the first time on the page we have a platform and version compatitble addon
        if( gLatestVersionID == null) {
            var installs = outer.find("p.install-button");			
            if (installs.find(".platform-ALL") || installs.find(".platform-"+gPlatform)) {
                gLatestVersionID = versionID;
			    var tmpAddonVersion = outer.prev().prev().prev();
			    gLatestAddonVersion = tmpAddonVersion.clone();
			    gLatestApplicationVersion = version;
		    }
		}
        return true;
	}

    if(versionsPage) return true;
	
    var links = outer.find("p:visible a"); // find visible install boxes
    if (links.length == 0) return true; // nothing to do
    
    // duplicate button and hide original (to be able to restore it later)
    var cloned = outer.clone();
    cloned.attr('id', 'orig-'+ versionID);
    cloned.hide();
    outer.after(cloned);
    
    // gray out button
    var mydiv = document.createElement('div');
    mydiv.setAttribute('class', 'exp-loggedout');
    outer.wrapInner(mydiv);
    
    // remove link
    var url = links.attr('href');
    links.removeAttr("href");
    links.removeAttr("onClick");
    links.removeAttr("title");
    links.css('cursor', 'default');
    links.parent().css('float', 'none');
    
    if (needUpgrade) {
        // determine "all versions" page url
        if (url.indexOf('downloads') > 0)
            url = url.substring(0, url.indexOf('downloads'));
        else if (url.indexOf('addons') > 0)
            url = url.substring(0, url.indexOf('addons'));
        url = url+'addons/versions/'+addonID;
        
        links.parent().before(app_compat_update_firefox);
        links.parent().after(sprintf(app_compat_try_old_version, url));
    } else {
        links.parent().before(app_compat_older_firefox_only);
        if (showVersionLink) // allow restoring install button for logged-in users only
            links.parent().after('<a href="#" '
                +'onclick="removeCompatibilityHint(\''+versionID+'\');return false;">'
                +app_compat_ignore_check+ '</a>');
    }
    
    return true;
}

/**
 * Remove "incompatible" message for given version ID, by restoring the
 * original button.
 */
function removeCompatibilityHint(versionID) {
    // find all hidden install buttons
    var orig = $('#orig-' + versionID);
    // remove compatibility hints
    orig.prev().remove();
    // show original buttons
    orig.attr('id', 'install-'+versionID);
    orig.show();
    
    return true;
}

/**
 * This function is used on the addon version page to create an element
 * at the top of the page with the most recent compatible version of an addon
 */
function createLatestVersionElement(get_latest_version_text) {
	var container = $("#latest-version-container");
	
	container.wrapInner("<p>" + get_latest_version_text + " (" + gLatestApplicationVersion + ")" + "</p>");
	container.append(gLatestAddonVersion);	
    var installButton = $("#install-"+ gLatestVersionID);
    var cloned = installButton.clone();
    cloned.attr('id', 'install-0'+ gLatestVersionID); // prepend 0 to this version id to make unique
	container.append(cloned);
	container.attr('id', 'latest-version');
	fixPlatformLinks("0"+ gLatestVersionID, ""); // show only one platform when dealing with most recent compatible app.
}

/**
 *  replaces options in a select drop-down (used for advanced search)
 *  @param select_id - the id of the select tag to replace the options of
 *  @param opt_array - array of options to use
 *  @param selected - which option will be marked as selected
 *
 */
function replaceOptions( select_id , opt_array, selected) {
  $(select_id + " > *").remove();
  
  for( opt in opt_array) {
	  sel_text = "";
      val = opt_array[opt];
      opt_obj = document.createElement("option");
	  if( val == selected) {opt_obj.selected = "selected";}
      opt_obj.value = val;
      opt_obj.appendChild(document.createTextNode(opt));	  
      $(select_id).append(opt_obj);  
  }
}

/**
 * replace noscript email by an actual link
 * @param obj id of email node
 * @param lp local part
 * @param hp host part
 */
function emailLink(obj, lp, hp) {
    var cont = document.getElementById(obj);
    var em = lp +'@'+ hp;
    var a = document.createElement('a');
    a.setAttribute('href', 'mailto:'+em);
    a.appendChild(document.createTextNode(em));
    cont.replaceChild(a, cont.lastChild);
}


/**
 * sprintf() implementation for Javascript
 * adapted from public domain code initially published at:
 * http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/
 */
function sprintf()
{
    if (!arguments || arguments.length < 1 || !RegExp) {
        return null;
    }
    var str = arguments[0];
    var re = /([^%]*)%((\d+)\$)?('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
    var a = b = [], numSubstitutions = 0, numMatches = 0;
    
    while ((a = re.exec(str))) {
        var leftpart = a[1], pPos = a[3], pPad = a[4], pJustify = a[5];
        var pMinLength = a[6], pPrecision = a[7], pType = a[8];
        var rightPart = a[9];

        numMatches++;
        if (pType == '%') {
            subst = '%';
        } else {
            if (pPos == '') {
                numSubstitutions++;
                pPos = numSubstitutions;
            }
            if (parseInt(pPos) >= arguments.length) {
                alert('Error! Not enough function arguments (' + (arguments.length - 1)
                    + ', excluding the string)\n'
                    + 'for the number of substitution parameters in string ('
                    + numSubstitutions + ' so far).');
            }
            var param = arguments[parseInt(pPos)];
            var pad = '';
            if (pPad && pPad.substr(0,1) == "'")
                pad = leftpart.substr(1,1);
            else if (pPad)
                pad = pPad;
            var justifyRight = true;
            if (pJustify && pJustify === "-") justifyRight = false;
            var minLength = -1;
            if (pMinLength) minLength = parseInt(pMinLength);
            var precision = -1;
            if (pPrecision && pType == 'f')
                precision = parseInt(pPrecision.substring(1));
            var subst = param;
         
            switch (pType) {
            case 'b':
                subst = parseInt(param).toString(2);
                break;
            case 'c':
                subst = String.fromCharCode(parseInt(param));
                break;
            case 'd':
                subst = parseInt(param) ? parseInt(param) : 0;
                break;
            case 'u':
                subst = Math.abs(param);
                break;
            case 'f':
                subst = (precision > -1)
                    ? Math.round(parseFloat(param) * Math.pow(10, precision))
                    / Math.pow(10, precision)
                    : parseFloat(param);
                break;
            case 'o':
                subst = parseInt(param).toString(8);
                break;
            case 's':
                subst = param;
                break;
            case 'x':
                subst = ('' + parseInt(param).toString(16)).toLowerCase();
                break;
            case 'X':
                subst = ('' + parseInt(param).toString(16)).toUpperCase();
                break;
            }
            var padLeft = minLength - subst.toString().length;
            if (padLeft > 0) {
                var arrTmp = new Array(padLeft+1);
                var padding = arrTmp.join(pad?pad:" ");
            } else {
                var padding = "";
            }
        }
        str = leftpart + padding + subst + rightPart;
    }
    return str;
}


/*
 ### jQuery Star Rating Plugin v2.0 - 2008-03-12 ###
 By Diego A, http://www.fyneworks.com, diego@fyneworks.com
 - v2 by Keith Wood, http://keith-wood.name/, kbwood@virginbroadband.com.au
 
 Project: http://plugins.jquery.com/project/MultipleFriendlyStarRating
 Website: http://www.fyneworks.com/jquery/star-rating/
	
	This is a modified version of the star rating plugin from:
 http://www.phpletter.com/Demo/Jquery-Star-Rating-Plugin/
*/
// ORIGINAL COMMENTS:
/*************************************************
 This is hacked version of star rating created by <a href="http://php.scripts.psu.edu/rja171/widgets/rating.php">Ritesh Agrawal</a>
 It thansform a set of radio type input elements to star rating type and remain the radio element name and value,
 so could be integrated with your form. It acts as a normal radio button.
 modified by : Logan Cai (cailongqun[at]yahoo.com.cn)
 website:www.phpletter.com
************************************************/


/*# AVOID COLLISIONS #*/
;if(jQuery) (function($){
/*# AVOID COLLISIONS #*/

$.fn.rating = function(settings) {
 settings = $.extend({
		cancel: '', // advisory title for the 'cancel' link
		cancelValue: 0,         // value to submit when user click the 'cancel' link
		required: false,         // disables the 'cancel' button so user can only select one of the specified values
		readOnly: false          // disable rating plugin interaction/ values cannot be changed
	}, settings || {});
 
  var container = this;

 // multiple star ratings on one page
 var groups = {};
 
	// plugin events
 var event = {
  fill: function(n, el, style){ // fill to the current mouse position.
	  this.drain(n);
	  $(el).prevAll('.star').andSelf().addClass( style || 'star_hover' );
  },
  drain: function(n) { // drain all the stars.
  	$(groups[n].valueElem).siblings('.star').
		 removeClass('star_on').removeClass('star_hover');
  },
  reset: function(n){ // Reset the stars to the default index.
  	if (!$(groups[n].currentElem).is('.cancel')) {
 		 $(groups[n].currentElem).prevAll('.star').andSelf().addClass('star_on');
	  }
  },
  click: function(n, el) { // Selected a star or cancelled
			groups[n].currentElem = el;
			var curValue = $(el).children('a').text();
			// Set value
			$(groups[n].valueElem).val(curValue);
			// Update display
			event.drain(n);
			event.reset(n);
			// callback function, as requested here: http://plugins.jquery.com/node/1655
			if(settings.callback) settings.callback.apply(groups[n].valueElem, [curValue, el]);
  }      
 };
 
	// loop through each matched element
	
	var radioButtons = $(this).find('input[@type=radio]');

	$(this).empty();
	$(this).removeClass('degrade');

 	radioButtons.each(function(i){
		// grouping:
		var n = this.name;
	    
		if(!groups[n]) groups[n] = {count: 0};
		i = groups[n].count;
		groups[n].count++;
		
		// Things to do with the first element...
		if(i == 0){
			// Accept readOnly setting from 'disabled' property
			settings.readOnly = $(this).attr('disabled') || settings.readOnly;
			// Create value element (disabled if readOnly)
		 groups[n].valueElem = $('<input type="hidden" name="' + n + '" value=""' + (settings.readOnly ? ' disabled="disabled"' : '') + '>');
			// Insert value element into form
   $(container).append(groups[n].valueElem);
 		
			if(settings.readOnly || settings.required){
			// DO NOT display 'cancel' button
			}
			else{
			 
    
			}
		}; // if (i == 0) (first element)
		
		// insert rating option right after preview element
		eStar = $('<div class="star"><a title="' + (this.title || this.value) + '">' + this.value + '</a></div>');
		$(container).append(eStar);

		if(settings.readOnly){
			// Mark star as readOnly so user can customize display
			$(eStar).addClass('star_readonly');
		}
		else{
			// Attach mouse events
			$(eStar)
			.mouseover(function(){ event.drain(n); event.fill(n, this); })
			.mouseout(function(){ event.drain(n); event.reset(n); })
			.click(function(){ event.click(n, this); });
		};
		
		//if(console) console.log(['###', n, this.checked, groups[n].initial]);
		if(this.checked) groups[n].currentElem = eStar;
		
		
		
		// reset display if last element
		if(i + 1 == this.length) event.reset(n);
	
	}); // each element
  	

	
	// initialize groups...
	for(n in groups)//{ not needed, save a byte!
		if(groups[n].currentElem){
			event.fill(n, groups[n].currentElem, 'star_on');
			$(groups[n].valueElem).val($(groups[n].currentElem).children('a').text());
		}
	//}; not needed, save a byte!
	
	return this; // don't break the chain...
};



/*# AVOID COLLISIONS #*/
})(jQuery);
/*# AVOID COLLISIONS #*/


function VersionCompare() {
    /**
     * Mozilla-style version numbers comparison in Javascript
     * (JS-translated version of PHP versioncompare component)
     * @return -1: a<b, 0: a==b, 1: a>b
     */
    this.compareVersions = function(a,b) {
        var al = a.split('.');
        var bl = b.split('.');
    
        for (var i=0; i<al.length || i<bl.length; i++) {
            var ap = (i<al.length ? al[i] : null);
            var bp = (i<bl.length ? bl[i] : null);
    
            var r = this.compareVersionParts(ap,bp);
            if (r != 0)
                return r;
        }
    
        return 0;
    }
    
    /**
     * helper function: compare a single version part
     */
    this.compareVersionParts = function(ap,bp) {
        var avp = this.parseVersionPart(ap);
        var bvp = this.parseVersionPart(bp);
    
        var r = this.cmp(avp['numA'],bvp['numA']);
        if (r) return r;
    
        r = this.strcmp(avp['strB'],bvp['strB']);
        if (r) return r;
    
        r = this.cmp(avp['numC'],bvp['numC']);
        if (r) return r;
    
        return this.strcmp(avp['extraD'],bvp['extraD']);
    }

    /**
     * helper function: parse a version part
     */
    this.parseVersionPart = function(p) {
        if (p == '*') {
            return {
                'numA'   : Number.MAX_VALUE,
                'strB'   : '',
                'numC'   : 0,
                'extraD' : ''
                };
        }

        var pattern = /^([-\d]*)([^-\d]*)([-\d]*)(.*)$/;
        var m = pattern.exec(p);

        var r = {
            'numA'  : parseInt(m[1]),
            'strB'   : m[2],
            'numC'   : parseInt(m[3]),
            'extraD' : m[4]
            };

        if (r['strB'] == '+') {
            r['numA']++;
            r['strB'] = 'pre';
        }

        return r;
    }

    /**
     * helper function: compare numeric version parts
     */
    this.cmp = function(an,bn) {
        if (isNaN(an)) an = 0;
        if (isNaN(bn)) bn = 0;
        
        if (an < bn)
            return -1;

        if (an > bn)
            return 1;

        return 0;
    }

    /**
     * helper function: compare string version parts
     */
    this.strcmp = function(as,bs) {
        if (as == bs)
            return 0;
        
        // any string comes *before* the empty string
        if (as == '')
            return 1;
        
        if (bs == '')
            return -1;
        
        // normal string comparison for non-empty strings (like strcmp)
        if (as < bs)
            return -1;
        else if(as > bs)
            return 1;
        else
            return 0;
    }
}

/**
 * jQuery slider plugin - 2008-07-07
 * lorchard@mozilla.com
 */

/*# AVOID COLLISIONS #*/
;if(jQuery) (function($){
/*# AVOID COLLISIONS #*/

$.fn.slider = function(settings) {
    var $slider = arguments.callee.support;
    new $slider(this[0].id, settings);
    return this;
}

$.fn.slider.support = function(slider_id, options) {
    this.init(slider_id, options);
};

$.fn.slider.support.prototype = function() {

    return {

        /**
         * Wire up the scroll events.
         */
        init: function(slider_id, options) {
            this.options = $.extend({
                duration: 250,
                prev_img_src: '/img/slider-prev.gif',
                prev_disabled_img_src: '/img/slider-prev-disabled.gif',
                next_img_src: '/img/slider-next.gif',
                next_disabled_img_src: '/img/slider-next-disabled.gif'
            }, options || {});

            this.slider_id  = slider_id;
            this.slider_sel = '#' + slider_id;

            this._resize_timer = null;

            var that = this;
            $(window).unload(function() { return that.onUnload(); });
            $(window).resize(function() { return that.onResize(); });
            $(document).ready(function() { return that.onReady(); });
        },

        /**
         * Perform scroller initialization on page readiness
         */
        onReady: function() {
            this.items = $(this.slider_sel + ' .item');
            this.item_idx = 0;
            
            var that = this;
            $(this.slider_sel + ' .controls .prev').click(function(e) { 
                return that.onClickPrev(e) 
            });
            $(this.slider_sel + ' .controls .next').click(function(e) { 
                return that.onClickNext(e) 
            });

            this.onResize();
        },

        /**
         * Unload some resources on page unload, being superstitous about a
         * memory leak.
         */
        onUnload: function() {
            delete this.items;
        },

        /**
         * React to browser resizing, with a delayed timer to prevent lots of
         * overlapping calls.
         */
        onResize: function() {
            var that = this;
            if (this._resize_timer)
                clearTimeout(this._resize_timer);
            this._resize_timer = window.setTimeout(function() {
                that._doResize();
            }, 50);
        },

        /**
         * Perform the actual work of readjusting after resize.
         */
        _doResize: function() {
            var slider = $(this.slider_sel);
            var viewport = slider.select('.viewport')[0];
            
            $(this.slider_sel + ' .addon').width(viewport.offsetWidth - 260);
            this.revealSelectedItem(15);
        },

        /**
         * React to "prev" button click.
         */
        onClickPrev: function(e) {
            if ( (this.item_idx - 1) < 0 ) return false;
            this.item_idx--;
            return this.changeSelectedItem();
        },

        /**
         * React to "next" button click.
         */
        onClickNext: function(e) {
            if ( (this.item_idx + 1) >= this.items.length ) return false;
            this.item_idx++;
            return this.changeSelectedItem();
        },

        /**
         * Update the selected item, item number, and button display states.
         */
        changeSelectedItem: function() {
            this.updateItemNumber();
            this.updateButtonStates();
            this.revealSelectedItem();
            return false;
        },

        /**
         * Update the number displayed indicating current item.
         */
        updateItemNumber: function() {
            $(this.slider_sel + ' .controls .index').text( this.item_idx + 1 );
        },

        /**
         * Update the enabled / disabled images for the next / prev buttons.
         */
        updateButtonStates: function() {
            var img_p = $(this.slider_sel + ' .controls .prev img')[0];
            if ( this.item_idx == 0 ) {
                img_p.src = this.options.prev_disabled_img_src;
            } else {
                img_p.src = this.options.prev_img_src;
            }

            var img_n = $(this.slider_sel + ' .controls .next img')[0];
            if ( this.item_idx == this.items.length - 1 ) {
                img_n.src = this.options.next_disabled_img_src;
            } else {
                img_n.src = this.options.next_img_src;
            }
        },

        /**
         * Reveal the selected item with an animation.
         */
        revealSelectedItem: function(delay) {
            if (!delay) delay = this.options.slide_duration;
            $(this.slider_sel + ' .viewport').animate({ 
                scrollLeft: this.items[ this.item_idx ].offsetLeft 
            }, delay)
        },

        EOF: null // I hate trailing comma errors.
    };
}();

/*# AVOID COLLISIONS #*/
})(jQuery);
/*# AVOID COLLISIONS #*/
