funcunit
Version:
<!-- @hide title
358 lines (329 loc) • 10.1 kB
JavaScript
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;