/**
 * @fileOverview Creates a new extended version of the jQuery.browser object,
 * jQuery.browserx.  The original browser object is moved to jQuery.browsero
 * and jQuery.browser is repointed to jQuery.browserx.
 * <p>
 * Based on JQBrowser - 
 *    http://davecardwell.co.uk/geekery/javascript/jquery/jqbrowser/
 * <p>
 * This version has a rewritten internal structure, uses properties like the
 * native jQuery browser object instead of functions, and adds version
 * support for Safari.
 * <p>
 * String properties default to an empty string, boolean values default to
 * false, and version.number defaults to 0.
 */

/** 
 * Extended browser object for jQuery.
 *
 * @type object
 */
jQuery.browserx = {

   /**
    * The name of the browser.  Defaults to an empty string.
    *
    * @type string
    */
   name: "",

   /**
    * Contains properties related to browser version.
    *
    * @type object
    */
   version: {

      /**
       * This is an attempt to coerce version.string into
       * an actual number.  This is done by preserving the leftmost
       * decimal point if there is one, and converting the number to a
       * float.  This helps capture more of the versioning info.  For
       * example, '2.1.4.53' would become 2.1453.  This makes it easier to
       * test against (jQuery.browserx.version.number > 2.1).  Defaults to 0.0.
       *
       * @type float
       */
      number: 0.0,

      /**
       * Contains the browser version. Defaults to empty string.
       *
       * @type string
       */
      string: ""
   },

   /**
    * True if using AOL Explorer.
    *
    * @type boolean
    */
   aol: false,

   /**
    * True if using Camino.
    *
    * @type boolean
    */
   camino: false,

   /**
    * True if using Firefox.
    *
    * @type boolean
    */
   firefox: false,

   /**
    * True if using Flock.
    *
    * @type boolean
    */
   flock: false,

   /**
    * True if using iCab.
    *
    * @type boolean
    */
   icab: false,

   /**
    * True if using Konqueror.
    *
    * @type boolean
    */
   konqueror: false,

   /**
    * True if using Mozilla.
    *
    * @type boolean
    */
   mozilla: false,

   /**
    * True if using Internet Explorer.
    *
    * @type boolean
    */
   msie: false,

   /**
    * True if using Netscape.
    *
    * @type boolean
    */
   netscape: false,

   /**
    * True if using Opera.
    *
    * @type boolean
    */
   opera: false,

   /**
    * True if using Safari.
    *
    * @type boolean
    */
   safari: false,

   /** 
    * Contains properties related to the browser's operating system.
    *
    * @type object
    */
   OS:  {

      /**
       * The name of the OS, either Linux, Mac, or Win. Defaults to 
       * an empty string.
       *
       * @type string
       */
      name: "",

      /**
       * True if using Linux.
       *
       * @type boolean
       */
      linux: false,

      /**
       * True if using Macintosh.
       *
       * @type boolean
       */
      mac: false,

      /**
       * True if using Windows.
       *
       * @type boolean
       */
      win: false
   }

};

// repoint objects
jQuery.browsero = jQuery.browser;
jQuery.browser  = jQuery.browserx;

/** 
 * Executes portion of script dealing with setting of browserx properties
 * within an anonymous scope to keep transient objects out of global.
 *
 * @name     anonymous
 * @function
 * @private
*/
new function() {

   /**
    * This class is instantiated to represent a particular browser.
    * Functions may rely on the agent or vendor aliases.
    *
    * @constructor
    * @memberOf    anonymous
    * @param       {object} props
    * @config      {string} name The full name of the browser.
    * @config      {string} id The identifier used by browserPlus.
    * @config      {function} isCurrent Test used to identify the browser.
    * @config      {function} getVersion Gets the browser version.
    * @private
    */
   function Browser(props) {

      /**
       * The full name of the browser.  If not supplied in props, defaults to
       * an empty string.
       *
       * @type    string
       * @private
       */
      this.name = props.name ? props.name : "";

      /**
       * The identifier used by browserPlus. If not supplied in props, defaults to
       * browser.name in lowercase.
       *
       * @type    string
       * @private
       */
      this.id = props.id ? props.id : this.name.toLowerCase();

      /**
       * Tests to see if this is the browser being currently used. If not
       * supplied in props, returns false by default.
       *
       * @function
       * @returns  {boolean} True if this browser is the current browser.
       * @private
       */
      this.isCurrent = props.isCurrent ? props.isCurrent : function(){return false;};

      /**
       * Gets the version of the current browser. If not supplied in props,
       * uses a default function that looks for the version following the 
       * browser name in the user agent string.
       *
       * @function
       * @returns  {string} The browser version.
       * @private
       */
      this.getVersion = props.getVersion ? props.getVersion : defaultGetVersion;

      /**
       * A private inner function of Browser that is set to getVersion if
       * no getVersion is passed in.  Looks for the browser version after
       * the browser name in the user agent string.
       *
       * @function
       * @memberOf Browser
       * @name     defaultGetVersion
       * @returns  {string} The browser version.
       * @private
       */
      function defaultGetVersion()             {
         var re = new RegExp(
            this.name + '(?:\\s|\\/)(\\d+(?:\\.\\d+)+(?:(?:a|b)\\d*)?)'
         );
         var match = this.agent.match(re);
         return match ? match[1] : "";
      };
   };

   /**
    * Alias for navigator.userAgent.  Defaults to an empty string.
    *
    * @type    string
    * @private
    */
   Browser.prototype.agent =
         (navigator && navigator.userAgent) ? navigator.userAgent : "";

   /**
    * Alias for navigator.vendor.  Defaults to an empty string.
    *
    * @type    string
    * @private
    */
   Browser.prototype.vendor =
         (navigator && navigator.vendor) ? navigator.vendor : "";

   /**
    * Parses the version string into a float.  Tries to preserve as much
    * version information as possible by removing any decimal points other
    * than the leftmost.  For example, '2.1.4.53' would become 2.1453.
    *
    * @returns {float}  The version string as a float.
    * @private
    */
   Browser.prototype.parseVersion = function () {
      var version = this.getVersion();
      var split = version.split(".");

      if (split.length > 2)
         version = split.shift() + "." + split.shift() + split.join("");

      return parseFloat(version) ? parseFloat(version) : 0;
   };

   /**
    * Called when this is the current browser to set the appropriate values
    * in the browserx object.
    *
    * @private
    */
   Browser.prototype.set = function () {
      jQuery.browserx[this.id]       = true;
      jQuery.browserx.name           = this.name;
      jQuery.browserx.version.string = this.getVersion();
      jQuery.browserx.version.number = this.parseVersion();
   };

   /**
    * Represent a particular operating system
    * that the browser is running on.  The isCurrent function can rely on
    * the platform alias.
    *
    * @constructor
    * @memberOf    anonymous
    * @param       {object} props
    * @config      {string} name The basic name of the browser OS.
    * @config      {string} id The OS identifier used by browserx.
    * @config      {function} isCurrent Tests to see if this is the current OS.
    * @private
    */
   function OS(props) {

      /**
       * The basic name of the browser OS.  If not supplied in props, defaults
       * to an empty string.
       *
       * @type    string
       * @private
       */
      this.name = props.name ? props.name : "";

      /**
       * The OS identifier used by browserx.  If not supplied in props, defaults
       * to the name in lower case.
       *
       * @type    string
       * @private
       */
      this.id = props.id ? props.id : this.name.toLowerCase();

      /**
       * Tests to see if this is the OS being currently used. If not
       * supplied in props, returns false by default.
       *
       * @function
       * @returns  {boolean} True if this OS is the current OS.
       * @private
       */
      this.isCurrent = props.isCurrent ? props.isCurrent : function(){return false;};

   };

   /**
    * Alias for navigator.platform.  Defaults to an empty string.
    *
    * @type    string
    * @private
    */
   OS.prototype.platform =
         (navigator && navigator.platform) ? navigator.platform : "";

   /**
    * Called when this is the current OS to set the appropriate values
    * in the browserx object.
    *
    * @private
    */
   OS.prototype.set = function () {
      jQuery.browserx.OS.name     = this.name;
      jQuery.browserx.OS[this.id] = true;
   };


   /**
    * The browsers that we are testing.  Order matters.
    *
    * @type     array
    * @memberOf anonymous
    * @private
    */
   var browserProps = [

      {  name: "Safari",
         isCurrent: function() {return /Apple/.test(this.vendor);},
         getVersion: function() {
            var match = this.agent.match(/Safari\/([0-9\.]*)/);
            var lookup;
            if (match)
                lookup = safariBuildVersionMap[match[1]];
            return lookup ? lookup : "";
         }
      },

      {  name: "Opera",
         isCurrent: function() {return window.opera != undefined;}
      },

      {  name: "iCab",
         isCurrent: function() {return /iCab/.test(this.vendor);}
      },

      {  name: "Konqueror",
         isCurrent: function() {return /KDE/.test(this.vendor);}
      },

      {  name: "AOL Explorer",
         id: "aol",
         isCurrent: function() {
            return /America Online Browser/.test(this.agent);
         },
         getVersion: function() {
            var match = this.agent.match(/rev(\d+(?:\.\d+)+)/);
            return match ? match[1] : "";
         }
      },

      {  name: "Flock",
         isCurrent: function() {return /Flock/.test(this.agent);}
      },

      {  name: "Camino",
         isCurrent: function() {return /Camino/.test(this.vendor);}
      },

      {  name: "Firefox",
         isCurrent: function() {return /Firefox/.test(this.agent);}
      },

      {  name: "Netscape",
         isCurrent: function() {return /Netscape/.test(this.agent);}
      },

      {  name: "Internet Explorer",
         id: "msie",
         isCurrent: function() {return /MSIE/.test(this.agent);},
         getVersion: function() {
            var match = this.agent.match(/MSIE (\d+(?:\.\d+)+(?:b\d*)?)/);
            return match ? match[1] : "";
         }
      },

      {  name: "Mozilla",
         isCurrent: function() {return /Gecko|Mozilla/.test(this.agent);},
         getVersion: function() {
            var match = this.agent.match(/rv:(\d+(?:\.\d+)+)/);
            return match ? match[1] : "";
         }
      }

   ];

   /**
    * Defines all the operating systems that we are testing.
    *
    * @type     array
    * @memberOf anonymous
    * @private
    */
   var osProps = [

      {  name: "Windows",
         id: "win",
         isCurrent: function() {return /Win/.test(this.platform);}
      },

      {  name: "Mac",
         isCurrent: function() {return /Mac/.test(this.platform);}
      },

      {  name: "Linux",
         isCurrent: function() {return /Linux/.test(this.platform);}
      }

   ];

   /**
    * An associative array with the internal Safari build number as key,
    * and Safari version number as value.
    * <p>
    * Information obtained from:
    *     http://developer.apple.com/internet/safari/uamatrix.html
    *
    * @type     object
    * @memberOf anonymous
    * @private
    */
   var safariBuildVersionMap = {
      // Jaguar - Mac OS X 10.2.x
         '85.5': '1.0',
         '85.7': '1.0.2',
         '85.8': '1.0.3',
       '85.8.1': '1.0.3',

      // Panther - Mac OS X 10.3.x
          '100': '1.1',
        '100.1': '1.1.1',
        '125.7': '1.2.2',
        '125.8': '1.2.2',
        '125.9': '1.2.3',
       '125.11': '1.2.4',
       '125.12': '1.2.4',
          '312': '1.3',
        '312.3': '1.3.1',
      '312.3.1': '1.3.1',
        '312.5': '1.3.2',
        '312.6': '1.3.2',

      // Tiger - Mac OS X 10.4.x
          '412': '2.0',
        '412.2': '2.0',
      '412.2.2': '2.0',
        '412.5': '2.0.1',
       '416.12': '2.0.2',
       '416.13': '2.0.2',
        '417.8': '2.0.3',
      '417.9.2': '2.0.3',
      '417.9.3': '2.0.3',
        '419.3': '2.0.4',

      // 3.0 Beta - Mac OS X 10.4.10 and XP/Vista
     '522.13.1': '3.0.2',
       '522.12': '3.0.2'
   };

   // loop over browsers, find current, and set
   var browser;
   for (var i=0; i < browserProps.length; i++) {
      browser = new Browser(browserProps[i]);
      if (browser.isCurrent()) {
         browser.set();
         break;
      }
   }

   // loop over operating systems, find current, and set
   var os;
   for (var i=0; i < osProps.length; i++) {
      os = new OS(osProps[i]);
      if (os.isCurrent()) {
         os.set();
         break;
      }
   }

};