User:ZKang123/TitleCaseConverter.js

// titlecaseconverter.js

/* eslint-disable no-alert, no-console */

$( () => {

/**

* Convert titles to title case

*/

function toTitleCase( title, changeExistingCapitalization = true ) {

const isAllCaps = title.toUpperCase() === title;

if ( isAllCaps ) {

title = title.toLowerCase();

}

title = title.split( ' ' ).map( ( word, index, array ) => {

// Retain words that are already in uppercase or are special cases

if ( word.toUpperCase() === word || isSpecialCase( word ) ) {

return word;

}

// Retain capitalization for words following certain punctuation marks

if ( index > 0 && /[/;\-,]/.test( array[ index - 1 ] ) ) {

return word.charAt( 0 ).toUpperCase() + word.slice( 1 );

}

// Check for mixed capitalization

const hasMixedCase = /[a-z]/.test(word) && /[A-Z]/.test(word);

if (hasMixedCase) {

return word;

}

// If there's already a capital letter in the word, we probably don't want to change it

const hasUpperCaseLetter = /[A-Z]/.test(word);

if (!changeExistingCapitalization && hasUpperCaseLetter) {

return word;

} else if ( shouldCapitalize( word, index, array ) ) {

return word.charAt( 0 ).toUpperCase() + word.slice( 1 ).toLowerCase();

} else {

return word.toLowerCase();

}

} ).join( ' ' );

// Capitalize first letters that occur after punctuation

title = title.replace( / [^A-Za-z][a-z]/g, ( match ) => ' ' + match.slice( 1, 2 ) + match.slice( 2 ).toUpperCase() );

// Capitalize anything after a semicolon

title = title.replace( /;[a-z]/g, ( match ) => ';' + match.slice( 1 ).toUpperCase() );

// Capitalize letters mid-word that occur after hyphens or slashes

title = title.replace( /-[a-z]/g, ( match ) => '-' + match.slice( 1 ).toUpperCase() );

title = title.replace( /\/[a-z]/g, ( match ) => '/' + match.slice( 1 ).toUpperCase() );

return title;

}

/**

* Check if a word is an abbreviation or an exception

*/

function isSpecialCase( word ) {

// Define custom exceptions for abbreviations and specific titles

const exceptions = [ 'MRT', 'LTA', 'S$', 'US$', 'NASA', 'FBI', 'MP3' ]; // Add more exceptions as needed

return exceptions.includes( word ) || /^[A-Z0-9]+$/.test( word );

}

function shouldCapitalize( word, index, array ) {

const alwaysCapitalize = [ 'Me', 'It', 'His', 'If', 'Be', 'Am', 'Is', 'Are', 'Being', 'Was', 'Were', 'Been', 'During', 'Through', 'About', 'Until', 'Below', 'Under' ];

const doNotCapitalize = [ 'a', 'an', 'the', 'and', 'by', 'at', 'but', 'or', 'nor', 'for', 'yet', 'so', 'as', 'in', 'of', 'on', 'to', 'from', 'into', 'like', 'over', 'with', 'till', 'upon', 'off', 'per', 'up', 'out', 'via' ];

const punctuationMarks = [ '.', ',', ';', ':', '?', '!' ];

const isAbbr = isSpecialCase( word );

const isProperNoun = alwaysCapitalize.includes( word );

const isShortWord = doNotCapitalize.includes( word.toLowerCase() );

const isFirstOrLastWord = index === 0 || index === array.length - 1;

const isLongPreposition = word.length >= 5;

const isVerb = [ 'be', 'am', 'is', 'are', 'being', 'was', 'were', 'been' ].includes( word.toLowerCase() );

// Preserve capitalization after punctuation marks

if ( index > 0 ) {

const prevWord = array[ index - 1 ];

const lastChar = prevWord.charAt( prevWord.length - 1 );

if ( punctuationMarks.includes( lastChar ) ) {

return true;

}

}

return isAbbr || isFirstOrLastWord || isProperNoun || isLongPreposition || !isShortWord || isVerb;

}

/**

* Convert reference titles in the HTML content

*/

function convertReferenceTitles( htmlString, changeExistingCapitalization = true ) {

const citationRegex = /]*>.*?<\/ref>/gi;

const titleRegex = /(\|title=)([^|]+)(\|)/i;

return htmlString.replace( citationRegex, ( match ) => match.replace( titleRegex, ( titleMatch, p1, p2, p3 ) => {

const originalTitle = p2.trim();

const titleCaseTitle = toTitleCase( originalTitle, changeExistingCapitalization );

// Ensure space is retained at the end

const endingSpace = p2.endsWith(' ') ? ' ' : '';

return `${ p1 }${ titleCaseTitle }${ endingSpace }${ p3 }`;

} ) );

}

/**

* Load the script and add the sidebar links

*/

function loadTitleCaseConverter() {

// Create the first sidebar link

const sidebarLink1 = document.createElement( 'li' );

const link1 = document.createElement( 'a' );

link1.innerText = 'Convert Ref Titles to Title Case (Preserve Capitalization)';

link1.href = '#';

link1.style.cssText = 'cursor: pointer; color: #0645ad;';

// Add click event listener to the first link

link1.addEventListener( 'click', ( event ) => {

event.preventDefault();

const textArea = document.querySelector( '#wpTextbox1' );

if ( textArea ) {

const summary = 'Converted reference titles to title case per MOS:CT using TitleCaseConverter';

textArea.value = convertReferenceTitles( textArea.value, false );

// Set default editing summary

const summaryInput = document.querySelector( '#wpSummary' );

if ( summaryInput && !summaryInput.value.trim() ) {

summaryInput.value = summary;

}

} else {

alert( 'Error: Editing area not found!' );

}

} );

sidebarLink1.appendChild( link1 );

// Create the second sidebar link

const sidebarLink2 = document.createElement( 'li' );

const link2 = document.createElement( 'a' );

link2.innerText = 'Convert Ref Titles to Title Case (Change Capitalization)';

link2.href = '#';

link2.style.cssText = 'cursor: pointer; color: #0645ad;';

// Add click event listener to the second link

link2.addEventListener( 'click', ( event ) => {

event.preventDefault();

const textArea = document.querySelector( '#wpTextbox1' );

if ( textArea ) {

const summary = 'Converted reference titles to title case per MOS:CT using TitleCaseConverter';

textArea.value = convertReferenceTitles( textArea.value, true );

// Set default editing summary

const summaryInput = document.querySelector( '#wpSummary' );

if ( summaryInput && !summaryInput.value.trim() ) {

summaryInput.value = summary;

}

} else {

alert( 'Error: Editing area not found!' );

}

} );

sidebarLink2.appendChild( link2 );

// Add the links to the sidebar (p-tb section)

const sidebar = document.getElementById( 'p-tb' );

const ul = sidebar ? sidebar.querySelector( 'ul' ) : null;

if ( ul ) {

ul.appendChild( sidebarLink1 );

ul.appendChild( sidebarLink2 );

} else {

alert( 'Error: Sidebar section not found!' );

}

}

function runUnitTests() {

const tests = [

// normal

{

old: 'The South and West lines',

new: 'The South and West Lines'

},

{

old: 'Work on second phase of MRT system ahead of schedule',

new: 'Work on Second Phase of MRT System Ahead of Schedule'

},

{

old: 'Earlier target date for Phase II MRT',

new: 'Earlier Target Date for Phase II MRT'

},

{

old: 'MRT System to be Implemented in Eight Stages',

new: 'MRT System to Be Implemented in Eight Stages'

},

{

old: 'MRT to Bt Batok, Bt Gombak and Choa Chu Kang on Mar 10',

new: 'MRT to Bt Batok, Bt Gombak and Choa Chu Kang on Mar 10'

},

// mid-word hyphens and slashes

{

old: 'Revived, re-opened, newly appreciated',

new: 'Revived, Re-Opened, Newly Appreciated'

},

{

old: "Streetscapes/eldridge street Synagogue;a prayer-filled time capsule from the 1880's",

new: "Streetscapes/Eldridge Street Synagogue;A Prayer-Filled Time Capsule from the 1880's"

},

{

old: 'Phase 2 gets go-ahead to ensure continuity',

new: 'Phase 2 Gets Go-Ahead To Ensure Continuity'

},

// weird mid-word capitalization

{

old: 'Phase 2 gets go-ahead to build iPad',

new: 'Phase 2 Gets Go-Ahead To Build iPad'

},

{

old: 'Phase 2 gets go-ahead to build DataMall',

new: 'Phase 2 Gets Go-Ahead To Build DataMall'

},

// all caps

{

old: 'PHASE 2 GETS GO-AHEAD TO ENSURE CONTINUITY',

new: 'Phase 2 Gets Go-Ahead To Ensure Continuity'

},

// punctuation at beginning of word

{

old: 'She was "amazingly spectacular"',

new: 'She Was "Amazingly Spectacular"'

}

];

let i = 1;

let failures = 0;

for ( const test of tests ) {

const actual = toTitleCase( test.old );

if ( actual !== test.new ) {

console.log( `[Titlecaseconverter.js] Failed unit test ${ i }. Received "${ actual }" instead of "${ test.new }".` );

failures++;

}

i++;

}

if ( !failures ) {

console.log( '[Titlecaseconverter.js] All unit tests passed. Yay.' );

}

}

// Load the script when the page is ready

if ( document.readyState !== 'loading' ) {

loadTitleCaseConverter();

} else {

document.addEventListener( 'DOMContentLoaded', loadTitleCaseConverter );

}

// Put this at the top of your common.js file to run unit tests in the browser devtools console:

// window.TitleCaseConverterUnitTests = true;

if ( window.TitleCaseConverterUnitTests ) {

runUnitTests();

}

} );