diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index cabc4f6..66c1adf 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -129,10 +129,14 @@ itemListToArray = function (header) { if (!header) return undefined; var arr = []; - var tokens = strip(/\\?\"/g, header).split(', '); + var tokens = strip(/\\?\"/g, header).split(','); for (var i = 0; i < tokens.length; i++) { - var token = tokens[i].split(';v='); - arr[i] = { brand : token[0], version : token[1] }; + if (tokens[i].indexOf(';') > -1) { + var token = trim(tokens[i]).split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } else { + arr[i] = tokens[i]; + } } return arr; }, @@ -157,7 +161,7 @@ return str.replace(pattern, EMPTY); }, stripQuotes = function (val) { - return typeof val === STR_TYPE ? strip(/\"/g, val) : val; + return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val; }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { @@ -239,7 +243,7 @@ return (i === UNKNOWN) ? undefined : i; } } - return str; + return map.hasOwnProperty('*') ? map['*'] : str; }; /////////////// @@ -263,10 +267,11 @@ formFactorMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', - 'tablet' : 'Tablet', + 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR'], - '?' : 'Unknown' + 'wearable' : ['VR', 'XR', 'Watch'], + '?' : ['Desktop', 'Unknown'], + '*' : undefined }; ////////////// @@ -947,7 +952,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1029,8 +1034,7 @@ }; this.parseCH = function () { - var ua = this.ua, - uaCH = this.uaCH, + var uaCH = this.uaCH, rgxMap = this.rgxMap; switch (this.itemType) { @@ -1063,7 +1067,16 @@ this.set(MODEL, uaCH[MODEL]); } if (uaCH[FORMFACTOR]) { - this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap)); + var ff; + if (typeof uaCH[FORMFACTOR] !== 'string') { + var idx = 0; + while (!ff && idx < uaCH[FORMFACTOR].length) { + ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + } + } else { + ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + } + this.set(TYPE, ff); } break; case UA_OS: diff --git a/test/mocha-test.js b/test/mocha-test.js index 7c4cbde..97c5867 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -466,17 +466,26 @@ describe('Map UA-CH headers', function () { it('Can detect form-factor from client-hints', function () { const FFVR = { - 'sec-ch-ua-form-factor' : 'VR' + 'sec-ch-ua-form-factor' : '"VR"' + }; + + const FFEInk = { + 'sec-ch-ua-form-factor' : '"Tablet", "EInk"' }; const FFUnknown = { - 'sec-ch-ua-form-factor' : 'Unknown' + 'sec-ch-ua-form-factor' : '"Unknown"' }; UAParser(FFVR).withClientHints().then(function (ua) { assert.strictEqual(ua.device.type, 'wearable'); }); + UAParser(FFEInk).withClientHints().then(function (ua) { + assert.strictEqual(ua.device.type, 'tablet'); + }); + + UAParser(FFUnknown).withClientHints().then(function (ua) { assert.strictEqual(ua.device.type, undefined); }); diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index bab1f3a..83e5238 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -40,7 +40,8 @@ test('read client hints data', async ({ page }) => { version: '110' } ], - platform: 'New OS' + platform: 'New OS', + formFactor: 'New Form Factor' }); } } @@ -54,6 +55,7 @@ test('read client hints data', async ({ page }) => { expect(uap).toHaveProperty('browser.name', 'New Browser'); expect(uap).toHaveProperty('os.name', 'New OS'); + expect(uap).toHaveProperty('device.type', undefined); }); test('detect Brave', async ({ page }) => {