Source: modules/debug/dump.jsxinc

BX.use('brixy', 'modules/es/reflection.jsxinc');
BX.use('brixy', 'modules/es/types.jsxinc');
BX.use('brixy', 'modules/debug/helpers.jsxinc');

/**
* @module 'brixy.debug.dump'
*/
BX.module.define('brixy.debug.dump', function() {
	var reportDialog = BX.module('brixy.debug.helpers').reportDialog,
		types = BX.module('brixy.es.types'),
		reflection = BX.module('brixy.es.reflection');

	/**
	* Shows dialog window with structured information about own properties of the value.
	* 
	* @memberOf module:'brixy.debug.dump'
	* @param {*} value - Value to dump.
	* @param {int} [depth=3] - Level of nesting. Default is 3. (optional)
	* @param {string} [title] - The title of the dialog window. (optional)
	*/
	function dump(value, depth, title) {
		var dial,
			tree;
			
		if (depth == undefined)
			depth = 3;
			
		dial = reportDialog('Dump (' + types.className(value) + ')' + (title ? ' - ' +  title : ''));
		tree = dial.add ("treeview {alignment: ['fill', 'fill'], minimumSize: [400, 500], properties: {name: 'report'}}");

		dumpValue(tree, '', value, depth);

		dial.show();
	}
	
	/**
	* Maximum length of node text. Set zero to unlimited.
	* @memberOf module:'brixy.debug.dump'
	* @type {int}
	* @default 200
	*/
	dump.truncateLength = 200;
	
	/*
	* Adds node item for value.
	* 
	* @param {TreeView|ListItem} node.
	* @param {string} [name] - Name of value. (optional)
	* @param {*} value.
	* @param {int} depth - Level of nesting.
	*/
	function dumpValue(node, name, value, depth) {
		if (depth < 0)
			return;
		
		var i,
			n,
			c,
			title;
			
		// get property name
		title = name ? name += ':' : '';
		c = types.className(value);
		switch (c) {
			case 'undefined':
			case 'null':
				node.add('item', title + ' ' + c); // add simple node
				return;
			case 'Number':
			case 'Boolean':
				node.add('item', title + ' ' + types.valueString(value, c)); // add simple node
				return;
			case 'String':
				node.add('item', title + ' ' + truncate(types.valueString(value, c))); // add simple node
				return;
			case 'Function':
				break;
			default:
				title = name + ' (' + c + ')'; // object or XML
		}
		
		// get property value
		// XML
		if (c === 'XML') {
			node.add('item', title + ' ' + truncate(value.toXMLString()));
		}
		// Array or 'array like' objects (e.g. InDesign collections)
		else if (likeArray(c, value)) {
			n = value.length;
			if (depth >= 1 && n) {// has child items
				if (name)
					node = node.add('node', title + ' [...]');
			
				for (i = 0; i < n; i++) {
					if (i in value) // cannot use hasOwnProperty(), eg. index of chars in new String()
						dumpValue(node, i.toString(), value[i], depth-1);
				}
			}
			else // simple item
				node.add('item', title + ' [' + (n ? truncate(value.toString()) : '') + ']');
		}
		// object/function node may have own properties
		else if (depth >= 1) {
			var rootnode = null,
				pr,
				v,
				// Gets object own properties. Dumping of methods (value.reflect.methods) like show() has no meaning.
				props = reflection.getOwnProperties(value).sort();
			
			for (i = 0, n = props.length; i < n; i++) {
				pr = props[i];
				
				try {
					v = value[pr];
				}
				catch (e) { // not applicable property, eg. InDesign Document.filePath of the unsaved document
					v = 'Warning: ' + e.message;
				}
				if (!rootnode)
					rootnode = name ? node.add('node', title + ' {...}') : node;
					
				dumpValue(rootnode, pr, v, depth-1); // add child node
			}

			if (!rootnode) // object without properties
				node.add('item', title + ' ' + truncate(types.valueString(value, c)));
		}
		// simple item
		else {
			node.add('item', title + ' ' + truncate(types.valueString(value, c)));
		}
	}
	
	/*
	* Removes endlines. Truncate string if required.
	* @param {string} str
	*/
	function truncate(str) {
		str = str.replace(/\r|\n/g, ' ');
		if (dump.truncateLength > 0 && str.length > dump.truncateLength) { // truncate
			return str.substr(0, dump.truncateLength) + '...';
		}
		return str;
	}
	
	/*
	* Tests if value is Array or 'array like' object (e.g. InDesign collection).
	* @param className
	* @param value
	* @return {boolean}
	*/
	function likeArray(className, value) {
		try {
			return className === 'Array' || (('length' in value) && (typeof value.length === 'number') && ('0' in value));
		}
		catch (e) {
			return false;
		}
	}


	return {
		dump: dump
	};
});