/**************************************************************************
*
*  @@@BUILDINFO@@@ 00globals-2.jsx 2.0.1.66  04-May-2007
*  Copyright 2006-2007 Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains the property of
* Adobe Systems Incorporated  and its suppliers,  if any.  The intellectual 
* and technical concepts contained herein are proprietary to  Adobe Systems 
* Incorporated  and its suppliers  and may be  covered by U.S.  and Foreign 
* Patents,patents in process,and are protected by trade secret or copyright 
* law.  Dissemination of this  information or reproduction of this material
* is strictly  forbidden  unless prior written permission is  obtained from 
* Adobe Systems Incorporated.
**************************************************************************/

//
// set app.resourceFolder and app.requiredFolder
//
if( $.os.indexOf( 'Windows' ) > -1 )
{
    app.resourceFolder = Folder (Folder.appPackage + "/required");
    var f = app.resourceFolder.resolve();
    if (f)
        app.resourceFolder = f;

    app.requiredFolder = app.resourceFolder;
    app.resourceFolder += '/icons/';
}
else
{
	app.requiredFolder = Folder.appPackage.absoluteURI + '/Contents/SharedSupport/Required/';
    app.resourceFolder = Folder.appPackage.absoluteURI + '/Contents/Resources/';
}

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

const _win = (File.fs == "Windows");

// the list of all non-document panes
var panes = [];

// The list of all document
var documents = [];

// The current document
var document = null;

// All menus, grouped by name (like e.g. menus.file)
const menus = {};

// target manager
var targetMgr = null;

// global broadcaster
var globalBroadcaster = null;

// favorites
var favorites = null;

// workspace visibility
var menuToggleWS = null;
var workspaceVisible = true;
var wsHideOnNextStartup = false;

// in shutdown
var appInShutDown = false;

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

// A list of predefined HTML colors.

const colors = {
	AliceBlue			: 0xF0F8FF,
	AntiqueWhite  		: 0xFAEBD7,
	Aqua  				: 0x00FFFF,
	Aquamarine  		: 0x7FFFD4,
	Azure  				: 0xF0FFFF,
	Beige  				: 0xF5F5DC,
	Bisque  			: 0xFFE4C4,
	Black  				: 0x000000,
	BlanchedAlmond  	: 0xFFEBCD,
	Blue  				: 0x0000FF,
	BlueViolet  		: 0x8A2BE2,
	Brown  				: 0xA52A2A,
	BurlyWood  			: 0xDEB887,
	CadetBlue			: 0x5F9EA0,
	Chartreuse  		: 0x7FFF00,
	Chocolate			: 0xD2691E,
	Coral  				: 0xFF7F50,
	CornflowerBlue  	: 0x6495ED,
	Cornsilk  			: 0xFFF8DC,
	Crimson  			: 0xDC143C,
	Cyan  				: 0x00FFFF,
	DarkBlue  			: 0x00008B,
	DarkCyan  			: 0x008B8B,
	DarkGoldenRod		: 0xB8860B,
	DarkGray  			: 0xA9A9A9,
	DarkGreen  			: 0x006400,
	DarkKhaki			: 0xBDB76B,
	DarkMagenta  		: 0x8B008B,
	DarkOliveGreen  	: 0x556B2F,
	Darkorange  		: 0xFF8C00,
	DarkOrchid  		: 0x9932CC,
	DarkRed  			: 0x8B0000,
	DarkSalmon  		: 0xE9967A,
	DarkSeaGreen		: 0x8FBC8F,
	DarkSlateBlue		: 0x483D8B,
	DarkSlateGray		: 0x2F4F4F,
	DarkTurquoise		: 0x00CED1,
	DarkViolet  		: 0x9400D3,
	DeepPink  			: 0xFF1493,
	DeepSkyBlue  		: 0x00BFFF,
	DimGray  			: 0x696969,
	DodgerBlue  		: 0x1E90FF,
	Feldspar  			: 0xD19275,
	FireBrick			: 0xB22222,
	FloralWhite			: 0xFFFAF0,
	ForestGreen			: 0x228B22,
	Fuchsia  			: 0xFF00FF,
	Gainsboro  			: 0xDCDCDC,
	GhostWhite  		: 0xF8F8FF,
	Gold  				: 0xFFD700,
	GoldenRod  			: 0xDAA520,
	Gray  				: 0x808080,
	Green  				: 0x008000,
	GreenYellow  		: 0xADFF2F,
	HoneyDew  			: 0xF0FFF0,
	HotPink  			: 0xFF69B4,
	IndianRed   		: 0xCD5C5C,
	Indigo   			: 0x4B0082,
	Ivory				: 0xFFFFF0,
	Khaki				: 0xF0E68C,
	Lavender  			: 0xE6E6FA,
	LavenderBlush  		: 0xFFF0F5,
	LawnGreen			: 0x7CFC00,
	LemonChiffon  		: 0xFFFACD,
	LightBlue			: 0xADD8E6,
	LightCoral  		: 0xF08080,
	LightCyan			: 0xE0FFFF,
	LightGoldenRodYellow: 0xFAFAD2,
	LightGray			: 0xD3D3D3,
	LightGreen  		: 0x90EE90,
	LightPink  			: 0xFFB6C1,
	LightSalmon  		: 0xFFA07A,
	LightSeaGreen  		: 0x20B2AA,
	LightSkyBlue  		: 0x87CEFA,
	LightSlateBlue  	: 0x8470FF,
	LightSlateGray  	: 0x778899,
	LightSteelBlue  	: 0xB0C4DE,
	LightYellow  		: 0xFFFFE0,
	Lime  				: 0x00FF00,
	LimeGreen  			: 0x32CD32,
	Linen  				: 0xFAF0E6,
	Magenta  			: 0xFF00FF,
	Maroon  			: 0x800000,
	MediumAquaMarine  	: 0x66CDAA,
	MediumBlue  		: 0x0000CD,
	MediumOrchid  		: 0xBA55D3,
	MediumPurple  		: 0x9370D8,
	MediumSeaGreen  	: 0x3CB371,
	MediumSlateBlue  	: 0x7B68EE,
	MediumSpringGreen  	: 0x00FA9A,
	MediumTurquoise  	: 0x48D1CC,
	MediumVioletRed  	: 0xC71585,
	MidnightBlue  		: 0x191970,
	MintCream  			: 0xF5FFFA,
	MistyRose			: 0xFFE4E1,
	Moccasin  			: 0xFFE4B5,
	NavajoWhite  		: 0xFFDEAD,
	Navy				: 0x000080,
	OldLace				: 0xFDF5E6,
	Olive				: 0x808000,
	OliveDrab  			: 0x6B8E23,
	Orange  			: 0xFFA500,
	OrangeRed  			: 0xFF4500,
	Orchid  			: 0xDA70D6,
	PaleGoldenRod  		: 0xEEE8AA,
	PaleGreen  			: 0x98FB98,
	PaleTurquoise  		: 0xAFEEEE,
	PaleVioletRed  		: 0xD87093,
	PapayaWhip  		: 0xFFEFD5,
	PeachPuff  			: 0xFFDAB9,
	Peru  				: 0xCD853F,
	Pink  				: 0xFFC0CB,
	Plum  				: 0xDDA0DD,
	PowderBlue  		: 0xB0E0E6,
	Purple  			: 0x800080,
	Red  				: 0xFF0000,
	RosyBrown  			: 0xBC8F8F,
	RoyalBlue  			: 0x4169E1,
	SaddleBrown  		: 0x8B4513,
	Salmon  			: 0xFA8072,
	SandyBrown  		: 0xF4A460,
	SeaGreen  			: 0x2E8B57,
	SeaShell  			: 0xFFF5EE,
	Sienna  			: 0xA0522D,
	Silver  			: 0xC0C0C0,
	SkyBlue  			: 0x87CEEB,
	SlateBlue  			: 0x6A5ACD,
	SlateGray  			: 0x708090,
	Snow  				: 0xFFFAFA,
	SpringGreen  		: 0x00FF7F,
	SteelBlue  			: 0x4682B4,
	Tan  				: 0xD2B48C,
	Teal  				: 0x008080,
	Thistle  			: 0xD8BFD8,
	Tomato  			: 0xFF6347,
	Turquoise  			: 0x40E0D0,
	Violet  			: 0xEE82EE,
	VioletRed  			: 0xD02090,
	Wheat				: 0xF5DEB3,
	White				: 0xFFFFFF,
	WhiteSmoke  		: 0xF5F5F5,
	Yellow  			: 0xFFFF00,
	YellowGreen  		: 0x9ACD32
};

// Global Alert box that is localizable and displays an error title

function messageBox (msg)
{
	var title = app.title + ' ' + app.version;
	if (msg [0] == "$")
		msg = localize.apply (this, arguments);
	alert (msg, title);
}

// Global Error box that is localizable and displays an error title

function errorBox (msg)
{
    var target = app.title + ' ' + app.version;
	var title = localize ("$$$/ESToolkit/Alerts/ErrorTitle=%1 Error", target);
	if (msg [0] == "$")
		msg = localize.apply (this, arguments);
	alert (msg, title, true);
}

// Global Confirm box that is localizable and displays the right title

function queryBox (msg)
{
	var title = app.title + ' ' + app.version;
	if (msg [0] == "$")
		msg = localize.apply (this, arguments);
	return confirm (msg, false, title);
}

// A little modeless dialog floating in the middle of the window
// This dialog displays short, one-line messages. Use an empty message
// or no arguments to hide the dialog. The message may take additional
// arguments, and it is passed to localize().
// If cancelCB is supplied, the floatingMessage has a Cancel button,
// and if that button is clicked, cancelCB (userData) is called.

var floatDlg = null;
var floatDlgCB = null;

function floatingMessage (msg)
{
	// Hide any callback floater
	if (floatDlgCB && floatDlgCB.visible)
	{
		floatDlgCB.maxWidth = 0;
		floatDlgCB.hide();
	}
	if (!floatDlg && msg)
	{
		floatDlg = new Window ("palette { msg: StaticText { } }");
		floatDlg.text = app.title + ' ' + app.version;
		floatDlg.maxWidth = 0;
	}
	if (msg)
	{
		if (msg [0] == "$")
			msg = localize.apply (this, arguments);
		var uiMsg = floatDlg.msg;
		uiMsg.text = msg;
		var size = uiMsg.graphics.measureString (msg);
		if (size.width > floatDlg.maxWidth)
		{
			floatDlg.maxWidth = size.width;
			uiMsg.size = size;
			floatDlg.layout.layout (true);
		}
		floatDlg.center (Window.children[0]);
		floatDlg.show();
	}
	else if (floatDlg)
	{
		floatDlg.maxWidth = 0;
		floatDlg.hide();
	}
}

// Same as above; additional arguments to the message follow the userData argument.
// The floatingMessage has a Cancel button, and if that button is clicked, 
// cancelCB (userData) is called.
// IMPORTANT: while processing, call app.pumpEventLoop() to enable the Cancel button!

function floatingMessageCB (msg, cancelCB, userData)
{
	// Hide any non-callback floater
	if (floatDlg && floatDlg.visible)
	{
		floatDlg.maxWidth = 0;
		floatDlg.hide();
	}
	if (!floatDlgCB && msg)
	{
		floatDlgCB = new Window (
		"palette { msg: StaticText { },\
		btn: Button { text:'$$$/CT/ExtendScript/UI/Cancel=&Cancel', properties:{name:'cancel'} } }");
		floatDlgCB.text = app.title + ' ' + app.version;
		floatDlgCB.btn.onClick = function()
		{
			this.cancelCB (this.userData);
		}
	}
	if (msg)
	{
		floatDlgCB.btn.cancelCB = cancelCB;
		floatDlgCB.btn.userData = userData;
		if (msg [0] == "$")
		{
			var args = [msg];
			for (var i = 3; i < arguments.length; i++)
				args.push (arguments [i]);
			msg = localize.apply (this, args);
		}
		var uiMsg = floatDlgCB.msg;
		uiMsg.text = msg;
		var size = uiMsg.graphics.measureString (msg);
		if (size.width > floatDlgCB.maxWidth)
		{
			floatDlgCB.maxWidth = size.width;
			uiMsg.size = size;
			floatDlgCB.layout.layout (true);
		}
		floatDlgCB.center (Window.children[0]);
		floatDlgCB.show();
		// Disable the app
		app.enabled = false;
	}
	else if (floatDlgCB)
	{
		floatDlgCB.maxWidth = 0;
		floatDlgCB.hide();
		// re-enable the app
		app.enabled = true;
	}
}

///////////////////////////////////////////////////////////////////
//
//	ProgressBox
//
//	The ProgressBox displays a progress dialog. The ctor takes a 
//	text which is localized. The ctor may take additional arguments.
//	We have these functions:
//	setText (msg) - set text (additional args permitted)
//	setProgress (val[, max]) - set progress. Returns false on abort.
//	stop() - hide the dialog.
//
///////////////////////////////////////////////////////////////////

function ProgressBox (msg)
{
	this.dlg = new Window (
	"palette { \
	msg:	StaticText {				\
		alignment	: 'left',			\
		},								\
		bar:	Progressbar {			\
			preferredSize:	[200, 20],	\
		},								\
		btn:	Button {				\
			text:			'$$$/CT/ExtendScript/UI/Cancel=&Cancel',	\
			properties: {				\
				name		:'cancel'	\
			}							\
		}								\
	}");
	if (msg [0] == "$")
		msg = localize.apply (this, arguments);
	this.dlg.text = app.title + ' ' + app.version;
	this.dlg.btn.self = this;
	this.dlg.btn.onClick = function()
	{
		this.self.aborted = true;
		this.self.stop();
	}
	this.dlg.bar.maxvalue = 100;
	this.maxWidth = 0;
	this.aborted = false;

	this.setText (msg);
	this.dlg.center (Window.children [0]);
	this.dlg.show();
	app.enabled = false;
}

ProgressBox.prototype.setText = function (msg)
{
	if (this.dlg)
	{
		if (msg [0] == "$")
			msg = localize.apply (this, arguments);
		var uiMsg = this.dlg.msg;
		uiMsg.text = msg;
		var size = uiMsg.graphics.measureString (msg);
		if (size.width > this.maxWidth)
		{
			this.maxWidth = size.width;
			uiMsg.size = size;
			this.dlg.layout.layout (true);
		}
	}
}

ProgressBox.prototype.increment = function (n)
{
	if (!this.dlg || this.aborted)
		return false;

	if (!n)
		n = 1;
	return this.setProgress (this.dlg.bar.value + n);
}

ProgressBox.prototype.setProgress = function (n, end)
{
	var ok = app.pumpEventLoop (true);
	ok &= (!this.aborted && (this.dlg != null));
	if (ok)
	{
		if (end != undefined)
			this.dlg.bar.maxvalue = end;
		this.dlg.bar.value = n;
	}
	return ok;
}

ProgressBox.prototype.getRemainingProgress = function ()
{
	var n = 0;
	if (this.dlg)
		n = this.dlg.bar.maxvalue - this.dlg.bar.value;
	return n;
}

Progressbar.prototype.disableCancel = function()
{
	this.dlg.btn.enabled = false;
}

ProgressBox.prototype.stop = function()
{
	if (this.dlg)
		this.dlg.hide();
	this.dlg = null;
	app.enabled = true;
}

///////////////////////////////////////////////////////////////////
//
//	print()
//
///////////////////////////////////////////////////////////////////

function print()
{
	if (console)
	{
		var s = "";
		for (var i = 0; i < arguments.length; i++)
			s += arguments [i];
		console.print (s);
	}
}

//
// wait for BridgeTalk
//
function wait( abortFct, timeout )
{
    var ret = false;
    
    if( abortFct )
    {
        if( !timeout )
            timeout = 50000;
            
	    var then = new Date;
	    
	    while( abortFct() )
	    {
		    BridgeTalk.pump();
		    
		    var now = new Date;
		    
		    if( (now - then) > timeout )
		        return ret;
	    }
	    
	    ret = true;
	}
	
	return ret;
}

//
// enable/disable icon buttons
//
IconButton.prototype.setEnabled = function( state )
{
	if( !this.stateCount )
		this.stateCount = 0;
		
	if( state )
	{
		this.stateCount--;
		
		if( this.stateCount < 0 )
			this.stateCount = 0;
	}
	else
	{
		this.stateCount++;
	}

	this.enabled = state;//(this.stateCount == 0 );
//    this.icon	 = this.enabled ? this.enabledIcon : this.disabledIcon;
}

///////////////////////////////////////////////////////////////////
//
//	Delayed Tasks
//	call addDelayedTask (foo) to add a function that is executed
//	about 20 msecs after this function has been called. If there
//	are multiple delayed functions, the latest function is added
//	to the stack of functions to be executed. The function should
//	not take long to execute, and it should not alter any visible.
//	Only one instance of a funtcion is added.
//
///////////////////////////////////////////////////////////////////

function addDelayedTask (foo, param1, param2, param3)
{
	$.bp (typeof foo != "function");
	for (var i = 0; i < delayedTasks.length; i++)
	{
		if( delayedTasks[i].funct   == foo     &&
		    delayedTasks[i].arg1    == param1  &&
		    delayedTasks[i].arg2    == param2  &&
		    delayedTasks[i].arg03   == param3      )
		{
			// foo is there already - remove
			delayedTasks.splice (i, 1);
			break;
		}
	}
	delayedTasks.push( { funct:foo, arg1:param1, arg2:param2, arg3:param3 } );
	// app.onStartup sets delayedTaskID to 0 to signal that running
	// these tasks is OK now.
	if (delayedTasks.length && delayedTaskID == 0)
		delayedTaskID = app.scheduleTask ("delayedTaskExec()", 20, true);
}

// app.onStartup() calls this function at the end to get things going
// that vae accumulated during startup.

function startDelayedTasks()
{
	delayedTaskID = 0;
	if (delayedTasks.length)
		delayedTaskID = app.scheduleTask ("delayedTaskExec()", 20, true);
}

var delayedTasks  = [];
var delayedTaskID = -1;

function delayedTaskExec()
{
	var foo = delayedTasks[0].funct;
	var arg1 = delayedTasks[0].arg1;
	var arg2 = delayedTasks[0].arg2;
	var arg3 = delayedTasks[0].arg3;
	delayedTasks.shift();
	foo( arg1, arg2, arg3 );
	if (!delayedTasks.length)
	{
		app.cancelTask (delayedTaskID);
		delayedTaskID = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////
//
// string utils
//

function stripWS( str )
{
    var tmp = stripLeadingWS( str );
    tmp     = stripTrailingWS( tmp );
    
    return tmp;
}

function stripLeadingWS( str )
{
    for( var i=0; i<str.length; i++ )
    {
        var ch = str.charAt(i);
        
        if( ch != ' ' && ch != '\t' && ch != '\n' )
        {
            if( i+1 >= str.length )
                return '';
            else
                return str.substring( i+1 );
        }
    }
    
    return str;
}

function stripTrailingWS( str )    
{
    for( var i=str.length-1; i>=0; i-- )
    {
        var ch = str.charAt(i);
        
        if( ch != ' ' && ch != '\t' && ch != '\n' )
        {
            if( i+1 >= str.length )
                return str;
            else
                return str.substr( 0, i+1 );
        }
    }
    
    return str;
}

