From bb61d8da82d5c06ffe8bb5c5039f5d074d1d4147 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 4 May 2018 23:40:15 +0700 Subject: [PATCH] update to 0.7.18 --- src/ua-parser.js | 165 +++++++++++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 57 deletions(-) mode change 100644 => 100755 src/ua-parser.js diff --git a/src/ua-parser.js b/src/ua-parser.js old mode 100644 new mode 100755 index b847459..2aa68bb --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -1,10 +1,10 @@ -/** - * UAParser.js v0.7.14 +/*! + * UAParser.js v0.7.18 * Lightweight JavaScript-based User-Agent string parser * https://github.com/faisalman/ua-parser-js * * Copyright © 2012-2016 Faisal Salman - * Dual licensed under GPLv2 & MIT + * Dual licensed under GPLv2 or MIT */ (function (window, undefined) { @@ -16,7 +16,7 @@ ///////////// - var LIBVERSION = '0.7.14', + var LIBVERSION = '0.7.18', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -138,7 +138,7 @@ } i += 2; } - //console.log(this); + // console.log(this); //return this; }, @@ -244,7 +244,7 @@ // Mixed /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]+)*/i, + /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer // Trident based @@ -253,16 +253,16 @@ /(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based - /(rekonq)\/([\w\.]+)*/i, // Rekonq - /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser)\/([\w\.-]+)/i + /(rekonq)\/([\w\.]*)/i, // Rekonq + /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark)\/([\w\.-]+)/i // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser ], [NAME, VERSION], [ /(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11 ], [[NAME, 'IE'], VERSION], [ - /(edge)\/((\d+)?[\w\.]+)/i // Microsoft Edge - ], [NAME, VERSION], [ + /(edge|edgios|edgea)\/((\d+)?[\w\.]+)/i // Microsoft Edge + ], [[NAME, 'Edge'], VERSION], [ /(yabrowser)\/([\w\.]+)/i // Yandex ], [[NAME, 'Yandex'], VERSION], [ @@ -280,19 +280,34 @@ /(micromessenger)\/([\w\.]+)/i // WeChat ], [[NAME, 'WeChat'], VERSION], [ + /(qqbrowserlite)\/([\w\.]+)/i // QQBrowserLite + ], [NAME, VERSION], [ + /(QQ)\/([\d\.]+)/i // QQ, aka ShouQ ], [NAME, VERSION], [ /m?(qqbrowser)[\/\s]?([\w\.]+)/i // QQBrowser ], [NAME, VERSION], [ + /(BIDUBrowser)[\/\s]?([\w\.]+)/i // Baidu Browser + ], [NAME, VERSION], [ + + /(2345Explorer)[\/\s]?([\w\.]+)/i // 2345 Browser + ], [NAME, VERSION], [ + + /(MetaSr)[\/\s]?([\w\.]+)/i // SouGouBrowser + ], [NAME], [ + + /(LBBROWSER)/i // LieBao Browser + ], [NAME], [ + /xiaomi\/miuibrowser\/([\w\.]+)/i // MIUI Browser ], [VERSION, [NAME, 'MIUI Browser']], [ /;fbav\/([\w\.]+);/i // Facebook App for iOS & Android ], [VERSION, [NAME, 'Facebook']], [ - /(headlesschrome) ([\w\.]+)/i // Chrome Headless + /headlesschrome(?:\/([\w\.]+)|\s)/i // Chrome Headless ], [VERSION, [NAME, 'Chrome Headless']], [ /\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView @@ -326,6 +341,9 @@ /version\/([\w\.]+).+?(mobile\s?safari|safari)/i // Safari & Safari Mobile ], [VERSION, NAME], [ + /webkit.+?(gsa)\/([\w\.]+).+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Google Search Appliance on iOS + ], [[NAME, 'GSA'], VERSION], [ + /webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [ @@ -339,7 +357,8 @@ /(swiftfox)/i, // Swiftfox /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i, // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror - /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/([\w\.-]+)/i, + /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([\w\.-]+)$/i, + // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i, // Mozilla @@ -347,7 +366,7 @@ /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir)[\/\s]?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir /(links)\s\(([\w\.]+)/i, // Links - /(gobrowser)\/?([\w\.]+)*/i, // GoBrowser + /(gobrowser)\/?([\w\.]*)/i, // GoBrowser /(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser /(mosaic)[\/\s]([\w\.]+)/i // Mosaic ], [NAME, VERSION] @@ -509,9 +528,9 @@ /(dell)\s(strea[kpr\s\d]*[\dko])/i // Dell Streak ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(kf[A-z]+)\sbuild\/[\w\.]+.*silk\//i // Kindle Fire HD + /(kf[A-z]+)\sbuild\/.+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [ - /(sd|kf)[0349hijorstuw]+\sbuild\/[\w\.]+.*silk\//i // Fire Phone + /(sd|kf)[0349hijorstuw]+\sbuild\/.+silk\//i // Fire Phone ], [[MODEL, mapper.str, maps.device.amazon.model], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [ /\((ip[honed|\s\w*]+);.+(apple)/i // iPod/iPhone @@ -520,7 +539,7 @@ ], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [ /(blackberry)[\s-]?(\w+)/i, // BlackBerry - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[\s_-]?([\w-]+)*/i, + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[\s_-]?([\w-]*)/i, // BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron /(hp)\s([\w\s]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i // Asus @@ -554,8 +573,8 @@ ], [VENDOR, MODEL, [TYPE, TABLET]], [ /(htc)[;_\s-]+([\w\s]+(?=\))|\w+)*/i, // HTC - /(zte)-(\w+)*/i, // ZTE - /(alcatel|geeksphone|lenovo|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]+)*/i + /(zte)-(\w*)/i, // ZTE + /(alcatel|geeksphone|lenovo|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i // Alcatel/GeeksPhone/Lenovo/Nexian/Panasonic/Sony ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ @@ -575,8 +594,8 @@ ], [[MODEL, /\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [ // Motorola - /\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?(:?\s4g)?)[\w\s]+build\//i, - /mot[\s-]?(\w+)*/i, + /\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?:?(\s4g)?)[\w\s]+build\//i, + /mot[\s-]?(\w*)/i, /(XT\d{3,4}) build\//i, /(nexus\s6)/i ], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [ @@ -598,15 +617,15 @@ /smart-tv.+(samsung)/i ], [VENDOR, [TYPE, SMARTTV], MODEL], [ /((s[cgp]h-\w+|gt-\w+|galaxy\snexus|sm-\w[\w\d]+))/i, - /(sam[sung]*)[\s-]*(\w+-?[\w-]*)*/i, + /(sam[sung]*)[\s-]*(\w+-?[\w-]*)/i, /sec-((sgh\w+))/i ], [[VENDOR, 'Samsung'], MODEL, [TYPE, MOBILE]], [ - /sie-(\w+)*/i // Siemens + /sie-(\w*)/i // Siemens ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ /(maemo|nokia).*(n900|lumia\s\d+)/i, // Nokia - /(nokia)[\s_-]?([\w-]+)*/i + /(nokia)[\s_-]?([\w-]*)/i ], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [ /android\s3\.[\s\w;-]{10}(a\d{3})/i // Acer @@ -619,7 +638,7 @@ /(lg) netcast\.tv/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nexus\s[45])/i, // LG - /lg[e;\s\/-]+(\w+)*/i, + /lg[e;\s\/-]+(\w*)/i, /android.+lg(\-?[\d\w]+)\s+build/i ], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [ @@ -647,21 +666,24 @@ /android.+;\s(pixel xl|pixel)\s/i // Google Pixel ], [MODEL, [VENDOR, 'Google'], [TYPE, MOBILE]], [ - /android.+(\w+)\s+build\/hm\1/i, // Xiaomi Hongmi 'numeric' models + /android.+;\s(\w+)\s+build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i, // Xiaomi Hongmi - /android.+(mi[\s\-_]*(?:one|one[\s_]plus|note lte)?[\s_]*(?:\d\w)?)\s+build/i // Xiaomi Mi + /android.+(mi[\s\-_]*(?:one|one[\s_]plus|note lte)?[\s_]*(?:\d?\w?)[\s_]*(?:plus)?)\s+build/i, // Xiaomi Mi + /android.+(redmi[\s\-_]*(?:note)?(?:[\s_]*[\w\s]+))\s+build/i // Redmi Phones ], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [ - + /android.+(mi[\s\-_]*(?:pad)(?:[\s_]*[\w\s]+))\s+build/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, TABLET]], [ /android.+;\s(m[1-5]\snote)\sbuild/i // Meizu Tablet ], [MODEL, [VENDOR, 'Meizu'], [TYPE, TABLET]], [ - /android.+a000(1)\s+build/i // OnePlus + /android.+a000(1)\s+build/i, // OnePlus + /android.+oneplus\s(a\d{4})\s+build/i ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [ /android.+[;\/]\s*(RCT[\d\w]+)\s+build/i // RCA Tablets ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ - /android.+[;\/]\s*(Venue[\d\s]*)\s+build/i // Dell Venue Tablets + /android.+[;\/\s]+(Venue[\d\s]{2,7})\s+build/i // Dell Venue Tablets ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ /android.+[;\/]\s*(Q[T|M][\d\w]+)\s+build/i // Verizon Tablet @@ -673,8 +695,8 @@ /android.+[;\/]\s+(TM\d{3}.*\b)\s+build/i // Barnes & Noble Tablet ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ - /android.+[;\/]\s*(zte)?.+(k\d{2})\s+build/i // ZTE K Series Tablet - ], [[VENDOR, 'ZTE'], MODEL, [TYPE, TABLET]], [ + /android.+;\s(k88)\sbuild/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ /android.+[;\/]\s*(gen\d{3})\s+build.*49h/i // Swiss GEN Mobile ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ @@ -685,26 +707,26 @@ /android.+[;\/]\s*((Zeki)?TB.*\b)\s+build/i // Zeki Tablets ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ - /(android).+[;\/]\s+([YR]\d{2}x?.*)\s+build/i, - /android.+[;\/]\s+(Dragon[\-\s]+Touch\s+|DT)(.+)\s+build/i // Dragon Touch Tablet + /(android).+[;\/]\s+([YR]\d{2})\s+build/i, + /android.+[;\/]\s+(Dragon[\-\s]+Touch\s+|DT)(\w{5})\sbuild/i // Dragon Touch Tablet ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [ - /android.+[;\/]\s*(NS-?.+)\s+build/i // Insignia Tablets + /android.+[;\/]\s*(NS-?\w{0,9})\sbuild/i // Insignia Tablets ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ - /android.+[;\/]\s*((NX|Next)-?.+)\s+build/i // NextBook Tablets + /android.+[;\/]\s*((NX|Next)-?\w{0,9})\s+build/i // NextBook Tablets ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ - /android.+[;\/]\s*(Xtreme\_?)?(V(1[045]|2[015]|30|40|60|7[05]|90))\s+build/i + /android.+[;\/]\s*(Xtreme\_)?(V(1[045]|2[015]|30|40|60|7[05]|90))\s+build/i ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ // Voice Xtreme Phones - /android.+[;\/]\s*(LVTEL\-?)?(V1[12])\s+build/i // LvTel Phones + /android.+[;\/]\s*(LVTEL\-)?(V1[12])\s+build/i // LvTel Phones ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ /android.+[;\/]\s*(V(100MD|700NA|7011|917G).*\b)\s+build/i // Envizen Tablets ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ - /android.+[;\/]\s*(Le[\s\-]+Pan)[\s\-]+(.*\b)\s+build/i // Le Pan Tablets + /android.+[;\/]\s*(Le[\s\-]+Pan)[\s\-]+(\w{1,9})\s+build/i // Le Pan Tablets ], [VENDOR, MODEL, [TYPE, TABLET]], [ /android.+[;\/]\s*(Trio[\s\-]*.*)\s+build/i // MachSpeed Tablets @@ -719,14 +741,14 @@ /android.+(KS(.+))\s+build/i // Amazon Kindle Tablets ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [ - /android.+(Gigaset)[\s\-]+(Q.+)\s+build/i // Gigaset Tablets + /android.+(Gigaset)[\s\-]+(Q\w{1,9})\s+build/i // Gigaset Tablets ], [VENDOR, MODEL, [TYPE, TABLET]], [ /\s(tablet|tab)[;\/]/i, // Unidentifiable Tablet /\s(mobile)(?:[;\/]|\ssafari)/i // Unidentifiable Mobile ], [[TYPE, util.lowerize], VENDOR, MODEL], [ - /(android.+)[;\/].+build/i // Generic Android Device + /(android[\w\.\s\-]{0,9});.+build/i // Generic Android Device ], [MODEL, [VENDOR, 'Generic']] @@ -793,7 +815,7 @@ /(icab)[\/\s]([23]\.[\d\.]+)/i // iCab ], [NAME, VERSION], [ - /rv\:([\w\.]+).*(gecko)/i // Gecko + /rv\:([\w\.]{1,9}).+(gecko)/i // Gecko ], [VERSION, NAME] ], @@ -803,7 +825,7 @@ /microsoft\s(windows)\s(vista|xp)/i // Windows (iTunes) ], [NAME, VERSION], [ /(windows)\snt\s6\.2;\s(arm)/i, // Windows RT - /(windows\sphone(?:\sos)*)[\s\/]?([\d\.\s]+\w)*/i, // Windows Phone + /(windows\sphone(?:\sos)*)[\s\/]?([\d\.\s\w]*)/i, // Windows Phone /(windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [ /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i @@ -812,13 +834,13 @@ // Mobile/Embedded OS /\((bb)(10);/i // BlackBerry 10 ], [[NAME, 'BlackBerry'], VERSION], [ - /(blackberry)\w*\/?([\w\.]+)*/i, // Blackberry + /(blackberry)\w*\/?([\w\.]*)/i, // Blackberry /(tizen)[\/\s]([\w\.]+)/i, // Tizen - /(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]+)*/i, + /(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]*)/i, // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki /linux;.+(sailfish);/i // Sailfish OS ], [NAME, VERSION], [ - /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i // Symbian + /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]*)/i // Symbian ], [[NAME, 'Symbian'], VERSION], [ /\((series40);/i // Series 40 ], [NAME], [ @@ -829,43 +851,43 @@ /(nintendo|playstation)\s([wids34portablevu]+)/i, // Nintendo/Playstation // GNU/Linux based - /(mint)[\/\s\(]?(\w+)*/i, // Mint + /(mint)[\/\s\(]?(\w*)/i, // Mint /(mageia|vectorlinux)[;\s]/i, // Mageia/VectorLinux - /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|(?=\s)arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?(?!chrom)([\w\.-]+)*/i, + /(joli|[kxln]?ubuntu|debian|suse|opensuse|gentoo|(?=\s)arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?(?!chrom)([\w\.-]*)/i, // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus - /(hurd|linux)\s?([\w\.]+)*/i, // Hurd/Linux - /(gnu)\s?([\w\.]+)*/i // GNU + /(hurd|linux)\s?([\w\.]*)/i, // Hurd/Linux + /(gnu)\s?([\w\.]*)/i // GNU ], [NAME, VERSION], [ /(cros)\s[\w]+\s([\w\.]+\w)/i // Chromium OS ], [[NAME, 'Chromium OS'], VERSION],[ // Solaris - /(sunos)\s?([\w\.]+\d)*/i // Solaris + /(sunos)\s?([\w\.\d]*)/i // Solaris ], [[NAME, 'Solaris'], VERSION], [ // BSD based - /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly + /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]*)/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly ], [NAME, VERSION],[ - /(haiku)\s(\w+)/i // Haiku + /(haiku)\s(\w+)/i // Haiku ], [NAME, VERSION],[ /cfnetwork\/.+darwin/i, - /ip[honead]+(?:.*os\s([\w]+)*\slike\smac|;\sopera)/i // iOS + /ip[honead]{2,4}(?:.*os\s([\w]+)\slike\smac|;\sopera)/i // iOS ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ - /(mac\sos\sx)\s?([\w\s\.]+\w)*/i, + /(mac\sos\sx)\s?([\w\s\.]*)/i, /(macintosh|mac(?=_powerpc)\s)/i // Mac OS ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [ // Other - /((?:open)?solaris)[\/\s-]?([\w\.]+)*/i, // Solaris - /(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i, // AIX + /((?:open)?solaris)[\/\s-]?([\w\.]*)/i, // Solaris + /(aix)\s((\d)(?=\.|\)|\s)[\w\.])*/i, // AIX /(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms)/i, // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS - /(unix)\s?([\w\.]+)*/i // UNIX + /(unix)\s?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] }; @@ -1001,6 +1023,35 @@ if (typeof module !== UNDEF_TYPE && module.exports) { exports = module.exports = UAParser; } + // TODO: test!!!!!!!! + /* + if (require && require.main === module && process) { + // cli + var jsonize = function (arr) { + var res = []; + for (var i in arr) { + res.push(new UAParser(arr[i]).getResult()); + } + process.stdout.write(JSON.stringify(res, null, 2) + '\n'); + }; + if (process.stdin.isTTY) { + // via args + jsonize(process.argv.slice(2)); + } else { + // via pipe + var str = ''; + process.stdin.on('readable', function() { + var read = process.stdin.read(); + if (read !== null) { + str += read; + } + }); + process.stdin.on('end', function () { + jsonize(str.replace(/\n$/, '').split('\n')); + }); + } + } + */ exports.UAParser = UAParser; } else { // requirejs env (optional)