// ApplyCharacterStyleToCapitals.js
/*
Apply character style to capitals
Applies a certain character style to a sequence of capitals,
possibly separated by padding characters. The characters
used can be specified by the user.
Download script source.
(c) 2005 PM. All rights reserved.
This script is licensed under the Artistic License,
see http://csscripting.berlios.de/license/ for details.
Version: 0.2.0
Date: 2005-03-08
More scripts are available at http://csscripting.berlios.de/
*/
if ( app.documents.length < 1 )
exit();
if ( !(app.activeDocument.selection[0] instanceof Text) )
{
alert( "Select text to change." );
exit();
}
var d = app.dialogs.add( {name: "Format capitals"} );
var charStyleDD, dontApplyEG, charactersTEB, paddingCharsEG, paddingCharsTEB, keepLogCB;
var dontApplyCBs = new Array();
with ( d.dialogColumns.add() )
{
with ( dialogRows.add() )
{
dialogColumns.add().staticTexts.add( {staticLabel: "Apply character style"} );
charStyleDD = dialogColumns.add().dropdowns.add( {stringList: [].concat( app.activeDocument.characterStyles.everyItem().name ), selectedIndex: 0} );
}
with ( dialogRows.add() )
{
dialogColumns.add().staticTexts.add( {staticLabel: "Characters"} );
charactersTEB = dialogColumns.add().textEditboxes.add( {editContents: "A-Z", minWidth: 300 } );
}
with ( paddingCharsEG = dialogRows.add().enablingGroups.add( {staticLabel: "Allow padding characters", checkedState: true} ) )
paddingCharsTEB = textEditboxes.add( {editContents: "\\u2000-\\u200D\\u00A0\\u0026\\u00AD", minWidth: 300} );
with ( dontApplyEG = dialogRows.add().enablingGroups.add( {staticLabel: "Don't apply to paragraph styles", checkedState: false} ) )
{
var cols = [dialogColumns.add(), dialogColumns.add()];
for ( var k = 0; k < app.activeDocument.paragraphStyles.length; k++ )
dontApplyCBs.push( cols[k % 2].dialogRows.add().checkboxControls.add( {checkedState: false, staticLabel: app.activeDocument.paragraphStyles.item(k).name} ) );
}
keepLogCB = dialogRows.add().checkboxControls.add( {checkedState: false, staticLabel: "Save log file"} );
}
var re;
for ( ; ; )
{
if ( !d.show() )
{
d.destroy();
exit();
}
if ( charactersTEB.editContents.length < 1 )
{
alert( "Illegal specification of allowed characters." );
continue;
}
if ( paddingCharsEG.checkedState && charactersTEB.editContents.length > 0 )
re = "[" + charactersTEB.editContents.replace( /(?=[\[\]])/g, "\\" ) + "](?:[" + paddingCharsTEB.editContents.replace( /(?=[\[\]])/g, "\\" ) + "]?[" + charactersTEB.editContents.replace( /(?=[\[\]])/g, "\\" ) + "])+";
else
re = "[" + charactersTEB.editContents.replace( /(?=[\[\]])/g, "\\" ) + "]{2,}";
try { re = new RegExp( re, "g" ); }
catch ( e ) { alert( "Failed to compile regular expression: " + e ); continue; }
break;
}
var log, logFile, keepLog;
if ( keepLog = keepLogCB.checkedState )
{
for ( ; ; )
{
if ( logFile = File.saveDialog( "Select where to save log file." ) )
{
logFile = new File( logFile );
if ( logFile.exists && !confirm( 'Warning: "' + logFile.name + '" already exists. Are you sure you want to append to it?' ) )
continue;
if ( !logFile.open( "e", "TEXT", "????" ) )
alert( "Unable to open log file for editing." );
else
break;
}
else
{
d.destroy();
exit();
}
}
}
if ( keepLog ) log( "Searching for matching words..." );
var charStyle = app.activeDocument.characterStyles.item( charStyleDD.selectedIndex );
var txt = app.activeDocument.selection[0].contents;
var match;
var matches = new Object();
while ( match = re.exec( txt ) )
matches[match[0].replace( /\^/g, "^^" )] = 1;
var nmatches = 0;
for ( match in matches )
nmatches++;
if ( keepLog ) log( nmatches + " match(es) found. Abort execution by removing this file." );
var nreplacements = 0;
var start = new Date();
// Using extra search() rather than modifying characters.itemByRange(...) is far faster.
if ( !dontApplyEG.checkedState )
doReplacements( matches, {fontStyle:"Italic"} );
else
{
var n = 0;
for ( var k = 0; k < dontApplyCBs.length; k++ )
if ( !dontApplyCBs[k].checkedState )
n++;
nmatches *= n;
for ( var k = 0; k < dontApplyCBs.length; k++ )
if ( !dontApplyCBs[k].checkedState )
if ( !doReplacements( matches, {appliedParagraphStyle: app.activeDocument.paragraphStyles.item(k), fontStyle:"Italic"} ) )
break;
}
app.findPreferences = app.changePreferences = null;
d.destroy();
if ( keepLog ) log( "Total search time: " + formatDuration( (new Date()).getTime() - start.getTime() ) );
function log ( str )
{
if ( !logFile.exists )
{
if ( confirm( "Log file removed. Abort execution of script?" ) )
{
d.destroy();
exit();
}
}
// Ignore any errors here... (Poor mans flush().)
if ( logFile.open( "e", "TEXT", "????" ) )
try
{
logFile.lineFeed = "unix";
logFile.seek( 0, 2 );
logFile.writeln( str );
logFile.close();
}
catch ( e ) { ; }
else
alert( "failed to open log." );
}
function doReplacements ( matches, findAttributes )
{
var previous = new Date();
if ( findAttributes != undefined && keepLog )
log( 'Starting replacements for paragraph style "' + findAttributes.appliedParagraphStyle.name + '".' );
for ( match in matches )
{
var replacements;
for ( ; ; )
{
app.findPreferences = app.changePreferences = null;
// XXX: Assumes search() only returns undefined when aborted.
replacements = app.activeDocument.selection[0].search( match, false, true, undefined, findAttributes, {appliedCharacterStyle: charStyle} );
if ( replacements != undefined )
break;
else if ( confirm( "Are you sure you want to abort the script?" ) )
return(false);
}
nreplacements++;
if ( keepLog )
{
var current = new Date();
// Note: Each search() takes about the same, number of replacements doesn't matter.
log( '[' + (nreplacements/nmatches * 100).toFixed(0) + '%] Replaced ' + replacements.length + ' occurrence(s) of "' + match + '" in ' + ((current.getTime() - previous.getTime())/1000).toFixed(3) + 's. ETA ' + formatDuration( (current.getTime() - start.getTime()) * (nmatches-nreplacements)/nreplacements ) );
previous = current;
}
}
return(true);
}
function formatDuration ( ms )
{
var s, m, h, d;
s = ms/1000;
s -= (d = Math.floor( s/(60*60*24) )) * 60*60*24;
s -= (h = Math.floor( s/(60*60) )) * 60*60;
s -= (m = Math.floor( s/60 )) * 60;
s = Math.round(s);
if ( d != 0 )
return( d + "d " + h + "h " + m + "m " + s + "s" );
else if ( h != 0 )
return( h + "h " + m + "m " + s + "s" );
else if ( m != 0 )
return( m + "m " + s + "s" );
else
return( s + "s" );
}