jquery.frameready.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /****
  2. *
  3. * frameReady: Remote function calling for jQuery
  4. *
  5. * Version 1.2.1
  6. *
  7. * Copyright (c) 2007 Daemach (John Wilson) <daemach@gmail.com>, http://ideamill.synaptrixgroup.com
  8. * Licensed under the MIT License:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. *
  11. * Credit John Resig and his excellent book for the ready function concepts.
  12. *
  13. * Credit to Mike Alsup for the logging code.
  14. * ============================================================================================
  15. * Usage: $.frameReady(function (function),target (string)[,options (map)][,callback (function)]);
  16. *
  17. * Function: (function/required) An anonymous function to be run within the target frame.
  18. *
  19. * Target: (string/required) The target frame. This must be a window object name (in quotes),
  20. * so work from the top down. If you have 3 frames named topFrame, navFrame, mainFrame, and
  21. * an iframe inside of mainframe named iFrame, use "top.topFrame", "top.navFrame",
  22. * "top.mainFrame", "top.mainFrame.iFrame" respectively.
  23. *
  24. * Options: (object/optional) Map of options in object literal form. Options include:
  25. *
  26. * remote: (boolean/ default true) Run the function in the context of the target frame.
  27. * If true, jQuery will be loaded in the target frame automatically and you can run
  28. * jQuery selectors in the target frame as if they were local. ie: $("p") instead of
  29. * $("p",top.mainFrame.document). If false, jQuery will not be loaded automatically
  30. * and you must use a context in jquery selectors.
  31. *
  32. * data: (object) An object to be passed as-is to the target frame. This is where you pass
  33. * variable data, rather than in a closure.
  34. *
  35. * load: (object or array of objects) jquery is loaded by default. You can pass a single object to
  36. * frameReady, or an array of objects that will be loaded and tested in order. 2 types
  37. * of files can be loaded. Scripts and stylesheets:
  38. *
  39. * scripts: {type:"script", src:"/js/myscript.js", id:"_ms", test:"afunction"}
  40. * stylesheets: {type:"stylesheet", src:"/css/mycss.css", id:"_ss"}
  41. *
  42. * type: (string/required) "script" for script files, "stylesheet" for stylesheets.
  43. * src: (string/required) The source of the file, ie: /js/myscript.js.
  44. * id: (string/optional) An id for the id attribute. If one isn't provided it
  45. * will be generated.
  46. * test: (sting/optional) The name of a function that should exist once the script
  47. * is loaded properly. Until this function becomes available, the script will
  48. * be considered not ready and no other files will be loaded. If a test is not
  49. * provided, the next file will be loaded immediately. Tests are not useful
  50. * with stylesheets.
  51. *
  52. * One gotcha: You must have something other than space characters within the body tags of
  53. * target frame documents for frameReady to work properly. A single character is enough.
  54. * The reason for this is a workaround for an iFrame bug in Firefox, of all things.
  55. * ==============================================================================================
  56. *
  57. * Example:
  58. *
  59. * $.frameReady(function(){
  60. * $("<div>I am a div element</div>").prependTo("body");
  61. * }, "top.mainFrame",
  62. * { load: [
  63. * {type:"script",id:"_fr",src:"/js/jquery.frameReady.js",test: "$.frameReady"},
  64. * {type:"stylesheet",id:"_ss",src:"frameReady.css"}
  65. * ] }
  66. * );
  67. *
  68. *
  69. * Release Notes:
  70. *
  71. * 1.2.0 - Added provision for a local callback function;
  72. * Added functionality to reset frame information if frame unloads for any reason;
  73. *
  74. * 1.1.0 - Added the ability to load scripts and stylesheets inside the target frame before
  75. * processing function stack;
  76. *
  77. ****/
  78. if (typeof $daemach == "undefined") {
  79. $daemach = {};
  80. $daemach.debug = false; // set this to true to enable logging
  81. $daemach.log = function() {
  82. if (!top.window.console || !top.window.console.log || !$daemach.debug) {
  83. return;
  84. } else {
  85. top.window.console.log([].join.call(arguments,''));
  86. };
  87. };
  88. $daemach.time = function() {
  89. if (!top.window.console || !top.window.console.time || !$daemach.debug) {
  90. return;
  91. } else {
  92. top.window.console.time([].join.call(arguments,''));
  93. };
  94. };
  95. $daemach.timeEnd = function() {
  96. if (!top.window.console || !top.window.console.timeEnd || !$daemach.debug) {
  97. return;
  98. } else {
  99. top.window.console.timeEnd([].join.call(arguments,''));
  100. };
  101. };
  102. };
  103. if (typeof $daemach["frameReady"] == "undefined") {
  104. $daemach["frameReady"] = {};
  105. };
  106. jQuery.frameReady = function(f,t,r,j) {
  107. /************************************************************
  108. You must specify the path to your jquery.js file below!
  109. *************************************************************/
  110. //var jQueryPath = "/main/inc/lib/javascript/jquery.js";
  111. var jQueryPath = jQueryFrameReadyConfigPath; // Define this configuration parameter before loading this script.
  112. var u = "undefined";
  113. var $fr = $daemach["frameReady"];
  114. var fn = t.split(".").join("_");
  115. // create a branch
  116. if (typeof $fr[fn] == u) {
  117. $fr[fn] = {};
  118. $fr[fn]["settings"] = {
  119. remote: true,
  120. jquery: true,
  121. load: [ {type:"script",id:"_jq", src:jQueryPath, test:"jQuery"} ],
  122. bLoaded: false,
  123. loadInit: [],
  124. data: {},
  125. callback: false
  126. };
  127. $fr[fn]["target"] = t;
  128. };
  129. var fr = $fr[fn];
  130. var frs = fr["settings"];
  131. if (fr.done) {
  132. $daemach.log(fr.target + " is ready. Running functions now.");
  133. return (frs.remote) ? eval(fr.target).eval("(" + f.toString() + ")()") : f();
  134. };
  135. // process arguments
  136. for (var a=2;a<arguments.length;a++){
  137. var arg = arguments[a];
  138. if ($.isFunction(arg)){
  139. frs.callback = arg;
  140. } else if (typeof arg == "object") {
  141. if (typeof arg.remote !== u) {
  142. frs.remote = arg.remote;
  143. };
  144. if (typeof arg.jquery !== u) {
  145. frs.jquery = arg.jquery;
  146. };
  147. if (typeof arg.data !== u) {
  148. frs.data = arg.data;
  149. };
  150. // if we're not running functions in the remote frame itself, no need for jQuery
  151. if (!frs.remote || !frs.jquery) {
  152. frs.load.pop();
  153. };
  154. if (typeof arg.load !== u) {
  155. var bl = true;
  156. if (arg.load.constructor == Array && arg.load.length){
  157. for (var i=0;i<arg.load.length;i++){
  158. bl = true;
  159. for (var h=0;h<frs.load.length;h++){
  160. if (frs.load[h].src == arg.load[i].src) { bl=false; };
  161. };
  162. if (bl) { frs.load.push(arg.load[i]); };
  163. };
  164. } else if (typeof arg.load == "object") {
  165. for (var h=0;h<frs.load.length;h++){
  166. if (frs.load[h].src == arg.load.src) { bl=false; };
  167. };
  168. if (bl) { frs.load.push(arg.load); };
  169. };
  170. };
  171. };
  172. };
  173. if (fr.timer) {
  174. fr.ready.push(f);
  175. } else {
  176. fr.ready=[f];
  177. if (typeof addEvent !== "undefined"){ addEvent(window,"load",function(){ jQuery.isFrameReady(fn); }); };
  178. fr.timer = setInterval(function(){ jQuery.isFrameReady(fn); },13);
  179. };
  180. };
  181. jQuery.isFrameReady = function(fn){
  182. var u = "undefined";
  183. var $d = $daemach;
  184. var fr = $d["frameReady"][fn];
  185. var frs = fr["settings"];
  186. if (fr.done) { return false; };
  187. var fx = eval(fr.target);
  188. $d.log(fn, ": New Pass. Checking target");
  189. // make sure we have a target
  190. if (typeof fx !== "undefined") {
  191. $d.log(fn, ": Found target. Checking DOM");
  192. try {
  193. var fd = fx.document;
  194. } catch (ex) { }
  195. // make sure we have a DOM
  196. if (fd && fd.getElementsByTagName && fd.getElementById && fd.body && fd.body.innerHTML.length) {
  197. $d.log(fn, ": Found DOM");
  198. if (frs.load.length && !frs.bLoaded){
  199. for (var i=0;i<frs.load.length;i++){
  200. var s = frs.load[i];
  201. var _test;
  202. try { _test = eval('typeof fx.'+s.test+ ' !== "undefined"'); }
  203. catch(ex){ _test = false;}
  204. finally { $d.log(fn, ": Running test for script ",i,". ", (_test || !s.test) ? "Passed.":"Failed."); };
  205. if ((_test || !s.test) && frs.loadInit[i]) {
  206. frs.bLoaded = (typeof s.test == u) ? true : _test;
  207. continue;
  208. } else {
  209. frs.bLoaded = false;
  210. if (typeof frs.loadInit[i] == u){
  211. var id = s.id || "frs_"+i;
  212. switch (s.type) {
  213. case "script" :
  214. $d.log(fn, ": Loading script "+ i + " (" + s.src + ")");
  215. var ele=fd.createElement('script');
  216. ele.setAttribute('id', id);
  217. ele.setAttribute('src', s.src);
  218. if (fd.getElementsByTagName("body") && fd.getElementsByTagName("body")[0]) {
  219. fd.getElementsByTagName("body")[0].appendChild(ele);
  220. }
  221. frs.loadInit[i] = true;
  222. break;
  223. case "stylesheet" :
  224. $d.log(fn, ": Loading stylesheet "+ i + " (" + s.src + ")");
  225. var ele=fd.createElement('link');
  226. ele.setAttribute('href', s.src);
  227. ele.setAttribute('rel', "stylesheet");
  228. ele.setAttribute('type', "text/css");
  229. fd.getElementsByTagName("body")[0].appendChild(ele);
  230. frs.loadInit[i] = true;
  231. break;
  232. default :
  233. $d.log(fn, ": Script "+i+" has a bad or missing type attribute..." );
  234. };
  235. };
  236. break;
  237. };
  238. };
  239. } else {
  240. clearInterval(fr.timer);
  241. fr.timer = null;
  242. for (i in frs.data){
  243. if (!fx.frData){
  244. fx.frData={};
  245. }
  246. fx.frData[i] = frs.data[i];
  247. };
  248. fr.ready.push(function(){ window.frameReadyUnload = function(root, fn){ $(window).bind("unload",function(){ root.jQuery.frameReady.unload(fn); }); } });
  249. $d.log(fn, ": Processing function stack:");
  250. for (var i=0; i<fr.ready.length;i++){
  251. (frs.remote) ? fx.eval("(" + fr.ready[i].toString() + ")()") : fr.ready[i]();
  252. };
  253. fx.frameReadyUnload(window,fn);
  254. $d.log(fn, ": Function stack processing complete.");
  255. // we're done here. let's have a beer.
  256. fr.ready = null;
  257. fr.done=true;
  258. if (frs.callback){
  259. $d.log(fn, ": Found a callback. Executing...");
  260. frs.callback();
  261. };
  262. };
  263. };
  264. };
  265. $d.log(fn, ":");
  266. };
  267. jQuery.frameReady.unload = function(fn){
  268. $daemach.log("Frame " + fn + " is unloading. Resetting state.");
  269. $daemach["frameReady"][fn].done = false;
  270. $daemach["frameReady"][fn]["settings"].bLoaded = false;
  271. $daemach["frameReady"][fn]["settings"].loadInit = [];
  272. };