(function (global) { var detector = {}; var na_version = "-1"; var win = this; var external; var re_msie = /\b(?:msie |ie |trident\/[0-9].*rv[ :])([0-9.]+)/; var re_blackberry_10 = /\bbb10\b.+?\bversion\/([\d.]+)/; var re_blackberry_6_7 = /\bblackberry\b.+\bversion\/([\d.]+)/; var re_blackberry_4_5 = /\bblackberry\d+\/([\d.]+)/; function tostring(object){ return object.prototype.tostring.call(object); } function isobject(object){ return tostring(object) === "[object object]"; } function isfunction(object){ return tostring(object) === "[object function]"; } function each(object, factory, argument){ for(var i=0,b,l=object.length; i= 0) { return /\bblackberry\s?(\d+)/; } return "bb10"; }] ]; // 操作系统信息识别表达式 var os = [ ["wp", function(ua){ if(ua.indexof("windows phone ") !== -1){ return /\bwindows phone (?:os )?([0-9.]+)/; }else if(ua.indexof("xblwp") !== -1){ return /\bxblwp([0-9.]+)/; }else if(ua.indexof("zunewp") !== -1){ return /\bzunewp([0-9.]+)/; } return "windows phone"; }], ["windows", /\bwindows nt ([0-9.]+)/], ["macosx", /\bmac os x ([0-9._]+)/], ["ios", function(ua){ if(/\bcpu(?: iphone)? os /.test(ua)){ return /\bcpu(?: iphone)? os ([0-9._]+)/; }else if(ua.indexof("iph os ") !== -1){ return /\biph os ([0-9_]+)/; }else{ return /\bios\b/; } }], ["yunos", /\baliyunos ([0-9.]+)/], ["android", function(ua){ if(ua.indexof("android") >= 0){ return /\bandroid[ \/-]?([0-9.x]+)?/; }else if(ua.indexof("adr") >= 0){ if(ua.indexof("mqqbrowser") >= 0){ return /\badr[ ]\(linux; u; ([0-9.]+)?/; }else{ return /\badr(?:[ ]([0-9.]+))?/; } } return "android"; //return /\b(?:android|\badr)(?:[\/\- ](?:\(linux; u; )?)?([0-9.x]+)?/; }], ["chromeos", /\bcros i686 ([0-9.]+)/], ["linux", "linux"], ["windowsce", /\bwindows ce(?: ([0-9.]+))?/], ["symbian", /\bsymbian(?:os)?\/([0-9.]+)/], ["blackberry", function(ua){ var m = ua.match(re_blackberry_10) || ua.match(re_blackberry_6_7) || ua.match(re_blackberry_4_5); return m ? {version: m[1]} : "blackberry"; }] ]; // 解析使用 trident 内核的浏览器的 `浏览器模式` 和 `文档模式` 信息。 // @param {string} ua, useragent string. // @return {object} function iemode(ua){ if(!re_msie.test(ua)){return null;} var m, enginemode, engineversion, browsermode, browserversion, compatible=false; // ie8 及其以上提供有 trident 信息, // 默认的兼容模式,ua 中 trident 版本不发生变化。 if(ua.indexof("trident/") !== -1){ m = /\btrident\/([0-9.]+)/.exec(ua); if(m && m.length>=2){ // 真实引擎版本。 engineversion = m[1]; var v_version = m[1].split("."); v_version[0] = parseint(v_version[0], 10) + 4; browserversion = v_version.join("."); } } m = re_msie.exec(ua); browsermode = m[1]; var v_mode = m[1].split("."); if("undefined" === typeof browserversion){ browserversion = browsermode; } v_mode[0] = parseint(v_mode[0], 10) - 4; enginemode = v_mode.join("."); if("undefined" === typeof engineversion){ engineversion = enginemode; } return { browserversion: browserversion, browsermode: browsermode, engineversion: engineversion, enginemode: enginemode, compatible: engineversion !== enginemode }; } // 针对同源的 theworld 和 360 的 external 对象进行检测。 // @param {string} key, 关键字,用于检测浏览器的安装路径中出现的关键字。 // @return {undefined,boolean,object} 返回 undefined 或 false 表示检测未命中。 function checktw360external(key){ if(!external){return;} // return undefined. try{ // 360安装路径: // c:%5cprogra~1%5c360%5c360se3%5c360se.exe var runpath = external.twgetrunpath.tolowercase(); // 360se 3.x ~ 5.x support. // 暴露的 external.twgetversion 和 external.twgetsecurityid 均为 undefined。 // 因此只能用 try/catch 而无法使用特性判断。 var security = external.twgetsecurityid(win); var version = external.twgetversion(security); if(runpath && runpath.indexof(key) === -1){return false;} if(version){return {version: version};} }catch(ex){} } var engine = [ ["trident", re_msie], ["blink", function(ua){ return "chrome" in win && "css" in win && /\bapplewebkit[\/]?([0-9.+]+)/; }], ["webkit", /\bapplewebkit[\/]?([0-9.+]+)/], ["gecko", function(ua){ var match; if (match = ua.match(/\brv:([\d\w.]+).*\bgecko\/(\d+)/)) { return { version: match[1] + "." + match[2] } } }], ["presto", /\bpresto\/([0-9.]+)/], ["androidwebkit", /\bandroidwebkit\/([0-9.]+)/], ["coolpadwebkit", /\bcoolpadwebkit\/([0-9.]+)/], ["u2", /\bu2\/([0-9.]+)/], ["u3", /\bu3\/([0-9.]+)/] ]; var browser = [ // sogou. ["sogou", function(ua){ if (ua.indexof("sogoumobilebrowser") >= 0) { return /sogoumobilebrowser\/([0-9.]+)/ } else if (ua.indexof("sogoumse") >= 0){ return true; } return / se ([0-9.x]+)/; }], // theworld (世界之窗) // 由于裙带关系,theworld api 与 360 高度重合。 // 只能通过 ua 和程序安装路径中的应用程序名来区分。 // theworld 的 ua 比 360 更靠谱,所有将 theworld 的规则放置到 360 之前。 ["theworld", function(ua){ var x = checktw360external("theworld"); if(typeof x !== "undefined"){return x;} return "theworld"; }], // 360se, 360ee. ["360", function(ua) { var x = checktw360external("360se"); if(typeof x !== "undefined"){return x;} if(ua.indexof("360 aphone browser") !== -1){ return /\b360 aphone browser \(([^\)]+)\)/; } return /\b360(?:se|ee|chrome|browser)\b/; }], // maxthon ["maxthon", function(ua){ try{ if(external && (external.mxversion || external.max_version)){ return { version: external.mxversion || external.max_version }; } }catch(ex){} return /\b(?:maxthon|mxbrowser)(?:[ \/]([0-9.]+))?/; }], ["qq", /\bm?qqbrowser\/([0-9.]+)/], ["green", "greenbrowser"], ["tt", /\btencenttraveler ([0-9.]+)/], ["liebao", function(ua){ if (ua.indexof("liebaofast") >= 0){ return /\bliebaofast\/([0-9.]+)/; } if(ua.indexof("lbbrowser") === -1){return false;} var version; try{ if(external && external.liebaogetversion){ version = external.liebaogetversion(); } }catch(ex){} return { version: version || na_version }; }], ["tao", /\btaobrowser\/([0-9.]+)/], ["coolnovo", /\bcoolnovo\/([0-9.]+)/], ["saayaa", "saayaa"], // 有基于 chromniun 的急速模式和基于 ie 的兼容模式。必须在 ie 的规则之前。 ["baidu", /\b(?:ba?idubrowser|baiduhd)[ \/]([0-9.x]+)/], // 后面会做修复版本号,这里只要能识别是 ie 即可。 ["ie", re_msie], ["mi", /\bmiuibrowser\/([0-9.]+)/], // opera 15 之后开始使用 chromniun 内核,需要放在 chrome 的规则之前。 ["opera", function(ua){ var re_opera_old = /\bopera.+version\/([0-9.ab]+)/; var re_opera_new = /\bopr\/([0-9.]+)/; return re_opera_old.test(ua) ? re_opera_old : re_opera_new; }], ["oupeng", /\boupeng\/([0-9.]+)/], ["yandex", /yabrowser\/([0-9.]+)/], // 支付宝手机客户端 ["ali-ap", function(ua){ if(ua.indexof("aliapp") > 0){ return /\baliapp\(ap\/([0-9.]+)\)/; }else{ return /\balipayclient\/([0-9.]+)\b/; } }], // 支付宝平板客户端 ["ali-ap-pd", /\baliapp\(ap-pd\/([0-9.]+)\)/], // 支付宝商户客户端 ["ali-am", /\baliapp\(am\/([0-9.]+)\)/], // 淘宝手机客户端 ["ali-tb", /\baliapp\(tb\/([0-9.]+)\)/], // 淘宝平板客户端 ["ali-tb-pd", /\baliapp\(tb-pd\/([0-9.]+)\)/], // 天猫手机客户端 ["ali-tm", /\baliapp\(tm\/([0-9.]+)\)/], // 天猫平板客户端 ["ali-tm-pd", /\baliapp\(tm-pd\/([0-9.]+)\)/], // uc 浏览器,可能会被识别为 android 浏览器,规则需要前置。 // uc 桌面版浏览器携带 chrome 信息,需要放在 chrome 之前。 ["uc", function(ua){ if(ua.indexof("ucbrowser/") >= 0){ return /\bucbrowser\/([0-9.]+)/; } else if(ua.indexof("ubrowser/") >= 0){ return /\bubrowser\/([0-9.]+)/; }else if(/\buc\/[0-9]/.test(ua)){ return /\buc\/([0-9.]+)/; }else if(ua.indexof("ucweb") >= 0){ // `ucweb/2.0` is compony info. // `ucweb8.7.2.214/145/800` is browser info. return /\bucweb([0-9.]+)?/; }else{ return /\b(?:ucbrowser|uc)\b/; } }], ["chrome", / (?:chrome|crios|crmo)\/([0-9.]+)/], // android 默认浏览器。该规则需要在 safari 之前。 ["android", function(ua){ if(ua.indexof("android") === -1){return;} return /\bversion\/([0-9.]+(?: beta)?)/; }], ["blackberry", function(ua){ var m = ua.match(re_blackberry_10) || ua.match(re_blackberry_6_7) || ua.match(re_blackberry_4_5); return m ? {version: m[1]} : "blackberry"; }], ["safari", /\bversion\/([0-9.]+(?: beta)?)(?: mobile(?:\/[a-z0-9]+)?)? safari\//], // 如果不能被识别为 safari,则猜测是 webview。 ["webview", /\bcpu(?: iphone)? os (?:[0-9._]+).+\bapplewebkit\b/], ["firefox", /\bfirefox\/([0-9.ab]+)/], ["nokia", /\bnokiabrowser\/([0-9.]+)/] ]; // useragent detector. // @param {string} ua, useragent. // @param {object} expression // @return {object} // 返回 null 表示当前表达式未匹配成功。 function detect(name, expression, ua){ var expr = isfunction(expression) ? expression.call(null, ua) : expression; if(!expr){return null;} var info = { name: name, version: na_version, codename: "" }; var t = tostring(expr); if(expr === true){ return info; }else if(t === "[object string]"){ if(ua.indexof(expr) !== -1){ return info; } }else if(isobject(expr)){ // object if(expr.hasownproperty("version")){ info.version = expr.version; } return info; }else if(expr.exec){ // regexp var m = expr.exec(ua); if(m){ if(m.length >= 2 && m[1]){ info.version = m[1].replace(/_/g, "."); }else{ info.version = na_version; } return info; } } } var na = {name:"na", version:na_version}; // 初始化识别。 function init(ua, patterns, factory, detector){ var detected = na; each(patterns, function(pattern){ var d = detect(pattern[0], pattern[1], ua); if(d){ detected = d; return false; } }); factory.call(detector, detected.name, detected.version); } // 解析 useragent 字符串 // @param {string} ua, useragent string. // @return {object} var parse = function(ua){ ua = (ua || "").tolowercase(); var d = {}; init(ua, devices, function(name, version){ var v = parsefloat(version); d.device = { name: name, version: v, fullversion: version }; d.device[name] = v; }, d); init(ua, os, function(name, version){ var v = parsefloat(version); d.os = { name: name, version: v, fullversion: version }; d.os[name] = v; }, d); var iecore = iemode(ua); init(ua, engine, function(name, version){ var mode = version; // ie 内核的浏览器,修复版本号及兼容模式。 if(iecore){ version = iecore.engineversion || iecore.enginemode; mode = iecore.enginemode; } var v = parsefloat(version); d.engine = { name: name, version: v, fullversion: version, mode: parsefloat(mode), fullmode: mode, compatible: iecore ? iecore.compatible : false }; d.engine[name] = v; }, d); init(ua, browser, function(name, version){ var mode = version; // ie 内核的浏览器,修复浏览器版本及兼容模式。 if(iecore){ // 仅修改 ie 浏览器的版本,其他 ie 内核的版本不修改。 if(name === "ie"){ version = iecore.browserversion; } mode = iecore.browsermode; } var v = parsefloat(version); d.browser = { name: name, version: v, fullversion: version, mode: parsefloat(mode), fullmode: mode, compatible: iecore ? iecore.compatible : false }; d.browser[name] = v; }, d); return d; }; // nodejs. if(typeof process === "object" && process.tostring() === "[object process]"){ // 加载更多的规则。 // var morerule = module["require"]("./morerule"); // [].unshift.apply(devices, morerule.devices || []); // [].unshift.apply(os, morerule.os || []); // [].unshift.apply(browser, morerule.browser || []); // [].unshift.apply(engine, morerule.engine || []); }else{ var useragent = navigator.useragent || ""; //var platform = navigator.platform || ""; var appversion = navigator.appversion || ""; var vendor = navigator.vendor || ""; external = win.external; detector = parse(useragent + " " + appversion + " " + vendor); win.detector = detector; } // exports `parse()` api anyway. detector.parse = parse; //module.exports = detector; global.detector = detector; })(window);