/****
 *
 * frameReady: Remote function calling for jQuery
 *
 * Version 1.2.0
 *
 * Copyright (c) 2007 Daemach (John Wilson) <daemach@gmail.com>, http://ideamill.synaptrixgroup.com
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 * 
 *	Credit John Resig and his excellent book for the ready function concepts.
 *
 * ============================================================================================
 * Usage:  	$.frameReady(function (function),target (string)[,options (map/object)]);
 * 
 * Function: (function/required) An anonymous function to be run within the target frame.
 *
 * Target: (string/required) The target frame.  This must be a window object name (in quotes), 
 *		so work from the top down.  If you have 3 frames named topFrame, navFrame, mainFrame, and 
 *		an iframe inside of mainframe named iFrame, use "top.topFrame", "top.navFrame", 
 *		"top.mainFrame", "top.mainFrame.iFrame" respectively.
 *
 *	Options: (object/optional) Map of options in object literal form.  Options include:
 *
 * 		remote: (boolean/ default true)  Run the function in the context of the target frame. 
 *				If true, jQuery will be loaded in the target frame automatically and you can run 
 *				jQuery selectors in the target frame as if they were local. ie: $("p") instead of 
 *				$("p",top.mainFrame.document).  If false, jQuery will not be loaded automatically 
 *				and you must use a context in jquery selectors.
 *				
 *			load: (array or object)  jquery is loaded by default. You can pass a single object to 
 *				frameReady, or an array of objects that will be loaded and tested in order.  2 types 
 *				of files can be loaded.  Scripts	and stylesheets:
 *				
 *				scripts: {type:"script", src:"/js/myscript.js", id:"_ms", test:"afunction"}
 *				stylesheets: {type:"stylesheet", src:"/css/mycss.css", id:"_ss"}
 *				
 *					type: (string/required) "script" for script files, "stylesheet" for stylesheets.
 *					src: (string/required)  The source of the file, ie: /js/myscript.js.
 *					id: (string/optional)  An id for the id attribute.  If one isn't provided it 
 *						will be generated.
 *					test: (sting/optional) The name of a function that should exist once the script 
 *						is loaded properly.  Until this function becomes available, the script will 
 *						be considered not ready and no other files will be loaded.  If a test is not 
 *						provided, the next file will be loaded immediately.  Tests are not useful 
 *						with stylesheets.
 *
 *	One gotcha: You must have something other than space characters within the body tags of 
 * 	target frame documents for frameReady to work properly.  A single character is enough.  
 *		The reason for this is a workaround for an iFrame bug in Firefox, of all things.
 * ==============================================================================================
 *
 * Example: 
 *
 *		$.frameReady(function(){
 *		  $("<div>I am a div element</div>").prependTo("body");
 *		}, "top.mainFrame", 
 *		{ load: [ 
 *			{type:"script",id:"_fr",src:"/js/jquery.frameReady.js",test: "$.frameReady"},
 *			{type:"stylesheet",id:"_ss",src:"frameReady.css"} 
 *			] } 
 *		); 
 *
 *
 * Release Notes:
 *	
 *	1.2.0 - Added provision for a local callback function;
 *	        Added functionality to reset frame information if frame unloads for any reason;
 *			  
 *	1.1.0 - Added the ability to load scripts and stylesheets inside the target frame before
 *					processing function stack;
 *	
 ****/
 
if (typeof $daemach == "undefined") { 
	$daemach = {}; 
	$daemach.debug = false;  // set this to true to enable logging
	$daemach.log = function() {
		if (!top.window.console || !top.window.console.log || !$daemach.debug) { 
			return;
		} else { 
			top.window.console.log([].join.call(arguments,'')); 
		}
	}
}

if (typeof $daemach["frameReady"] == "undefined") { 
	$daemach["frameReady"] = {}; 
}
 
jQuery.frameReady = function(f,t,r,j) {
	
	/************************************************************
		You must specify the path to your jquery.js file below!	
	*************************************************************/
	
	var jQueryPath = "/js/jQuery.js";
	
	var u = "undefined";	
	var $fr = $daemach["frameReady"];
	var fn = t.split(".").join("_");	
	// create a branch
	if (typeof $fr[fn] == u) { 
		$fr[fn] = {}; 
		$fr[fn]["settings"] = {	
			remote: true,
			jquery: true,
			load: [ {type:"script",id:"_jq", src:jQueryPath, test:"jQuery"} ],
			bLoaded: false,
			loadInit: [],
			callback: false
		};
		$fr[fn]["target"] = t;
	}
	var fr = $fr[fn];	
	var frs = fr["settings"];
	
	if (fr.done) { 
		$daemach.log(fr.target + " is ready.  Running functions now.");
		return (frs.remote) ? eval(fr.target).eval("(" + f.toString() + ")()") : f(); 
	};
	
	// process arguments

	for (var a=2;a<arguments.length;a++){
		var arg = arguments[a];
		
		if ($.isFunction(arg)){
			frs.callback = arg;
		} else if (typeof arg == "object") {
			if (typeof arg.remote !== u) { 
				frs.remote = arg.remote;
			};
			if (typeof arg.jquery !== u) { 
				frs.jquery = arg.jquery;
			};
			
			// if we're not running functions in the remote frame itself, no need for jQuery
			if (!frs.remote || !frs.jquery) { 
				frs.load.pop(); 
			}
			
			if (typeof arg.load !== u) {
				var bl = true;
				if (arg.load.constructor == Array && arg.load.length){
					for (var i=0;i<arg.load.length;i++){
						bl = true;
						for (var h=0;h<frs.load.length;h++){
							if (frs.load[h].src == arg.load[i].src) { bl=false; };
						}
						if (bl) { frs.load.push(arg.load[i]); }
					}
				} else if (typeof arg.load == "object") {
					for (var h=0;h<frs.load.length;h++){
						if (frs.load[h].src == arg.load.src) { bl=false; };
					}
					if (bl) { frs.load.push(arg.load); }
				}
			}
		}
	}

	if (fr.timer) {
		fr.ready.push(f);
	} else {
		fr.ready=[f];
		if (typeof addEvent !== "undefined"){ addEvent(window,"load",function(){ jQuery.isFrameReady(fn); }); };
		fr.timer = setInterval(function(){ jQuery.isFrameReady(fn); },13);
	}
}

jQuery.isFrameReady = function(fn){
	var u = "undefined";	
	var $d = $daemach;
	var fr = $d["frameReady"][fn];
	var frs = fr["settings"];
	
	if (fr.done) { return false; };

	var fx = eval(fr.target);
	$d.log(fn, ": New Pass. Checking target");
	// make sure we have a target
	if (typeof fx !== "undefined") {
		$d.log(fn, ": Found target.  Checking DOM");
		var fd = fx.document;
		
		// make sure we have a DOM
		if (fd && fd.getElementsByTagName && fd.getElementById && fd.body && fd.body.innerHTML.length) {
			
			$d.log(fn, ": Found DOM");
			
			if (frs.load.length && !frs.bLoaded){
				for (var i=0;i<frs.load.length;i++){
					var s = frs.load[i];
					var _test;
					try { _test = eval('typeof fx.'+s.test+ ' !== "undefined"'); } 
					catch(ex){ _test = false;}
					finally { $d.log(fn, ": Running test for script ",i,". ", (_test || !s.test) ? "Passed.":"Failed."); }

					if ((_test || !s.test) && frs.loadInit[i]) {
						frs.bLoaded = (typeof s.test == u) ? true : _test; 
						continue; 
					} else {
						frs.bLoaded = false;
						if (typeof frs.loadInit[i] == u){
							var id = s.id || "frs_"+i;
							switch (s.type) {
								case "script" :
									$d.log(fn, ": Loading script "+ i + " (" + s.src + ")");
									var ele=fd.createElement('script');
									ele.setAttribute('id', id);
									ele.setAttribute('src', s.src);
									fd.getElementsByTagName("body")[0].appendChild(ele);
									void(ele);
									frs.loadInit[i] = true;
									break
								case "stylesheet" :
									$d.log(fn, ": Loading stylesheet "+ i + " (" + s.src + ")");
									var ele=fd.createElement('link');
									ele.setAttribute('id', id);
									ele.setAttribute('href', s.src);
									ele.setAttribute('rel', "stylesheet");
									ele.setAttribute('type', "text/css");
									fd.getElementsByTagName("body")[0].appendChild(ele);
									void(ele);
									frs.loadInit[i] = true;
								break
								default :
									$d.log(fn, ": Script "+i+" has a bad or missing type attribute..." );
							}
						}
						break;
					}
				}
			} else {
				clearInterval(fr.timer);	
				fr.timer = null;

				fr.ready.push(function(){ window.frameReadyUnload = function(root, fn){ $(window).bind("unload",function(){ root.jQuery.frameReady.unload(fn); }); } });
				
				$d.log(fn, ": Processing function stack:");
				for (var i=0; i<fr.ready.length;i++){
					(frs.remote) ? fx.eval("(" + fr.ready[i].toString() + ")()") : fr.ready[i]();
				}
				
				fx.frameReadyUnload(window,fn);
				
				$d.log(fn, ": Function stack processing complete.");
				
				// we're done here.  let's have a beer.
				fr.ready = null;
				fr.done=true;
				
				if (frs.callback){ 
					$d.log(fn, ": Found a callback.  Executing...");
					frs.callback(); 
				}
			}
		}
	} 

	$d.log(fn, ":");
}

jQuery.frameReady.unload = function(fn){
	$daemach.log("Frame " + fn + " is unloading.  Resetting state.")
	$daemach["frameReady"][fn].done = false;
	$daemach["frameReady"][fn]["settings"].bLoaded = false;
	$daemach["frameReady"][fn]["settings"].loadInit = [];
}
