/* jQuery general-purpose utility extensions
 *
 * @copyright 2006-2011, Centova Technologies Inc.
 */
/*jslint browser:true, white:false, eqeqeq:false, onevar:false, plusplus:false, regexp:false, nomen:false, immed:false */
/*global window:false, self:false, jQuery:false, $:false, printStackTrace:false, alert:false, ActiveXObject:false */


var IE = (document.all) ? 1 : 0;
var IE7 = (IE && window.XMLHttpRequest) ? 1 : 0;
var IE6 = IE && !IE7;

(function($) {

	$.fn.dbg = function(s) {
		$(this).css({ overflowY: 'scroll' }).append($('<div>').html($.htmlentities(s)));
	};

	$.getCSSRule = function(selector, filename) {
		for (var i = 0; i < document.styleSheets.length; i++) {
			if (filename && filename.length) {
				var match = document.styleSheets[i].href && (document.styleSheets[i].href.indexOf(filename) >= 0);
				if (!match) {
					continue;
				}
			}

			var r = document.all ? document.styleSheets[i].rules : document.styleSheets[i].cssRules;
			for (var j = 0; j < r.length; j++) {
				if (r[j].selectorText == selector) {
					return r[j];
				}
			}
		}
	};

	$.addCSSRule = function(selector,declaration) {
		var ds = document.styleSheets;
		var sheets = ds.length - 1;
		ds[sheets].addRule(selector,declaration);
	}

	$.fn.centerToScreen = function(options) {
		var clientsize = $.clientSize,
				opts = $.extend({
					vthirds: false,
					width: undefined,
					height: undefined,
					offsetx: undefined,
					offsety: undefined
				}, options);

		return this.each(function() {
			var $this = $(this),
					w = opts.width ? opts.width : $this.outerWidth(),
					h = opts.height ? opts.height : $this.outerHeight(),
					css = {
						position: 'absolute'
					};

			// calculate our positioning for the middle of the screen
			var centerx = Math.max(10, Math.floor((clientsize.width - w) / 2)),
					centery = Math.max(10, Math.floor((clientsize.height - h) / (opts.vthirds ? 3 : 2)) + $(document).scrollTop());

			if (opts.width) {
				css.width = opts.width + 'px';
			}
			if (opts.height) {
				css.height = opts.height + 'px';
			}
			if (opts.offsetx) {
				centerx += opts.offsetx;
			}
			if (opts.offsety) {
				centerx += opts.offsety;
			}

			css.left = centerx + 'px';
			css.top = centery + 'px';

			$this.css(css);
		});
	};

	//
	// plugin definition
	//
	$.fn.centovaAccordion = function(options) {

		function handle_legend_click() {
			var $this = $(this);
			var $parentfs = $this.parent();

			var $container = $parentfs.parent();

			$container.find('fieldset.accordion_active > table').slideUp('fast', function() {
				$container.children('fieldset.accordion_active').removeClass('accordion_active');
				$parentfs.addClass('accordion_active');
				$parentfs.find('table').slideDown();
			});
		}

		// build main options before element iteration
		$.extend({}, $.fn.centovaAccordion.defaults, options);

		// iterate and reformat each matched element
		return this.each(function() {
			var $this = $(this);
			$this.find('fieldset:not(.accordion_active) > table').hide();
			$this.find('fieldset > legend').click(handle_legend_click);
		});
	};

	$.fn.centovaAccordion.defaults = {
	};


	$.fn.identString = function() {
		var strlist = [];
		this.each(function() {
			var ident = [];
			if (this.id) {
				ident.push('#' + this.id);
			}
			if (this.className != '') {
				ident.push('.' + this.className.replace(/ /g, ' .'));
			}

			var content = this.innerHTML;
			if (!content || !content.length) {
				if (this.value) {
					content = this.value;
				} else {
					if (this.src) {
						content = this.src;
					} else {
						if (this.href) {
							content = this.href;
						}
					}
				}
			}
			if (content && content.length) {
				content = content.replace(/[\s\r\n\t]+/g, ' ').replace(/(^[\s\r\n\t]+|[\s\r\n\t]+$)/, '');
				if (content.length >= 80) {
					content = content.substr(0, 75) + ' ...';
				}
				content = '"<span style="color: #A0A0A0">' + $.htmlentities(content) + '</span>"';
				if (ident.length) {
					content = '; ' + content;
				}
			} else {
				content = '';
			}


			strlist.push(this.tagName.toLowerCase() + '(' + ident.join(' ') + content + ')');
		});

		return strlist.join(' ');
	};

	$.fn.dump = function(selector) {
		$.dbg(this.length + ' element(s)');
		this.each(function() {
			var $this = selector ? $(this).find(selector) : $(this);
			$.dbg($this.identString());
		});
	};

	$.fn.scrollToBottom = function() {
		var $this = $(this);
		return $this.scrollTop($this.attr('scrollHeight'));
	};

	$.fn.scrollToTop = function() {
		return $(this).scrollTop(0);
	};

	/**
	 * Binds an event handler to the specified event.  When the event is triggered, the callback
	 * eventmethod will be called in the context of eventobject (i.e., "this" will be eventobject)
	 * and the jQuery element to which the handler is bound will be passed as the first parameter
	 * to eventmethod.
	 *
	 * @var string eventname
	 * @var object eventobject
	 * @var function($obj) eventmethod
	 */
	$.fn.contextBind = function(eventname, eventobject, eventmethod) {

		if (typeof eventobject != 'object' || typeof eventmethod != 'function') {
			return;
		}

		// iterate and update each matched element
		return this.each(function() {
			$(this).data('ctxbind', { o: eventobject, m: eventmethod }).bind(eventname, $.contextBindHandler);
		});
	};

	$.contextBindHandler = function() {
		var $this = $(this);
		var ctxbind = $this.data('ctxbind');
		if (typeof ctxbind != 'object') {
			return null;
		}

		return ctxbind.m.call(ctxbind.o, $this);
	};

	$.escape = function(s) {
		return s.replace(/([ #;&,.+*~\':"!\^\$\[\]()=>|\/])/g, '\\$1');
	};

	$.preload = function() {
		for (var i = 0; i < arguments.length; i++) {
			$.create('img', { src: arguments[i] });
			/* never mind :)
			 var ext = arguments[i].match(/\.([a-z])$/i).toLowerCase();
			 switch(ext) {
			 case 'jpg':
			 case 'jpeg':
			 case 'png':
			 case 'gif':
			 break;
			 }
			 */
		}
	};

	$.clientSize = function() {
		var dimensions = {width: 0, height: 0};
		if (document.documentElement) {
			if (document.documentElement.clientWidth) {
				dimensions.width = document.documentElement.clientWidth;
				dimensions.height = document.documentElement.clientHeight;
			} else {
				dimensions.width = document.body.clientWidth;
				dimensions.height = document.body.clientHeight;
			}
		} else {
			if (window.innerWidth && window.innerHeight) {
				dimensions.width = window.innerWidth;
				dimensions.height = window.innerHeight;
			}
		}
		return dimensions;
	};

	$.getJSON = function(url, qdata, callback) {
		return jQuery.ajax({
			type: "GET",
			url: url,
			data: qdata,
			success: callback,
			error: (function(xhr, status) {
				if (typeof callback == "function") {
					var json = {
						type: 'error',
						error: xhr.status + ': ' + xhr.responseText.replace(/<.*?>/g, "").replace(/[ \t\r\n]+/g, " "),
						requestdata: qdata
					};
					callback(json, status);
				}
			}),
			dataFilter: function(data, dataType) {
				if (dataType == 'json') {
					// make sure it doesn't contain any cr/lf sequences
					if (data.match(/\\r/) || data.match(/\\n/) || data.match(/[\r\n]/)) {
						data = data.replace(/[\r\n]/g, " ").replace(/\\r/g, " ").replace(/\\n/g, " ").replace(/(^[ \t]+|[ \t]+$)/g, "");
					}

					// make sure it at least resembles a valid JSON response; if not, wrap it
					// handle json data
					if (!data.match(/^\{.*\}$/)) {
						data = '{ "type": "error", "error": "' + data.replace(/"/g, "&quot;") + '" }';
					}
				}

				return data;
			},
			dataType: 'json'
		});
	};

	$.dbg_dialog = function(title, x) {

		var $existing = $('#dbg_dialog');
		if ($existing.length) {
			var $more = $('<div/>', {
				css: {
					borderTop: '1px dashed #909090',
					marginTop: '10px',
					padding: '10px 3px 3px 3px',
					whiteSpace: 'pre',
					fontFamily: 'Courier New, Courier, serif',
					fontSize: '11px',
					color: '#303030'
				}
			});
			$more.html('<strong>' + title + '</strong>' + ( (typeof x != 'undefined') ? ('<br /><br />' + x) : ''));
			$existing.append($more);

			return;
		}

		var winwidth = $(window).width();
		var winheight = $(window).height();
		var w = Math.floor(winwidth * 0.75);
		var h = Math.floor(winheight * 0.75);
		var t = Math.floor((winheight - h) / 2);
		var l = Math.floor((winwidth - w) / 2);

		var $container = $('<div/>', {
			id: 'dbg_dialog',
			css: {
				position: 'fixed',
				top: t + 'px',
				left: l + 'px',
				width: w + 'px',
				height: h + 'px',
				border: '1px solid #b0b0b0',
				backgroundColor: '#f0f0f0',
				overflow: 'scroll',
				zIndex: 255
			}
		});
		var $dump = $('<div/>', {
			css: {
				padding: '3px',
				whiteSpace: 'pre',
				fontFamily: 'Courier New, Courier, serif',
				fontSize: '11px',
				color: '#303030'
			}
		});
		$dump.html('<strong>' + title + '</strong>' + ( (typeof x != 'undefined') ? ('<br /><br />' + x) : ''));

		var $titlebar = $('<div/>', {
			css: {
				position: 'fixed',
				top: (t + 1) + 'px',
				left: (l + w - 16 - 18) + 'px',
				//'float': 'right',
				width: '16px',
				height: '16px',
				backgroundColor: 'red',
				color: 'white',
				textAlign: 'center',
				cursor: 'pointer'
			}
		});
		$titlebar.html('x');
		$titlebar.click(function() {
			$container.remove();
		});

		$titlebar.appendTo($container);
		$dump.appendTo($container);
		$('body').append($container);
	};


	// helper function for $.var_dump()
	function _var_dump(v, html, maxdepth) {
		//window._vd_indent++;

		if (window._vd_indent.length > 20) {
			return "*RECURSION*";
		}

		var s = '(' + (html ? '<span style="color: #0000FF">' : '') + typeof v + (html ? '</span>' : '') + ')';

		var norecurse = maxdepth && (window._vd_indent.length >= maxdepth * 2);

		if (!norecurse && ( (typeof v == 'Array') || (typeof v == 'object') )) {
			s += " {\n";

			for (var key in v) {
				if (typeof v[key] != 'function') {
					s += window._vd_indent + key + ': ';
					window._vd_indent += '  ';
					try {
						s += _var_dump(v[key], html, maxdepth);
					} catch(e) {
						var etext = e + '';
						if (etext.length > 75) {
							etext = etext.substring(0, 75) + ' ...';
						}
						s += "[Exception: " + etext + "]\n";
					}
					window._vd_indent = window._vd_indent.substring(0, window._vd_indent.length - 2);
				}
			}

			s += window._vd_indent.substring(0, window._vd_indent.length - 2) + "}\n";

		} else {
			s += " \"" + (html ? "<span style='font-weight: bold; color: black'>" : '') + v + (html ? "</span>" : "") + "\"\n";
		}

		//window._vd_indent --;
		return s;
	}

	$.var_dump = function(x, async, maxdepth) {
		window._vd_indent = '  ';
		if (async) {
			$.dbg_dialog('var_dump()', printStackTrace() + "\n\n" + _var_dump(x, true, maxdepth));
		} else {
			alert('var_dump()\n' + _var_dump(x, false, maxdepth));
		}
	};

	$.dbg = function(s, st) {
		if (st) {
			st = printStackTrace({ html: true });
		}
		$.dbg_dialog(s, st);
	};

	$.backtrace = function(async) {
		if (async) {
			$.dbg_dialog('backtrace()', printStackTrace({ html: true }));
		} else {
			alert('backtrace()\n' + printStackTrace());
		}
	};

	$.regex_escape = function(s) {
		return s.replace(/[.*+?\^\${}()\|\[\]\/\\]/g, '\\$0');
	};

	$.htmlentities = function(s) {
		return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/, '&#039;').replace(/"/, '&quot;');
	};

	$.flash_version = function(minimum) {

		function get_version() {
			// ie
			try {
				try {
					var obj = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
					try {
						obj.AllowScriptAccess = 'always';
					} catch(e1) {
						return '6,0,0';
					}
				} catch(e2) {
				}
				return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
				// other browsers
			} catch(e3) {
				try {
					if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin) {
						return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
					}
				} catch(e4) {
				}
			}
			return '0,0,0';
		}

		var flashver = get_version();
		if (minimum === null) {
			return flashver;
		} else {
			var havever = flashver.split(',');
			var minver = minimum.split(',');
			if (havever.length != minver.length) {
				return false;
			} // something's fracked

			return (havever[0] > minver[0]) || (havever[0] == minver[0] && havever[1] > minver[1]) || (havever[0] == minver[0] && havever[1] == minver[1] && havever[2] >= minver[2]);
		}
	};

	window.$.dbg_dialog = $.dbg_dialog;
	window.var_dump = $.var_dump;
	window.dbg = $.dbg;
	window.backtrace = $.backtrace;
	window.htmlentities = $.htmlentities;
	window.regex_escape = $.regex_escape;
	window.flash_version = $.flash_version;

	// credit: Prestaul @ http://stackoverflow.com/questions/743876/list-all-javascript-events-wired-up-on-a-page-using-jquery
    $.eventReport = function(selector, root) {
        var s = [];
        $(selector || '*', root).andSelf().each(function() {
            var e = $.data(this, 'events');
            if(!e) return;
            s.push(this.tagName);
            if(this.id) s.push('#', this.id);
            if(this.className) s.push('.', this.className);
            for(var p in e) s.push('\n', p);
            s.push('\n\n');
        });
        return s.join('');
    }
    $.fn.eventReport = function(selector) {
        return $.eventReport(selector, this);
    }

	//
	// end of closure
	//
})(jQuery);




