From a3ba3c52b892831a3ff3caf4d23135c639f46936 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bartosz=20Dziewo=C5=84ski?= Date: Mon, 18 Nov 2013 00:19:42 +0100 Subject: [PATCH] jquery.client: Component-wise version comparison in #test with strings Version '1.10' is larger than '1.2'. Using numbers in the version map is not affected. Change-Id: Ic6f3a848645bd05af4b2fdaa29eb58b8aa8f2571 --- RELEASE-NOTES-1.23 | 4 + resources/jquery/jquery.client.js | 31 +++++++- .../resources/jquery/jquery.client.test.js | 76 +++++++++++++++++-- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/RELEASE-NOTES-1.23 b/RELEASE-NOTES-1.23 index db80ad3c09..1a36354115 100644 --- a/RELEASE-NOTES-1.23 +++ b/RELEASE-NOTES-1.23 @@ -57,6 +57,10 @@ production. * Added ApiBeforeMain hook, roughly equivalent to the BeforeInitialize hook: it's called after everything is set up but before any major processing happens. +* The jquery.client module now performs a component-wise version comparison in + its #test method when strings are used in the browser map: version '1.10' is + now correctly considered larger than '1.2'. Using numbers in the version map + is not affected. === Bug fixes in 1.23 === * (bug 41759) The "updated since last visit" markers (on history pages, recent diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js index 5a95dc5b13..f35782be1c 100644 --- a/resources/jquery/jquery.client.js +++ b/resources/jquery/jquery.client.js @@ -190,6 +190,10 @@ /** * Checks the current browser against a support map object. * + * Version numbers passed as numeric values will be compared like numbers (1.2 > 1.11). + * Version numbers passed as string values will be compared using a simple component-wise + * algorithm, similar to PHP's version_compare ('1.2' < '1.11'). + * * A browser map is in the following format: * { * // Multiple rules with configurable operators @@ -223,7 +227,7 @@ test: function ( map, profile, exactMatchOnly ) { /*jshint evil: true */ - var conditions, dir, i, op, val; + var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare; profile = $.isPlainObject( profile ) ? profile : $.client.profile(); if ( map.ltr && map.rtl ) { dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'; @@ -247,7 +251,30 @@ op = conditions[i][0]; val = conditions[i][1]; if ( typeof val === 'string' ) { - if ( !( eval( 'profile.version' + op + '"' + val + '"' ) ) ) { + // Perform a component-wise comparison of versions, similar to PHP's version_compare + // but simpler. '1.11' is larger than '1.2'. + pieceVersion = profile.version.toString().split( '.' ); + pieceVal = val.split( '.' ); + // Extend with zeroes to equal length + while ( pieceVersion.length < pieceVal.length ) { + pieceVersion.push( '0' ); + } + while ( pieceVal.length < pieceVersion.length ) { + pieceVal.push( '0' ); + } + // Compare components + compare = 0; + for ( j = 0; j < pieceVersion.length; j++ ) { + if ( Number( pieceVersion[j] ) < Number( pieceVal[j] ) ) { + compare = -1; + break; + } else if ( Number( pieceVersion[j] ) > Number( pieceVal[j] ) ) { + compare = 1; + break; + } + } + // compare will be -1, 0 or 1, depending on comparison result + if ( !( eval( '' + compare + op + '0' ) ) ) { return false; } } else if ( typeof val === 'number' ) { diff --git a/tests/qunit/suites/resources/jquery/jquery.client.test.js b/tests/qunit/suites/resources/jquery/jquery.client.test.js index 4c7c302222..6030206b5f 100644 --- a/tests/qunit/suites/resources/jquery/jquery.client.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.client.test.js @@ -396,7 +396,7 @@ rtl: true } }, - // Bug #34924 + // Rekonq (bug 34924) 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': { title: 'Rekonq', platform: 'Linux i686', @@ -413,12 +413,64 @@ ltr: true, rtl: true } + }, + // Konqueror + 'Mozilla/5.0 (X11; Linux i686) KHTML/4.9.1 (like Gecko) Konqueror/4.9': { + title: 'Konqueror', + platform: 'Linux i686', + profile: { + name: 'konqueror', + layout: 'khtml', + layoutVersion: 'unknown', + platform: 'linux', + version: '4.9.1', + versionBase: '4', + versionNumber: 4.9 + }, + wikiEditor: { + // '4.9' is less than '4.11'. + ltr: false, + rtl: false + }, + wikiEditorLegacy: { + // The check is missing in legacyTestMap + ltr: true, + rtl: true + } } }, testMap = { - // Example from WikiEditor - // Make sure to use raw numbers, a string like "7.0" would fail on a - // version 10 browser since in string comparaison "10" is before "7.0" :) + // Example from WikiEditor, modified to provide version identifiers as strings and with + // Konqueror 4.11 check added. + 'ltr': { + 'msie': [['>=', '7.0']], + 'firefox': [['>=', '2']], + 'opera': [['>=', '9.6']], + 'safari': [['>=', '3']], + 'chrome': [['>=', '3']], + 'netscape': [['>=', '9']], + 'konqueror': [['>=', '4.11']], + 'blackberry': false, + 'ipod': false, + 'iphone': false + }, + 'rtl': { + 'msie': [['>=', '8']], + 'firefox': [['>=', '2']], + 'opera': [['>=', '9.6']], + 'safari': [['>=', '3']], + 'chrome': [['>=', '3']], + 'netscape': [['>=', '9']], + 'konqueror': [['>=', '4.11']], + 'blackberry': false, + 'ipod': false, + 'iphone': false + } + }, + legacyTestMap = { + // Original example from WikiEditor. + // This is using the old, but still supported way of providing version identifiers as numbers + // instead of strings; with this method, 4.9 would be considered larger than 4.11. 'ltr': { 'msie': [['>=', 7.0]], 'firefox': [['>=', 2]], @@ -516,23 +568,33 @@ }, ie7Profile, true ), false, 'returns false if browser not found and exactMatchOnly is set' ); } ); - QUnit.test( 'test( testMap) - WikiEditor sample', uacount * 2, function ( assert ) { + QUnit.test( 'test( testMap ), test( legacyTestMap ) - WikiEditor sample', uacount * 2 * 2, function ( assert ) { var $body = $( 'body' ), bodyClasses = $body.attr( 'class' ); // Loop through and run tests $.each( uas, function ( agent, data ) { $.each( ['ltr', 'rtl'], function ( i, dir ) { - var profile, testMatch; + var profile, testMatch, legacyTestMatch; $body.removeClass( 'ltr rtl' ).addClass( dir ); profile = $.client.profile( { userAgent: agent, platform: data.platform } ); testMatch = $.client.test( testMap, profile ); + legacyTestMatch = $.client.test( legacyTestMap, profile ); $body.removeClass( dir ); - assert.equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent ); + assert.equal( + testMatch, + data.wikiEditor[dir], + 'testing comparison based on ' + dir + ', ' + agent + ); + assert.equal( + legacyTestMatch, + data.wikiEditorLegacy ? data.wikiEditorLegacy[dir] : data.wikiEditor[dir], + 'testing comparison based on ' + dir + ', ' + agent + ' (legacyTestMap)' + ); } ); } ); -- 2.20.1