﻿/**
 * Copyright (C) 2010 Mikael Wistoft-Ibsen. All rights reserved.
 */

 
/*
 * Extract functioner fra denne til wBasic_dev.js
 */


//testing

var dontPop 			= false;	//hvornår skal denne bruges til noget?
if (isBrowserTesting === undefined)
	var isBrowserTesting 	= false;	//devLog mv. udskrives til dokumentet.
var md5Testing			= false;

//Denne gør at det er nemt at stoppe logging fra browser til server. Denne kan
// sættes i _conf.php
var logFromBrowser 		= false;
var serverLogPrefix 	= '';	//Denne skal sættes onload. Dette skal gøres i alle jasml templates.

//null betyder ingen buffer. array betyder, der skal buffers.
var bufferLogs 			= null;	

//Denne tælles ned, og kan derfor gensættes dynamisk.
var serverLogsLeft 	= 10;	//Hvis dette er for småt, må de enkelte moduler overskrive, eller anvende log caching.


//Denne bruges til at stoppe error handler functioner, og de forskellige functioner, de kunne
// finde på at bruge, når der sker fejl under fejlhåndtering.
//	Disse fejl kunne være:
// 		*) Der kan ikke logges til server.
// 		*) Input conditions er forkerte.
// 		*) o.l.
var jasml_handling_error = false;

//betyder at jasml_error_handler ikke skal logge den exception, som exit() kaster. Denne bruges udelukkende
// til at emitere clean exit i javascript.
var jasmlHasStoppet = false;

//Pop Blocker
var maxPopCount			= 10;
var popCount			= 0;		//bruges til at forhindre pop uendelig.

//max url length. Hvis url overstiger denne, så skal der bruges POST i stedet.
var maxUrlLength		= 1000;

//Denne variabel bruges af getPageUniqueTempElmId() til at generere unikke id'er.
var clickTime = 0;

//bruges af thread()
var globalThreads 		= 0;
var concurrentThreads 	= 0;

/* Denne styrer om fejlbeskeder også vises i browseren. Dette fravælges, når
 * 	der testes en errorcase.
 * Denne funktionalitet skal også eksisterer, når der browser testes. Altså
 *	 i alle mulige moduler, andre end TestRunner.
 */
if (logErrMsgToDoc === undefined)
	var logErrMsgToDoc = false;			//Kan sættes i _conf

/* Denne holder stdOut under testing, og stdout fra javascript udskrives først, når testcasen er
 *	færdig, for så bliver det muligt, for udvalgte testcases, at logge stdOut.
 *
 * Hvis denne er null, så betyder det, at buffering af stdOut er off. Buffer er on, hvis
 *	denne variabel er string type. Det bør kunne laves lidt smartere.
 *
 * Default er buffer off.
 */
 
var stdOut 		= null;



function wBasic(){
	//sæt variabler.
}

//
// methods
//

wBasic.prototype.imp = 
function imp(msg1, msg2, msg3, msg4, msg5, msg6){
	rawLog('impLog', argvToString(arguments));
}


/**
 *
 */
wBasic.prototype.dev = 
function dev(msg1, msg2, msg3, msg4, msg5, msg6){
	rawLog('devLog', argvToString(arguments));
}


/**
 *
 */
wBasic.prototype.div = 
function div(msg1, msg2, msg3, msg4, msg5, msg6){
	rawLog('md5Log', argvToString(arguments));
}


/**
 * Denne burde kunne gøre brug af pre-/rawLog()
 */
wBasic.prototype.out = 
function out(msg1, msg2, msg3, msg4, msg5, msg6){
	putStdOut(argvToString(arguments) + "<br>\n");
} 


/**
 * Used for debug.
 */
function o(){
	alert(argvToString(arguments));
}


/**
 * utestet, fordi det kontrolleres ikke hvilken ændring det har i DOM
 *
 * Skrives ikke til server endnu.
 * Her kunne man lave prettyToString() el. toPrettyString()
 *
 */
wBasic.prototype.userLog = 
function userLog(msg1, msg2, msg3, msg4, msg5, msg6){
	$('#userLogHolder').show();
	appendId('userLog', argvToString(arguments) + "<br>");
}


/**
 *
 */
wBasic.prototype.err = 
function err(msg1, msg2, msg3, msg4, msg5, msg6){

	assertNotHandlingErrors(msg1);

	var msg = argvToString(arguments);
	
	//Dette skal gøres mere elegant.
	if (logErrMsgToDoc)	logToDoc(msg);				//Dette gøres før logging til server. Så er det nemmere at debug, ved fejl i serverlogging
	
	rawLog('errLog', msg);
	
	//fejlhåndtering er færdig.
	jasml_handling_error = false;
}


/**
 * Denne eksisterer, fordi den giver mulighed for at kunne logge til dokument. Det kunne være interessant
 *	ved tests, men også for userLog.
 * Desuden er det ikke alle, der kan bruge preLog()
 *
 * Det er denne, der skal bestemme, om der skal logges til  dokument.
 */
wBasic.prototype.rawLog = 
function rawLog(msgType, msg){
	assert(is_string(msg)	, "rawLog() - msg skal være string: "  	+ msg);
	
	//alert(msg);
	
	if (!logFromBrowser) return;

	serverLog(msgType, msg);
}


/**
 * pattern behøver da ikke være string.
 *
 */
wBasic.prototype.regMatch = 
function regMatch(pattern, str) {
	assert(is_string(pattern)	, "regMatch() - pattern skal være string: " 	+ pattern);
	assert(is_string(str)		, "regMatch() - str skal være string: " 		+ str);

	re = new RegExp(pattern);		//her mangler flags
	result = str.match(re);
	assert(is_object(result)		, "regMatch() - result skal være object: " 	+ result);
	
	return (result != null);
}

/**
 * omdøb til isIntString()
 *
 * Denne skal også match negative integers, da det jo er int compatible. Altså udtrykkes som
 *	et integer type i php.
 *
 * Skal denne også match negative tal. De er jo faktisk int compatible.
 */
wBasic.prototype.isIntCompatible = 
function isIntCompatible(str){
	assert(is_string(str), "isIntCompatible() - input skal være string: " + str);
	return regMatch("^(0|[1-9]\\d*)$", str);
}

/**
 *
 * Denne match'er også leading zero.
 */
wBasic.prototype.isIrreversibelIntString = 
function isIrreversibelIntString(str){
	assert(is_string(str), "isIrreversibelIntString() - input skal være string: " + str);
	return regMatch("^(\\+|-)?\\d+$", str);
}

/**
 *
 */
wBasic.prototype.setLeadZero = 
function setLeadZero(str, length){
	assert(isIrreversibelIntString(str)	, "setLeadZero() - input skal være irreversibel int string: " 	+ str);
	assert(is_int(length)				, "setLeadZero() - length skal være int: " 		+ length);
	while (str.length < length) str = '0' + str;
	return str;
}

/**
 * match'er bogstaver, inkl. æøå
 * match'er ikke blank.
 *
 * Skal dette ikke være et enkelt bogstav.
 */
wBasic.prototype.isLetter = 
function isLetter(str){
	assert(is_string(str), "isLetter() - input skal være string: " + str);
	return regMatch("^[a-zæøåA-ZÆØÅ]+$", str);
}

/**
 * match'er bogstaver, inkl. æøå
 * match'er ikke blank.
 */
wBasic.prototype.isEmail = 
function isEmail(str){
	assert(is_string(str), "isEmail() - input skal være string: " + str);

	//findes der ikke et case less match directive, som i php (perl)
	return regMatch("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$", str);
}


/**
 *

 */
wBasic.prototype.isSomeString = 
function isSomeString(input){
	if (typeof(input) != "string") return false;
	return (input !== "");
}


/**
 *

 */
wBasic.prototype.isIntlistString = 
function isIntlistString(str){
	assert(is_string(str), "isIntlistString() - str must be string: " + str);

	if (str === "") return true;	//empty list

	var ints = str.split(",");

	for (var key in ints){
		if (!isIntCompatible(ints[key]))
			return false;
	}

	return true;
}

/**
 * fejl
 *	parseInt() laver også 10afadf om til 10
 *
 */
wBasic.prototype.toInt = 
function toInt(input, defaultValue){
	var tmp = parseInt(input);
	if (isNaN(tmp)) tmp = defaultValue;

	assert(is_int(tmp), 'toInt() - Det var ikke muligt at konvertere til int: ', input, ' ,', defaultValue);
	return tmp;
}


/**
 * Fejler vist fatal i IE, hvis input er null
 */
wBasic.prototype.is_string = 
function is_string(input){
	return (typeof(input) == "string");
}

/**
 *
 */
wBasic.prototype.is_int = 
function is_int(input){
	if (typeof(input) !== "number") return false;
	if (isNaN(input)) 				return false;
	if (input === Infinity) 		return false;
	return true;
}

/**
 *
 */
wBasic.prototype.is_bool = 
function is_bool(input){
	return (typeof(input) === "boolean");
}

/**
 *
 */
wBasic.prototype.is_array = 
function is_array(input){
	return (input instanceof Array);
}

/**
 * Dur ikke i IE, for der er function er 'object'
 *
 */
wBasic.prototype.is_function = 
function is_function(input){
	return (typeof(input) === "function");
}

/**
 *
 */
wBasic.prototype.is_object = 
function is_object(input){
	return (typeof(input) === "object");
}


/**
 * Denne sørger for at jasml_error_handler() ikke udskriver fejlen endnu en gang, når den får
 *	exception'en skal kastes her.
 */
wBasic.prototype.exit = 
function exit(msg){
	if (arguments.length === 0) msg = '';	//default
	
	jasmlHasStoppet = true;
	throw(msg);
}

/**
 *
 * Denne skal anvende: assertNotHandlingErrors().
 */
wBasic.prototype.errExit = 
function errExit(msg1, msg2, msg3, msg4, msg5, msg6){

	//quick fix
	//Her skal der skrives fuld fejlmeddelelse.
	var msg = argvToString(arguments);

	err("Exit: " + msg);
	
	exit(msg);
}


/**
 * Her kan assert ikke anvendes til input kontrol. Fordi assert() vil igen kalde assert(), 
 *	og der er ingen end condition.
 * Og der skal exit'es, hvis input valideting fejler. For denne funktion exit'er
 *	jo ikke altid. Hvis b er true, så kaldes exit() i bunden jo ikke.
 *
 * lav og brug rawAssert
 */
wBasic.prototype.assert = 
function assert(b, msg1, msg2, msg3, msg4, msg5, msg6){
	
	if (!is_bool(b)) throw("assert() - 1. input skal være boolean: " + b);
		
	
	if (b == true) return;

	//Denne kan ikke stå før assert tjeks, det vil jo gøre at fejl funktioner ikke kan bruge denne, selv
	// deres assertations er succesfulde
	//
		
		assertNotHandlingErrors(msg1);	
	
	//Her kan preLog ikke kaldes, fordi den tager et array/object af arguments. Og denne har ekstra boolean parameter.
	// Men dette er ækvivalent.

		var msg = argvToString(arguments, 1);		//1 betyder at boolean ikke tages med.
		
		//Det er lidt noget gris, men det virker. Dette gør det muligt at kalde err() fra assert()
		jasml_handling_error = false;				//Dette gør at err() nu kan kaldes.
		
		err("assErr: " + msg);
		//rawLog("errLog", "assErr: " + msg);

	//fejlhåndtering er færdig.
	
		exit(msg);
}

/**
 * Dette har det eneste formål at sikre, at fejl under fejlhåndtering
 *	medfører afslutning af alt jasml kode.
 *
 * Denne tager en besked, fordi parseBacktrace ikke er implementeret i js endnu.
 *
 * Når jasml har håndteret fejlen, skal jasml_handling_error sættes til false igen, så der på følgende
 *	events kan fortsættes. Fejlen er jo ikke nødvendigvis global på alle websiden.
 */
wBasic.prototype.assertNotHandlingErrors = 
function assertNotHandlingErrors(msg){

	if (jasml_handling_error) {
	
		//fejlhåndtering er færdig.
		jasml_handling_error = false;
		throw('errLoop: ' + msg);
		
	}
	
	jasml_handling_error = true;
}

/**
 *
 * Denne skælner ikke mellem variabler, som aldrig har været sat/deklareret, og variabler som er null.
 *
 * Denne bør ikke bruges til at tjekke om en property på et objekt eller element i et array er sat,
 *	for de kan sagtens være null, og dermed er denne function misvisende.
 *
 * typeof input kan være object, hvis variablen er instantieret til null 
 * og undefined, hvis variablen ikke er instantieret overhovedet.
 * Derfor kan typeof ikke bruges til at sige noget entydigt.
 */
wBasic.prototype.isset = 
function isset(input){

	a = input != null;
	b = ((input !== null) && (input !== undefined));
	c = ((input !== null) && (typeof(input) !== 'undefined'));
	
	assert(a === b, "isset() - alt_impl var ikke enig, b : " + a + ", " + b);
	assert(a === c, "isset() - alt_impl var ikke enig, c : " + a + ", " + c);
	
	return a;
}


/**
 *
 */
wBasic.prototype.isdefined = 
function isdefined(input){
	return input !== undefined;
}

/**
 * utestet
 */
wBasic.prototype.currentAar = 
function currentAar(){
	return new Date().getFullYear();
}

/**
 * Fejl
 *	Er ikke case insensitive. Modsat wBasic.php.endsWith
 *
 * @Return 		Boolean 	true, hvis subject ender med match
 */
wBasic.prototype.endsWith = 
function endsWith(subject, match){
	assert(is_string(subject), "endsWith() - subject skal være string: " + subject);
	assert(is_string(match)	, "endsWith() - match skal være string: " 	+ match);
	
	return regMatch(match + "$"	, subject);
}

/**
 * Fejl
 *	Er ikke case insensitive. Modsat wBasic.php.startsWith
 *
 * @Return 		Boolean 	true, hvis subject starter med match
 */
wBasic.prototype.startsWith = 
function startsWith(subject, match){
	assert(is_string(subject), "startsWith() - subject skal være string: " 	+ subject);
	assert(is_string(match)	, "startsWith() - match skal være string: " 	+ match);
	//out(subject);
	return regMatch('^' + match, subject);
}

/*
 * testet
 */
wBasic.prototype.getLeft = 
function getLeft(str, amount) {
	assert(is_string(str)			, "getLeft() - str must be string");
	assert(is_int(amount)			, "getLeft() - amount must be int");
	assert(amount <= str.length		, "getLeft() - No more than strlen can be returned: ", amount, ", ", str );

	return str.substring(0, amount);
}

/**
 *
 */
wBasic.prototype.removeLeft = 
function removeLeft(str, amount){
	assert(is_string(str)		, "removeLeft() - str must be string: " 					+ str);
	assert(is_int(amount)		, "removeLeft() - amount must be int: " 					+ amount);
	
	assert(amount <= str.length	, "removeLeft() - No more than strlen can be removed: " 		+ amount + ", " + str);
	assert(amount >= 0			, "removeLeft() - amount must be positive int: " 				+ amount);
	
	return str.substring(amount);
}

/**
 * untested
 */
function getRight(str, amount) {
	assert(is_string(str)			, "getRight() - str must be string");
	assert(is_int(amount)			, "getRight() - amount must be int");
	assert(amount <= str.length		, "getRight() - No more than strlen can be returned: ", amount, ", ", str );

	return str.substring(str.length - amount, str.length);
}

/**
 * untested
 */
function removeRight(str, amount) {
	assert(is_string(str)			, "removeRight() - str must be string");
	assert(is_int(amount)			, "removeRight() - amount must be int");
	
	assert(amount <= str.length		, "removeRight() - No more than strlen can be removed: " 		+ amount + ", " + str);
	assert(amount >= 0			, "removeRight() - amount must be positive int: " 				+ amount);

	return str.substring(amount, str.length);
}

/**
 * utestet
 *
 */
wBasic.prototype.chr = 
function chr(input){
	assert(is_int(input)		, "chr() - input skal være int: " + input);
	
	return String.fromCharCode(input);
}

/**
 * utestet
 *
 */
wBasic.prototype.ord = 
function ord(input){
	assert(is_string(input)		, "ord() - input skal være string: " 	+ input);
	assert(input.length === 1	, "ord() - input skal være char: " 		+ input);
	
	return input.charCodeAt(0);
}

/**
 * ubrugt
 *
 */
wBasic.prototype.implode = 
function implode(glue, arr){
	return arr.join(glue);
}

/**
 * ubrugt
 *
 */
wBasic.prototype.explode = 
function explode(glue, str){
	return str.split(glue); 
}

/** 
 * 
 * Denne function returnerer true, hvis blot én af de to angivne precisioner er korrekt.
 *	
 *
 * @param	number
 * @param	number
 * @param	int		absolute precision
 * @param	int		relative precision
 * @return	boolean	
 */
wBasic.prototype.precision = 
function precision(nr1, nr2, absolute, relative){
	assert(is_int(nr1)		, "precision() - nr1 skal være int: " + nr1);
	assert(is_int(nr2)		, "precision() - nr2 skal være int: " + nr2);

	//absolute
	if (absolute >= Math.abs(nr1 - nr2)) return true;
	
	//relative
	return relative >= 2 * Math.abs(nr1 - nr2) / (Math.abs(nr1) + Math.abs(nr2));
}	

/**
 * bruges af assert() og testes indirekte af den.
 */
//hvorfor kan dette ikke lade sig gøre
//wBasic.prototype.argvToString = 
function argvToString(argv, startPos){
	if (startPos === undefined) startPos = 0;

	var msg = '';
	
	for (var i = startPos; i < argv.length; i++) {
		if (i > startPos + 1) msg += ', ';				//første _to_ gange skal der ikke være comma.
		msg += toString(argv[i]);
	}
	
	return msg;
}

/**
 * opera kan ikke bruge denne i eval(). Fx under testcases.
 *
 * Indfør parameter, der kan afgøre at de commuterer.
 */
//hvorfor kan dette ikke lade sig gøre
//wBasic.prototype.toString = 
function toString(anyType){

	//Denne fejler i chrome, når man laver dumpObject(window)
	//assert(arguments.length === 1, 'toString() - Der skal være præcis en parameter: ' + arguments.length);

	if (anyType === undefined)			return "Undefined";

	if (anyType === null)				return "Null";
	
	//int
	if (is_int(anyType))				return "Int: " + anyType;
	
	//string
	if (anyType === '')					return "Blank string";
	if (is_string(anyType))				return anyType;
	
	//string object
	if (anyType instanceof String)		return anyType.valueOf();
	
	//array object
//	if (anyType instanceof Array)		return 'Array: (' + anyType.toString() + ")";
	if (anyType instanceof Array)		return arrayToString(anyType);
	
	//NaN
	if (typeof(anyType) === 'number')
		if (isNaN(anyType))				return "Number: Not.";
	
	//Infinity
	if (anyType === Infinity)			return "Number: Infinity.";
	
	//bool
	if (is_bool(anyType))				return "Boolean: " + anyType;
	
	//function
	//if (is_function(anyType))	return "Function: " + anyType;
	
	//ukendt
	return "Ukendt type, " + typeof(anyType) + ": " + anyType;
}

/**
 *
 */
wBasic.prototype.arrayToString = 
function arrayToString(arr){

	var result = arrayToStringReal(arr);

	if (result === "EmptyArray") return "EmptyArray";

	return "<pre>" + indent("\n" + result, 1) + "</pre>\n";
}

/**
 * This puts no <pre> tag on the result.
 */
wBasic.prototype.arrayToStringReal = 
function arrayToStringReal(arr){

	//this cann't be done, because elements may be ignored
//	if (arr.length === 0) return "EmptyArray";

	var result 	= "";
	var count 	= 0;

		result += "Array(\n";

		for (var key in arr){
			count++;
			value = arr[key];

			if (is_array(value)){
				value = arrayToStringReal(value); //recurs

				value = indent(value, 1);
			}
		
			result += "\t" + key + " => " + value + "\n"
		}

		result += ")\n";

	if (count === 0) return "EmptyArray";

	return result;
}

/**
 * untested
 *
 */
function indent(str, amount){
	
	if (str === "") return "";
	
	var tabs = '';

	for (var i = 0; i < amount; i++)
		tabs += "\t";
	
	return str.replace(/\n/g, "\n" + tabs)

//	return tabs . str_replace("\n", "\n".$tabs, $str);
}

/**

 *
 */
wBasic.prototype.toBytecodes = 
function toBytecodes(str){
	assert(is_string(str)		, "toBytecodes() - input skal være string: " + str);
	
	var result = "";
	for (i = 0; i < str.length; i++) {
		if (result !== "") result += " ";
		result += str.charCodeAt(i);
		}
	return result;
}

/**
 * utestet
 *
 * består af tal.
 */
wBasic.prototype.getRandomString = 
function getRandomString(length){ 
	return Math.max(1 , Math.round(Math.random() * Math.pow(10,length)));
}

/**
 * utestet
 */
wBasic.prototype.busySleep = 
function busySleep(milisekonds){
        var startingMSeconds = new Date().getTime();
        while (startingMSeconds + milisekonds > new Date().getTime()) /*do nothing*/;
}

/**
 * deprecated
 *
 */
wBasic.prototype.timeNow = 
function timeNow(){
	return millitime();
}

/**
 *
 * time in milliseconds since 1970
 */
wBasic.prototype.millitime = 
function millitime(){
	var tmp = new Date().getTime();
	assert(is_int(tmp)		, "timeNow() - tmp skal være string: ", tmp);
		
	return tmp;
}

/**
 * utestet
 *
 */
wBasic.prototype.urlencode = 
function urlencode(str){
	//assert simple string
	assert(is_string(str)		, "urlencode() - input skal være string: ", str);

	return encodeURIComponent(str);	
}

/**
 * mangler grundig test.
 *
 * svarer til PHP's basename. Virker dog kun på / ikker backslash
 */
wBasic.prototype.basename = 
function basename(str){
	assert(is_string(str)		, "basename() - input skal være string: ", str);
	
	indexLastChar = str.lastIndexOf('/');
	
	return removeLeft(str, indexLastChar + 1);
}
	

/**
 *
 */
wBasic.prototype.strContains = 
function strContains(str, needle){
	assert(is_string(str)		, "strContains() - str skal være string: "		, str);
	assert(is_string(needle)	, "strContains() - needle skal være string: "	, needle);
	
	return str.indexOf(needle) >= 0;
}

	
/**
 *
 */
wBasic.prototype.str_repeat = 
function str_repeat(str, amount){
	assert(is_string(str)					, "str_repeat() - str skal være string: "			, str);
	assert(is_int(amount) && amount >= 0	, "str_repeat() - amount skal være positiv int: "	, amount);
	
	if (true){
		var result = ''
		while (amount-- > 0) result += str;
		return result;
	} else {
		//Dette er maginalt hurtigere.
		//Men overraskende nok bruger dette mindre memory.
		return new Array( amount + 1 ).join( str );	//source: stackoverflow.com
	}
}


/**
 * utestet
 *
 * Denne giver et id, som kan bruges til et midlertidigt/dynamisk tag. Denne garanterer at der
 *	ikke vil komme navnekonflikter.
 */
wBasic.prototype.getPageUniqueTempElmId = 
function getPageUniqueTempElmId(){
	clickTime++;
	return 'UniqueElm_' + clickTime;
}

/**
 * utestet
 *
 * trimmer kun forende og bagende af strengen.
 *
 * Denne burde da bruge /^\s+/. Der er jo ingen pointe i at match'e 'ingen space'.
 */
wBasic.prototype.trim = 
function trim(str){
	assert(is_string(str), "trim() - str skal være string");
	
	//fjerner leading og trailing space
    return str.replace(/^\s*/, "").replace(/\s*$/, "");
}



/**
 * utestet
 */
wBasic.prototype.trimDobbeltSpace = 
function trimDobbeltSpace(str){
	assert(is_string(str), "trimDobbeltSpace() - str skal være string: ", str);
	
	//g betyder exhaustive
    return str.replace(/\s+/g, " ");
}



/**
 * Denne fjerner chars, så den resulterende streng er int compatible.
 *
 * Denne fjerner non-numbers. Desuden leading zero'es og evt. plus og minus tegn.
 */
wBasic.prototype.trimToPosIntString = 
function trimToPosIntString(str){
	
	str = str.replace(/[^0-9]/g, '');
	
	//fjerner leading plus/minus
	str = str.replace(/^(\+|-)/, '');
	
	return trimLeadingZero(str);
}


/**
 * Denne fjerner kun leading zero. Resultatet er ikke nødvendigvis int compatible.
 *
 * Hvis der står plus eller minus tegn foran leadingzero, så gør denne funktion ingen trim.
 */
wBasic.prototype.trimLeadingZero = 
function trimLeadingZero(str){
	
	//reducerer leaading zeroes
	str = str.replace(/^0+/, '0');
	
	if (str === '0') return '0';
	
	//fjerner leaading zeroes
	str = str.replace(/^0+/, '');

	return str;
}


/**
 * utestet
 */
wBasic.prototype.capitalize = 
function capitalize(str){
	assert(is_string(str), "capitalize() - str skal være string");

	var wasIndikator 	= true;	//Første bogstav skal være uppercase.
	var output 			= '';
	
	for (var i = 0; i < str.length; i++) {
	
		if (wasIndikator)	output += str.charAt(i).toUpperCase();
		else				output += str.charAt(i).toLowerCase();

		//afgør om efterfølgende char skal være uppercase.
		
			if (str.charAt(i).match(/ |-/))	wasIndikator = true;
			else							wasIndikator = false;
	}
	
	return output;
}


/**
 * Anvendes til at trimme navne. Fx ' mikael wistoft' -> 'Mikael Wistoft'
 */
wBasic.prototype.trimName = 
function trimName(value){
	if (value === '') 			return '';
	value = trim(value);
	value = trimDobbeltSpace(value);
	value = capitalize(value);
	return value;
}


/**
 *
 */
wBasic.prototype.trimAllSpace = 
function trimAllSpace(str){
	str = str.replace(/\s/g, '');
	return str;
}


/**
 * Fjerner space i start og slutning, og erstater dobbelt space med enkelt space.
 *
 */
wBasic.prototype.trimRedundantSpace = 
function trimRedundantSpace(value){
	if (value === '') 			return '';
	value = trim(value);
	value = trimDobbeltSpace(value);
	return value;
}


/**
 * Denne trimmer automatisk, uden at inddrage brugeren.
 *
 * Der trimmes alt space. Det er fordi denne er tiltænkt til brug ved tal, hvor space ikke er tilladt.
 */
wBasic.prototype.inputTrimLeadingZero = 
function inputTrimLeadingZero(inputTagName){

	var value	= getElementContent(inputTagName);
	var trimmed	= trimAllSpace(trimLeadingZero(value));

	//Dette opdateres automatisk.
	if (trimmed !== value) getElement(inputTagName).value = trimmed;
}


/**
 * Denne fjener automatisk alt space uden at inddrage brugeren.
 */
wBasic.prototype.inputTrimAllSpace = 
function inputTrimAllSpace(inputTagName){

	var value	= getElementContent(inputTagName);
	var trimmed	= trimAllSpace(value);
	
	//Kan der ikke bruges setId i stedet.
	if (trimmed !== value) getElement(inputTagName).value = trimmed;
}

/**
 * Denne fjener overflødig space automatisk uden at inddrage brugeren.
 */
wBasic.prototype.inputTrimRedundantSpace = 
function inputTrimRedundantSpace(inputTagName){

	var value	= getElementContent(inputTagName);
	var trimmed	= trimRedundantSpace(value);
	
	//Kan der ikke bruges setId i stedet.
	if (trimmed !== value) getElement(inputTagName).value = trimmed;
}


/**
 * trimmed value skal encode htmlentites.
 *
 * @param	String inputTagName 	navnet på den input tag, hvor værdien skal trimmes.
 * @param	String outId			navnet på det element, hvor link til at update med den trimmed værdi, skal skrives til.
 */
wBasic.prototype.inputTrimName = 
function inputTrimName(inputTagName, outId){
	assert(is_string(inputTagName)	, "inputTrimName() - inputTagName skal vaere string: " 	+ inputTagName);
	assert(is_string(outId)			, "inputTrimName() - outId skal vaere string: " 			+ outId);

	//silent auto trim af space
	inputTrimRedundantSpace(inputTagName);
	
	var value  	= getElementContent(inputTagName);
	var trimmed = trimName(value);
	
	if (trimmed !== value) {
		//Kan man ikke bare sætte outId blank, i stedet for at fjerne linket.
		var myTagId = getPageUniqueTempElmId();
		
		var url = '<a href="" id="' + myTagId + '" onclick="pd(event); getElement(\'' + inputTagName + '\').value = \'' + trimmed + '\'; removeElement(\'' + myTagId + '\'); return false;">Anvend: ' + trimmed + '</a>';
		setId(outId, url);
	} else {
		setId(outId, '');	//fjerner evt. gammel besked.
	}
		
}


/**
 *
 */
wBasic.prototype.getAjaxObject = 
function getAjaxObject(){
	if (window.XMLHttpRequest) 		return new XMLHttpRequest();	
	else if (window.ActiveXObject)	return new ActiveXObject("Microsoft.XMLHTTP");				//For IE6 og ældrer
	else 							alert('Det var ikke muligt at oprette et AJAX objekt.');
}

/**
 * utestet
 *
 * Det er svært at teste denne, fordi Test Runner jo griber exceptions. Og hvis denne exit'er som den skal,
 *	så stoppper Test Runner jo. Det kunne måske gøres med setTimeout()
 *
 * Denne skal detect om exception blev kastet af jasmlExit(), og i såfald skal der ikke gives fejlbesked. For
 *	dens ønske at at lave clean exit.
 *
 * return false fra denne betyder: stop alt. (Måske)
 *	true betyder at fejlen skal ignoreres, og execution fortsætte.
 *
 * Der må kunne hentes backtrace her.
 */
wBasic.prototype.jasml_error_handler = 
function jasml_error_handler(msg, url, line){

	if (jasmlHasStoppet) {
		jasmlHasStoppet	= false;	//Så andre kode stykker får håndteret fejl rigtigt.
		return false;				//Execution skal stoppe.
	}

	var data = '';
	data += 'Msg: ' 	+ msg	+ "\r\n";
	data += 'URL: ' 	+ url	+ "\r\n";
	data += 'Line: ' 	+ line	+ "\r\n";

	err(data);

	return false; //false betyder stop alt.
}

/**
 *
 */
wBasic.prototype.flushBuffer = 
function flushBuffer(){
	assert(is_array(bufferLogs)	, "flushBuffer() - bufferLogs skal være array: ", bufferLogs);

	var tmp = bufferLogs;
	bufferLogs = null;		//buffering stoppes.
	for (var key in tmp)	serverLog(key, tmp[key]);
}


/**
 * stdOut bruges til variablen med stdOut content, og derfor kan der ikke også være en function med
 *	det navn. Fucked, men det er vel en nødvendig konsekvens af functions er variabler. Det kunne gøre
 *	strict ved at varibler ikke blot bliver silent overskrevet. Specielt functioner.
 *
 * Denne emitere en stdOut i javascript, hvor der jo ikke findes en sådan.
 *
 * Dette logges til '<div id="stdOut" />', når testcasen er færdig.
 *
 * rawLog kunne måske håndtere denne buffering, som andre logs jo også kan have gavne af.
 */
wBasic.prototype.putStdOut = 
function putStdOut(msg){
	
	if (stdOut === null) {
		//buffer off.
		logToDoc(msg);
		return;
	}
	
	assert(is_string(msg)	, "putStdOut() - input skal være string: " + msg);
	stdOut += msg;
}  


/**
 * Denne sender i uft-8. Det sørger encodeURIComponent() for.
 *
 * både type og msg skal assert string.
 */
//wBasic.prototype.serverLog = 
function serverLog(type, msg){
	if (bufferLogs !== null){
		if (!isset(bufferLogs[type])) 	bufferLogs[type] = '';
		if (bufferLogs[type] !== '') 	bufferLogs[type] += "\r\n";
		bufferLogs[type] += msg;
		return;
	}


	if (serverLogsLeft <= 0) return;
	
	if (--serverLogsLeft <= 0) pop('Der er ikke flere server logs tilbage: ' + serverLogsLeft);
	
	//spørgsmålet er, om der overhovedet skal bruges ajax() her. Der skal da i stedet sørges for
	// at stoppe logging, hvis der kommer alt andet end 'ok' herfra.
	
	if (msg.length < maxUrlLength/3)	ajax('get', serverLogPrefix + type + '&data=' + encodeURIComponent(toString(msg)));
	else								//Dette er nødvendigt, fordi ajax() højest kan tage 1000 chars.
										serverLogHelper(type, msg);
}

/**
 * Denne kan fejle på mange måder, men skal også udgå.
 *
 * Desuden lægges der newline ved hver request, så denne medfører, at der kommer for mange newlines
 *	i logging.
 */
//wBasic.prototype.serverLogHelper = 
function serverLogHelper(type, msg){
	assert(msg.length < 10*maxUrlLength, "serverLogHelper() - Der kan højeste logges: 10*maxUrlLength"	+ msg.length);

	var prefix = serverLogPrefix + type + '&data=';
	
	var tmp 		= toString(msg);
	//kun halvdelen, for der ikke er tage højde for urlencode()
	// Desuden skal der også være plads til prefix.
	var newLength	= maxUrlLength/3
	
	while(tmp.length !== 0){
		newLength = Math.min(newLength, tmp.length);
		
		ajax('get', prefix + encodeURIComponent(getLeft(tmp, newLength)));
		tmp = removeLeft(tmp, newLength);
	}
}

/**
 * Denne er dublet - skal fjernes
 */
wBasic.prototype.getElement = 
function getElement(elmId){
	assert(is_string(elmId)	, "getElement() - input skal være string: "		, elmId);
	var elm = document.getElementById(elmId);
	assert(isset(elm)		, "getElement() - html element ikke fundet: "	, elmId);
	return elm;
}
		

/**
 * FF skal have px som postfix, når style sættes.
 */
wBasic.prototype.moveElement = 
function moveElement(image, x, y){
	image.style.left 	= (getStyleLeft(image) + x) + 'px';
	image.style.top 	= (getStyleTop(image) + y)  + 'px';
}

/**
 *
 */
wBasic.prototype.removeElement = 
function removeElement(elmId){
	var elm = getElement(elmId); 
	elm.parentNode.removeChild(elm);
}

/**
 *	Der skal fejles fatalt, hvis der findes to elementer med samme navn.
 *
 */
wBasic.prototype.elementExists = 
function elementExists(elmId){
	if (document.getElementById(elmId) == null) return false;
	return true;
}

/**
 * Skal udgå
 */
wBasic.prototype.getElementContent = 
function getElementContent(elmId){
	return getContent(elmId);
}

/**
 * utestet
 */
wBasic.prototype.getContent = 
function getContent(elmId){
	var tmp = getElement(elmId);
	if      (tmp.nodeName === 'DIV')		return tmp.innerHTML
	else if (tmp.nodeName === 'SPAN')		return tmp.innerHTML
	else if (tmp.nodeName === 'TEXTAREA')	return tmp.value
	else if (tmp.nodeName === 'INPUT')		return tmp.value
	else errExit('getContent() - nodeName ikke genkendt: ' + tmp.nodeName);
}

/**
 *
 */
wBasic.prototype.tryGetElement = 
function tryGetElement(elmId){
	if (!elementExists(elmId)) return false;
	return document.getElementById(elmId);
}

/**
 * Denne skal blot tjekke om getElementById() giver null, ikke bruge elementExists(), det er for langsomt.
 * Men elementExists() kunne måske være smart til at finde dubletter med samme elmId. Men det er jo stadig
 *	kun interessant i dev og test.
 *
 */
wBasic.prototype.getElement = 
function getElement(elmId){
	assert(elementExists(elmId), 'getElement() - elm ikke fundet: ' + elmId);
	return document.getElementById(elmId);
}

/**
 * omdøb: stdOutToDoc()
 *
 * lav tilsvarende userLogToDoc(), errLogToDoc() o.l.
 *
 * mangel
 *	Denne skal tage højde for den delayed DOM update, som IE her. For at undgå at beskeder går tabt.
 * 
 * <div id="log" ></div>
 * Der kunne her logges til en skjult div, som kan ses vha. FireBug el. Desuden kunne en bookmarklet vise indhold
 * 	af loggen. Dette kunne muligvis også bruges i production.
 * 	En sådan skjult div kunne laves af denne function, det gør den mere stabil.
 *
 */
wBasic.prototype.logToDoc = 
function logToDoc(msg){

	if (!isBrowserTesting) return;
	
	msg = msg.replace(/\n/g, "\n<br>")		//replace er utestet. extract til nl2br()

	appendId('log', msg);
}


/**
 * Denne gør vel ikke noget. Smelt sammen med pop()
 */
wBasic.prototype.rawPop = 
function rawPop(msg){
	if (dontPop) return;
	alert(msg);
}

/**
 * Denne skal testes manuelt, fordi den ikke bliver brugt i TestRunner.php
 *
 * Denne er en erstatning for alert();
 */
wBasic.prototype.pop = 
function pop(msg){
	var tmp;
	popCount++;
	if (popCount > maxPopCount) {
		if (popCount === maxPopCount + 1) {
			if (!confirm('Der har været mange popup beskeder, vil de blokkere flere beskeder.')) 
				popCount=0;
			}
		return;
		}
		
	//length
	
		max_length = 1000;
		
		if (msg.length > max_length)
			msg = "max length exceeded\n\n" + getLeft(msg, max_length);
		
	//pop
		
		rawPop(msg);
}


/**
 * mangel
 *	kan denne ikke bruge getElement()
 *	targetId skal være identifier
 *
 * I chrome og FF kan textContent anvendes i stedet for innerHTML (i hvert fald på span)
 *
 * Id skal eksisterer, ellers sker fatal fejl.
 */
wBasic.prototype.setId = 
function setId(targetId, content){
	assert(is_string(content)	, "setId() - 2. argument skal være string: "	, content);
	
	rawSetId(targetId, content);
	
	// assert(is_string(targetId)	, "setId() - 1. argument skal være string: "	, targetId);

	// var target = document.getElementById(targetId);
	// assert(target != null, "setId() - Id ikke fundet: " + targetId);

	// target.innerHTML = content;
}


/**
 * Denne kan bruges, hvis man ikke ønsker at have den begrænsning at content skal være string.
 */
wBasic.prototype.rawSetId = 
function rawSetId(targetId, content){
	assert(is_string(targetId)	, "rawSetId() - 1. argument skal være string: "	, targetId);

	var target = document.getElementById(targetId);
	assert(target != null, "setId() - Id ikke fundet: " + targetId);

	target.innerHTML = content;
}


/**
 * ubrugt
 *
 * mangel
 *	targetId skal være identifier
 */
wBasic.prototype.appendId = 
function appendId(targetId, content){
	assert(is_string(targetId)	, "appendId() - 1. argument skal være string: "	, targetId);
	assert(is_string(content)	, "appendId() - 2. argument skal være string: "	, content);

	//Kan dette ikke samles i en linie.
	
		var target = getElement(targetId);
		target.innerHTML += content;
}

/**
 *  ubrugt/utestet
 *
 */
wBasic.prototype.getFormObj = 
function getFormObj(formId){
	//bør være identifier
	assert(is_string(formId)	, "getFormObj() - 1. argument skal være identifier: " 	+ formId);
	var form = getElement(formId);
	
	//assert is form
	//assert(is_string(formId)	, "getFormObj() - 1. argument skal være identifier: " 	+ formId);
	
	return form;
}

/**
 * 
 * @Return FromElement Værdien af et child element i formen.
 */
wBasic.prototype.getFormValue = 
function getFormValue(form, elmId){
	//bør være identifier
	assert(isset(form)		, "getFormValue() - form obj skal være angivet: " 	+ form);
	assert(is_string(elmId)	, "getFormValue() - elmId skal være identifier: " 	+ elmId);
	
	var elm = form.elements[elmId];
	assert(isset(elm)		, "getFormValue() - elm blev ikke fundet: " 			+ elmId);
	
	return elm.value;
}

/**
 * ubrugt
 *
 * Denne kan kun bruges hvis et felt har ét validate tjek. For ellers vil det
 *	sidste tjek overrule det første ligegyldig hvad.
 * Dette skyldes at denne reset'er, når tjekket er valid.
 */
wBasic.prototype.validate = 
function validate(b, targetId, text){
	if (b) 
	{
		setId(targetId, '');	//validate text fjernes
		return true;
	} else {
		setId(targetId, text);	//var ikke valid
		return false;
	}
}


/**
 * ubrugt/utestet
 *
 */
wBasic.prototype.appendAjaxToId = 
function appendAjaxToId(method, url, targetId){
	assert(is_string(targetId)	, 'appendAjaxToId() - targetId skal være string: '	, targetId);

	appendId('targetId', getAjax(method, url)); //validerer input
}

/**
 * parametre skal være (targetId, url)
 */
wBasic.prototype.ajaxToId = 
function ajaxToId(method, url, targetId){
	assert(is_string(targetId)	, 'ajaxToId() - targetId skal være string: '		, targetId);

	setId(targetId, getAjax(method, url)); //validerer input
}

/**
 * Denne understøtter kun post. Desuden skal controlleren på serveren sende en ajax string. Atlså "str:$whatever".
 */
wBasic.prototype.ajaxToId_new = 
function ajaxToId_new(url, queryString, targetId){
	assert(is_string(targetId)	, 'ajaxToId() - targetId skal være string: '		, targetId);

	setId(targetId, postAjax(url, queryString)); //validerer input
}


/**
 * omdøb tryAjax() el. tryAC()
 *
 * Kunne muligvis omdøbes til: ajaxCommand() el. lign.
 *
 * Denne kan ikke bruges med callback function, da denne function jo netop skal
 *	tjekke for fejl ved ajax kaldet.
 *
 * Denne skal vel kun bruges til ajax requests, hvor fejl er noget brugeren skal have at vide. Og
 *	hvor det er muligt at controlleren, der kaldes, giver en besked eller en action tilbage. Men
 *	er det ikke hvert eneste request her i browseren, som skal bestemme hvilke af disse ting, som
 *	skal ske. Man kan jo ikke stole på, at serveren giver meningsfulde beskeder tilbage.
 *	Serveren ved jo ikke nødvendigvis vide hvad browseren vil fortæller bruger i
 *	de forskellige fejlsituationer. 
 * Desuden kan serveren være malicious. Men i hvilken situation kunne dette udnyttes?
 */
wBasic.prototype.ajax = 
function ajax(method, url){
	handleAjaxResponse(getAjax(method, url), url);	//getAjax() validerer input
}


/**
 * Denne skal brug tryHandleAjaxError()
 *
 * Denne bliver faktisk brugt, når et request var ok. Så bliver denne kaldt, for at håndtere evt.
 *	fejl. Men så skal det laves, så ajax() får en function til callback on succes i stedet.
 *
 * Denne kan jo blot ligges i ajax()
 */
wBasic.prototype.handleAjaxResponse = 
function handleAjaxResponse(respText, url){

	if (respText === "ok") {
		//ingen default handling.
		}
	else if (respText === "")					pop('Serveren svarede blankt.');
	else if (startsWith(respText, "msg:"))		pop(removeLeft(respText,4));
	else if (startsWith(respText, "action:"))	wEval(removeLeft(respText,7));
	else {
		if (isBrowserTesting)					{
		
			pop(respText);
			
			//Det er meget rart at få url'en med i output
			//pop(url + " \n\n"+ respText);
		
		} else									pop('Ukendt svar fra server: ' + respText);
	}
}


/**
 *
 * Denne logger fejlen til serveren, hvis det ikke er en velkendt ajax fejl. Ellers udføres den handling
 *	så ajax fejlen angiver.
 *
 * Under alle omstændigheder, så returnerer denne funktion ikke.
 */
wBasic.prototype.ajaxErr = 
function ajaxErr(respText, msg){
	assert(arguments.length === 2	, 'ajaxErr() - Der skal være præcis to parametere: ' + arguments.length);
	
	if (!tryHandleAjaxError(respText)) errExit('ajaxErr() - ', msg + "<br>\n svar: " + respText);
	
	//Dette bliver ikke logget til server eller browser, så der et ingen exception handler, som kan
	// gribe toplevel exceptions i javascript.
	throw('Ajax Error');
}

/**
 *
 */
wBasic.prototype.tryHandleAjaxError = 
function tryHandleAjaxError(respText){

	//Her er de velkendte fejl i ajax requests. Disse har nogle standard konsekvenser, som serveren jo
	// er med til at bestemme.

	if (startsWith(respText, "msg:"))			pop(removeLeft(respText,4));
	else if (startsWith(respText, "action:"))	wEval(removeLeft(respText,7));
	else 										return false;
	
	return true;
}


/**
 * Henter en streng fra serveren, og håndterer fejl, hvis de opstår. Denne er altså mere sikker at bruge
 *	end getAjax()
 *
 */
wBasic.prototype.getAjaxString = 
function getAjaxString(method, url){
	var tmp = getAjax(method, url);	//validerer input

	if (startsWith(tmp, "str:")) return removeLeft(tmp, 4);		//4 er 'str:'.length
	
	//fejl
	
	ajaxErr(tmp, 'getAjaxString() - Server svarede forkert.<br>\n url : ' + url);
}

/**
 * method skal blot være get. post varetages af postAjax()
 *
 * mangler
 *  afklar, hvem der urlencoder, og overvej:
 *	foretager altid url encode
 *	Assert url er strict url encoded
 *  url length burde vurderes, når den er blevet url encoded.
 *
 * Denne skal bruge en ajax cookie, som kun bruges ved ajax. Det forhindrer en masse XSS attacks.
 *	fordi ingen statiske links på fremmede sider kan starte et ajax request. Og dette er selvom der
 *	bruges GET til ajax. Ajax cookien skal tilhøre det enkelte page i browseren, men klienten skal
 *	ikke vælge den helt alene.
 * Udover ajax cookien, er der selvfølgelig session cookien, 
 * Der skal også være en action cookie, så hver eneste 'action request' har en session-, ajax- og action-cookie/hash.
 *
 * @param	String	get eller post
 * @param	String	url der skal hentes via. ajax. Denne skal være url encoded.
 */
wBasic.prototype.getAjax = 
function getAjax(method, url){

	//dedikated object
	
		var ajaxObj	= getAjaxObject();
		var result 	= rawAjaxReal(ajaxObj, method, url);	//validerer input

	//kontrolér response.
		
		if (ajaxObj.status !== 200){
		
			userLog(ajaxObj.status);
			userLog(ajaxObj.statusText);
			userLog(url);
			//userLog(parseRequestHeaders(ajaxObj));
		}
		
		
		assert(ajaxObj.status === 200			, "getAjax() - HTTP fejl: " 				, ajaxObj.status, url);
		var result = ajaxObj.responseText;
		assert(is_string(result)				, "getAjax() - result skal være string: " 	, result);
	
	return result;
}

/**
 * utestet
 */
wBasic.prototype.postAjax = 
function postAjax(url, queryString){
	var ajaxObj	= getAjaxObject();
	
	ajaxObj.open('post', url, false);
	ajaxObj.setRequestHeader("Content-type","application/x-www-form-urlencoded");
	ajaxObj.send(queryString);

	return ajaxObj.responseText;
}

/**
 * omdøb til rawAjax()
 *
 * Denne tager det object, som skal bruges til at lave ajax kaldet. Det giver mulighed, for at kunne
 *	hente information fra det efterfølgende.
 *
 * @return String	responseTeksten fra ajax kaldet.
 */
wBasic.prototype.rawAjaxReal = 
function rawAjaxReal(ajaxObj, method, url){
	assert(is_string(method)						, 'rawAjaxReal() - method skal være string: '			, method);
	assert(is_string(url)							, 'rawAjaxReal() - url skal være string: '				, url);
	assert(url !== ''								, 'rawAjaxReal() - url må ikke være blank: '			, url);
	
	//yderligere kontrol
	assert(method === 'get' || method === 'post'	, 'rawAjaxReal() - method skal være get eller post: '	, method);
	assert(url.length < maxUrlLength				, "rawAjaxReal() - Url for stor: "						+ url.length);

	
	ajaxObj.open(method, url, false); 	//false betyder ingen callback function (synckront)
	ajaxObj.send(null);					//FF skal have null argumentet.
	
	return ajaxObj.responseText;
}

/**
 * ubrugt ajax med callback
 */
wBasic.prototype.rawAjax_old = 
function rawAjax_old(method, url, callBackFunction){
	assert(is_string(method), 'rawAjax() - method skal være string: '	, method);
	assert(is_string(url)	, 'rawAjax() - url skal være string: '		, url);
	assert(url !== ''		, 'rawAjax() - url må ikke være blank: '	, url);
	
	//yderligere kontrol
	assert(method === 'get' || method === 'post'	, 'rawAjax() - method skal være get eller post: '	, method);
	assert(url.length < maxUrlLength				, "rawAjax() - Url for stor: "						+ url.length);
	
	//input valideret.

	ajaxObj.open(method, url, true ); 					//true er default.
														//Dette boolean gør ingen forskel i IE 7, for den blokker tilsyneladende aldrig. 
														//FF respekterer dette, og kører ikke callback function ved false.	
														//Dette betyder at der altid skal bruges false.
	ajaxObj.onreadystatechange = callBackFunction;		//Hvis handlen add'es efter open(), så kommer der et event mindre. Nemlig det, når open() kaldes. for både FF og IE.
	ajaxObj.send(null);									//Der skal være null argument her, ellers fejler FF.
}
	


/**
 *
 */
wBasic.prototype.redirect = 
function redirect(targetLink){
	window.location.href = targetLink;
	
	//Her skal der vist stoppes yderligere execution, men exit() kaster exception.
	//	Det er ikke særlig hensigtsmæssigt.
	
	//throw('redirect');
}

/**
 *
 */
wBasic.prototype.removeAllCookies = 
function removeAllCookies(){
	document.cookie = "";		//fjerner ikke alle cookie's mærkeligt.
	dev(document.cookie);
}


/**
 *
 *
 * @param Event		key event
 */
wBasic.prototype.getKeyCodeFromKeyEvent = 
function getKeyCodeFromKeyEvent(e){

	if (window.event) 	return e.keyCode;
	if (e.which)		return e.which;
	
	errExit('getKeyCodeFromKeyEvent() - key code not found.');
}

/**
 * ubrugt
 *
 * @param Event		key event
 */
wBasic.prototype.getCharFromKeyEvent = 
function getCharFromKeyEvent(e){
	return String.fromCharCode(getKeyCodeFromKeyEvent(e));
}

/**
 *
 */
wBasic.prototype.getStyleLeft = 
function getStyleLeft(element){
	var curStrValue = element.style.left;

	//og nu har man en string, med 'px' til sidste, som blot skal tages af.
	curStrValue = curStrValue.substring(0,curStrValue.indexOf('px'));
	
	if (curStrValue === '') return 0; //style.left aldrig har været sat.
	
	return parseInt(curStrValue);	//lav toInt()
}

/**
 *
 */
wBasic.prototype.getStyleTop = 
function getStyleTop(element){
	var curStrValue = element.style.top;

	//og nu har man en string, med 'px' til sidste, som blot skal tages af.
	curStrValue = curStrValue.substring(0,curStrValue.indexOf('px'));
	
	if (curStrValue === '') return 0; //style.top aldrig har været sat.
	
	return parseInt(curStrValue);	//lav toInt()
}


/**
 * Burde dette ikke kun understøtte POST?.
 *
 * @return String	html anchor, hvor onclick indeholder et ajaxLink.
 */
wBasic.prototype.ajaxLink = 
function ajaxLink(text, url){
	return '<a href="" onclick="ajax(\'get\', \'' + url + '\'); return false;">' + text + '</a>';
}


/**
 *
 */
wBasic.prototype.jsLink = 
function jsLink(text, code){
	return '<a href="javascript:pop(\'Fejl i onclick code\')" onclick="' + code + '">' + text + '</a>';
}


/**
 * toggler visibility af et element
 *	Denne er en simpel version af den tilsvarende function i jasml_html.php
 *	omdøb evt.: simpleToggle()
 *
 * @return String	html anchor, hvor onclick indeholder et toggle.
 */
wBasic.prototype.toggleLink = 
function toggleLink(text, element){
	var code = "$('#" + element + "').slideToggle();";
	return jsLink(text, code);
}


/**
 *
 */
wBasic.prototype.pre = 
function pre(str){
	return "<pre>\n" + str + "<pre>\n";
}


/**
 * Denne har kun det formål, at kunne test brugen af eval()
 */
wBasic.prototype.wEval = 
function wEval(code){
	if (md5Testing){
		imp('eval: ', code);
		return;
	}
	eval(code);
}


/**
 * alias: preventDefault
 *
 * Denne tager højde for browser diversitet
 */
wBasic.prototype.pd = 
function pd(event){
	if ( event.preventDefault ) event.preventDefault();		//FF (virker ikke i IE)
	else						event.returnValue = false;	//IE (virker ikke i FF)
}


/**
 *
 */
wBasic.prototype.thread = 
function thread(name, times, time){
	stopThreads = false;
	globalThreads++;
	threadReal(name, times, time);
}

/**
 * 2.param er antal gange
 * 3.param er timeout time
 */
wBasic.prototype.threadReal = 
function threadReal(name, times, time){
	concurrentThreads++;
	out('in : ' + name + " " + times + ' g:' + globalThreads + ' c:' + concurrentThreads);
	
	times--;
	
	if (times <= 0 || stopThreads) {
		globalThreads--;
		concurrentThreads--;
		return;
	}
	
	setTimeout("threadReal('" + name + "', " + times + ", " + time + ")", time);
	
	if (true) busySleep(time);
	
	concurrentThreads--;
}




/**
 *
 */
wBasic.prototype.dumpObject = 
function dumpObject(obj){
	assert(is_object(obj)		, "dumpObject() - input skal være object: " + obj);
	assert(elementExists('log')	, "dumpObject() - Html elementet log skal eksisterer.");
	document.getElementById('log').innerHTML += "<h3>Dump</h3>";
	
	//kun keyes
	//for (key in obj) document.getElementById('log').innerHTML += key + " -> <br />";
	
	//url escaped
	//for (key in obj) document.getElementById('log').innerHTML += key + " -> " + escape(obj[key]) + "<br />";
	
	//keyes og values
	for (key in obj) {
		//Disse giver mærkelig fejl i IE.
			if (key == 'fileCreatedDate') 	continue;
			if (key == 'mimeType') 			continue;
			if (key == 'fileModifiedDate') 	continue;
			if (key == 'fileSize') 			continue;

		document.getElementById('log').innerHTML += key + " -> " + obj[key] + "<br />";
		}
}


/**
 * utestet
 *
 * Hvordan forholder denne sig for iframes.
 */
wBasic.prototype.isTopFrame = 
function isTopFrame(){
	
	tmp1 = top === self;
	tmp2 = top == window;
	
	assert(tmp1 === tmp2	, 'isTopFrame() - invariant 1 fejlede: ' + tmp1 + ', ' + tmp2);

	//Det er ikke sikkert denne side kan få lov til at tilgå 'top.location.href'. Fx hvis det er et andet domaine.
		//tmp4 = top.location.href === window.location.href;
		//assert(tmp1 === tmp4		, 'isTopFrame() - invariant 3 fejlede: ' + tmp1 + ', ' + tmp4);
	
	//Dette gælder selvfølgelig ikke, hvis denne er top, men har children.
		//tmp5 = top.length === 0;							//Dette betyder ingen frame, så denne må være den eneste
		//assert(tmp1 === tmp5		, 'isTopFrame() - invariant 4 fejlede: ' + tmp1 + ', ' + tmp5);
	
	return tmp1;
}

/**
 * utestet
 *
 * Dette gør at hvis denne side ikke er den eneste frame på siden, så ligges denne øverst og alle andre
 *	frames forsvinder.
 */
wBasic.prototype.forceToTopFrame = 
function forceToTopFrame(targetLink){
	if (isTopFrame()) return;			//intet arbejde. Måske denne skulle være try. Men frame er fucked, så måske egentlig bare permissive

	if (targetLink === undefined)	top.location.replace(window.location.href)
	else							top.location.replace(targetLink)
}

/**
 * utestet
 *
 * This returns the value of the selected element in the checkbox group.
 */
wBasic.prototype.checkboxGroupValue = 
function checkboxGroupValue(check_input_fields){
		
	for (var i = 0; i < check_input_fields.length; i++) 
		
		if (check_input_fields[i].checked) 
			
			return check_input_fields[i].value;
	
	return false;
}

/**
 *
 */
wBasic.prototype.wSetEnabled = 
function wSetEnabled(inputId, new_state, blankValue){

	if (blankValue === undefined)
		blankValue = false;

	//create/remove hidden input field
	
		var inputObj 		= getElement(inputId);
		var hiddenId		= inputId + "_hidden"
		
		//remove hidden field
			
			//exists er ikke nødvendig her, fordi
			if (new_state === true)
				//if (elementExists(hiddenId))
					removeElement(hiddenId);
		
		//create hidden field
			
			if (new_state === false){
		
				var hidden_field = inputObj.cloneNode(false);
				
				hidden_field.id 	= hiddenId;
				hidden_field.type 	= "hidden";
				
				if (blankValue)
					hidden_field.value 	= "";
				
				inputObj.parentNode.appendChild(hidden_field);			
			}
			
	//disable
		
		getElement(inputId).disabled 	= !new_state;
	
}

/**
 *
 */
function loadJsFile(filename){
	var scriptElm 	= document.createElement("script");
	scriptElm.type 	= "text/javascript";
	scriptElm.src 	= filename;


	var parent;
	
	//Gør det nogen forskel, om det ligges i body eller i head?
	if (true)	parent = document.body;
	else		parent = document.getElementsByTagName("head")[0];
	
	parent.appendChild(scriptElm);

}

/**
 *
 * Dette virker ikke fordi functions blot bliver i eval() scope, og ikke dette.
 *	Men det ville være smart, for så kunne man vide med sikkerhed
 *	at scriptet er loaded. Modsat med DOM metoden.
 *
 */
// function loadJsFile(filename){
	// var tmp = getAjax('get', filename);
	// eval(tmp);
// }

/**
 * utestet
 *
 * Denne bruges til at logge headers til div()
 */
function getAjaxWhileLoggingHeaders(testfile){
		var ajaxObj	= getAjaxObject();	
		var stdOut 	= rawAjaxReal(ajaxObj, 'get', testfile);

	//log headers
	
		div(parseRequestHeaders(ajaxObj));
		
		return stdOut;
}

/**
 * utestet
 * Der skal fejles fatal, hvis option ikke existerer
 * Er option_value værdien brugeren kan se eller index'et på elementet
 *
 */
function getOptionInSelect(select_id, option_value){
		
		var selectObj = getElement(select_id);

//		for (var key in selectObj.childNodes){
		for (var key in selectObj.options){
			
			child = selectObj.options[key];
			//child = selectObj.childNodes[key];
			
			assert(child !== undefined, "getOptionInSelect() - child is undefined");

			//There are text elements, which have no interest
			//if (child.nodeName != "OPTION") continue;
			
			//this is the one
				if (child.text == option_value)
						return child;
		}

	errExit("getOptionInSelect() - option not found: " + option_value);		
}

/**
 * utestet
 * Der skal fejles fatal, hvis option existerer
 * Er option_value værdien brugeren kan se eller index'et på elementet
 *
 */
function addOptionToSelect(select_id, option_value){

	var option = document.createElement('option');
	
    option.text 	= option_value;
    option.value 	= option_value;

	var selectObj = getElement(select_id);
	
	//quick fix
	//hent isIE() til wBasic
	
	if (navigator.appName === "Microsoft Internet Explorer")
		selectObj.add(option);			//old versions of IE needs this
	else
		selectObj.add(option, null);	//null indicates the option will be placed last
}

/**
 * untested
 *
 */
function removeOptionFromSelect(select_id, option_value){

	var option = getOptionInSelect(select_id, option_value);
	
	//remove the element
	option.parentNode.removeChild(option);
}


/**
 * untested
 *
 */
function sort_num(array){

	array.sort(function (a, b) {	return a - b;});

	return array;
}

/**
 * untested
 *
 * the resulting array is sorted and keys are ignored and 
 *
 */
function unique(array){

	array.sort();

	var last_value 	= null;
	var result 	= new Array();
	
	for (var key in array){

		if (last_value === null) {

			result.push(array[key])		//keys ignored

//			result[key] 	= array[key];	//keys preserved

			last_value 	= array[key];

			continue; //first
		}

		if (array[key] != last_value){

			result.push(array[key])		//keys ignored

//			result[key] 	= array[key];	//keys preserved

			last_value 	= array[key];

		}
	}

	return result;
}

/**
 * untested
 *
 * this removes the specified value from the array. keys are ignored and destroyed.
 */
function array_remove_value(array, value_to_remove){

	var result 	= new Array();
	
	for (var key in array){

		if (array[key] != value_to_remove){

			result.push(array[key])		//keys ignored

		}
	}

	return result;
}

/**
 *
 * brug alert(1) for at få pop up.
 */
function her(){
	putStdOut("her");
}





/**
 *
 * @param Event		eventet, som skal dump'es
 * @param String	navnet på dette event, som forventes
 * @param Element	elementet, hvor eventet skete. Denne er valgfri og bruges til at 
 *						tjek at det korrekt source element kan fremskaffes udfra event'et.
 */
function dumpEvent(event, expectedEvent, expectedSource){
	var source;
	
	if 		(event.target)		source = event.target;
	else if (event.srcElement)	source = event.srcElement;

	if (isset(expectedEvent)) {
		assert((event.type === expectedEvent)	, "dumpEvent() - event type var ikke som forventet: ", event.type, ", ", expectedEvent);
	}
		
	if (expectedSource !== undefined) {
		assert((source === expectedSource)		, "dumpEvent() - source var ikke som forventet: ", source, ", ", expectedSource);
	}

	//IE throw'er resize, når dette udskrives.
	//	Så for ikke at live lock IE.
	if (isIE() && event.type === 'resize')	return; 
		

	//Da udskrivning kan forårsage scroll udskrives dette event ikke.
	if (event.type === 'scroll')		return;
		
	userLog('Event: ' + event.type);
	
	
	//simple events stopper her.
	if (event.type === 'focus')				return;
	if (event.type === 'blur')				return;
	
	if (event.type === 'load')				return;
	if (event.type === 'unload')			return;
	if (event.type === 'beforeunload')		return;
	
	//diverse
		
		if (event.type === 'contextmenu')	return;
		if (event.type === 'resize')		return;
		if (event.type === 'select')		return;
		if (event.type === 'submit')		return;

	//key
	
		if (event.type === 'keydown')		return;
		if (event.type === 'keyup')			return;
		if (event.type === 'keypress')		return;

	//mouse

		if (event.type === 'mousedown')		return;
		if (event.type === 'mousemove')		return;
		if (event.type === 'mouseover')		return;
		if (event.type === 'mouseout')		return;
		if (event.type === 'mouseup')		return;
		if (event.type === 'mousewheel')	return;
	
	//drag
	
		if (event.type === 'dragstart')		return;
		//if (event.type === 'dragend')		return;

		if (event.type === 'drag')			return;
		if (event.type === 'drop')			return;
		
		if (event.type === 'dragenter')		return;
		if (event.type === 'dragover')		return;
		if (event.type === 'dragleave')		return;
		
	if (event.type === 'click')				return;
	if (event.type === 'dblclick')			return;
	
	//
	// yderligere
	//
	
	userLog('tagName : '		+ source.tagName);
	userLog('name : '			+ source.name);
	userLog('id : '				+ source.id);

	userLog('cancelBubble : '	+ event.cancelBubble);

	userLog('<br><b>more:</b>');

		assert(isset(event.cancelBubble)	, "dumpEvent() - event.cancelBubble var ikke sat: "	, event.cancelBubble);
		assert(isset(event.altKey)			, "dumpEvent() - event.altKey var ikke sat: "		, event.altKey);
		assert(isset(event.ctrlKey)			, "dumpEvent() - event.ctrlKey var ikke sat: "		, event.ctrlKey);
		assert(isset(event.shiftKey)		, "dumpEvent() - event.shiftKey var ikke sat: "		, event.shiftKey);

		userLog('altKey : '        + event.altKey);
		userLog('ctrlKey : '       + event.ctrlKey);
		userLog('shiftKey : '      + event.shiftKey);
		userLog('metaKey : '       + event.metaKey);

		//I Firefox har key'es ikke koordinater. Det har det vist i IE.
		//	Button giver ingen mening ved key events.
		if (!(event.type === 'keydown' || event.type === 'keyup' || event.type === 'keypress')) {
			assert(isset(event.clientX)			, "dumpEvent() - event.clientX var ikke sat: "		, event.clientX);
			assert(isset(event.clientY)			, "dumpEvent() - event.clientY var ikke sat: "		, event.clientY);
			assert(isset(event.screenX)			, "dumpEvent() - event.screenX var ikke sat: "		, event.screenX);
			assert(isset(event.screenY)			, "dumpEvent() - event.screenY var ikke sat: "		, event.screenY);
			assert(isset(event.button)			, "dumpEvent() - event.button var ikke sat: "		, event.button);

			userLog('clientX : '       + event.clientX);
			userLog('clientY : '       + event.clientY);
			userLog('screenX : '       + event.screenX);
			userLog('screenY : '       + event.screenY);
			userLog('button : '        + event.button);
		}

		if (event.type === 'dragend') {
			assert(isset(event.dataTransfer)			, "dumpEvent() - event.dataTransfer var ikke sat: "		, event.dataTransfer);
			// var o = event.dataTransfer;
			// pop(o.getData());
			userLog('dataTransfer : '      		+ event.dataTransfer.getData('Text'));
			
			if (!isIE()) {
				assert(isset(event.originalTarget)			, "dumpEvent() - event.originalTarget var ikke sat: "	, event.originalTarget);
				assert(isset(event.explicitOriginalTarget)	, "dumpEvent() - event.explicitOriginalTarget var ikke sat: "	, event.explicitOriginalTarget);
				assert(isset(event.rangeParent)				, "dumpEvent() - event.rangeParent var ikke sat: "		, event.rangeParent);
				assert(isset(event.rangeOffset)				, "dumpEvent() - event.rangeOffset var ikke sat: "		, event.rangeOffset);
				assert(isset(event.view)					, "dumpEvent() - event.view var ikke sat: "		, event.view);

				userLog('');
				userLog('originalTarget : '     	+ event.originalTarget);
				userLog('explicitOriginalTarget : ' + event.explicitOriginalTarget);
				userLog('rangeParent : '      		+ event.rangeParent);
				userLog('rangeOffset : '      		+ event.rangeOffset);
				userLog('view : '      				+ event.view);
			}
		}			

	//flygtig understøttelse
		
		//supportet
		if (!isIE())	assert(isset(event.metaKey)			, "dumpEvent() - event.metaKey var ikke sat i FF: "			, event.metaKey);
		if (!isIE())	assert(isset(event.timeStamp)		, "dumpEvent() - event.timeStamp var ikke sat i FF: "		, event.timeStamp);
		if (!isIE())	assert(isset(event.currentTarget)	, "dumpEvent() - event.currentTarget var ikke sat i FF: "	, event.currentTarget);
		if (!isIE())	assert(isset(event.cancelable)		, "dumpEvent() - event.cancelable var ikke sat i FF: "		, event.cancelable);
		if (!isIE())	assert(isset(event.bubbles)			, "dumpEvent() - event.bubbles var ikke sat i FF: "			, event.bubbles);
		if (!isIE())	assert(isset(event.eventPhase)		, "dumpEvent() - event.eventPhase var ikke sat i FF: "		, event.eventPhase);
		if (!isIE())	assert(isset(event.isTrusted)		, "dumpEvent() - event.isTrusted var ikke sat i FF: "		, event.isTrusted);
		
		//unsupportet
		if (isIE())		assert(!isset(event.metaKey)		, "dumpEvent() - event.metaKey var sat i IE: "				, event.metaKey);
		if (isIE())		assert(!isset(event.timeStamp)		, "dumpEvent() - event.timeStamp var sat i IE: "			, event.timeStamp);
		if (isIE())		assert(!isset(event.currentTarget)	, "dumpEvent() - event.currentTarget var sat i IE: "		, event.currentTarget);
		if (isIE())		assert(!isset(event.cancelable)		, "dumpEvent() - event.cancelable var sat i IE: "			, event.cancelable);
		if (isIE())		assert(!isset(event.bubbles)		, "dumpEvent() - event.bubbles var sat i IE: "				, event.bubbles);
		if (isIE())		assert(!isset(event.eventPhase)		, "dumpEvent() - event.eventPhase var sat i IE: "			, event.eventPhase);


	if (!isIE()) {
		userLog('<br><b>Ikke IE:</b>');
		userLog('timeStamp : '		+ event.timeStamp		);
		userLog('currentTarget : '	+ event.currentTarget	);
		userLog('cancelable : '		+ event.cancelable		);
		userLog('bubbles : '		+ event.bubbles			);
		userLog('eventPhase : '		+ event.eventPhase		);
		userLog('isTrusted : '		+ event.isTrusted		);
		}
	
	
	//dump alt
	//	Dette tager mærkeligt nok meget langt tid i FF.
	//dumpObject(event.dataTransfer);
	dumpObject(event);
}


/**
 *
 */
function isIE8(){
	var id = getBrowserId();
	return id === 'IE' && getBrowserVersion(id) === '8';
}


/**
 *
 */
function isIE7(){
	var id = getBrowserId();
	return id === 'IE' && getBrowserVersion(id) === '7';
}

/**
 *
 */
function isIE6(){
	var id = getBrowserId();
	return id === 'IE' && getBrowserVersion(id) === '6';
}

/**
 *
 */
function isIE(){
	return 'IE' === getBrowserId();
}

/**
 *
 */
function isOpera(){
	return 'OP' === getBrowserId();
}

/**
 *
 */
function isOpera9(){
	var id = getBrowserId();
	return id === 'OP' && getBrowserVersion(id) === '9';
}

/**
 *
 */
function isChrome(){
	return 'CR' === getBrowserId();
}

/**
 *
 */
function isFF(){
	return 'FF' === getBrowserId();
}

/**
 *
 */
function isFF2(){
	var id = getBrowserId();
	return id === 'FF' && getBrowserVersion(id) === '2';
}

/**
 *
 */
function isFF3(){
	var id = getBrowserId();
	return id === 'FF' && getBrowserVersion(id) === '3';
}

/**
 *
 */
function isFF4(){
	var id = getBrowserId();
	return id === 'FF' && getBrowserVersion(id) === '4';
}

/**
 *
 */
function isSafari(){
	return 'SA' === getBrowserId();
}


/**
 *
 * @return int
 */
function getBrowserVersion(id){
	var version = '';
	
	if (id === 'IE'){
		if (document.documentMode) 						return '8';
		if (isset(window.XMLHttpRequest) === true) 		version = '7';
		if (isset(window.XMLHttpRequest) === false) 	version = '6';
	}
	
	if (id === 'FF'){
		//Denne function antager at FF versionen står sidst i userAgent, og 'Firefox/' står lige foran.
		version =  removeLeft(navigator.userAgent, navigator.userAgent.indexOf("Firefox/") + "Firefox/".length);
		version =  getLeft(version, 1);
	}
	
	if (id === 'CR'){
		//Denne function antager at FF versionen står sidst i userAgent, og 'Chrome/' står lige foran. Og har
		// længden 3
		version =  getLeft(removeLeft(navigator.userAgent, navigator.userAgent.indexOf("Chrome/") + "Chrome/".length), 3);
	}
	
	if (id === 'OP'){
		version =  getLeft(navigator.appVersion, navigator.appVersion.indexOf("."));
	}
	
	assert(is_string(version), 'getBrowserVersion() - version skal være string: ', version);
	
	return version;
}


/**
 * Denne fejler fatalt, hvis browseren ikke genkendes. Derfor bør den ikke anvendes i live kode.
 *
 */
function getBrowserId(){
	var id = '';
	
	if (navigator.appName === "Microsoft Internet Explorer")	id += "IE";
	
	if (navigator.appName === "Opera")							id += "OP";
	
	if (navigator.appVersion.indexOf("Chrome") !== -1)			id += "CR";
	
	if (navigator.userAgent.indexOf("Firefox") !== -1)			id += "FF";
	
	if (navigator.vendor === "Apple Computer, Inc.")			id += "SA";
	
	return id;
}

function getTraceBack(e) {

	//ther must be an other way to get an event.

		try {
			dont_exist();
		} catch(event) {
			e = event;
		}

	//Firefox
	if (e.stack) {
		assert(is_string(e.stack), "e.stack should be string: " + e.stack);
	
		var result = new Array();

		var lines = e.stack.split("\n");

		for (var key in lines){
			line = lines[key];

			if (line === "") continue

			var tmp = new Array();

			//line
				var lastColon = line.lastIndexOf(":");

				tmp['line'] = removeLeft(line, lastColon + 1);

			//file

				var func_n_file = getLeft(line, lastColon);

				var firstAlpha = func_n_file.lastIndexOf("@");

				tmp['file'] = removeLeft(func_n_file, firstAlpha + 1);

				tmp['func'] = getLeft(func_n_file, firstAlpha);

			result.push(tmp);
		}

		return result;
	}

}

//
// instantiation
//

wb = new wBasic();

//quick fix, der gør at wBasic kommer i global scope.

for (key in wb) window[key] = wb[key]

//
// error handling
//

onerror = jasml_error_handler;

