﻿// c2009 Adobe Systems, Inc. All rights reserved.
// Written by Naoki Hada

/*
@@@BUILDINFO@@@ Delete All Empty Layers.jsx 1.0.0.3
*/

/*

// BEGIN__HARVEST_EXCEPTION_ZSTRING

<javascriptresource>
<name>$$$/JavaScripts/DeleteAllEmptyLayers/Menu=Delete All Empty Layers</name>
<category>Delete</category>
<enableinfo>true</enableinfo>
<eventid>a0754df2-9c60-4b64-a940-6a2bb1102652</eventid>
<terminology><![CDATA[<< /Version 1 
                         /Events << 
                          /a0754df2-9c60-4b64-a940-6a2bb1102652 [($$$/JavaScripts/DeleteAllEmptyLayers/Menu=Delete All Empty Layers) /noDirectParam <<
                          >>] 
                         >> 
                      >> ]]></terminology>
</javascriptresource>

// END__HARVEST_EXCEPTION_ZSTRING

*/

// enable double clicking from the 
// Macintosh Finder or the Windows Explorer
#target photoshop

// Make Photoshop the frontmost application
app.bringToFront();

/////////////////////////
// SETUP
/////////////////////////

// all the strings that need localized
var strDeleteAllEmptyLayersHistoryStepName = localize("$$$/JavaScripts/DeleteAllEmptyLayers/Menu=Delete All Empty Layers" );

/////////////////////////
// MAIN
/////////////////////////

var doc = app.activeDocument; // remember the document, the selected layer, the visibility setting of the selected layer
var currentLayer = doc.activeLayer; // remember the selected layer
var currentVisible = currentLayer.visible;// remember the visibility setting of the selected layer
var mySelectedLayers = getSelectedLayers(); // remember the selected layers
var myDialogMode = app.displayDialogs; // remember the dialog mode
app.displayDialogs = DialogModes.NO; // set it to no to avoid dialogs

// Create only one history state for the entire script
doc.suspendHistory(strDeleteAllEmptyLayersHistoryStepName, "main()");

// restore the selected layer
try{ 
	doc.activeLayer = currentLayer;
}catch(e) {
	; // do nothing
}

// restore the visibility setting of the original layer
try{ 
	currentLayer.visible = currentVisible;
}catch(e) {
	; // do nothing
}

if (mySelectedLayers.length != 0){//,more than one layer selected
	// restore the selected layers
	try{ 
		setSelectedLayers(mySelectedLayers);
	}catch(e) {
		; // do nothing
	}
}

// Record the script in the Actions palette when recording an action
try{ 
	var playbackDescription = new ActionDescriptor;
	var playbackReference = new ActionReference;
	playbackReference.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	playbackDescription.putReference( charIDToTypeID("null"), playbackReference);
	app.playbackDisplayDialogs = DialogModes.NO;
	app.playbackParameters = playbackDescription;
}catch(e) {
	; // do nothing
}

app.displayDialogs = myDialogMode;


/////////////////////////
// FUNCTIONS
/////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Function: main
// Usage: container function to hold all the working code that generates history states
// Input: <none> Must have an open document
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function main(){
    try {
		var originalRulerUnits = app.preferences.rulerUnits; // remember current setting
		app.preferences.rulerUnits = Units.PIXELS; // set Pixel for checking bounds

        var doc = app.activeDocument;
		deleteAllEmptyLayers(doc);
		deleteAllEmptyLayerSets(doc);
		
		app.preferences.rulerunits = originalRulerUnits; // restore unit
    } catch(e) {
    	return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script
    }
}


///////////////////////////////////////////////////////////////////////////////
// Function: deleteAllEmptyArtLayers
// Usage: the worker routine, take our params and save the file accordingly
// Input: Object o: It can be document object or LayerSets.
//        It is called recursively.
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function deleteAllEmptyLayers(o){
	try{
		for(var i = o.artLayers.length-1; 0 <= i; i--){ // top level layers
			var layer = o.artLayers[i];
			if(    layer.allLocked
				|| layer.pixelsLocked
				|| layer.positionLocked
				|| layer.transparentPixelsLocked
				||(0 != layer.linkedLayers.length)){ // linked
				continue; // skip locked or linked
			}

			if((LayerKind.TEXT == layer.kind)){ // TEXT Layer
				if("" == layer.textItem.contents)	layer.remove();
			}else{ // Other Layer
				if(    (0 == layer.bounds[2])	// width = 0
					&& (0 == layer.bounds[3])){	// height = 0
					layer.remove();
				}
			}
		}
		for(var i = o.layerSets.length-1; 0 <= i; i--){ // LayerSet
			var layerSet = o.layerSets[i];
			if((layerSet.allLocked)||(0 != layerSet.linkedLayers.length)){
				continue; // skip locked or linked
			}
			deleteAllEmptyLayers(layerSet); // recursive call
		}
	}catch(e){
		; // do nothing
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function: deleteAllEmptyLayerSets
// Usage: the worker routine, take our params and save the file accordingly
// Input: Object o: It can be document object or LayerSets.
//        It is called recursively.
// Return: <none>, a file on disk
///////////////////////////////////////////////////////////////////////////////
function deleteAllEmptyLayerSets(o){
	try{
		var bEmpty = true;
		for(var i = o.layerSets.length-1; 0 <= i; i--) {
			var layerSet = o.layerSets[i];
			if((layerSet.allLocked)||(0!=layerSet.linkedLayers.length)){
				continue; // skip locked or linked
			}
			if(deleteAllEmptyLayerSets(layerSet)){ // empty
				layerSet.remove(); // delete
			} else 	bEmpty = false;		 // not empty
		}
		if(0 != o.artLayers.length) bEmpty = false; // not empty
	}catch(e){
		; // do nothing
	}
	return bEmpty;
}


///////////////////////////////////////////////////////////////////////////////
// Function: getSelectedLayers
// Usage: creates and array of the currently selected layers
// Input: <none> Must have an open document
// Return: Array selectedLayers
///////////////////////////////////////////////////////////////////////////////
function getSelectedLayers() {
	var selectedLayers = [];
	try {
		var backGroundCounter = activeDocument.artLayers[activeDocument.artLayers.length-1].isBackgroundLayer ? 0 : 1;
		var ref = new ActionReference();
		var keyTargetLayers = app.stringIDToTypeID( 'targetLayers' );
		ref.putProperty( app.charIDToTypeID( 'Prpr' ), keyTargetLayers );
		ref.putEnumerated( app.charIDToTypeID( 'Dcmn' ), app.charIDToTypeID( 'Ordn' ), app.charIDToTypeID( 'Trgt' ) );
		var desc = executeActionGet( ref );
		if ( desc.hasKey( keyTargetLayers ) ) {
			var layersList = desc.getList( keyTargetLayers );
			for ( var i = 0; i < layersList.count; i++) {
				var listRef = layersList.getReference( i );
				selectedLayers.push( listRef.getIndex() + backGroundCounter );
			}
			//hasLayerMask = true;
		}
	}catch(e) {
		; // do nothing
	}
	return selectedLayers;
}

///////////////////////////////////////////////////////////////////////////////
// Function: setSelectedLayers
// Usage: Selects an array of layers
// Input:  Array selectedLayers
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function setSelectedLayers( layerIndexesOrNames ) {
	// first select the first one
	setSelectedLayer( layerIndexesOrNames[0] );

	// then add to the selection
	for ( var i = 1; i < layerIndexesOrNames.length; i++) {
		addSelectedLayer( layerIndexesOrNames[i] );
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function: setSelectedLayer
// Usage: Selects the first layer
// Input:  Array selectedLayers
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function setSelectedLayer( layerIndexOrName ) {
	try {
		var id239 = charIDToTypeID( "slct" );
		var desc45 = new ActionDescriptor();
		var id240 = charIDToTypeID( "null" );
		var ref43 = new ActionReference();
		var id241 = charIDToTypeID( "Lyr " );
		if ( typeof layerIndexOrName == "number" ) {
			ref43.putIndex( id241, layerIndexOrName );
		} else {
			ref43.putName( id241, layerIndexOrName );
		}
		desc45.putReference( id240, ref43 );
		var id242 = charIDToTypeID( "MkVs" );
		desc45.putBoolean( id242, false );
		executeAction( id239, desc45, DialogModes.NO );
	}catch(e) {
		; // do nothing
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function: addSelectedLayer
// Usage: adds the rest of the layers in the array to the first layer
// Input:  Array selectedLayers
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function addSelectedLayer( layerIndexOrName ) {
	try {
		var id243 = charIDToTypeID( "slct" );
		var desc46 = new ActionDescriptor();
		var id244 = charIDToTypeID( "null" );
		var ref44 = new ActionReference();
		var id245 = charIDToTypeID( "Lyr " );
		if ( typeof layerIndexOrName == "number" ) {
			ref44.putIndex( id245, layerIndexOrName );
		} else {
			ref44.putName( id245, layerIndexOrName );
		}
		desc46.putReference( id244, ref44 );
		var id246 = stringIDToTypeID( "selectionModifier" );
		var id247 = stringIDToTypeID( "selectionModifierType" );
		var id248 = stringIDToTypeID( "addToSelection" );
		desc46.putEnumerated( id246, id247, id248 );
		var id249 = charIDToTypeID( "MkVs" );
		desc46.putBoolean( id249, false );
		executeAction( id243, desc46, DialogModes.NO );
	}catch(e) {
		; // do nothing
	}
}

// End Delete All Empty Layers.jsx

