Skip to main content
Flowgun
Inspiring
July 29, 2025
Answered

treat each closed selection area separately?

  • July 29, 2025
  • 2 replies
  • 280 views

Hello,
I have a function that gets the average color in a selection area. I want to be able to select multiple areas, and get an array of the averages in those area in one go (e.g: to use in a gradient map). 
I figured out that I can convert the selection (with all the areas) to a path, and then I would have different subpaths for each selection area that I can theoretically loop through, convert into a selection, and get their average color, then delete the created path/subpaths.

However, I wasn't able to implement my idea. apparently selecting different subpaths on the same path is not possible via scripting (?), and I also failed in separating the subpaths into different paths to loop through (I found some posts about that here but I wasn't able to implement).

Any help would be appreciated.

Correct answer c.pfaffenbichler
quote

apparently selecting different subpaths on the same path is not possible via scripting (?), and I also failed in separating the subpaths into different paths to loop through (I found some posts about that here but I wasn't able to implement).

// make paths from first path’s subpathitems;
// 2014, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
if (myDocument.pathItems.length > 0) {
var theArray = collectPathInfoFromDesc2012 (myDocument, myDocument.pathItems[0]);
var theName = myDocument.pathItems[0];
for (var m = 0; m < theArray.length; m++) {
createPath2012([theArray[m]], theName+"_"+m)
}
}
};
////// collect path info from actiondescriptor, smooth added //////
function collectPathInfoFromDesc2012 (myDocument, thePath) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// based of functions from xbytor’s stdlib;
var ref = new ActionReference();
for (var l = 0; l < myDocument.pathItems.length; l++) {
	var thisPath = myDocument.pathItems[l];
	if (thisPath == thePath && thisPath.name == "Work Path") {
		ref.putProperty(cTID("Path"), cTID("WrPt"));
		};
	if (thisPath == thePath && thisPath.name != "Work Path" && thisPath.kind != PathKind.VECTORMASK) {
		ref.putIndex(cTID("Path"), l + 1);
		};
	if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {
        var idPath = charIDToTypeID( "Path" );
        var idPath = charIDToTypeID( "Path" );
        var idvectorMask = stringIDToTypeID( "vectorMask" );
        ref.putEnumerated( idPath, idPath, idvectorMask );
		};
	};
var desc = app.executeActionGet(ref);
var pname = desc.getString(cTID('PthN'));
// create new array;
var theArray = new Array;
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
// for subpathitems;
for (var m = 0; m < pathComponents.count; m++) {
	var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
	var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));
	switch (operation1) {
		case 1097098272:
		var operation = 1097098272 //cTID('Add ');
		break;
		case 1398961266:
		var operation = 1398961266 //cTID('Sbtr');
		break;
		case 1231975538:
		var operation = 1231975538 //cTID('Intr');
		break;
		default:
//		case 1102:
		var operation = sTID('xor') //ShapeOperation.SHAPEXOR;
		break;
		};
// for subpathitem’s count;
	for (var n = 0; n < listKey.count; n++) {
		theArray.push(new Array);
		var points = listKey.getObjectValue(n).getList(sTID('points'));
		try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}
		catch (e) {var closed = false};
// for subpathitem’s segment’s number of points;
		for (var o = 0; o < points.count; o++) {
			var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));
			var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
			var thisPoint = [anchor];
			try {
				var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));
				var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];
				thisPoint.push(leftDirection)
				}
			catch (e) {
				thisPoint.push(anchor)
				};
			try {
				var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));
				var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];
				thisPoint.push(rightDirection)
				}
			catch (e) {
				thisPoint.push(anchor)
				};
			try {
				var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));
				thisPoint.push(smoothOr)
				}
			catch (e) {thisPoint.push(false)};
			theArray[theArray.length - 1].push(thisPoint);
			};
		theArray[theArray.length - 1].push(closed);
		theArray[theArray.length - 1].push(operation);
		};
	};
// by xbytor, thanks to him;
function cTID (s) { return cTID[s] || cTID[s] = app.charIDToTypeID(s); };
function sTID (s) { return sTID[s] || sTID[s] = app.stringIDToTypeID(s); };
// reset;
app.preferences.rulerUnits = originalRulerUnits;
return theArray;
};
////// create a path from collectPathInfoFromDesc2012-array //////
function createPath2012(theArray, thePathsName) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// thanks to xbytor;
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };

    var desc1 = new ActionDescriptor();
    var ref1 = new ActionReference();
    ref1.putProperty(cTID('Path'), cTID('WrPt'));
    desc1.putReference(sTID('null'), ref1);
    var list1 = new ActionList();
	
for (var m = 0; m < theArray.length; m++) {
	var thisSubPath = theArray[m];
	
    var desc2 = new ActionDescriptor();
    desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);
    var list2 = new ActionList();
    var desc3 = new ActionDescriptor();
    desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);
    var list3 = new ActionList();
	
for (var n = 0; n < thisSubPath.length - 2; n++) {
	var thisPoint = thisSubPath[n];
	
    var desc4 = new ActionDescriptor();
    var desc5 = new ActionDescriptor();
    desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);
    desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);
    desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);
    var desc6 = new ActionDescriptor();
    desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);
    desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);
    desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);
    var desc7 = new ActionDescriptor();
    desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);
    desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);
    desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);
    desc4.putBoolean(cTID('Smoo'), thisPoint[3]);
    list3.putObject(cTID('Pthp'), desc4);
	
	};
	
    desc3.putList(cTID('Pts '), list3);
    list2.putObject(cTID('Sbpl'), desc3);
    desc2.putList(cTID('SbpL'), list2);
    list1.putObject(cTID('PaCm'), desc2);
	};
	
    desc1.putList(cTID('T   '), list1);
    executeAction(cTID('setd'), desc1, DialogModes.NO);

if (hasVectorMask() == false) {
if (thePathsName != undefined) {app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1].name = thePathsName};
var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1];
}
else {
if (thePathsName != undefined) {app.activeDocument.pathItems[app.activeDocument.pathItems.length - 2].name = thePathsName};
var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 2];
};
app.preferences.rulerUnits = originalRulerUnits;
return myPathItem
};
// from »Flatten All Masks.jsx« by jeffrey tranberry;
///////////////////////////////////////////////////////////////////////////////
// Function: hasVectorMask
// Usage: see if there is a vector layer mask
// Input: <none> Must have an open document
// Return: true if there is a vector mask
///////////////////////////////////////////////////////////////////////////////
function hasVectorMask() {
	var hasVectorMask = false;
	try {
		var ref = new ActionReference();
		var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
		var keyKind = app.charIDToTypeID( 'Knd ' );
		ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
		var desc = executeActionGet( ref );
		if ( desc.hasKey( keyKind ) ) {
			var kindValue = desc.getEnumerationValue( keyKind );
			if (kindValue == keyVectorMaskEnabled) {
				hasVectorMask = true;
			}
		}
	}catch(e) {
		hasVectorMask = false;
	}
	return hasVectorMask;
};

2 replies

c.pfaffenbichler
Community Expert
c.pfaffenbichlerCommunity ExpertCorrect answer
Community Expert
July 30, 2025
quote

apparently selecting different subpaths on the same path is not possible via scripting (?), and I also failed in separating the subpaths into different paths to loop through (I found some posts about that here but I wasn't able to implement).

// make paths from first path’s subpathitems;
// 2014, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
if (myDocument.pathItems.length > 0) {
var theArray = collectPathInfoFromDesc2012 (myDocument, myDocument.pathItems[0]);
var theName = myDocument.pathItems[0];
for (var m = 0; m < theArray.length; m++) {
createPath2012([theArray[m]], theName+"_"+m)
}
}
};
////// collect path info from actiondescriptor, smooth added //////
function collectPathInfoFromDesc2012 (myDocument, thePath) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// based of functions from xbytor’s stdlib;
var ref = new ActionReference();
for (var l = 0; l < myDocument.pathItems.length; l++) {
	var thisPath = myDocument.pathItems[l];
	if (thisPath == thePath && thisPath.name == "Work Path") {
		ref.putProperty(cTID("Path"), cTID("WrPt"));
		};
	if (thisPath == thePath && thisPath.name != "Work Path" && thisPath.kind != PathKind.VECTORMASK) {
		ref.putIndex(cTID("Path"), l + 1);
		};
	if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {
        var idPath = charIDToTypeID( "Path" );
        var idPath = charIDToTypeID( "Path" );
        var idvectorMask = stringIDToTypeID( "vectorMask" );
        ref.putEnumerated( idPath, idPath, idvectorMask );
		};
	};
var desc = app.executeActionGet(ref);
var pname = desc.getString(cTID('PthN'));
// create new array;
var theArray = new Array;
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
// for subpathitems;
for (var m = 0; m < pathComponents.count; m++) {
	var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
	var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));
	switch (operation1) {
		case 1097098272:
		var operation = 1097098272 //cTID('Add ');
		break;
		case 1398961266:
		var operation = 1398961266 //cTID('Sbtr');
		break;
		case 1231975538:
		var operation = 1231975538 //cTID('Intr');
		break;
		default:
//		case 1102:
		var operation = sTID('xor') //ShapeOperation.SHAPEXOR;
		break;
		};
// for subpathitem’s count;
	for (var n = 0; n < listKey.count; n++) {
		theArray.push(new Array);
		var points = listKey.getObjectValue(n).getList(sTID('points'));
		try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}
		catch (e) {var closed = false};
// for subpathitem’s segment’s number of points;
		for (var o = 0; o < points.count; o++) {
			var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));
			var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
			var thisPoint = [anchor];
			try {
				var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));
				var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];
				thisPoint.push(leftDirection)
				}
			catch (e) {
				thisPoint.push(anchor)
				};
			try {
				var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));
				var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];
				thisPoint.push(rightDirection)
				}
			catch (e) {
				thisPoint.push(anchor)
				};
			try {
				var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));
				thisPoint.push(smoothOr)
				}
			catch (e) {thisPoint.push(false)};
			theArray[theArray.length - 1].push(thisPoint);
			};
		theArray[theArray.length - 1].push(closed);
		theArray[theArray.length - 1].push(operation);
		};
	};
// by xbytor, thanks to him;
function cTID (s) { return cTID[s] || cTID[s] = app.charIDToTypeID(s); };
function sTID (s) { return sTID[s] || sTID[s] = app.stringIDToTypeID(s); };
// reset;
app.preferences.rulerUnits = originalRulerUnits;
return theArray;
};
////// create a path from collectPathInfoFromDesc2012-array //////
function createPath2012(theArray, thePathsName) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// thanks to xbytor;
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };

    var desc1 = new ActionDescriptor();
    var ref1 = new ActionReference();
    ref1.putProperty(cTID('Path'), cTID('WrPt'));
    desc1.putReference(sTID('null'), ref1);
    var list1 = new ActionList();
	
for (var m = 0; m < theArray.length; m++) {
	var thisSubPath = theArray[m];
	
    var desc2 = new ActionDescriptor();
    desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);
    var list2 = new ActionList();
    var desc3 = new ActionDescriptor();
    desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);
    var list3 = new ActionList();
	
for (var n = 0; n < thisSubPath.length - 2; n++) {
	var thisPoint = thisSubPath[n];
	
    var desc4 = new ActionDescriptor();
    var desc5 = new ActionDescriptor();
    desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);
    desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);
    desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);
    var desc6 = new ActionDescriptor();
    desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);
    desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);
    desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);
    var desc7 = new ActionDescriptor();
    desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);
    desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);
    desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);
    desc4.putBoolean(cTID('Smoo'), thisPoint[3]);
    list3.putObject(cTID('Pthp'), desc4);
	
	};
	
    desc3.putList(cTID('Pts '), list3);
    list2.putObject(cTID('Sbpl'), desc3);
    desc2.putList(cTID('SbpL'), list2);
    list1.putObject(cTID('PaCm'), desc2);
	};
	
    desc1.putList(cTID('T   '), list1);
    executeAction(cTID('setd'), desc1, DialogModes.NO);

if (hasVectorMask() == false) {
if (thePathsName != undefined) {app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1].name = thePathsName};
var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1];
}
else {
if (thePathsName != undefined) {app.activeDocument.pathItems[app.activeDocument.pathItems.length - 2].name = thePathsName};
var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 2];
};
app.preferences.rulerUnits = originalRulerUnits;
return myPathItem
};
// from »Flatten All Masks.jsx« by jeffrey tranberry;
///////////////////////////////////////////////////////////////////////////////
// Function: hasVectorMask
// Usage: see if there is a vector layer mask
// Input: <none> Must have an open document
// Return: true if there is a vector mask
///////////////////////////////////////////////////////////////////////////////
function hasVectorMask() {
	var hasVectorMask = false;
	try {
		var ref = new ActionReference();
		var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
		var keyKind = app.charIDToTypeID( 'Knd ' );
		ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
		var desc = executeActionGet( ref );
		if ( desc.hasKey( keyKind ) ) {
			var kindValue = desc.getEnumerationValue( keyKind );
			if (kindValue == keyVectorMaskEnabled) {
				hasVectorMask = true;
			}
		}
	}catch(e) {
		hasVectorMask = false;
	}
	return hasVectorMask;
};
Participant
July 30, 2025

hmm... why don't we try just copying the pathComponents and then putting them as a parameter to create the working path?

#target photoshop
s2t = stringIDToTypeID;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('pathContents'));
r.putEnumerated(s2t('path'), s2t('ordinal'), s2t('targetEnum'));
var pathComponents = executeActionGet(r).getObjectValue(p).getList(s2t('pathComponents'));
for (var i = 0; i < pathComponents.count; i++) {
    (r = new ActionReference()).putProperty(s2t('path'), s2t('workPath'));
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    (l = new ActionList).putObject(s2t('pathComponent'), pathComponents.getObjectValue(i));
    d.putList(s2t('to'), l);
    executeAction(s2t('set'), d, DialogModes.NO);
}

then the @Flowgun  task does not require such a complex code with copying individual points:

#target photoshop
s2t = stringIDToTypeID;
var apl = new AM('application'),
    doc = new AM('document'),
    pth = new AM('path');
if (apl.getProperty('numberOfDocuments') && doc.hasProperty('selection')) {
    pth.workPathFromSelection(1);
    var pathComponents = (pth.getProperty('pathContents').value).getList(s2t('pathComponents'));
    for (var i = 0; i < pathComponents.count; i++) {
        pth.workPathFromDesc(pathComponents.getObjectValue(i));
        pth.selectionFromWorkPath();
        alert('selection ' + i+1)
    }
    pth.delete();
    doc.deselect();
}
function AM(target) {
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    target = target ? s2t(target) : null;
    this.getProperty = function (property, id, idxMode) {
        property = s2t(property);
        (r = new ActionReference()).putProperty(s2t('property'), property);
        id != undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
            r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
        return getDescValue(executeActionGet(r), property)
    }
    this.hasProperty = function (property, id, idxMode) {
        property = s2t(property);
        (r = new ActionReference()).putProperty(s2t('property'), property);
        id ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id))
            : r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
        return executeActionGet(r).hasKey(property)
    }
    this.workPathFromSelection = function (tolerance) {
        (r = new ActionReference()).putClass(s2t("path"));
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        (r1 = new ActionReference()).putProperty(s2t("selectionClass"), s2t("selection"));
        d.putReference(s2t("from"), r1);
        d.putUnitDouble(s2t("tolerance"), s2t("pixelsUnit"), tolerance);
        executeAction(s2t("make"), d, DialogModes.NO);
    }
    this.selectionFromWorkPath = function () {
        (r = new ActionReference()).putProperty(s2t("channel"), s2t("selection"));
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        (r1 = new ActionReference()).putProperty(s2t("path"), s2t("workPath"));
        d.putReference(s2t("to"), r1);
        executeAction(s2t("set"), d, DialogModes.NO);
    }
    this.workPathFromDesc = function (desc) {
        (r = new ActionReference()).putProperty(s2t('path'), s2t('workPath'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        (l = new ActionList).putObject(s2t('pathComponent'), desc);
        d.putList(s2t('to'), l);
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    this.delete = function () {
        (r = new ActionReference()).putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        executeAction(s2t("delete"), d, DialogModes.NO);
    }
    this.deselect = function () {
        (r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        d.putEnumerated(s2t('to'), s2t('ordinal'), s2t('none'));
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    function getDescValue(d, p) {
        switch (d.getType(p)) {
            case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
            case DescValueType.LISTTYPE: return d.getList(p);
            case DescValueType.REFERENCETYPE: return d.getReference(p);
            case DescValueType.BOOLEANTYPE: return d.getBoolean(p);
            case DescValueType.STRINGTYPE: return d.getString(p);
            case DescValueType.INTEGERTYPE: return d.getInteger(p);
            case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(p);
            case DescValueType.DOUBLETYPE: return d.getDouble(p);
            case DescValueType.ALIASTYPE: return d.getPath(p);
            case DescValueType.CLASSTYPE: return d.getClass(p);
            case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(p));
            case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
            default: break;
        };
    }
}
Flowgun
FlowgunAuthor
Inspiring
July 30, 2025

I'm using this code to get the average color instead of the average filter. ChatGPT gave it to me and it seems to work great:

        function get_histogram(n) {
            var r = new ActionReference();
            r.putProperty(stringIDToTypeID('property'), stringIDToTypeID('histogram'));
            if (n == 1)
                r.putEnumerated(stringIDToTypeID('channel'), stringIDToTypeID('channel'), stringIDToTypeID('red'));
            else if (n == 2)
                r.putEnumerated(stringIDToTypeID('channel'), stringIDToTypeID('channel'), stringIDToTypeID('green'));
            else
                r.putEnumerated(stringIDToTypeID('channel'), stringIDToTypeID('channel'), stringIDToTypeID('blue'));

            try {
                var data = executeActionGet(r).getList(stringIDToTypeID('histogram'));
            } catch (e) {
                return null;
            }

            var hist = [];
            for (var i = 0; i < 256; i++) hist.push(data.getInteger(i));
            return hist;
        }

        function run() {
            try { app.activeDocument.selection.bounds.toString(); }
            catch (e) { return 'NO_SELECTION'; }

            var lm = [], px = [];

            for (var i = 1; i <= 3; i++) {
                var hst = get_histogram(i);
                if (!hst) return 'ERROR';
                var l = 0, p = 0;
                for (var n in hst) {
                    l += hst[n] * n;
                    p += hst[n];
                }
                lm.push(l);
                px.push(p);
            }

            var r = px[0] ? Math.round(lm[0] / px[0]) : 0;
            var g = px[1] ? Math.round(lm[1] / px[1]) : 0;
            var b = px[2] ? Math.round(lm[2] / px[2]) : 0;
            return r + '|' + g + '|' + b;

 The JSXs I got from those posts work, but the code is not that clean so I wasn't able to convert it to Autohotkey code that uses COM, which is my way of doing things (I like the hotkeying freedom and having a single file that I can run from anywhere to have all my code).