﻿// Copyright 2007.  Adobe Systems, Incorporated.  All rights reserved.

// This script will apply each comp and then export to a PDF Presentation

// Written by Naoki Hada

// ZStrings and auto layout by Tom Ruark



/*

@@@BUILDINFO@@@ Layer Comps To PDF.jsx 1.0.0.12

*/



/*



// BEGIN__HARVEST_EXCEPTION_ZSTRING



<javascriptresource>

    <name>$$$/JavaScripts/LayerCompsToPDF/Menu=Layer Comps to PDF...</name>

    <category>scriptexport</category>

    <menu>export</menu>

    

    <eventid>B20FB700-B96A-4C10-B666-8C9B9DEF594E</eventid>

    <enableinfo>PSHOP_DocHasLayerComps</enableinfo>

        <terminology>

                <![CDATA[<< /Version 1

                             /Events <<

                              /B20FB700-B96A-4C10-B666-8C9B9DEF594E [($$$/JavaScripts/LayerCompsToPDF/Action=Layer Comps to PDF) /noDirectParam <<

                              >>]

                             >>

                          >> ]]>

     </terminology>

</javascriptresource>



// END__HARVEST_EXCEPTION_ZSTRING



*/



// enable double clicking from the Macintosh Finder or the Windows Explorer

#target photoshop



// debug level: 0-2 (0:disable, 1:break on error, 2:break at beginning)

// $.level = 0;

// debugger; // launch debugger on next line



// on localized builds we pull the $$$/Strings from a .dat file, see documentation for more details

$.localize = true;



// enable double clicking from the Macintosh Finder or the Windows Explorer

#target photoshop



// debug level: 0-2 (0:disable, 1:break on error, 2:break at beginning)

// $.level = 0;

// debugger; // launch debugger on next line



// on localized builds we pull the $$$/Strings from a .dat file, see documentation for more details

$.localize = true;



//=================================================================

// Globals

//=================================================================



// ok and cancel button

var runButtonID = 1;

var cancelButtonID = 2;



// UI strings to be localized

var strTitle = localize("$$$/JavaScripts/LayerCompsToPDF/Title=Layer Comps To PDF");

var strButtonRun = localize("$$$/JavaScripts/LayerCompsToPDF/Run=Run");

var strButtonCancel = localize("$$$/JavaScripts/LayerCompsToPDF/Cancel=Cancel");

var strHelpText = localize("$$$/JavaScripts/LayerCompsToPDF/HelpText=Please specify the location where flat image files should be saved. Once Photoshop has saved these files, it will launch PDF Presentation in order to convert each file into a PDF page.");

var strLabelDestination = localize("$$$/JavaScripts/LayerCompsToPDF/Destination=Destination:");

var strButtonBrowse = localize("$$$/JavaScripts/LayerCompsToPDF/Browse=&Browse...");

var strCheckboxSelectionOnly = localize("$$$/JavaScripts/LayerCompsToPDF/Selected=&Selected Layer Comps Only");

var strPanelSlideShowOptions = localize("$$$/JavaScripts/LayerCompsToPDF/SlideShow=Slideshow Options:");

var strLabelAdvaceEvery = localize("$$$/JavaScripts/LayerCompsToPDF/AdvanceEvery=&Advance Every");

var strLabelSecond = localize("$$$/JavaScripts/LayerCompsToPDF/Seconds=Seconds");

var strLabelLoopAfterLastPage = localize("$$$/JavaScripts/LayerCompsToPDF/Loop=&Loop after last page");

var strAlertSpecifyDestination = localize("$$$/JavaScripts/LayerCompsToPDF/SpecifyDestination=Please specify destination.");

var strTitleSelectDestination = localize("$$$/JavaScripts/LayerCompsToPDF/SelectDestination=Select Destination");

var strAlertDocumentMustBeOpened = localize("$$$/JavaScripts/LayerCompsToPDF/OneDocument=You must have a document open to export!");

var strAlertNoLayerCompsFound = localize("$$$/JavaScripts/LayerCompsToPDF/NoComps=No layer comps found in document!");

var strAlertWasSuccessful = localize("$$$/JavaScripts/LayerCompsToPDF/Success= was successful.");

var strAlertFailed = localize("$$$/JavaScripts/LayerCompsToPDF/Fail= failed.");

var strMessage = localize("$$$/JavaScripts/LayerCompsToPDF/Message=Layer Comps To PDF action settings");

var strEditTextDestinationLength = localize("$$$/locale_specific/JavaScripts/LayerCompsToPDF/EditTextDestinationLength=160" );

var strEditTextSecondsLength = localize("$$$/locale_specific/JavaScripts/LayerCompsToPDF/EditTextSecondsLength=25" );

var strAlertIncorrectDestination = localize("$$$/JavaScripts/LayerCompsToPDF/IncorrectDestination=Unable to export. PDF destination does not exist on system.");

 var strAlertSaveDocument= localize("$$$/JavaScripts/LayerCompsToPDF/Filenotsaved= Active document has not been saved.  Please save before exporting to PDF.");



main();



///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////







///////////////////////////////////////////////////////////////////////////////

// Function: settingDialog

// Usage: create the main dialog

// Input: settings to initialize the dialog with , ExportInfo object

// Return: true if ok, false if cancel

///////////////////////////////////////////////////////////////////////////////

function settingDialog(exportInfo) 

{

    dlgMain = new Window("dialog", strTitle);

    

	dlgMain.orientation = 'column';

	dlgMain.alignChildren = 'left';

	

	// -- top of the dialog, first line

    dlgMain.add("statictext", undefined, strLabelDestination);



	// -- two groups, one for left and one for right ok, cancel

	dlgMain.grpTop = dlgMain.add("group");

	dlgMain.grpTop.orientation = 'row';

	dlgMain.grpTop.alignChildren = 'top';

	dlgMain.grpTop.alignment = 'fill';



	// -- group contains four lines

	dlgMain.grpTopLeft = dlgMain.grpTop.add("group");

	dlgMain.grpTopLeft.orientation = 'column';

	dlgMain.grpTopLeft.alignChildren = 'left';

	dlgMain.grpTopLeft.alignment = 'fill';

	

	// -- the second line in the dialog

	dlgMain.grpSecondLine = dlgMain.grpTopLeft.add("group");

	dlgMain.grpSecondLine.orientation = 'row';

	dlgMain.grpSecondLine.alignChildren = 'center';



    dlgMain.etDestination = dlgMain.grpSecondLine.add("edittext", undefined, exportInfo.destination);

    dlgMain.etDestination.preferredSize.width = StrToIntWithDefault( strEditTextDestinationLength, 160 );



    dlgMain.btnBrowse = dlgMain.grpSecondLine.add("button", undefined, strButtonBrowse);

    dlgMain.btnBrowse.onClick = btnBrowseOnClick;



	// -- the third line in the dialog

    dlgMain.cbSelection = dlgMain.grpTopLeft.add("checkbox", undefined, strCheckboxSelectionOnly);

    dlgMain.cbSelection.value = exportInfo.selectionOnly;



	// -- the fourth line is a panel

	dlgMain.pnlSlideShow = dlgMain.grpTopLeft.add("panel");

	dlgMain.pnlSlideShow.orientation = 'column';

	dlgMain.pnlSlideShow.alignChildren = 'left';

	dlgMain.pnlSlideShow.alignment = 'fill';

	dlgMain.pnlSlideShow.text = strPanelSlideShowOptions;

	

	// -- the fifth line, three things in row orientation

	dlgMain.grpFifthLine = dlgMain.pnlSlideShow.add("group");

	dlgMain.grpFifthLine.orientation = 'row';

	dlgMain.grpFifthLine.alignChildren = 'center';

	dlgMain.grpFifthLine.alignment = 'left';

	

    dlgMain.cbAdvance = dlgMain.grpFifthLine.add( "checkbox", undefined, strLabelAdvaceEvery );

    dlgMain.cbAdvance.value = exportInfo.ssoAdvance;



    dlgMain.etSeconds = dlgMain.grpFifthLine.add("edittext", undefined, exportInfo.ssoSeconds.toString());

    dlgMain.etSeconds.preferredSize.width = StrToIntWithDefault( strEditTextSecondsLength, 25 );



    dlgMain.grpFifthLine.add("statictext", undefined, strLabelSecond);

	

    dlgMain.cbLoop = dlgMain.pnlSlideShow.add( "checkbox", undefined, strLabelLoopAfterLastPage );

    dlgMain.cbLoop.value = exportInfo.ssoLoop;



	// the right side of the dialog, the ok and cancel buttons

	dlgMain.grpTopRight = dlgMain.grpTop.add("group");

	dlgMain.grpTopRight.orientation = 'column';

	dlgMain.grpTopRight.alignChildren = 'fill';

	

	dlgMain.btnRun = dlgMain.grpTopRight.add("button", undefined, strButtonRun );

    dlgMain.btnRun.onClick = btnRunOnClick;



	dlgMain.btnCancel = dlgMain.grpTopRight.add("button", undefined, strButtonCancel );

    dlgMain.btnCancel.onClick = function() { 

		var d = this;

		while (d.type != 'dialog') {

			d = d.parent;

		}

		d.close(cancelButtonID); 

	}



	dlgMain.defaultElement = dlgMain.btnRun;

	dlgMain.cancelElement = dlgMain.btnCancel;



	// the bottom of the dialog

	dlgMain.grpBottom = dlgMain.add("group");

	dlgMain.grpBottom.orientation = 'column';

	dlgMain.grpBottom.alignChildren = 'left';

	dlgMain.grpBottom.alignment = 'fill';

    

    dlgMain.pnlHelp = dlgMain.grpBottom.add("panel");

    dlgMain.pnlHelp.alignment = 'fill';



    dlgMain.etHelp = dlgMain.pnlHelp.add("statictext", undefined, strHelpText, {multiline:true});

    dlgMain.etHelp.alignment = 'fill';



	// do not allow anything except for numbers 0-9

	//dlgMain.etSeconds.addEventListener ('keydown', NumericEditKeyboardHandler);



	// in case we double clicked the file

    app.bringToFront();



    dlgMain.center();

    var result = dlgMain.show();



    if (cancelButtonID == result) {

		return result;

	}

    

    // get setting from dialog

    exportInfo.destination = dlgMain.etDestination.text;

    exportInfo.selectionOnly = dlgMain.cbSelection.value;

    exportInfo.ssoAdvance = dlgMain.cbAdvance.value;

    exportInfo.ssoSeconds = dlgMain.etSeconds.text;

    exportInfo.ssoLoop = dlgMain.cbLoop.value;



    var pdf = ".pdf";

    

    if ((File.fs === "Macintosh" ) && (exportInfo.destination.toLowerCase().lastIndexOf("/") == -1))

    {

        exportInfo.destination = Folder.myDocuments + "/" + exportInfo.destination;

    }



    if ((exportInfo.destination.length - pdf.length) != exportInfo.destination.toLowerCase().lastIndexOf(pdf )) {

        exportInfo.destination += pdf; // add ".pdf" if there is no PDF extension

    }



    return result;

}





///////////////////////////////////////////////////////////////////////////////

// Function: btnRunOnClick

// Usage: routine is assigned to the onClick method of the run button

// Input: checks the dialog params and closes with the dialog with true

// Return: <none>, dialog is closed with true on success

///////////////////////////////////////////////////////////////////////////////

function btnRunOnClick()

{

    // check if the setting is properly

    var destination = dlgMain.etDestination.text;

    if (destination.length == 0) {

        alert(strAlertSpecifyDestination);

        return;

    }



 	var destPath = Folder(destination).parent;

	if (!destPath.exists) 

	{

	    alert(strAlertIncorrectDestination + "\n" +destPath );

        return;	

	} 



	// find the dialog in this auto layout mess

	var d = this;

	while (d.type != 'dialog') {

		d = d.parent;

	}

	d.close(runButtonID); 

}





///////////////////////////////////////////////////////////////////////////////

// Function: btnBrowseOnClick

// Usage: routine is assigned to the onClick method of the browse button

// Input: pop the selectDialog, and get a folder

// Return: <none>, sets the destination edit text

///////////////////////////////////////////////////////////////////////////////

function btnBrowseOnClick()

{

    var selFile = File.saveDialog(strTitleSelectDestination);

    if ( selFile != null ) {

        dlgMain.etDestination.text = selFile.fsName;

    }

	dlgMain.defaultElement.active = true;

    return;

}





///////////////////////////////////////////////////////////////////////////////

// Function: ExportInfo

// Usage: object for holding the dialog parameters

// Input: <none>

// Return: object holding the export info

///////////////////////////////////////////////////////////////////////////////

function ExportInfo() {

    this.destination = app.activeDocument.path + "/" + app.activeDocument.name.substr(0,app.activeDocument.name.lastIndexOf("."))+".pdf" ;

    this.selectionOnly = false;

    this.ssoAdvance = true;

    this.ssoSeconds = 5;

    this.ssoLoop = false;

}





///////////////////////////////////////////////////////////////////////////////

// Function: setTempFolder

// Usage: create a temp folder using random numbers

// Input: export info object

// Return: tempLocation of the object is set 

///////////////////////////////////////////////////////////////////////////////

function setTempFolder(exportInfo)

{

    var tempfolder = Folder.userData; 

    while(true) {  

      exportInfo.tempLocation = tempfolder.path + "/temp_psLCexp";

        var testFolder = new Folder(exportInfo.tempLocation);

        if (!testFolder.exists) {

            testFolder.create();

            break;

        }

    break;

    }

}





///////////////////////////////////////////////////////////////////////////////

// Function: zeroSuppress

// Usage: create a string padded with 0's

// Input: num and number of digits to pad

// Return: zero padded num

///////////////////////////////////////////////////////////////////////////////

function zeroSuppress (num, digit) {

    var tmp = num.toString();

    while(tmp.length < digit)   tmp = "0" + tmp;

    return tmp;

}



function logToHeadLights(eventRecord) {

    var headlightsActionID = stringIDToTypeID("headlightsLog");

    var desc = new ActionDescriptor();

    desc.putString(stringIDToTypeID("subcategory"), "Export");

    desc.putString(stringIDToTypeID("eventRecord"), eventRecord);

    executeAction(headlightsActionID, desc, DialogModes.NO);

}





///////////////////////////////////////////////////////////////////////////////

// Function: main

// Usage: the main routine for this JavaScript

// Input: <none>

// Return: <none>

///////////////////////////////////////////////////////////////////////////////

function main()

{



    if ( app.documents.length <= 0 ) {

        if ( DialogModes.NO != app.playbackDisplayDialogs ) {

            alert( strAlertDocumentMustBeOpened );

        }

    	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script

    }



 // check to make sure document is saved before running exportInfo

    try { app.activeDocument.path } catch (e) 

         {

            if ( DialogModes.NO != app.playbackDisplayDialogs ) 

            {

            alert(strAlertSaveDocument );

             this.destination = false; 

            }

        return 'cancel'; 

     }

 

    var exportInfo = new ExportInfo();

    

//~  	// look for last used params via Photoshop registry, getCustomOptions will throw if none exist

//~ 	try {

//~ 		var d = app.getCustomOptions("f2e27792-1ef0-4f6f-a157-3a6ad8f6edf0");

//~ 		descriptorToObject(exportInfo, d, strMessage);

//~ 	}

//~ 	catch(e) {

//~ 		// it's ok if we don't have any options, continue with defaults

//~ 	}

    

	// see if I am getting descriptor parameters

    descriptorToObject(exportInfo, app.playbackParameters, strMessage);

    

    if ( DialogModes.ALL == app.playbackDisplayDialogs ) {

    	if (cancelButtonID == settingDialog(exportInfo)) {

        	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script

	    }

	}



    try {

        var docName = app.activeDocument.name;

        setTempFolder(exportInfo);

    

        var compsName = new String("none");

        var compsCount = app.activeDocument.layerComps.length;

        if ( compsCount <= 0 ) {

            if ( DialogModes.NO != app.playbackDisplayDialogs ) {

                alert ( strAlertNoLayerCompsFound );

            }

        	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script

        } else {

            app.activeDocument = app.documents[docName];

            docRef = app.activeDocument;

            var tempFileList = new Array();   // array of File

    

            var exportFileCount = 0;

            for ( compsIndex = 0; compsIndex < compsCount; compsIndex++ ) {

                var compRef = docRef.layerComps[compsIndex];

                if (exportInfo.selectionOnly && !compRef.selected) continue; // selected only

                compRef.apply();

                var duppedDocument = app.activeDocument.duplicate();    

	    	    if (duppedDocument.bitsPerChannel == BitsPerChannelType.THIRTYTWO)

				    duppedDocument.bitsPerChannel = BitsPerChannelType.SIXTEEN;

                    

                var fileNameBody = zeroSuppress(compsIndex, 4);

                fileNameBody += "_" + compRef.name;

                if (null != compRef.comment)    fileNameBody += "_" + compRef.comment;

                fileNameBody = fileNameBody.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n]/g, "_");  // '/\:*?"<>|\r\n' -> '_'

                if (fileNameBody.length > 120) fileNameBody = fileNameBody.substring(0,120);

                var tempFile = File.encode(exportInfo.tempLocation + "/" + fileNameBody + ".psd");

                tempFile = new File( tempFile );

                tempFileList[exportFileCount] = tempFile;   

                duppedDocument.saveAs( tempFile );

                duppedDocument.close();

                exportFileCount++;

            }

    

            if (exportFileCount == 0) {

                if ( DialogModes.NO != app.playbackDisplayDialogs ) {

                    alert(strTitle + strAlertFailed );

                }

            	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script

            }



            // run PDF Presentation

            var presentationOptions = new PresentationOptions();

            presentationOptions.presentation = true;

            presentationOptions.view = true;

            presentationOptions.autoAdvance = exportInfo.ssoAdvance;

            presentationOptions.interval = exportInfo.ssoSeconds;

            presentationOptions.loop = exportInfo.ssoLoop;

            app.makePDFPresentation(tempFileList, File(exportInfo.destination), presentationOptions);

    

            // delete temporary files

            for ( compsIndex = 0; compsIndex < exportFileCount; compsIndex++ ) {

                tempFileList[compsIndex].remove();

            }

            // delete temprary folder

            var tempFolder = new Folder(exportInfo.tempLocation);

            tempFolder.remove();

    

    		var d = objectToDescriptor(exportInfo, strMessage);

            app.putCustomOptions("f2e27792-1ef0-4f6f-a157-3a6ad8f6edf0", d);



    		var dd = objectToDescriptor(exportInfo, strMessage);

            app.playbackParameters = dd;



            if ( DialogModes.ALL == app.playbackDisplayDialogs ) {

                alert(strTitle + strAlertWasSuccessful + "\n" + exportInfo.destination);

            }



            app.playbackDisplayDialogs = DialogModes.ALL;

            logToHeadLights("Layer Comp to PDF count: " + exportFileCount);

        }

    } catch (e) {

        if ( DialogModes.NO != app.playbackDisplayDialogs ) {

            alert(e);

        }

    	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script

    }

}

///////////////////////////////////////////////////////////////////////////////

// Function: objectToDescriptor

// Usage: create an ActionDescriptor from a JavaScript Object

// Input: JavaScript Object (o)

//        object unique string (s)

//        Pre process converter (f)

// Return: ActionDescriptor

// NOTE: Only boolean, string, number and UnitValue are supported, use a pre processor

//       to convert (f) other types to one of these forms.

// REUSE: This routine is used in other scripts. Please update those if you 

//        modify. I am not using include or eval statements as I want these 

//        scripts self contained.

///////////////////////////////////////////////////////////////////////////////

function objectToDescriptor (o, s, f) {

	if (undefined != f) {

		o = f(o);

	}

	var d = new ActionDescriptor;

	var l = o.reflect.properties.length;

	d.putString( app.charIDToTypeID( 'Msge' ), s );

	for (var i = 0; i < l; i++ ) {

		var k = o.reflect.properties[i].toString();

		if (k == "__proto__" || k == "__count__" || k == "__class__" || k == "reflect")

			continue;

		var v = o[ k ];

		k = app.stringIDToTypeID(k);

		switch ( typeof(v) ) {

			case "boolean":

				d.putBoolean(k, v);

				break;

			case "string":

				d.putString(k, v);

				break;

			case "number":

				d.putDouble(k, v);

				break;

			default:

			{

				if ( v instanceof UnitValue ) {

					var uc = new Object;

					uc["px"] = charIDToTypeID("#Rlt"); // unitDistance

					uc["%"] = charIDToTypeID("#Prc"); // unitPercent

					d.putUnitDouble(k, uc[v.type], v.value);

				} else {

					throw( new Error("Unsupported type in objectToDescriptor " + typeof(v) ) );

				}

			}

		}

	}

    return d;

}





///////////////////////////////////////////////////////////////////////////////

// Function: descriptorToObject

// Usage: update a JavaScript Object from an ActionDescriptor

// Input: JavaScript Object (o), current object to update (output)

//        Photoshop ActionDescriptor (d), descriptor to pull new params for object from

//        object unique string (s)

//        JavaScript Function (f), post process converter utility to convert

// Return: Nothing, update is applied to passed in JavaScript Object (o)

// NOTE: Only boolean, string, number and UnitValue are supported, use a post processor

//       to convert (f) other types to one of these forms.

// REUSE: This routine is used in other scripts. Please update those if you 

//        modify. I am not using include or eval statements as I want these 

//        scripts self contained.

///////////////////////////////////////////////////////////////////////////////

function descriptorToObject (o, d, s, f) {

	var l = d.count;

	if (l) {

	    var keyMessage = app.charIDToTypeID( 'Msge' );

        if ( d.hasKey(keyMessage) && ( s != d.getString(keyMessage) )) return;

	}

	for (var i = 0; i < l; i++ ) {

		var k = d.getKey(i); // i + 1 ?

		var t = d.getType(k);

		strk = app.typeIDToStringID(k);

		switch (t) {

			case DescValueType.BOOLEANTYPE:

				o[strk] = d.getBoolean(k);

				break;

			case DescValueType.STRINGTYPE:

				o[strk] = d.getString(k);

				break;

			case DescValueType.DOUBLETYPE:

				o[strk] = d.getDouble(k);

				break;

			case DescValueType.UNITDOUBLE:

				{

				var uc = new Object;

				uc[charIDToTypeID("#Rlt")] = "px"; // unitDistance

				uc[charIDToTypeID("#Prc")] = "%"; // unitPercent

				uc[charIDToTypeID("#Pxl")] = "px"; // unitPixels

				var ut = d.getUnitDoubleType(k);

				var uv = d.getUnitDoubleValue(k);

				o[strk] = new UnitValue( uv, uc[ut] );

				}

				break;

			case DescValueType.INTEGERTYPE:

			case DescValueType.ALIASTYPE:

			case DescValueType.CLASSTYPE:

			case DescValueType.ENUMERATEDTYPE:

			case DescValueType.LISTTYPE:

			case DescValueType.OBJECTTYPE:

			case DescValueType.RAWTYPE:

			case DescValueType.REFERENCETYPE:

			default:

				throw( new Error("Unsupported type in descriptorToObject " + t ) );

		}

	}

	if (undefined != f) {

		o = f(o);

	}

}



///////////////////////////////////////////////////////////////////////////

// Function: StrToIntWithDefault

// Usage: convert a string to a number, first stripping all characters

// Input: string and a default number

// Return: a number

///////////////////////////////////////////////////////////////////////////

function StrToIntWithDefault( s, n ) {

    var onlyNumbers = /[^0-9]/g;

    var t = s.replace( onlyNumbers, "" );

	t = parseInt( t );

	if ( ! isNaN( t ) ) {

        n = t;

    }

    return n;

}



///////////////////////////////////////////////////////////////////////////////

// Function: NumericEditKeyboardHandler

// Usage: Do not allow anything except for numbers 0-9

// Input: ScriptUI keydown event

// Return: <nothing> key is rejected and beep is sounded if invalid

///////////////////////////////////////////////////////////////////////////////

function NumericEditKeyboardHandler (event) {

    try {

        var keyIsOK = KeyIsNumeric (event) || 

		              KeyIsDelete (event) || 

					  KeyIsLRArrow (event) ||

					  KeyIsTabEnterEscape (event);

					  

        if (! keyIsOK) {

            //    Bad input: tell ScriptUI not to accept the keydown event

            event.preventDefault();

            /*    Notify user of invalid input: make sure NOT

                to put up an alert dialog or do anything which

                requires user interaction, because that

                interferes with preventing the 'default'

                action for the keydown event */

            app.beep();

        }

    }

    catch (e) {

        ; // alert ("Ack! bug in NumericEditKeyboardHandler: " + e);

    }

}





//    key identifier functions

function KeyHasModifier (event) {

    return event.shiftKey || event.ctrlKey || event.altKey || event.metaKey;

}



function KeyIsNumeric (event) {

    return  (event.keyName >= '0') && (event.keyName <= '9') && ! KeyHasModifier (event);

}



function KeyIsDelete (event) {

    //    Shift-delete is ok

    return (event.keyName == 'Backspace') && ! (event.ctrlKey);

}



function KeyIsLRArrow (event) {

    return ((event.keyName == 'Left') || (event.keyName == 'Right')) && ! (event.altKey || event.metaKey);

}



function KeyIsTabEnterEscape (event) {

	return event.keyName == 'Tab' || event.keyName == 'Enter' || event.keyName == 'Escape';

}



// End Layer Comps To PDF.jsx

