Rearrange internal class & remove old Safari map

This commit is contained in:
Faisal Salman 2023-04-08 04:40:59 +07:00
parent 59d8d836c2
commit e01663b48f
2 changed files with 189 additions and 174 deletions

View File

@ -95,18 +95,7 @@
// Helper
//////////
var assignFromEntries = function (arr) {
for (var i in arr) {
var propName = arr[i];
if (typeof propName == OBJ_TYPE && propName.length == 2) {
this[propName[0]] = propName[1];
} else {
this[propName] = undefined;
}
}
return this;
},
extend = function (regexes, extensions) {
var extend = function (regexes, extensions) {
var mergedRegexes = {};
for (var i in regexes) {
mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i];
@ -150,6 +139,17 @@
majorize = function (version) {
return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
},
setProps = function (arr) {
for (var i in arr) {
var propName = arr[i];
if (typeof propName == OBJ_TYPE && propName.length == 2) {
this[propName[0]] = propName[1];
} else {
this[propName] = undefined;
}
}
return this;
},
strip = function (pattern, str) {
return str.replace(pattern, EMPTY);
},
@ -243,18 +243,7 @@
// String map
//////////////
// Safari < 3.0
var oldSafariMap = {
'1.0' : '/8',
'1.2' : '/1',
'1.3' : '/3',
'2.0' : '/412',
'2.0.2' : '/416',
'2.0.3' : '/417',
'2.0.4' : '/419',
'?' : '/'
},
windowsVersionMap = {
var windowsVersionMap = {
'ME' : '4.90',
'NT 3.11' : 'NT3.51',
'NT 4.0' : 'NT4.0',
@ -386,7 +375,7 @@
/version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
], [NAME, [VERSION, strMapper, oldSafariMap]], [
], [NAME, [VERSION, '1']], [
/(webkit|khtml)\/([\w\.]+)/i
], [NAME, VERSION], [
@ -427,7 +416,7 @@
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
], [[ARCHITECTURE, 'ia32']], [
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
], [[ARCHITECTURE, 'arm64']], [
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
@ -811,23 +800,23 @@
var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
assignFromEntries.call(props.init, [
setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]]
]);
assignFromEntries.call(props.isIgnore, [
setProps.call(props.isIgnore, [
[UA_BROWSER, [VERSION, MAJOR]],
[UA_ENGINE, [VERSION]],
[UA_OS, [VERSION]]
]);
assignFromEntries.call(props.isIgnoreRgx, [
setProps.call(props.isIgnoreRgx, [
[UA_BROWSER, / ?browser$/i],
[UA_OS, / ?os$/i]
]);
assignFromEntries.call(props.toString, [
setProps.call(props.toString, [
[UA_BROWSER, [NAME, VERSION]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [VENDOR, MODEL]],
@ -837,7 +826,7 @@
return props;
})();
var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
var createUAParserData = function (item, itemType) {
var init_props = defaultProps.init[itemType],
is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
@ -845,27 +834,30 @@
toString_props = defaultProps.toString[itemType] || 0;
function UAParserData () {
assignFromEntries.call(this, init_props);
setProps.call(this, init_props);
}
UAParserData.prototype.withClientHints = function () {
var prevData = this;
UAParserData.prototype.getItem = function () {
return item;
};
UAParserData.prototype.withClientHints = function () {
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
var item = new UAParserItem(itemType, ua, rgxMap, uaCH);
item.data = prevData;
return item.parseCH().get();
return item
.parseCH()
.get();
}
// browsers based on chromium 85+
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
var JS_UACH = new UAParserDataCH(res, false);
var item = new UAParserItem(itemType, ua, rgxMap, JS_UACH);
item.data = prevData;
return item.parseCH().get();
return item
.setCH(new UAParserDataCH(res, false))
.parseCH()
.get();
});
};
@ -923,9 +915,9 @@
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
assignFromEntries.call(this, CH_ALL_VALUES);
setProps.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
assignFromEntries.call(this, [
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
[BRANDS, itemListToArray(uach[CH_HEADER])],
@ -941,133 +933,138 @@
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
}
}
return this;
}
function UAParserItem (itemType, ua, rgxMap, uaCH) {
assignFromEntries.call(this, [
this.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
this.set = function (prop, val) {
this.data[prop] = val;
return this;
};
this.setCH = function (ch) {
this.uaCH = ch;
return this;
};
this.detectFeature = function () {
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
switch (this.itemType) {
case UA_BROWSER:
// Brave-specific detection
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
this.set(NAME, 'Brave');
}
break;
case UA_DEVICE:
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case UA_OS:
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
}
}
return this;
};
this.parseUA = function () {
if (this.itemType != UA_RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap);
}
if (this.itemType == UA_BROWSER) {
this.set(MAJOR, majorize(this.get(VERSION)));
}
return this;
};
this.parseCH = function () {
var ua = this.ua,
uaCH = this.uaCH,
rgxMap = this.rgxMap;
switch (this.itemType) {
case UA_BROWSER:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
brandVersion = brands[i].version;
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
}
}
}
break;
case UA_CPU:
var archName = uaCH[ARCHITECTURE];
if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap);
}
break;
case UA_DEVICE:
if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE);
}
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
}
break;
case UA_OS:
var osName = uaCH[PLATFORM];
if(osName) {
var osVersion = uaCH[PLATFORMVER];
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
this.set(NAME, osName)
.set(VERSION, osVersion);
}
break;
case UA_RESULT:
var data = this.data;
var parse = function (itemType) {
return data[itemType]
.getItem()
.setCH(uaCH)
.parseCH()
.get();
};
this.set('ua', ua)
.set(UA_BROWSER, parse(UA_BROWSER))
.set(UA_CPU, parse(UA_CPU))
.set(UA_DEVICE, parse(UA_DEVICE))
.set(UA_ENGINE, parse(UA_ENGINE))
.set(UA_OS, parse(UA_OS));
}
return this;
};
setProps.call(this, [
['itemType', itemType],
['ua', ua],
['uaCH', uaCH],
['rgxMap', rgxMap],
['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
['data', createUAParserData(this, itemType)]
]);
if(this.itemType == UA_RESULT) {
var createUAParserItem = function (itemType) {
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseUA().get();
};
this.set('ua', ua)
.set(UA_BROWSER, createUAParserItem(UA_BROWSER))
.set(UA_CPU, createUAParserItem(UA_CPU))
.set(UA_DEVICE, createUAParserItem(UA_DEVICE))
.set(UA_ENGINE, createUAParserItem(UA_ENGINE))
.set(UA_OS, createUAParserItem(UA_OS))
.get();
}
return this;
}
UAParserItem.prototype.detectFeature = function () {
var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua;
switch(this.itemType) {
case UA_BROWSER:
// Brave-specific detection
if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
this.set(NAME, 'Brave');
}
this.set(MAJOR, majorize(this.get(VERSION)));
break;
case UA_DEVICE:
if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case UA_OS:
if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
}
return this;
};
UAParserItem.prototype.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
UAParserItem.prototype.parseUA = function () {
if (this.itemType != UA_RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap);
}
this.detectFeature();
return this;
};
UAParserItem.prototype.parseCH = function () {
var ua = this.ua,
uaCH = this.uaCH,
rgxMap = this.rgxMap;
switch (this.itemType) {
case UA_BROWSER:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
brandVersion = brands[i].version;
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
}
}
}
break;
case UA_CPU:
var archName = uaCH[ARCHITECTURE];
if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap);
}
break;
case UA_DEVICE:
if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE);
}
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
}
break;
case UA_OS:
var osName = uaCH[PLATFORM];
if(osName) {
var osVersion = uaCH[PLATFORMVER];
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
this.set(NAME, osName)
.set(VERSION, osVersion);
}
break;
case UA_RESULT:
var prevData = this.data;
var createUAParserItemWithCH = function (itemType) {
var item = new UAParserItem(itemType, ua, rgxMap[itemType], uaCH);
item.data = prevData[itemType];
return item.parseCH().get();
};
this.set('ua', ua)
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
.set(UA_CPU, createUAParserItemWithCH(UA_CPU))
.set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
.set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
.set(UA_OS, createUAParserItemWithCH(UA_OS));
}
return this;
};
UAParserItem.prototype.set = function (prop, val) {
this.data[prop] = val;
return this;
};
function UAParser (ua, extensions, headers) {
@ -1104,26 +1101,44 @@
extend(defaultRegexes, extensions) :
defaultRegexes,
createUAParserItemFunc = function (itemType) {
return function () {
return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).parseUA().get();
};
createItemFunc = function (itemType) {
if (itemType == UA_RESULT) {
return function () {
return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH)
.set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU())
.set(UA_DEVICE, this.getDevice())
.set(UA_ENGINE, this.getEngine())
.set(UA_OS, this.getOS())
.get();
};
} else {
return function () {
return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
.parseUA()
.detectFeature()
.get();
};
}
};
// public methods
assignFromEntries.call(this, [
['getBrowser', createUAParserItemFunc(UA_BROWSER)],
['getCPU', createUAParserItemFunc(UA_CPU)],
['getDevice', createUAParserItemFunc(UA_DEVICE)],
['getEngine', createUAParserItemFunc(UA_ENGINE)],
['getOS', createUAParserItemFunc(UA_OS)],
['getResult', createUAParserItemFunc(UA_RESULT)],
setProps.call(this, [
['getBrowser', createItemFunc(UA_BROWSER)],
['getCPU', createItemFunc(UA_CPU)],
['getDevice', createItemFunc(UA_DEVICE)],
['getEngine', createItemFunc(UA_ENGINE)],
['getOS', createItemFunc(UA_OS)],
['getResult', createItemFunc(UA_RESULT)],
['getUA', function () { return userAgent; }],
['setUA', function (ua) {
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
}]
]).setUA(userAgent);
])
.setUA(userAgent);
return this;
}

View File

@ -1084,8 +1084,8 @@
"expect" :
{
"name" : "Safari",
"version" : "2.0.4",
"major" : "2"
"version" : "1",
"major" : "1"
}
},
{