API Docs for: 3.8.0
Show:

File: yui/js/yui-ua.js

  1. /**
  2.  * The YUI module contains the components required for building the YUI seed
  3.  * file.  This includes the script loading mechanism, a simple queue, and the
  4.  * core utilities for the library.
  5.  * @module yui
  6.  * @submodule yui-base
  7.  */

  8. /**
  9.  * YUI user agent detection.
  10.  * Do not fork for a browser if it can be avoided.  Use feature detection when
  11.  * you can.  Use the user agent as a last resort.  For all fields listed
  12.  * as @type float, UA stores a version number for the browser engine,
  13.  * 0 otherwise.  This value may or may not map to the version number of
  14.  * the browser using the engine.  The value is presented as a float so
  15.  * that it can easily be used for boolean evaluation as well as for
  16.  * looking for a particular range of versions.  Because of this,
  17.  * some of the granularity of the version info may be lost.  The fields that
  18.  * are @type string default to null.  The API docs list the values that
  19.  * these fields can have.
  20.  * @class UA
  21.  * @static
  22.  */

  23. /**
  24. * Static method on `YUI.Env` for parsing a UA string.  Called at instantiation
  25. * to populate `Y.UA`.
  26. *
  27. * @static
  28. * @method parseUA
  29. * @param {String} [subUA=navigator.userAgent] UA string to parse
  30. * @return {Object} The Y.UA object
  31. */
  32. YUI.Env.parseUA = function(subUA) {

  33.     var numberify = function(s) {
  34.             var c = 0;
  35.             return parseFloat(s.replace(/\./g, function() {
  36.                 return (c++ === 1) ? '' : '.';
  37.             }));
  38.         },

  39.         win = Y.config.win,

  40.         nav = win && win.navigator,

  41.         o = {

  42.         /**
  43.          * Internet Explorer version number or 0.  Example: 6
  44.          * @property ie
  45.          * @type float
  46.          * @static
  47.          */
  48.         ie: 0,

  49.         /**
  50.          * Opera version number or 0.  Example: 9.2
  51.          * @property opera
  52.          * @type float
  53.          * @static
  54.          */
  55.         opera: 0,

  56.         /**
  57.          * Gecko engine revision number.  Will evaluate to 1 if Gecko
  58.          * is detected but the revision could not be found. Other browsers
  59.          * will be 0.  Example: 1.8
  60.          * <pre>
  61.          * Firefox 1.0.0.4: 1.7.8   <-- Reports 1.7
  62.          * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8
  63.          * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81
  64.          * Firefox 3.0   <-- 1.9
  65.          * Firefox 3.5   <-- 1.91
  66.          * </pre>
  67.          * @property gecko
  68.          * @type float
  69.          * @static
  70.          */
  71.         gecko: 0,

  72.         /**
  73.          * AppleWebKit version.  KHTML browsers that are not WebKit browsers
  74.          * will evaluate to 1, other browsers 0.  Example: 418.9
  75.          * <pre>
  76.          * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
  77.          *                                   latest available for Mac OSX 10.3.
  78.          * Safari 2.0.2:         416     <-- hasOwnProperty introduced
  79.          * Safari 2.0.4:         418     <-- preventDefault fixed
  80.          * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
  81.          *                                   different versions of webkit
  82.          * Safari 2.0.4 (419.3): 419     <-- Tiger installations that have been
  83.          *                                   updated, but not updated
  84.          *                                   to the latest patch.
  85.          * Webkit 212 nightly:   522+    <-- Safari 3.0 precursor (with native
  86.          * SVG and many major issues fixed).
  87.          * Safari 3.0.4 (523.12) 523.12  <-- First Tiger release - automatic
  88.          * update from 2.x via the 10.4.11 OS patch.
  89.          * Webkit nightly 1/2008:525+    <-- Supports DOMContentLoaded event.
  90.          *                                   yahoo.com user agent hack removed.
  91.          * </pre>
  92.          * http://en.wikipedia.org/wiki/Safari_version_history
  93.          * @property webkit
  94.          * @type float
  95.          * @static
  96.          */
  97.         webkit: 0,

  98.         /**
  99.          * Safari will be detected as webkit, but this property will also
  100.          * be populated with the Safari version number
  101.          * @property safari
  102.          * @type float
  103.          * @static
  104.          */
  105.         safari: 0,

  106.         /**
  107.          * Chrome will be detected as webkit, but this property will also
  108.          * be populated with the Chrome version number
  109.          * @property chrome
  110.          * @type float
  111.          * @static
  112.          */
  113.         chrome: 0,

  114.         /**
  115.          * The mobile property will be set to a string containing any relevant
  116.          * user agent information when a modern mobile browser is detected.
  117.          * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
  118.          * devices with the WebKit-based browser, and Opera Mini.
  119.          * @property mobile
  120.          * @type string
  121.          * @default null
  122.          * @static
  123.          */
  124.         mobile: null,

  125.         /**
  126.          * Adobe AIR version number or 0.  Only populated if webkit is detected.
  127.          * Example: 1.0
  128.          * @property air
  129.          * @type float
  130.          */
  131.         air: 0,
  132.         /**
  133.          * PhantomJS version number or 0.  Only populated if webkit is detected.
  134.          * Example: 1.0
  135.          * @property phantomjs
  136.          * @type float
  137.          */
  138.         phantomjs: 0,
  139.         /**
  140.          * Detects Apple iPad's OS version
  141.          * @property ipad
  142.          * @type float
  143.          * @static
  144.          */
  145.         ipad: 0,
  146.         /**
  147.          * Detects Apple iPhone's OS version
  148.          * @property iphone
  149.          * @type float
  150.          * @static
  151.          */
  152.         iphone: 0,
  153.         /**
  154.          * Detects Apples iPod's OS version
  155.          * @property ipod
  156.          * @type float
  157.          * @static
  158.          */
  159.         ipod: 0,
  160.         /**
  161.          * General truthy check for iPad, iPhone or iPod
  162.          * @property ios
  163.          * @type Boolean
  164.          * @default null
  165.          * @static
  166.          */
  167.         ios: null,
  168.         /**
  169.          * Detects Googles Android OS version
  170.          * @property android
  171.          * @type float
  172.          * @static
  173.          */
  174.         android: 0,
  175.         /**
  176.          * Detects Kindle Silk
  177.          * @property silk
  178.          * @type float
  179.          * @static
  180.          */
  181.         silk: 0,
  182.         /**
  183.          * Detects Kindle Silk Acceleration
  184.          * @property accel
  185.          * @type Boolean
  186.          * @static
  187.          */
  188.         accel: false,
  189.         /**
  190.          * Detects Palms WebOS version
  191.          * @property webos
  192.          * @type float
  193.          * @static
  194.          */
  195.         webos: 0,

  196.         /**
  197.          * Google Caja version number or 0.
  198.          * @property caja
  199.          * @type float
  200.          */
  201.         caja: nav && nav.cajaVersion,

  202.         /**
  203.          * Set to true if the page appears to be in SSL
  204.          * @property secure
  205.          * @type boolean
  206.          * @static
  207.          */
  208.         secure: false,

  209.         /**
  210.          * The operating system.  Currently only detecting windows or macintosh
  211.          * @property os
  212.          * @type string
  213.          * @default null
  214.          * @static
  215.          */
  216.         os: null,

  217.         /**
  218.          * The Nodejs Version
  219.          * @property nodejs
  220.          * @type float
  221.          * @default 0
  222.          * @static
  223.          */
  224.         nodejs: 0,
  225.         /**
  226.         * Window8/IE10 Application host environment
  227.         * @property winjs
  228.         * @type Boolean
  229.         * @static
  230.         */
  231.         winjs: !!((typeof Windows !== "undefined") && Windows.System),
  232.         /**
  233.         * Are touch/msPointer events available on this device
  234.         * @property touchEnabled
  235.         * @type Boolean
  236.         * @static
  237.         */
  238.         touchEnabled: false
  239.     },

  240.     ua = subUA || nav && nav.userAgent,

  241.     loc = win && win.location,

  242.     href = loc && loc.href,

  243.     m;

  244.     /**
  245.     * The User Agent string that was parsed
  246.     * @property userAgent
  247.     * @type String
  248.     * @static
  249.     */
  250.     o.userAgent = ua;


  251.     o.secure = href && (href.toLowerCase().indexOf('https') === 0);

  252.     if (ua) {

  253.         if ((/windows|win32/i).test(ua)) {
  254.             o.os = 'windows';
  255.         } else if ((/macintosh|mac_powerpc/i).test(ua)) {
  256.             o.os = 'macintosh';
  257.         } else if ((/android/i).test(ua)) {
  258.             o.os = 'android';
  259.         } else if ((/symbos/i).test(ua)) {
  260.             o.os = 'symbos';
  261.         } else if ((/linux/i).test(ua)) {
  262.             o.os = 'linux';
  263.         } else if ((/rhino/i).test(ua)) {
  264.             o.os = 'rhino';
  265.         }

  266.         // Modern KHTML browsers should qualify as Safari X-Grade
  267.         if ((/KHTML/).test(ua)) {
  268.             o.webkit = 1;
  269.         }
  270.         if ((/IEMobile|XBLWP7/).test(ua)) {
  271.             o.mobile = 'windows';
  272.         }
  273.         if ((/Fennec/).test(ua)) {
  274.             o.mobile = 'gecko';
  275.         }
  276.         // Modern WebKit browsers are at least X-Grade
  277.         m = ua.match(/AppleWebKit\/([^\s]*)/);
  278.         if (m && m[1]) {
  279.             o.webkit = numberify(m[1]);
  280.             o.safari = o.webkit;

  281.             if (/PhantomJS/.test(ua)) {
  282.                 m = ua.match(/PhantomJS\/([^\s]*)/);
  283.                 if (m && m[1]) {
  284.                     o.phantomjs = numberify(m[1]);
  285.                 }
  286.             }

  287.             // Mobile browser check
  288.             if (/ Mobile\//.test(ua) || (/iPad|iPod|iPhone/).test(ua)) {
  289.                 o.mobile = 'Apple'; // iPhone or iPod Touch

  290.                 m = ua.match(/OS ([^\s]*)/);
  291.                 if (m && m[1]) {
  292.                     m = numberify(m[1].replace('_', '.'));
  293.                 }
  294.                 o.ios = m;
  295.                 o.os = 'ios';
  296.                 o.ipad = o.ipod = o.iphone = 0;

  297.                 m = ua.match(/iPad|iPod|iPhone/);
  298.                 if (m && m[0]) {
  299.                     o[m[0].toLowerCase()] = o.ios;
  300.                 }
  301.             } else {
  302.                 m = ua.match(/NokiaN[^\/]*|webOS\/\d\.\d/);
  303.                 if (m) {
  304.                     // Nokia N-series, webOS, ex: NokiaN95
  305.                     o.mobile = m[0];
  306.                 }
  307.                 if (/webOS/.test(ua)) {
  308.                     o.mobile = 'WebOS';
  309.                     m = ua.match(/webOS\/([^\s]*);/);
  310.                     if (m && m[1]) {
  311.                         o.webos = numberify(m[1]);
  312.                     }
  313.                 }
  314.                 if (/ Android/.test(ua)) {
  315.                     if (/Mobile/.test(ua)) {
  316.                         o.mobile = 'Android';
  317.                     }
  318.                     m = ua.match(/Android ([^\s]*);/);
  319.                     if (m && m[1]) {
  320.                         o.android = numberify(m[1]);
  321.                     }

  322.                 }
  323.                 if (/Silk/.test(ua)) {
  324.                     m = ua.match(/Silk\/([^\s]*)\)/);
  325.                     if (m && m[1]) {
  326.                         o.silk = numberify(m[1]);
  327.                     }
  328.                     if (!o.android) {
  329.                         o.android = 2.34; //Hack for desktop mode in Kindle
  330.                         o.os = 'Android';
  331.                     }
  332.                     if (/Accelerated=true/.test(ua)) {
  333.                         o.accel = true;
  334.                     }
  335.                 }
  336.             }

  337.             m = ua.match(/(Chrome|CrMo|CriOS)\/([^\s]*)/);
  338.             if (m && m[1] && m[2]) {
  339.                 o.chrome = numberify(m[2]); // Chrome
  340.                 o.safari = 0; //Reset safari back to 0
  341.                 if (m[1] === 'CrMo') {
  342.                     o.mobile = 'chrome';
  343.                 }
  344.             } else {
  345.                 m = ua.match(/AdobeAIR\/([^\s]*)/);
  346.                 if (m) {
  347.                     o.air = m[0]; // Adobe AIR 1.0 or better
  348.                 }
  349.             }
  350.         }

  351.         if (!o.webkit) { // not webkit
  352. // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
  353.             if (/Opera/.test(ua)) {
  354.                 m = ua.match(/Opera[\s\/]([^\s]*)/);
  355.                 if (m && m[1]) {
  356.                     o.opera = numberify(m[1]);
  357.                 }
  358.                 m = ua.match(/Version\/([^\s]*)/);
  359.                 if (m && m[1]) {
  360.                     o.opera = numberify(m[1]); // opera 10+
  361.                 }

  362.                 if (/Opera Mobi/.test(ua)) {
  363.                     o.mobile = 'opera';
  364.                     m = ua.replace('Opera Mobi', '').match(/Opera ([^\s]*)/);
  365.                     if (m && m[1]) {
  366.                         o.opera = numberify(m[1]);
  367.                     }
  368.                 }
  369.                 m = ua.match(/Opera Mini[^;]*/);

  370.                 if (m) {
  371.                     o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
  372.                 }
  373.             } else { // not opera or webkit
  374.                 m = ua.match(/MSIE\s([^;]*)/);
  375.                 if (m && m[1]) {
  376.                     o.ie = numberify(m[1]);
  377.                 } else { // not opera, webkit, or ie
  378.                     m = ua.match(/Gecko\/([^\s]*)/);
  379.                     if (m) {
  380.                         o.gecko = 1; // Gecko detected, look for revision
  381.                         m = ua.match(/rv:([^\s\)]*)/);
  382.                         if (m && m[1]) {
  383.                             o.gecko = numberify(m[1]);
  384.                         }
  385.                     }
  386.                 }
  387.             }
  388.         }
  389.     }

  390.     //Check for known properties to tell if touch events are enabled on this device or if
  391.     //the number of MSPointer touchpoints on this device is greater than 0.
  392.     if (win && nav && !(o.chrome && o.chrome < 6)) {
  393.         o.touchEnabled = (("ontouchstart" in win) || (("msMaxTouchPoints" in nav) && (nav.msMaxTouchPoints > 0)));
  394.     }

  395.     //It was a parsed UA, do not assign the global value.
  396.     if (!subUA) {

  397.         if (typeof process === 'object') {

  398.             if (process.versions && process.versions.node) {
  399.                 //NodeJS
  400.                 o.os = process.platform;
  401.                 o.nodejs = numberify(process.versions.node);
  402.             }
  403.         }

  404.         YUI.Env.UA = o;

  405.     }

  406.     return o;
  407. };


  408. Y.UA = YUI.Env.UA || YUI.Env.parseUA();

  409. /**
  410. Performs a simple comparison between two version numbers, accounting for
  411. standard versioning logic such as the fact that "535.8" is a lower version than
  412. "535.24", even though a simple numerical comparison would indicate that it's
  413. greater. Also accounts for cases such as "1.1" vs. "1.1.0", which are
  414. considered equivalent.

  415. Returns -1 if version _a_ is lower than version _b_, 0 if they're equivalent,
  416. 1 if _a_ is higher than _b_.

  417. Versions may be numbers or strings containing numbers and dots. For example,
  418. both `535` and `"535.8.10"` are acceptable. A version string containing
  419. non-numeric characters, like `"535.8.beta"`, may produce unexpected results.

  420. @method compareVersions
  421. @param {Number|String} a First version number to compare.
  422. @param {Number|String} b Second version number to compare.
  423. @return -1 if _a_ is lower than _b_, 0 if they're equivalent, 1 if _a_ is
  424.     higher than _b_.
  425. **/
  426. Y.UA.compareVersions = function (a, b) {
  427.     var aPart, aParts, bPart, bParts, i, len;

  428.     if (a === b) {
  429.         return 0;
  430.     }

  431.     aParts = (a + '').split('.');
  432.     bParts = (b + '').split('.');

  433.     for (i = 0, len = Math.max(aParts.length, bParts.length); i < len; ++i) {
  434.         aPart = parseInt(aParts[i], 10);
  435.         bPart = parseInt(bParts[i], 10);

  436.         isNaN(aPart) && (aPart = 0);
  437.         isNaN(bPart) && (bPart = 0);

  438.         if (aPart < bPart) {
  439.             return -1;
  440.         }

  441.         if (aPart > bPart) {
  442.             return 1;
  443.         }
  444.     }

  445.     return 0;
  446. };

  447.