// JSON
if(typeof JSON!=='object'){JSON={};}(function(){'use strict';function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf();};}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==='string'){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());

function s2t (s) { return app.stringIDToTypeID(s) }
function c2t (c) { return app.charIDToTypeID(c) }

function channelInitialization() {
	var originalLayerID = getLayerID();
    deselectAll();
	mergeVisibleOnTop();
	addLayerMask(true);
	selectLayer(true);
	var mergedLayerID = getLayerID();
	return JSON.stringify({
		originalLayerID : originalLayerID,
		mergedLayerID   : mergedLayerID
	});
}

/**
 * Merge visible layers on top
 */
function mergeVisibleOnTop() {
	// if (isBackgroundLayer()) {
		createNewLayer('TK Infinity (temp)');
	// }
	var d1 = new ActionDescriptor();
	d1.putBoolean(s2t('duplicate'), true);
	executeAction(s2t('mergeVisible'), d1, DialogModes.NO);
}

/**
 * Add a layer mask to the current layer
 * @param {Boolean} reveal true reveal all, false hide all
 */
function addLayerMask(reveal) {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	d1.putClass(s2t('new'), s2t('channel'));
	r1.putEnumerated(s2t('channel'), s2t('channel'), s2t('mask'));
	d1.putReference(s2t('at'), r1); 
	d1.putEnumerated(s2t('using'), s2t('userMaskOptions'), s2t(reveal ? 'revealAll' : 'hideAll'));
	executeAction( s2t('make'), d1, DialogModes.NO );
}

/**
 * Select the Mask or the Layer in a RGB Layer (with mask)
 * @param  {String} maskOrRGB 'mask' or 'RGB'
 * @param  {Boolean} visible [optional] activate the mask visibility, dafault: false
 */
function selectMaskOrRGB(maskOrRGB, visible) {
    var d1 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putEnumerated(s2t('channel'), s2t('channel'), s2t(maskOrRGB));
    d1.putReference(s2t('target'), r1);
    d1.putBoolean(s2t('makeVisible'), visible || false);
    executeAction(s2t('select'), d1, DialogModes.NO);   
}

/**
 * Select the Mask of a Layer
 * @param  {Boolean} visible [optional] activate the mask visibility, dafault: false
 */
function selectMask (visible) {
	if(visible == null) { visible = true }
	selectMaskOrRGB('mask', visible);
}

/**
 * Select the Layer when a Layer mask is selected
 */
function selectLayer() {
	selectMaskOrRGB('RGB', true);
}

/**
 * Check if current layer is Background
 * @return {Boolean} true if is background, false if it's not
 */
function isBackgroundLayer() {
	var ref = new ActionReference();
	ref.putProperty(s2t('property'), s2t('background'));
	ref.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
	return executeActionGet(ref).getBoolean(s2t('background'));
}

/**
 * Create a new Layer with name
 * @param  {String} layerName The name of the layer
 */
function createNewLayer(layerName) {
    var d1 = new ActionDescriptor();
    var d2 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putClass(s2t('layer'));
    d1.putReference(s2t('target'), r1);
    d2.putString(s2t('name'), layerName);
    d1.putObject(s2t('using'), s2t('layer'), d2);
	executeAction(s2t('make'), d1, DialogModes.NO );
}

function RunTonalRangeSelector(opt) {
	// alert(opt.toSource());
	var d = new ActionDescriptor();
	// don't use opt.something || defaultValue, for it will fail with 0!
	if (opt.tonalRangeCenter      == null) { opt.tonalRangeCenter      = 255 }
	if (opt.tonalRangeRadius      == null) { opt.tonalRangeRadius      = 100 }
	if (opt.curveContrastLevel    == null) { opt.curveContrastLevel    = 0 }
	if (opt.tonalRangeGain        == null) { opt.tonalRangeGain        = 100 }
	if (opt.curveFeatheringRadius == null) { opt.curveFeatheringRadius = 30 }
	if (opt.maskFeatheringRadius  == null) { opt.maskFeatheringRadius  = 0 }
	if (opt.maskSource            == null) { opt.maskSource            = 1 }
	if (opt.autoTonalRange        == null) { opt.autoTonalRange        = false }

	// Tonal Range Center: 0..255 step 1
	d.putDouble(c2t('CENR'), opt.tonalRangeCenter);
	// Tonal Range Radius: 0..100 step 1
	d.putDouble(c2t('RADI'), opt.tonalRangeRadius);
	// Curve Contrast Level: -100..100 step 1
	d.putDouble(c2t('LEVL'), opt.curveContrastLevel);
	// Tonal Range Gain: 0..150 step 1
	d.putDouble(c2t('GAIN'), opt.tonalRangeGain);
	d.putDouble(c2t('FERA'), opt.curveFeatheringRadius);
	d.putDouble(c2t('MARA'), opt.maskFeatheringRadius);
	// MASK_SOURCE_LUMINOSITY_CHANNEL  = 1;
	// MASK_SOURCE_SATURATION_CHANNEL  = 2;
	// MASK_SOURCE_U_CHANNEL           = 3;
	// MASK_SOURCE_V_CHANNEL           = 4;
	// MASK_SOURCE_LAB_L_CHANNEL       = 5;
	// MASK_SOURCE_LAB_A_CHANNEL       = 6;
	// MASK_SOURCE_LAB_B_CHANNEL       = 7;
	// MASK_SOURCE_R_CHANNEL           = 8;
	// MASK_SOURCE_G_CHANNEL           = 9;
	// MASK_SOURCE_B_CHANNEL           = 10;
	// MASK_SOURCE_P_CHANNEL           = 11;
	d.putInteger(c2t('MASK'), opt.maskSource);
	d.putBoolean(c2t('AUTO'), opt.autoTonalRange);
	executeAction(s2t('98b5a608-46ce-11d3-bd6b-Tonal0a13dc4'), d, DialogModes.NO);
}

/**
 * Open the Color Picker
 * @return {Object} Object containing L, R, G, B, Luminosity and Sat
 */
function openPicker() {
	var col = showColorPicker();
	if (col === true) {
		var forCol = app.foregroundColor;
		return JSON.stringify({
			L : Math.round(forCol.rgb.red * 0.25 + forCol.rgb.green * 0.5 + forCol.rgb.blue * 0.25),
			R : Math.round(forCol.rgb.red),
			G : Math.round(forCol.rgb.green),
			B : Math.round(forCol.rgb.blue),
			Luminosity : Math.round(forCol.rgb.red * 0.25 + forCol.rgb.green * 0.5 + forCol.rgb.blue * 0.25),
//			Luminosity : Math.round(forCol.lab.l * 2.5),
			Saturation : Math.round(forCol.hsb.saturation * (forCol.hsb.brightness / 100) * 2.55)
		});	
	} else {
		return JSON.stringify({
			error: "User has canceled"
		});
	}
}

function loadSelectionFromActiveMask () {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	var r2 = new ActionReference();
	r1.putProperty(s2t('channel'), s2t('selection'));
	d1.putReference(s2t('target'), r1);
	r2.putEnumerated(s2t('channel'), s2t('ordinal'), s2t('targetEnum'));
	d1.putReference(s2t('to'), r2);
	executeAction(s2t('set'), d1, DialogModes.NO);
}

function getLayerID() {
	var ref = new ActionReference();  
	ref.putProperty(s2t('property'), s2t('layerID'));
	ref.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));   
	var desc = executeActionGet(ref);  
	return desc.getInteger(s2t('layerID'));  
}


function deleteLayerByID(id){  
	var ref = new ActionReference();  
	ref.putIdentifier(s2t('layer'), id);  
    var desc = new ActionDescriptor();  
    desc.putReference(s2t('target'), ref );  
    executeAction(s2t('delete'), desc, DialogModes.NO );  
}

function deleteLayerMask() {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	r1.putEnumerated(s2t("channel"), s2t("channel"), s2t("mask"));
	d1.putReference(s2t("target"), r1 );
	executeAction(s2t("delete"), d1, DialogModes.NO );
}

function cleanLayer(layerID) {
	selectLayer();
	try {
		deleteLayerByID(layerID);
	} catch(e) {
		// alert(e + e.line);
	}
}

function postAdjustmentLayerRoutine(mergedLayerID) {
    if(hasLayerMask()) {
        deleteLayerMask()
    }
    copyLayerMaskFromLayerByID(mergedLayerID)
    selectLayer();
}

function createAdjBrightnessContrastWithMask(mergedLayerID, layName) {
    if (layName == null) { layName = "TK Infinity Mask" }
    var d1 = new ActionDescriptor();
    var d2 = new ActionDescriptor();
    var d3 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putClass(s2t('adjustmentLayer'));
    d1.putReference(s2t('target'), r1 );
    d3.putBoolean(s2t('useLegacy'), false );
    // d2.putString(s2t('name'), layName );
    d2.putObject(s2t('type'), s2t('brightnessContrast'), d3 );
    d1.putObject(s2t('using'), s2t('adjustmentLayer'), d2 );
    executeAction(s2t('make'), d1, DialogModes.NO);    
    
    postAdjustmentLayerRoutine(mergedLayerID);
}

//function createAdjLevelsWithMask(layName) {
//    if (layName == null) { layName = "TK Infinity Mask" }
//    var d1 = new ActionDescriptor();
//    var d2 = new ActionDescriptor();
//    var d3 = new ActionDescriptor();
//    var r1 = new ActionReference();
//    r1.putClass(s2t('adjustmentLayer'));
//    d1.putReference(s2t('target'), r1);
//    // d2.putString(s2t('name'), layName );
//    d3.putEnumerated(s2t('presetKind'), s2t('presetKindType'), s2t('presetKindDefault'));
//    d2.putObject(s2t('type'), s2t('levels'), d3);
//    d1.putObject(s2t('using'), s2t('adjustmentLayer'), d2);
//    executeAction(s2t('make'), d1, DialogModes.NO);
//    selectLayer();
//}

function createAdjLevelsWithMask(mergedLayerID, layName) {
    if (layName == null) { layName = "TK Infinity Mask" }

    var d1 = new ActionDescriptor();
    var d2 = new ActionDescriptor();
    var d3 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putClass(s2t('adjustmentLayer'));
    d1.putReference(s2t('target'), r1);
    // d2.putString(s2t('name'), layName );
    d3.putEnumerated(s2t('presetKind'), s2t('presetKindType'), s2t('presetKindDefault'));
    d2.putObject(s2t('type'), s2t('levels'), d3);
    d1.putObject(s2t('using'), s2t('adjustmentLayer'), d2);
    executeAction(s2t('make'), d1, DialogModes.NO);
        
    postAdjustmentLayerRoutine(mergedLayerID);
}


function applyLayerMask(originalLayerID, mergedLayerID) {
	// (w/ make visible)
	selectLayerByID(originalLayerID);
	if(isBackgroundLayer()) {
		app.activeDocument.activeLayer.isBackgroundLayer = false;
	} else if(hasLayerMask()) {
		deleteLayerMask();
	}
    copyLayerMaskFromLayerByID(mergedLayerID);
}

function createAdjCurvesWithMask(mergedLayerID, layName) {
    if (layName == null) { layName = "TK Infinity Mask" }
    var d1 = new ActionDescriptor();
    var d2 = new ActionDescriptor();
    var d3 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putClass(s2t('adjustmentLayer'));
    d1.putReference(s2t('target'), r1);
    // d2.putString(s2t('name'), layName );
    d3.putEnumerated(s2t('presetKind'), s2t('presetKindType'), s2t('presetKindDefault'));
    d2.putObject(s2t('type'), s2t('curves'), d3);
    d1.putObject(s2t('using'), s2t('adjustmentLayer'), d2);
    executeAction(s2t('make'), d1, DialogModes.NO);
    
    postAdjustmentLayerRoutine(mergedLayerID);
}

function createAdjHueSaturationWithMask(mergedLayerID, layName) {
    if (layName == null) { layName = "TK Infinity Mask" }
    var d1 = new ActionDescriptor();
    var d2 = new ActionDescriptor();
    var d3 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putClass(s2t('adjustmentLayer'));
    d1.putReference(s2t('target'), r1);
    d3.putEnumerated(s2t('presetKind'), s2t('presetKindType'), s2t('presetKindDefault'));
    d3.putBoolean(s2t('colorize'), false);
    // d2.putString(s2t('name'), layName );
    d2.putObject(s2t('type'), s2t('hueSaturation'), d3);
    d1.putObject(s2t('using'), s2t('adjustmentLayer'), d2);
    executeAction(s2t('make'), d1, DialogModes.NO);
    
    postAdjustmentLayerRoutine(mergedLayerID);
}

function createChannelFromSelection(channelName) {
	if (channelName == null) { channelName = "TK Infinity Mask" }
	var d1 = new ActionDescriptor();
	var idDplc = s2t('duplicate');
	var r1 = new ActionReference();
	r1.putProperty( s2t('channel'), s2t('selection') );
    d1.putString( s2t('name'), channelName);
	d1.putReference( s2t('target'), r1 );
	executeAction( s2t('duplicate'), d1, DialogModes.NO );
}

function deselect() {
    var d1 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putProperty( s2t('channel'), s2t('selection') );
    d1.putReference( s2t('target'), r1 );
    d1.putEnumerated( s2t('to'), s2t('ordinal'), s2t('none') );
    executeAction( s2t('set'), d1, DialogModes.NO );
}

function createChannelAndDeselect (channelName) {
	createChannelFromSelection(channelName);
	deselect();
}

function hasLayerMask() {
    var r1 = new ActionReference();
    r1.putProperty(s2t('property'), s2t('userMaskOptions'));
    r1.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
    var d1 = executeActionGet(r1);
    return d1.hasKey(s2t('userMaskOptions'));
}

function showLayerByID (layerID) {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	var l1 = new ActionList();
	r1.putIdentifier(s2t('layer'), layerID);  
	l1.putReference(r1);
	d1.putList(s2t('target'), l1 );
	executeAction( s2t('show'), d1, DialogModes.NO );
}

function selectLayerByID (layerID) {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	r1.putIdentifier(s2t('layer'), layerID);  
	d1.putReference( s2t('target'), r1 );
	d1.putBoolean( s2t('makeVisible'), true );
	executeAction( s2t('select'), d1, DialogModes.NO );
}

function loadSelectionFromLayerMaskByID (layerID) {
	// alert(layerID)
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	var r2 = new ActionReference();
	r1.putProperty( s2t('channel'), s2t('selection'));
	d1.putReference( s2t('target'), r1 );
	r2.putEnumerated( s2t('channel'), s2t('channel'), s2t('mask') );
	r2.putIdentifier(s2t('layer'), layerID);  
	// r2.putName(s2t('layer'), 'TK Infinity (temp) copy');
	d1.putReference(s2t('to'), r2);
	executeAction( s2t('set'), d1, DialogModes.NO );
}

function makeLayerMaskFromActiveSelection() {
	var d1 = new ActionDescriptor();
	var r1 = new ActionReference();
	d1.putClass( s2t( "new" ), s2t( "channel" ) );
	r1.putEnumerated( s2t("channel"), s2t("channel"), s2t("mask") );
	d1.putReference( s2t( "at" ), r1 );
	d1.putEnumerated( s2t("using"), s2t("userMaskOptions"), s2t("revealSelection") );
	executeAction( s2t( "make" ), d1, DialogModes.NO );
}

function applyMaskAsMaskToOriginalLayer(originalLayerID, mergedLayerID) {
	// (w/ make visible)
	selectLayerByID(originalLayerID);
	if(isBackgroundLayer()) {
		app.activeDocument.activeLayer.isBackgroundLayer = false;
	} else if(hasLayerMask()) {
		deleteLayerMask();
	}
	loadSelectionFromLayerMaskByID(mergedLayerID);
	makeLayerMaskFromActiveSelection();
	deselect();
}

function applyMaskToBitmapLayer() {
	selectLayer();
	var d1 = new ActionDescriptor();
	var d2 = new ActionDescriptor();
	var r1 = new ActionReference();
	r1.putEnumerated( s2t("channel"), s2t("channel"), s2t("mask"));
	d2.putReference( s2t("to"), r1 );
	d1.putObject( s2t("with"), s2t("calculation"), d2 );
	executeAction( s2t("applyImageEvent"), d1, DialogModes.NO );
	deleteLayerMask();
	app.activeDocument.activeLayer.name = "TK Infinity Mask";
}

function hasLayerError() {
    var _hasLayerMask = hasLayerMask();
    if (!_hasLayerMask) { 
        return JSON.stringify({ 
            hasLayerMask: _hasLayerMask, 
            layerID: undefined 
        });
    }
    var _layerID = getLayerID();
    return JSON.stringify({ 
        hasLayerMask: _hasLayerMask, 
        layerID: _layerID 
    });
}

function createChannelFromLayerMask(channelName) {
    var d1 = new ActionDescriptor();
    var r1 = new ActionReference();
    r1.putEnumerated( s2t("channel"), s2t("ordinal"), s2t("targetEnum") );
    d1.putReference( s2t("target"), r1 );
    d1.putString( s2t("name"), channelName );
    executeAction( s2t("duplicate"), d1, DialogModes.NO );
}

function copyLayerMaskFromLayerByID(layerID) {
    var d1 = new ActionDescriptor();
    var r1 = new ActionReference();
    var r2 = new ActionReference();
    d1.putClass( s2t("new"), s2t("channel") );
    r1.putEnumerated( s2t("channel"), s2t("channel"), s2t("mask") );
    r1.putEnumerated( s2t("layer"), s2t("ordinal"), s2t("targetEnum") );
    d1.putReference( s2t("at"), r1 );
    r2.putEnumerated( s2t("channel"), s2t("channel"), s2t("mask") );
//    r2.putName( s2t("layer"), "TK Infinity (temp)" );
    r2.putIdentifier( s2t("layer"), layerID );
    d1.putReference( s2t("using"), r2 );
    d1.putBoolean( s2t("duplicate"), false ); /* copy mask instead of moving it */
    executeAction( s2t("make"), d1, DialogModes.NO );
}

function applyLayerMask(originalLayerID, mergedLayerID) {
	// (w/ make visible)
	selectLayerByID(originalLayerID);
	if(isBackgroundLayer()) {
		app.activeDocument.activeLayer.isBackgroundLayer = false;
	} else if(hasLayerMask()) {
		deleteLayerMask();
	}
    copyLayerMaskFromLayerByID(mergedLayerID);
}

function deselectAll() {
    try {
        var d1 = new ActionDescriptor(),
        r1 = new ActionReference();
        r1.putProperty( s2t("channel"), s2t("selection") );
        d1.putReference( s2t("target"), r1 );
        d1.putEnumerated( s2t("to"), s2t("ordinal"), s2t("none") );
        executeAction( s2t("set"), d1, DialogModes.NO );
    } catch(e) { ; }
}