mediawiki.String: Add new functions lcFirst and ucFirst

The new functions `lcFirst` and `ucFirst` lowercases/uppercases the
first character of a JavaScript string with support of UTF-16
surrogates for characters out of the Unicode BMP.

Use these new functions at jqueryMsg.

Change-Id: I007b63bfbcc9e4bc7e89f48df9687111bc4db715
This commit is contained in:
Fomafix 2021-09-18 13:27:56 +00:00
parent 213d45e5dd
commit 0b56ca7065
5 changed files with 68 additions and 7 deletions

View file

@ -71,6 +71,28 @@
}
}
/**
* Lowercase the first character. Support UTF-16 surrogates for characters outside of BMP.
*
* @param {string} string
* @return {string}
*/
function lcFirst( string ) {
var firstChar = charAt( string, 0 );
return firstChar.toLowerCase() + string.slice( firstChar.length );
}
/**
* Uppercase the first character. Support UTF-16 surrogates for characters outside of BMP.
*
* @param {string} string
* @return {string}
*/
function ucFirst( string ) {
var firstChar = charAt( string, 0 );
return firstChar.toUpperCase() + string.slice( firstChar.length );
}
function trimLength( safeVal, newVal, length, lengthFn ) {
var startMatches, endMatches, matchesLen, inpParts, chopOff, oldChar, newChar,
oldVal = safeVal;
@ -207,6 +229,8 @@
byteLength: byteLength,
codePointLength: codePointLength,
charAt: charAt,
lcFirst: lcFirst,
ucFirst: ucFirst,
trimByteLength: trimByteLength,
trimCodePointLength: trimCodePointLength
};

View file

@ -12,7 +12,7 @@
*/
var slice = Array.prototype.slice,
charAt = require( 'mediawiki.String' ).charAt,
mwString = require( 'mediawiki.String' ),
parserDefaults = {
// Magic words and their expansions. Server-side data is added to this below.
magic: {
@ -1351,8 +1351,7 @@ mw.jqueryMsg.HtmlEmitter.prototype = {
*/
int: function ( nodes ) {
var msg = textify( nodes[ 0 ] );
var firstChar = charAt( msg, 0 );
return mw.jqueryMsg.getMessageFunction()( firstChar.toLowerCase() + msg.slice( firstChar.length ) );
return mw.jqueryMsg.getMessageFunction()( mwString.lcFirst( msg ) );
},
/**
@ -1419,8 +1418,7 @@ mw.jqueryMsg.HtmlEmitter.prototype = {
*/
lcfirst: function ( nodes ) {
var text = textify( nodes[ 0 ] );
var firstChar = charAt( text, 0 );
return firstChar.toLowerCase() + text.slice( firstChar.length );
return mwString.lcFirst( text );
},
/**
@ -1431,8 +1429,7 @@ mw.jqueryMsg.HtmlEmitter.prototype = {
*/
ucfirst: function ( nodes ) {
var text = textify( nodes[ 0 ] );
var firstChar = charAt( text, 0 );
return firstChar.toUpperCase() + text.slice( firstChar.length );
return mwString.ucFirst( text );
}
};

View file

@ -70,6 +70,8 @@ return [
'tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.String.charAt.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.String.lcFirst.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.String.ucFirst.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js',

View file

@ -0,0 +1,19 @@
( function () {
var lcFirst = require( 'mediawiki.String' ).lcFirst;
QUnit.module( 'mediawiki.String.lcFirst', QUnit.newMwEnvironment() );
QUnit.test( 'lcFirst', function ( assert ) {
assert.strictEqual( lcFirst( '' ), '', 'Empty string' );
assert.strictEqual( lcFirst( '/' ), '/', 'Slash is unchanged' );
assert.strictEqual( lcFirst( 'AB' ), 'aB', 'Upper case letters' );
assert.strictEqual( lcFirst( 'ab' ), 'ab', 'Lower case letters' );
assert.strictEqual( lcFirst( '\uD803' ), '\uD803', 'First surrogate only' );
assert.strictEqual( lcFirst( '\uD803x' ), '\uD803x', 'First surrogate with char' );
assert.strictEqual( lcFirst( '\uDC80' ), '\uDC80', 'Second surrogate only' );
assert.strictEqual( lcFirst( '\uDC80x' ), '\uDC80x', 'Second surrogate with char' );
assert.strictEqual( lcFirst( '\uD803\uDC80\uD803\uDCC0' ), '\uD803\uDCC0\uD803\uDCC0',
'U+10C80 (OLD HUNGARIAN CAPITAL LETTER A) -> U+10CC0 (OLD HUNGARIAN SMALL LETTER A)'
);
} );
}() );

View file

@ -0,0 +1,19 @@
( function () {
var ucFirst = require( 'mediawiki.String' ).ucFirst;
QUnit.module( 'mediawiki.String.ucFirst', QUnit.newMwEnvironment() );
QUnit.test( 'ucFirst', function ( assert ) {
assert.strictEqual( ucFirst( '' ), '', 'Empty string' );
assert.strictEqual( ucFirst( '/' ), '/', 'Slash is unchanged' );
assert.strictEqual( ucFirst( 'AB' ), 'AB', 'Upper case letters' );
assert.strictEqual( ucFirst( 'ab' ), 'Ab', 'Lower case letters' );
assert.strictEqual( ucFirst( '\uD803' ), '\uD803', 'First surrogate only' );
assert.strictEqual( ucFirst( '\uD803x' ), '\uD803x', 'First surrogate with char' );
assert.strictEqual( ucFirst( '\uDC80' ), '\uDC80', 'Second surrogate only' );
assert.strictEqual( ucFirst( '\uDC80x' ), '\uDC80x', 'Second surrogate with char' );
assert.strictEqual( ucFirst( '\uD803\uDCC0\uD803\uDCC0' ), '\uD803\uDC80\uD803\uDCC0',
'U+10CC0 (OLD HUNGARIAN SMALL LETTER A) -> U+10C80 (OLD HUNGARIAN CAPITAL LETTER A)'
);
} );
}() );