/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       JSON Treater.
 *
 *    @version rev012.2007-08-29
 */
/* -------------------------------------------------------------------------- */




/* --------------- Constructor : BAJsonObject --------------- */

function BAJsonObject(arg) {
	this.jsonObj = null;

	if (arguments.length > 0) {
		this.set(arg);
	}
}

BAJsonObject.prototype.get = function() {
	return this.jsonObj;
}

BAJsonObject.prototype.set = function(arg) {
	if (arguments.length == 0 || typeof arg != 'object' && typeof arg !='string') {
		throw 'BAJsonObject.set: first argument must be an object, or an JSON-formed string, or a DOM Node object.';
	} else if (arg == null) {
		this.jsonObj = null;
	} else if (typeof arg.nodeType == 'number' /* arg instanceof Node */) {
		if (arg.nodeType == 1 || arg.nodeType == 9) {
			this.jsonObj = this.nodeToObject(arg);
		} else {
			throw 'BAJsonObject.set: DOCUMENT_NODE or ELEMENT_NODE is acceptable as an argument.';
		}
	} else if (typeof arg == 'string') {
		this.jsonObj = this.stringToObject(arg);
	} else {
		this.jsonObj = arg;
	}
}

BAJsonObject.prototype.stringToObject = function(str) {
	if (typeof str !='string') {
		throw 'BAJsonObject.stringToObject: first argument must be a string';
	} else {
		str = str.replace(/\=/g, '\\=').replace(/\+/g, '\\+').replace(/\-/g, '\\-').replace(/\(/g, '\\(').replace(/\./g, '\\.');
		var strArr1 = str.match(/\"[^\"]*\"/g) || [];
		var strArr2 = str.match(/\'[^\']*\'/g) || [];
		var strArr  = strArr1.concat(strArr2);
		var keys    = {}
		for (var i = 0, n = strArr.length; i < n; i++) {
			var key = strArr[i];
			if (/\n/.test(key)) {
				keys[key] = key.replace(/\n/g, '\\n');
			}
		}
		for (var key in keys) {
			str = str.replace(key, keys[key]);
		}

		try {
			// fixme with security consideration..
			var ret = eval('(' + str + ')');
			if (typeof ret != 'object' || !ret) {
				throw '';  // goto catch.
			}
			return ret;
		} catch(err) {
			throw 'BAJsonObject.stringToObject: an error occurred during evaluation of the argument.';
		}
	}
}

BAJsonObject.prototype.nodeToObject = function(node) {
	if (!node || typeof node.nodeType != 'number' /* node instanceof Node */) {
		throw 'BAJsonObject.nodeToObject: first argument must be an DOM Node.';
	} else {
		switch(node.nodeType) {
			case 1 :  // ELEMENT_NODE
				var _obj = {};

				// search attributes
				for (var i = 0, n = node.attributes.length; i < n; i++) {
					var _node  = node.attributes[i];
					var _name  = '@' + _node.nodeName.replace(/:/g, '\$');
					var _value = null;

					try {
						// WinIE5.x can't treat attribute node...
						_value = arguments.callee(_node);
					} catch(err) { }

					if (_value != null) {
						_obj[_name] = _value;
					}
				}

				// search child nodes
				for (var i = 0, n = node.childNodes.length; i < n; i++) {
					var _node = node.childNodes[i];
					var _name = _node.nodeName.replace(/[:#]/g, '\$');
					if (!_obj[_name]) {
						_obj[_name] = [];
					}
					_obj[_name][_obj[_name].length] = arguments.callee(_node);
				}

				if (!node.hasChildNodes()) {
//					_obj.$text = '';
				} else if (_obj.$text && _obj.$text.length == 1 && node.childNodes.length == 1) {
					// use textnode's value as current node's value when node has only a text node.
					_obj.$text = _obj.$text[0];
				} else {
					delete _obj.$text;
				}

				return _obj;
			case 2 :  // ATTRIBUTE_NODE
				return (typeof node.nodeValue == 'string' || typeof node.nodeValue == 'number') ?
				       	node.nodeValue.toString().replace(/^\s+/, '').replace(/\s+$/, '') :
				       	null;
			case 3 :  // TEXT_NODE
				return node.nodeValue.replace(/^\s+/, '').replace(/\s+$/, '');
			case 9 :  // DOCUMENT_NODE
				return arguments.callee(node.documentElement);
			default :
				return null;
		}
	}
}

BAJsonObject.prototype.toString = function() {
	var obj = (arguments.length > 0) ? arguments[0] : this.jsonObj;
	if (typeof obj == 'undefined' || (typeof obj == 'object' && !obj)) {
		return 'null';
	} else if (obj.constructor == String) {
		return '"' + _escape(obj) + '"';
	} else if (obj.constructor == Number) {
		return obj;
	} else if (obj.constructor == Boolean) {
		return obj.toString();
	} else if (obj.constructor == Array) {
		var arr = [];
		for (var i = 0, n = obj.length; i < n; i++) {
			arr[arr.length] = arguments.callee(obj[i]);
		}
		return '[' + arr.join(',') + ']';
	} else if (obj.constructor == Function) {
		return '"[object Function]"';
	} else if (obj == window) {
		return '"[object Window]"';
	} else if (obj.constructor == Object) {
		var arr = [];
		for (var prop in obj) {
			if (typeof obj.hasOwnProperty == 'function' && !obj.hasOwnProperty(prop)) {
				// Safari1.x doesn't understand obj.hasOwnProperty...
				continue;
			} else {
				arr[arr.length] = '"' + _escape(prop) + '":' + arguments.callee(obj[prop]);
			}
		}
		return '{' + arr.join(',') + '}';
	} else if (typeof obj.toString == 'function') {
		return '"' + _escape(obj.toString()) + '"';
	} else {
		return '""';
	}

	function _escape(str) {
		return str.replace(/\\/g, '\\\\').replace(/\"/g, '\\"').replace(/\n/g, '\n');
	}
}
