UNPKG

funcunit

Version:
358 lines (329 loc) 10.1 kB
var $ = require("funcunit/browser/jquery"); var FuncUnit = require("funcunit/browser/core"); var syn = require("syn"); if(FuncUnit.frameMode){ var ifrm = document.createElement("iframe"); ifrm.id = 'funcunit_app'; document.body.insertBefore(ifrm, document.body.firstChild); } var confirms = [], prompts = [], currentDocument, currentHref, // pointer to the popup window appWin, lookingForNewDocument = false, urlWithoutHash = function(url){ return url.replace(/\#.*$/, ""); }, // returns true if url matches current window's url isCurrentPage = function(url){ var pathname = urlWithoutHash(FuncUnit.win.location.pathname), href = urlWithoutHash(FuncUnit.win.location.href), url = urlWithoutHash(url); // must strip off hash from URLs if( pathname === url || href === url ){ return true; } return false; }; /** * @add FuncUnit */ // $.extend(FuncUnit,{ // open is a method /** * @parent utilities * @function FuncUnit.open F.open() * @signature `open(path, success, timeout)` * * Opens a page. It will error if the page can't be opened before timeout. If a URL begins with "//", pages are opened * from the FuncUnit root (the root folder where funcunit is located) * ### Example * * @codestart * F.open("//app/app.html") * @codeend * * @param {String} path a full or partial url to open. * @param {Function} success * @param {Number} timeout * @return {undefined} */ open: function( path, success, timeout ) { if(typeof success != 'function'){ timeout = success; success = undefined; } FuncUnit.add({ method: function(success, error){ //function that actually does stuff, if this doesn't call success by timeout, error will be called, or can call error itself if(typeof path === "string"){ var fullPath = FuncUnit.getAbsolutePath(path); FuncUnit._open(fullPath, error); FuncUnit._onload(function(){ success() }, error); } else { FuncUnit.win = path; success(); } }, success: success, error: "Page " + path + " not loaded in time!", timeout: timeout || 30000 }); }, _open: function(url){ FuncUnit.win = appWin; hasSteal = false; // this will determine if this is supposed to open within a frame FuncUnit.frame = $('#funcunit_app').length? $('#funcunit_app')[0]: null; // if the first time .. if (newPage) { if(FuncUnit.frame){ FuncUnit.win = FuncUnit.frame.contentWindow; FuncUnit.win.location = url; } else{ // giving a large height forces it to not open in a new tab and just opens to the window's height var width = $(window).width(); FuncUnit.win = window.open(url, "funcunit", "height=1000,toolbar=yes,status=yes,width="+width/2+",left="+width/2); // This is mainly for opera. Other browsers will hit the unload event and close the popup. // This block breaks in IE (which never reaches it) because after closing a window, it throws access // denied any time you try to access it, even after reopening. if(FuncUnit.win && FuncUnit.win.___FUNCUNIT_OPENED) { FuncUnit.win.close(); FuncUnit.win = window.open(url, "funcunit", "height=1000,toolbar=yes,status=yes,left="+width/2); } if(!FuncUnit.win){ throw "Could not open a popup window. Your popup blocker is probably on. Please turn it off and try again"; } } appWin = FuncUnit.win; } // otherwise, change the frame's url else { lookingForNewDocument = true; if(isCurrentPage(url)){ /*Sometimes readyState does not correctly reset itself, so we remove the body from the document we are navigating away from, which will get set again when the page has reloaded*/ FuncUnit.win.document.body.parentNode.removeChild(FuncUnit.win.document.body); // set the hash and reload FuncUnit.win.location.hash = url.split('#')[1] || ''; FuncUnit.win.location.reload(true); } else { // setting the location forces a reload; IE freaks out if you try to do both FuncUnit.win.location = url; } // setting to null b/c opera uses the same document currentDocument = null; } lookingForNewDocument = true; }, /** * @parent utilities * @function FuncUnit.confirm F.confirm() * @signature `confirm(answer)` * * When a browser's native confirm dialog is used, this method is used to repress the dialog and simulate * clicking OK or Cancel. Alerts are repressed by default in FuncUnit application windows. * * @codestart * F.confirm(true); * @codeend * * @param {Boolean} answer true if you want to click OK, false otherwise * @return {undefined} */ confirm: function(answer){ confirms.push(!!answer) }, /** * @parent utilities * @function FuncUnit.prompt F.prompt() * @signature `prompt(answer)` * * When a browser's native prompt dialog is used, this method is used to repress the dialog and simulate * clicking typing something into the dialog. * @codestart * F.prompt("Harry Potter"); * @codeend * @param {String} answer Whatever you want to simulate a user typing in the prompt box * @return {undefined} */ prompt: function(answer){ prompts.push(answer) }, _opened: function(){ if (!this._isOverridden("alert")) { FuncUnit.win.alert = function(){} } if (!this._isOverridden("confirm")) { FuncUnit.win.confirm = function(){ var res = confirms.shift(); return res; } } if (!this._isOverridden("prompt")) { FuncUnit.win.prompt = function(){ return prompts.shift(); } } }, _isOverridden:function(type) { return !(/(native code)|(source code not available)/.test(FuncUnit.win[type])); }, _onload: function(success, error){ // saver reference to success loadSuccess = function(){ if(FuncUnit.win.steal){ hasSteal = true; } if(!hasSteal) { return success(); } // Wait for steal to be done FuncUnit.win.steal.done().then(success); }; // we only need to do this setup stuff once ... if (!newPage) { return; } newPage = false; if (FuncUnit.support.readystate) { poller(); } else { unloadLoader(); } }, /** * @hide * @parent utilities * Gets a path * @param {String} path */ getAbsolutePath: function( path ) { if ( /^\/\//.test(path) ){ path = path.substr(2); } return path; }, /** * @parent utilities * @property {window} FuncUnit.win F.win * Use this to refer to the window of the application page. * @codestart * F(F.window).innerWidth(function(w){ * ok(w > 1000, "window is more than 1000 px wide") * }); * @codeend */ win: window, // for feature detection support: { readystate: "readyState" in document }, /** * @parent utilities * @function FuncUnit.eval F.eval() * @signature `eval(str)` * * Used to evaluate code in the application page. * @param {String} str the code to evaluate * @return {Object} the result of the evaluated code */ eval: function(str){ return FuncUnit.win.eval(str) }, // return true if document is currently loaded, false if its loading // actions check this documentLoaded: function(){ var loaded = FuncUnit.win.document.readyState === "complete" && FuncUnit.win.location.href != "about:blank" && FuncUnit.win.document.body; return loaded; }, // return true if new document found checkForNewDocument: function(){ var documentFound = false; // right after setting a new hash and reloading, IE barfs on this occassionally (only the first time) try { documentFound = ((FuncUnit.win.document !== currentDocument && // new document !FuncUnit.win.___FUNCUNIT_OPENED) // hasn't already been marked loaded // covers opera case after you click a link, since document doesn't change in opera || (currentHref != FuncUnit.win.location.href)) && // url is different FuncUnit.documentLoaded(); // fully loaded } catch(e){} if(documentFound){ // reset flags lookingForNewDocument = false; currentDocument = FuncUnit.win.document; currentHref = FuncUnit.win.location.href; // mark it as opened FuncUnit.win.___FUNCUNIT_OPENED = true; // reset confirm, prompt, alert FuncUnit._opened(); } return documentFound; } }); var newPage = true, hasSteal = false, unloadLoader, loadSuccess, firstLoad = true, onload = function(){ FuncUnit.win.document.documentElement.tabIndex = 0; setTimeout(function(){ FuncUnit.win.focus(); var ls = loadSuccess loadSuccess = null; if (ls) { ls(); } }, 0); syn.unbind(FuncUnit.win, "load", onload); }, onunload = function(){ FuncUnit.stop = true; removeListeners(); setTimeout(unloadLoader, 0) }, removeListeners = function(){ syn.unbind(FuncUnit.win, "unload", onunload); Syn.unbind(FuncUnit.win, "load", onload); } unloadLoader = function(){ if(!firstLoad) // dont remove the first run, fixes issue in FF 3.6 removeListeners(); syn.bind(FuncUnit.win, "load", onload); //listen for unload to re-attach syn.bind(FuncUnit.win, "unload", onunload) } //check for window location change, documentChange, then readyState complete -> fire load if you have one var newDocument = false, poller = function(){ var ls; if (lookingForNewDocument && FuncUnit.checkForNewDocument() ) { ls = loadSuccess; loadSuccess = null; if (ls) { FuncUnit.win.focus(); FuncUnit.win.document.documentElement.tabIndex = 0; ls(); } } setTimeout(arguments.callee, 500) } // All browsers except Opera close the app window on a reload. This is to fix the case the URL to be opened // has a hash. In this case, window.open doesn't cause a reload if you reuse an existing popup, so we need to close. $(window).unload(function(){ if(FuncUnit.win && FuncUnit.win !== window.top) { FuncUnit.win.close(); } }); module.exports = FuncUnit;