/**************************************************************************
*
*  @@@BUILDINFO@@@ 31dataBrowser-2.jsx 2.0.0.54  08-Feb-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.
**************************************************************************/

databrowser = null;

app.createDataBrowser = function (where)
{
	databrowser = new Window( 
		"dockpalette {													\
			id				: 'databrowser',							\
			text			: '$$$/ESToolkit/Panes/Variables/Title=Data Browser',\
			properties		: { 										\
			    defaultVisibility : true,								\
				name			: 'databrowser',						\
				flyoutmenu		: true,									\
				icon			: '#PDataBrowser_N',					\
				rollover		: '#PDataBrowser_R',"
			 +  where
			 +  "},														\
			preferredSize	: [100, 100],								\
			margins			: 2,										\
			spacing         : 2,                                        \
			orientation		: 'column',									\
			alignment		: ['fill','fill'],                          \
			edit			: EditText {								\
				properties		: { enterKeySignalsOnChange: true },	\
				preferredSize	: [40, 20],								\
				alignment		: 'fill',								\
				enabled			: false,								\
                helpTip			: '$$$/ESToolkit/Panes/Variables/htEdit=Assign a new value to the selected variable here.'                \
			},														\
			list			: TreeView {								\
				preferredSize	: [10, 20],								\
				alignment		: ['fill', 'fill' ]						\
			},															\
            infogrp : Group                                         \
            {                                                       \
                orientation : 'row',                                \
                margins      : 0,                                   \
                spacing      : 0,                                   \
                alignment	: ['fill','bottom'],				\
                targetField: StaticText                                 \
                {								                    \
                    alignment	: ['fill','bottom'],				\
                    characters : 40                                 \
                },													\
                sizeBoxSpacer    : Group                            \
                {                                                   \
                    alignment	: ['right','bottom'],				\
                    preferredSize        : [14,1]                   \
                }                                                   \
            }                                                       \
		}");

	databrowser.menuText = localize ("$$$/ESToolkit/Menu/Window/DataBrowser/Title=&Data Browser");

	databrowser.icons = {};
	databrowser.icons.UNDEFINED		= ScriptUI.newImage ("#Undefined");
	databrowser.icons.NULL			= ScriptUI.newImage ("#Null");
	databrowser.icons.BOOLEAN		= ScriptUI.newImage ("#Boolean");
	databrowser.icons.NUMBER		= ScriptUI.newImage ("#Number");
	databrowser.icons.STRING		= ScriptUI.newImage ("#String");
	databrowser.icons.OBJECT		= ScriptUI.newImage ("#Object");
	databrowser.icons.FUNCTION		= ScriptUI.newImage ("#Function");
	databrowser.icons.INVALID		= ScriptUI.newImage ("#Invalid");

	// This variable tells onExpand that it has been called programmatically.
	// This is necessary because item.expanded = true causes onExpand() to be called,
	// but we have collected the data already.

	databrowser.calledByProgram = false;

	databrowser.onNotify = function( reason )
	{
		switch (reason)
		{
			case 'shutdown':
			    globalBroadcaster.unregisterClient( this );
			    this.blockListChange = true;
				this.list.removeAll();
				break;
				
			case 'newDebugPrefs':
				this.showUndefined	  = PrefUtils.getValue( 'prefs.databrowser.showUndefined', 'Boolean' );
				this.showCore		  = PrefUtils.getValue( 'prefs.databrowser.showCore', 'Boolean' );
				this.showFunctions	  = PrefUtils.getValue( 'prefs.databrowser.showFunctions', 'Boolean' );
				this.showPrototype	  = PrefUtils.getValue( 'prefs.databrowser.showPrototype', 'Boolean' );
				this.maxArrayElements = PrefUtils.getValue( 'prefs.databrowser.maxArrayElements', 'Number' );
				// during startup, this is null
				if (currentDebugger)
					this.update();
				break;
	            
	        case 'changeActiveTarget':
	        case 'changeActiveEngine':
	        {
	            this.enabled = targetMgr.getActiveTarget().getConnected();
	            
	            if( this.enabled )
	            {
	                var targetName = BridgeTalk.getDisplayName( targetMgr.getActiveTarget().targetName )   ;
	                this.infogrp.targetField.text = ( targetName ? targetName : targetMgr.getActiveTarget().targetName );
	            }
	            else
	                this.infogrp.targetField.text = '';
            }
            break;
		}
	}
	
	globalBroadcaster.registerClient( databrowser );
	targetMgr.registerClient( databrowser );
	databrowser.onNotify ('newDebugPrefs');

	databrowser.onResize = 
	databrowser.onResizing = function ()
	{
		this.layout.resize();
	}
	
	// used properties:
	// target - the current target
	// engine - the current engine
	// global - true if the scope is global
	databrowser.target = "";
	databrowser.engine = "";
	databrowser.global = true;

    //
    // clear list
    //
    databrowser.erase = function()
    {
        this.list.removeAll();
    }
    	
	// Get the data for the given target and engine.
	// If globalScope is true, force a get for the global scope.
	// Otherwise, get the current scope.
		
	databrowser.getVariables = function (target, engine, globalScope, all)
	{
		this.target = target;
		this.engine = engine;
		this.global = globalScope;
		this.update ();
	}
	
	// Update the pane.

	databrowser.update = function (item)
	{
		// Visual clues
		if (item)
		{
			// not really expanded yet
			item.expanded = false;
			item.oldIcon = item.icon;
			item.oldText = item.text;
			item.text = localize ("$$$/ESToolkit/DataBrowser/Pending=pending...");
			item.icon = databrowser.icons.UNDEFINED;
			item.removeAll();
		}
		var bt = BridgeTalk.create (this.target, "Variables");
		bt.headers.Engine = this.engine;
		// create the setting for things to exclude
		var excludes = [];
		if (!this.showUndefined)
			 excludes.push ("undefined");
		if (!this.showCore)
			excludes.push ("builtin");
		if (!this.showFunctions)
			excludes.push ("Function");
		if (!this.showPrototype)
			excludes.push ("prototype");
		bt.headers.Exclude = excludes.join (',');
		bt.headers.MaxArrayElements = this.maxArrayElements;

		bt.onOwnResult = function (bt)
		{
			if (this.item)
			{
				this.item.text = this.item.oldText;
				this.item.icon = this.item.oldIcon;
			}
			databrowser._readReply (bt, this.item);
		}
		// Create the scope chain
		bt.item = item;
		bt.body = this.makePath (item);
		bt.headers.All = "1";
		bt.safeSend();
	}
	
	// Helper method: Interpret the incoming BridgeTalk message body and update the variables.
	// The item is the scope node (null if global)

	databrowser._readReply = function (bt, item)
	{
		if (!item)
			item = this.list;
		
		this.edit.enabled = false;
		
		// Update the display. The BridgeTalk instance contains the data. Each body line has 4 elements,
		// the data type, the variable name, the variable value, and a read-only indicator.

		var items = [];
		var lines = bt.body.split ('\n');
		for (var i = 0; i < lines.length; i++)
		{
			// now split the line manually to digest escaped characters.
			var line = [];
			var s = lines [i];
			var cur = "";
			for (var j = 0; j < s.length; j++)
			{
				switch (s[j])
				{
					case '\\': cur += (s [++j] == 'n') ? '\n' : s[j]; break;
					case ',':  line.push (cur); cur = ""; break;
					default:   cur += s[j];
				}
			}
			if (cur.length)
				line.push (cur);
			if (line.length < 3)
				continue;
			// The line has four elements.
			var data = {
				dataType:	line[0],				// the JS data type
				name:		line[1],				// the variable name
				sortName:	line[1].toUpperCase(),	// the variable name for sorting
				index:		Number (line[1]),		// An eventual array index for sorting
				value:		line[2],				// the variable value
				readonly:	line[3] == "true",		// true if readonly
				isObject:	false,					// true for objects
				showName:	true,					// true if to display the variable name
				valid:		line[0].indexOf ("invalid ") < 0,
				icon:		databrowser.icons.UNDEFINED
			};
			// If the line is an invalid object, the data type is "invalid classname"
			if (!data.valid)
				data.dataType = data.dataType.substr (8);
			data.isIndex = !isNaN (data.index);

			switch (data.dataType)
			{
				case "undefined":	
					data.value = undefined;					
					break;
				case "null":		
					data.value = null;
					data.icon = databrowser.icons.NULL;
					break;
				case "boolean":		
					data.value = (data.value == "true");			
					data.icon = databrowser.icons.BOOLEAN;
					break;
				case "number":		
					data.value = parseFloat (eval (data.value));	
					data.icon = databrowser.icons.NUMBER;
					break;
				case "string":		
					if (typeof data.value == "undefined")
						data.value = "";						
					data.icon = databrowser.icons.STRING;
					break;
				case "Function":
					// a function may be defined in several ways:
					// Function,foo,foo()   defined as function foo(){}
					// Function,foo,()      defined as foo = function()
					// Function,bar,foo()   defined as bar = foo
					if (data.value[0] == '(')
						data.value = data.name + data.value;
					data.icon = databrowser.icons.FUNCTION;
					data.isObject = true;
					data.showName = false;
					break;
				case "EnumError":
					// On an enumeration error, the value is the error message
					data.icon = databrowser.icons.INVALID;
					data.readonly = true;
					data.valid = false;
					break;
				default:
					data.isObject = data.valid;
					data.icon = data.valid ? databrowser.icons.OBJECT : databrowser.icons.INVALID;
					// show the class if the value does not start with '['
					if (typeof data.value == "undefined")
						data.value = '[' + data.dataType + ']';
					else if (data.value.length && data.value[0] != '[')
						data.value = '[' + data.dataType + '] ' + data.value;
			}

			// All data has been set, now save the data into my array
			items.push (data);
		}
		// sort the table alphabetically by name
		function sortfn (a, b)
		{
			if (a.isIndex && b.isIndex)
				// numeric compare for array indexes
				return a.index - b.index;

			a = a.sortName;
			b = b.sortName;
			if (a < b)
				return -1;
			else if (a > b)
				return 1;
			else
				return 0;
		}
		items.sort (sortfn);

		// Now create the list
		this.blockListChange = true;
		item.removeAll();
		this.blockListChange = false;
		for (i = 0; i < items.length; i++)
		{
			data = items [i];
			var child;
			var itemType = (data.isObject && data.valid) ? "node" : "item";
			var itemText = data.showName ? (data.name + " = " + data.value) : data.value;
			var index = itemText.indexOf ('\n');
			if (index >= 0)
				itemText = itemText.substr (0, index);
			index = itemText.indexOf ('\r');
			if (index >= 0)
				itemText = itemText.substr (0, index);
			if (itemText.length > 64)
				itemText = itemText.substr (0, 64) + "...";
			child = item.add (itemType, itemText);
			child.data = data;
			child.scope = this.global 
						  // global scope: makePath() inserts "$.global" in front of [name][name] elements
						? "['" + app.escape (data.name) + "']"
						  // local scope: use the variable as-is (it is a local var) - but check for top-level
						  : (item == this.list ? data.name : "['" + app.escape (data.name) + "']");
			child.image = data.icon;
		}
		// inhibit onExpand() processing
		// onExpand() did set expanded to false, because it was not filled with data yet
		databrowser.calledByProgram = true;
		item.expanded = true;
		databrowser.calledByProgram = false;
	}

	// The callback for a new value selection.

	databrowser.list.onChange = function()
	{
	    if( !this.window.blockListChange )
	    {
		    var text = "";
		    var enabled = false;
		    var item = this.selection;
		    if (item && item.data.valid)
		    {
			    enabled = !item.data.readonly;
			    text = item.data.value;
		    }
		    databrowser.edit.text = text;
		    databrowser.edit.enabled = enabled;
		}
	}

	// The callback if the user clicked on a node to expand it,
	// or the expanded property has been set.

	databrowser.list.onExpand = function (item)
	{
		// could be called because of item.expanded = true
		if (item.data.isObject && !databrowser.calledByProgram)
		{
			// causes an update
			databrowser.update (item);
		}
	}

	// The callback if the user clicked on a node to collapse it.

	databrowser.list.onCollapse = function (item)
	{
//		if (item.isObject)
//			item.removeAll();
	}

	// The callback if the user typed in a new value.

	databrowser.edit.onChange = function()
	{
		var text = this.text;
		var result = app.checkSyntax( text, document.includePath );
		
		if (result.error)
		{
			app.beep();
			Document.setStatusLine (result.message);
		}
		else
		{
			// eval the expression, but do not print the reply
			var item = databrowser.list.selection;
			if (item)
			{
				var name = databrowser.makePath (item);
				currentDebugger.eval (name + '=' + text, true);
			}
		}
	}

	databrowser.makePath = function (item)
	{
		var scopes = [];
		while (item && item != this.list)
		{
			scopes.unshift (item.scope);
			item = item.parent;
		}
		if (this.global)
			scopes.unshift ("$.global");
		return scopes.join ('');
	}

	var item = new MenuElement( 'command', '$$$/ESToolkit/Panes/DataBrowser/Flyout/ShowUndefined=&Undefined Variables', 
								 "at the end of databrowser/flyout", "databrowser/flyout/undefined" );
	item.onDisplay = function()
	{
		this.checked = databrowser.showUndefined;
		this.enabled = databrowser.enabled;
	}
	item.onSelect = function()
	{
		databrowser.showUndefined       = !databrowser.showUndefined;
		prefs.databrowser.showUndefined = databrowser.showUndefined;
		databrowser.update();
	}

	item = new MenuElement( 'command', '$$$/ESToolkit/Panes/DataBrowser/Flyout/ShowFunctions=&Functions', 
								 "at the end of databrowser/flyout", "databrowser/flyout/functions" );
	item.onDisplay = function()
	{
		this.checked = databrowser.showFunctions;
		this.enabled = databrowser.enabled;
	}
	item.onSelect = function()
	{
		databrowser.showFunctions       = !databrowser.showFunctions;
		prefs.databrowser.showFunctions = databrowser.showFunctions;
		databrowser.update();
	}

	item = new MenuElement( 'command', '$$$/ESToolkit/Panes/DataBrowser/Flyout/ShowCore=\&Core JavaScript Elements', 
								 "at the end of databrowser/flyout", "databrowser/flyout/core" );
	item.onDisplay = function()
	{
		this.checked = databrowser.showCore;
		this.enabled = databrowser.enabled;
	}
	item.onSelect = function()
	{
		databrowser.showCore        = !databrowser.showCore;
		prefs.databrowser.showCore  = databrowser.showCore;
		databrowser.update();
	}

	item = new MenuElement( 'command', '$$$/ESToolkit/Panes/DataBrowser/Flyout/ShowPrototype=&Prototype Elements', 
								 "at the end of databrowser/flyout", "databrowser/flyout/proto" );
	item.onDisplay = function()
	{
		this.checked = databrowser.showPrototype;
		this.enabled = databrowser.enabled;
	}
	item.onSelect = function()
	{
		databrowser.showPrototype       = !databrowser.showPrototype;
		prefs.databrowser.showPrototype = databrowser.showPrototype;
		databrowser.update();
	}

	databrowser.show();	
	panes.push (databrowser);
}
