/* The NAJA engine itself */
var $nj = new Object;

$nj.includes       = new Array;
$nj.superFunctions = new Array;
$nj.intervals      = new Array;
$nj.timeouts       = new Array;
$nj.onloads        = new Array;
$nj.context        = new Array(window);
$nj.ready          = false;
$nj.flushInterval  = 250;
$nj.agent          = null;
$nj.userClasses    = new Object;
$nj.userObjects    = new Array;
$nj.location       = window.location + "";
$nj.httpHost       = $nj.location.substring(7, $nj.location.indexOf("/", 7));
$nj.basePath       = "";
$nj.fileName       = "naja.js";
$nj.userAgent      = navigator.userAgent.toLowerCase();
$nj.locationPath   = "";

/* Constants */
$AGENT_IE        = 0;
$AGENT_NS        = 1;
$AGENT_FIREFOX   = 2;
$AGENT_SAFARI    = 3;
$AGENT_OPERA     = 4;
$AGENT_KONQUEROR = 5;
$AGENT_WEBTV     = 6;
$AGENT_OMNIWEB   = 7;
$AGENT_ICAB      = 8;

/**
 * Includes another js file
 *
 * @param String pJSFile   File and path
 */
$include = function(pJSFile) {
	var includeSource = "";
	if (pJSFile.substring(0,7).toLowerCase() == "http://") {
		includeSource = "http://"+$parsePath(pJSFile.substring(7));
	} else {
		includeSource = $parsePath(pJSFile);
	}
		
    $nj.addInclude(includeSource);
}

$includeOnce = function(pJSFile) {
	var includeSource = "";
	if (pJSFile.substring(0,7).toLowerCase() == "http://") {
		includeSource = "http://"+$parsePath(pJSFile.substring(7));
	} else {
		includeSource = $parsePath(pJSFile);
	}
	
	
    if (!$nj.seekInclude(includeSource)) {
        $nj.addInclude(includeSource);
    }
}


/**
 * Parses a path
 *
 * @param String pPath
 * @return String
 */
$parsePath = function(pPath) {
    pPath = pPath.replace("\\", "/");
    pPath = pPath.replace("/./", "/");
    pPath = pPath.replace("//", "/");

    var chunks     = pPath.split("/");
    var returnPath = new Array();
    var piece      = null;
    var i          = null;

    for (var i = 0; i < chunks.length; i++) {
        piece = chunks[i];

        if (piece == '..' && returnPath.length > 0) {
            returnPath.pop();
        } else {
            returnPath.push(piece);
        }
    }

    return returnPath.join("/");
}

/**
 * Returns the basePath from a file string
 *
 * @param String pFile
 * @return String
 */
$basePath = function(pFile) {
	var chunks = pFile.split("/");
	void(chunks.pop());
	
	return chunks.join("/");
}

/**
 * Returns the baseName from a file string
 *
 * @param String pFile
 * @return String
 */
$baseName = function(pFile) {
	var chunks = pFile.split("/");
	return chunks.pop();
}

/**
 * Seeks an include
 *
 * @param String pJSFile Filename
 * @return Boolean
 */
$nj.seekInclude = function(pJSFile) {
    for (var i = 0; i < $nj.includes.length; i++) {
        if ($parsePath($nj.includes[i].file+"/"+$nj.includes[i].file) == $parsePath(pJSFile)) {
            return true;
        }
    }
    return false;
}

/**
 * Adds an include
 *
 * @param String pJSFile Filename
 */
$nj.addInclude = function(pJSFile) {

	$nj.requester.abort();
	$nj.requester.open("POST", pJSFile, false);
	$nj.requester.send(null);
	
	if ($nj.requester.status != 200) {
		throw new $NajaException("Could not include file ("+pJSFile+"), Http Status:"+$nj.requester.status);
	}

    var includeFile = new Object;
    includeFile.name = $baseName(pJSFile);
    
    includeFile.path = $basePath(pJSFile)+"/";
    if (pJSFile.charAt(0) != "/") {
	    includeFile.path = $parsePath($nj.locationPath+"/"+includeFile.path)+"/";
	}
	
    includeFile.file = includeFile.path+includeFile.name;
    includeFile.src = $nj.requester.responseText;
    
    /* Emulates an str_replace like function */
    var src = includeFile.src;

    src = (src.split("%SCRIPT_PATH%")).join("('"+includeFile.path+"')");
    src = (src.split("%SCRIPT_NAME%")).join("('"+includeFile.name+"')");
    src = (src.split("%SCRIPT_FILE%")).join("('"+includeFile.file+"')");
    
	try {
	    if($getAgent() == $AGENT_IE) {
		    window.execScript(src);
	    } else {
		    window.eval.call(window, src);
	    }
	} catch(e) {
		var errorMsg = "Unable to include file ("+pJSFile+")";
		if (e != undefined) {
			errorMsg += ", "+e;
		}
		
		throw new $NajaException(errorMsg);
	}

    var nextInclude = $nj.includes.length;
    $nj.includes[nextInclude] = includeFile;
}

/**
 * New setInterval
 *
 * @param mixed pFunction     Function or code string
 * @param int   pMilliseconds Interval
 * @param mixed pArguments...
 * @return int Interval identifier
 */
$setInterval = function(pFunction, pMilliseconds /*[, pArguments...]*/) {
    var functionCall = pFunction
    if(typeof pFunction == 'string' || pFunction.constructor == String) {
        functionCall = new Function(pFunction);
    }

    var nextIndex = $nj.intervals.length;
    $nj.intervals[nextIndex] = new Object;
    $nj.intervals[nextIndex].interval = pMilliseconds;
    $nj.intervals[nextIndex].code = functionCall;
    $nj.intervals[nextIndex].arguments = new Array;

    for (var i = 2; i < arguments.length; i++) {
        $nj.intervals[nextIndex].arguments[$nj.intervals[nextIndex].arguments.length] = arguments[i];
    }

    $nj.intervals[nextIndex].timerID = window.setTimeout("$nj.runInterval("+nextIndex+")", pMilliseconds);
    return nextIndex;
}

/**
 * New clearInterval
 *
 * @param int pIndex Interval identifier
 */
$clearInterval = function(pIndex) {
    if ($nj.intervals[pIndex]) {
        window.clearTimeout($nj.intervals[pIndex].timerID);
        delete $nj.intervals[pIndex];
    }
}

/**
 * Runs a given interval
 *
 * @param int pIndex Interval identifier
 */
$nj.runInterval = function(pIndex) {
    if (!$nj.intervals[pIndex]) {
        return;
    }

    var callArgs = "$nj.intervals["+pIndex+"].code(";
    if ($nj.intervals[pIndex].arguments.length > 0) {
        for (var i = 0; i < $nj.intervals[pIndex].arguments.length; i++) {
            callArgs += "$nj.intervals["+pIndex+"].arguments["+i+"],";
        }
        callArgs = callArgs.substr(0, (callArgs.length - 1));
    }
    callArgs += ");";

    window.eval(callArgs);

	// Checks if interval still exists (the evaled code could have deleted it)
    if ($nj.intervals[pIndex]) { 
        $nj.intervals[pIndex].timerID = window.setTimeout("$nj.runInterval("+pIndex+");", $nj.intervals[pIndex].interval );
    }
}

/**
 * New setTimeout
 *
 * @param mixed pFunction     Function or code string
 * @param int   pMilliseconds Timeout
 * @param mixed pArguments...
 * @return int Timeout identifier
 */
$setTimeout = function(pFunction, pMilliseconds /*[, pArguments...]*/) {
    var functionCall = pFunction
    if(typeof pFunction == 'string' || pFunction.constructor == String) {
        functionCall = new Function(pFunction);
    }

    var nextIndex = $nj.timeouts.length;
    $nj.timeouts[nextIndex] = new Object;
    $nj.timeouts[nextIndex].interval = pMilliseconds;
    $nj.timeouts[nextIndex].code = functionCall;
    $nj.timeouts[nextIndex].arguments = new Array;

    for (var i = 2; i < arguments.length; i++) {
        $nj.timeouts[nextIndex].arguments[$nj.timeouts[nextIndex].arguments.length] = arguments[i];
    }

    $nj.timeouts[nextIndex].timerID = window.setTimeout("$nj.runTimeout("+nextIndex+")", pMilliseconds);
    nextIndex;
}

/**
 * New clearTimeout
 *
 * @param int pIndex Timeout identifier
 */
 $clearTimeout = function(pIndex) {
    if ($nj.timeouts[pIndex]) {
        window.clearTimeout($nj.timeouts[pIndex].timerID);
        delete $nj.timeouts[pIndex];
    }
}

/**
 * Runs a given timeout
 *
 * @param int pIndex Timeout identifier
 */
$nj.runTimeout = function(pIndex) {
    if (!$nj.timeouts[pIndex]) {
        return;
    }

    var callArgs = "$nj.timeouts["+pIndex+"].code(";
    if ($nj.timeouts[pIndex].arguments.length > 0 ) {
        for (var i = 0; i < $nj.timeouts[pIndex].arguments.length; i++) {
            callArgs += "$nj.timeouts["+pIndex+"].arguments["+i+"]";
        }
        callArgs = callArgs.substr(0, (callArgs.length - 1));
    }
    callArgs += ");";

    window.eval(callArgs);

    delete $nj.timeouts[pIndex];
}

/**
 * Adds an onload to the $nj engine
 *
 * @param mixed pFunction Function or string code
 * @param pArguments...
 * @return int Onload identifier
 */
$addOnload = function(pFunction) {
    var functionCall = pFunction;

    if(typeof pFunction == 'string' || pFunction.constructor == String) {
        functionCall = new Function(pFunction);
    }

    var nextIndex = $nj.onloads.length;
    $nj.onloads[nextIndex] = new Object;
    $nj.onloads[nextIndex].code = functionCall;
    $nj.onloads[nextIndex].arguments = new Array;

    for (var i = 1; i < arguments.length; i++)
    $nj.onloads[nextIndex].arguments[$nj.onloads[nextIndex].arguments.length] = arguments[i];

    return nextIndex;
}

/**
 * Run onloads
 */
$nj.runOnloads = function () {
    var evalCode = "";


    for (var i = 0; i < $nj.onloads.length; i++) {
        evalCode += "$nj.onloads["+i+"].code(";
        if ($nj.onloads[i].arguments.length > 0) {
            for (var z = 0; z < $nj.onloads[i].arguments.length; z++ ) {
                evalCode += "$nj.onloads["+i+"].arguments["+z+"],";
            }
            evalCode = evalCode.substr(0, (evalCode.length - 1));
        }
        evalCode += ");";
    }

    window.eval(evalCode);
}

/* Registers standard onloads */
if (window.onload) {
    $nj.addOnload(window.onload);
    window.onload = function(){};
}

if (window.document.onload) {
    $nj.addOnload(window.document.onload);
    window.document.onload = function(){};
}

/* Prepares onloads */
window.onload = function() { $nj.runOnloads();};

/**
 * Returns the agent identifier
 *
 * @return int Agent identifier
 */
$getAgent = function() {
    if ($nj.agent == null) {
        $nj.agent = $nj.identifyAgent();
    }

    return $nj.agent;
}

/**
 * Tries to identify the user agent
 *
 * @return int Agent identifier
 */
$nj.identifyAgent = function(pAgent) {
    if ((this.userAgent.indexOf('konqueror') + 1)) return $AGENT_KONQUEROR;
    if ((this.userAgent.indexOf('safari') + 1)) return $AGENT_SAFARI;
    if ((this.userAgent.indexOf('omniweb') + 1)) return $AGENT_OMNIWEB;
    if ((this.userAgent.indexOf('opera') + 1)) return $AGENT_OPERA;
    if ((this.userAgent.indexOf('webtv') + 1)) return $AGENT_WEBTV;
    if ((this.userAgent.indexOf('icab') + 1)) return $AGENT_ICAB;
    if ((this.userAgent.indexOf('msie') + 1)) return $AGENT_IE;
    if ((this.userAgent.indexOf('firefox') + 1)) return $AGENT_FIREFOX;
    if ((this.userAgent.indexOf('compatible') + 1)) return $AGENT_NS;
    return -1;
}

/**
 * Emulates a 'speep' function (eats processing but doesnt hangs)
 *
 * @param int pMilliseconds
 */
$sleep = function(pMilliseconds) {
    var date = new Date();
    var curDate = null;

    do { var curDate = new Date(); }
    while(curDate - date < pMilliseconds);
}

/* Initializes its basePath */
var scripts = document.getElementsByTagName("SCRIPT");
for(var i = 0; i < scripts.length; i++ ) {
    if (scripts[i].src.match($nj.fileName)) {
        var src = scripts[i].src.replace($nj.fileName, "");
        if ($getAgent() == $AGENT_IE && src.substr(0,1) != "/") {
            var parts = new String(document.location).split("/");
            void(parts.pop());
            src = (parts.join("/")+"/") + src;
            delete parts;
        }
        src = src.replace(("http://" + $nj.httpHost), "");
        $nj.basePath = $parsePath(src);
        break;
    }
}
delete src;
delete scripts;
delete i;

/* Initializes its locationPath */
var locationArray = $nj.location.split("/");
void(locationArray.pop());
$nj.locationPath = locationArray.join("/");
delete locationArray;
$nj.locationPath = $parsePath($nj.locationPath.replace(("http://" + $nj.httpHost), ""));


/* The famous XMLHttpRequest itself */
var HttpRequester = function() {
	if ($getAgent() == $AGENT_IE) {
		return new ActiveXObject("MSXML2.XMLHTTP.3.0");
	}
	
	return new XMLHttpRequest();
}

$nj.requester= new HttpRequester();

/* Include some resources (core addons and stuff like that)*/
$includeOnce($nj.basePath+"resources/Array.js") ;
$includeOnce($nj.basePath+"resources/String.js") ;
$includeOnce($nj.basePath+"resources/Function.js") ;
$includeOnce($nj.basePath+"resources/Window.js") ;
$includeOnce($nj.basePath+"resources/OOModel.js") ;