/// <reference path="jquery/jquery-vsdoc.js" />

try {
    console
} catch (e) {
    console = {}; console.log = function () { };
}

var ColorSet = [
	'rgb(0,0,0)',
	'rgb(1,0,103)',
	'rgb(213,255,0)',
	'rgb(255,0,86)',
	'rgb(158,0,142)',
	'rgb(14,76,161)',
	'rgb(255,229,2)',
	'rgb(0,95,57)',
	'rgb(0,255,0)',
	'rgb(149,0,58)',
	'rgb(255,147,126)',
	'rgb(164,36,0)',
	'rgb(0,21,68)',
	'rgb(145,208,203)',
	'rgb(98,14,0)',
	'rgb(107,104,130)',
	'rgb(0,0,255)',
	'rgb(0,125,181)',
	'rgb(106,130,108)',
	'rgb(0,174,126)',
	'rgb(194,140,159)',
	'rgb(190,153,112)',
	'rgb(0,143,156)',
	'rgb(95,173,78)',
	'rgb(255,0,0)',
	'rgb(255,0,246)',
	'rgb(255,2,157)',
	'rgb(104,61,59)',
	'rgb(255,116,163)',
	'rgb(150,138,232)',
	'rgb(152,255,82)',
	'rgb(167,87,64)',
	'rgb(1,255,254)',
	'rgb(255,238,232)',
	'rgb(254,137,0)',
	'rgb(189,198,255)',
	'rgb(1,208,255)',
	'rgb(187,136,0)',
	'rgb(117,68,177)',
	'rgb(165,255,210)',
	'rgb(255,166,254)',
	'rgb(119,77,0)',
	'rgb(122,71,130)',
	'rgb(38,52,0)',
	'rgb(0,71,84)',
	'rgb(67,0,44)',
	'rgb(181,0,255)',
	'rgb(255,177,103)',
	'rgb(255,219,102)',
	'rgb(144,251,146)',
	'rgb(126,45,210)',
	'rgb(189,211,147)',
	'rgb(229,111,254)',
	'rgb(222,255,116)',
	'rgb(0,255,120)',
	'rgb(0,155,255)',
	'rgb(0,100,1)',
	'rgb(0,118,255)',
	'rgb(133,169,0)',
	'rgb(0,185,23)',
	'rgb(120,130,49)',
	'rgb(0,255,198)',
	'rgb(255,110,65)',
	'rgb(232,94,190)'];

function flashStatus(html, timeout) {
	// Create dynamic div, if not exit
	var popup = $('#FlashPopUp');
	if (!popup.length) {
		$("<div class='FlashPopUp' id='FlashPopUp'></div>").appendTo(document.body);
		popup = $('#FlashPopUp');
		if (!popup.length) {
			console.log("Unable to create FlashPopUp...")
			return;
		}
	}
	var currentTime = new Date();
	var newHTML = sprintf("<font size='-2'><i>%02d:%02d:%02d</i><br></font><p>%s</p>",
		currentTime.getHours(), currentTime.getMinutes(), currentTime.getSeconds(), html);
	popup.html(newHTML);
	popup.fadeIn("slow");
	if ((typeof timeout == "undefined") || (timeout < 100))
		setTimeout(function(){$('#FlashPopUp').fadeOut("slow")},3000);
	else
		setTimeout(function(){$('#FlashPopUp').fadeOut("slow")},timeout);
}

function showCardPopUp(name, x, y, html) {
	// Create dynamic div, if not exit
	// console.log("Showing CardPopUp named " + name + " at (" + x + "," + y + ")");
	var popup = $('#CardPopUp_' + name);
	if (!popup.length) {
		//console.log("Creating CardPopUp " + name)
		$("<div class='CardPopUp' id='CardPopUp_" + name + "'></div>").appendTo(document.body);
		popup = $('#CardPopUp_' + name);
		if (!popup.length) {
			console.log("Unable to create CardPopUp...")
			return;
		}
	}
	popup.html("<div id='CardPopUpInner_" + name + "' style='display: inline-block;'>" + html + "</div>");
	popup.css('left', (x+30) + 'px');
	popup.css('top', (y+30) + 'px');
	popup.fadeIn(50);
	if (!resizeCardPopUp(name, x, y, false)) {
		console.log("Popup not ready, retrying in 100ms...");
		setTimeout(function(){resizeCardPopUp(name, x, y, true)},100);
	}
}

function updateCardPopUp(name, html) {
	// Create dynamic div, if not exit
	var popup = $('#CardPopUp_' + name);
	if (!popup.length)
		return false;
	popup.html("<div id='CardPopUpInner_" + name + "' style='display: inline-block;'>" + html + "</div>");
}

function resizeCardPopUp(name, x, y, auto) {
	var popup = $('#CardPopUp_' + name);
	if (!popup.length) {
		console.log("Couldn't find popup");
		return false;
	}
	var inner = $('#CardPopUpInner_' + name);
	if (!inner.length) {
		console.log("Couldn't find inner popup");
		return false;
	}
	var width = inner.width();
	var height = inner.height();
	if (!width || !height) {
		if (auto) {
			console.log("Popup not ready, retrying in 100ms...");
			setTimeout(function(){resizeCardPopUp(name, x, y, true)},100);
		}
		return false;
	}
	//console.log("Popup ready...");

	var winWidth = $(window).width();
	var winHeight = $(window).height();
	popup.css('width', width + 30 + 'px');
	popup.css('height', height + 30 + 'px');
	//popup.css('left', x);
	//popup.css('top', y);
	var xx = x;
	if (x + width + 50 > winWidth) {
		//console.log("x: " + x + " width: " + width + " winWidth: " + winWidth);
		xx = x - width - 50;
		if (xx < 0)
			xx = 0;
		popup.css('left', xx + 'px');
	}
	var yy = y;
	if (y + height + 50 > winHeight) {
		// console.log("y: " + y + " height: " + height + " winHeight: " + winHeight);
		yy = y - height - 50;
		if (yy < 0) {
			yy = 0;
			// check if mouse is on top of popup
			//if ( (xx < x) && (x < xx+width) )
			//	popup.css('left', '-100px');
		}
		popup.css('top', yy + 'px');
		// console.log("new y: " + (y - height - 50));
	}
	// console.log("Resizing CardPopUp " + name + " to (" + x + "," + y + ") (" + width + "," + height + ")");

	return true;
}

function hideCardPopUp(name) {
	//console.log("Hiding CardPopUp named " + name);
	var popup = $('#CardPopUp_' + name);
	if (!popup.length)
		return
	popup.fadeOut(20);
}





function openNewWindow(name, x, y, html) {
	var w = window.open("", name, 'width=800,height=1000,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=20,top=20');
	var html = "<div id='WindowInner_" + name + "' style='display: inline-block;'>" + html + "</div>";
	// console.log(html);
	$(w.document.body).html(html);
	setTimeout(function(){resizeWindow(w, name, x, y, true)},100);
}

function resizeWindow(w, name, x, y, auto) {
	var inner = $('#WindowInner_' + name, w.document);
	if (!inner.length) {
		console.log("Couldn't find inner window");
		return false;
	}
	var width = inner.width();
	var height = inner.height();
	if (!width || !height) {
		if (auto) {
			console.log("Window not ready, retrying in 100ms...");
			setTimeout(function(){resizeWindow(name, x, y, true)},100);
		}
		return false;
	}
	//console.log("Resizing window to " + width + "," + height);
	w.resizeTo(width+50, height+120);
	return true;
}


function hideWindow(name) {
	var w = window.open("", name);
	if (!w.length)
		return
	w.close();
}

function Sizify(bytes) {
    var thresh = 1024;
    if(bytes < thresh) return bytes + 'B';
    var units = ['kB','MB','GB','TB','PB','EB','ZB','YB'];
    var u = -1;
    do {
        bytes /= thresh;
        ++u;
    } while(bytes >= thresh);
    return bytes.toFixed(2)+units[u];
};

function SizifyDec(bytes, dec) {
    var thresh = 1024;
    if(bytes < thresh)
		return sprintf("%."+dec+"fB", bytes);
    var units = ['kB','MB','GB','TB','PB','EB','ZB','YB'];
    var u = -1;
    do {
        bytes /= thresh;
        ++u;
    } while(bytes >= thresh);
	return sprintf("%."+dec+"f%s", bytes, units[u]);
};

function PsyTime(time) {
	this.raw = time;
	this.msec = parseInt(time.substring(0, time.length-3)) - 62167305600000;
	this.usec = parseInt(time.substring(time.length-3));
	this.date = new Date(this.msec);
	this.toString = function() {
		if (this.raw == 0)
			return "N/A";
		return sprintf("%04d/%02d/%02d %02d:%02d:%02d.%03d%03d", this.date.getFullYear(), this.date.getMonth(), this.date.getDay(),
			this.date.getHours(), this.date.getMinutes(), this.date.getSeconds(), this.date.getMilliseconds(), this.usec);
	};
	this.toDateString = function() {
		if (this.raw == 0)
			return "N/A";
		return sprintf("%04d/%02d/%02d", this.date.getFullYear(), this.date.getMonth(), this.date.getDay());
	};
	this.toTimeString = function() {
		if (this.raw == 0)
			return "N/A";
		return sprintf("%02d:%02d:%02d", this.date.getHours(), this.date.getMinutes(),
			this.date.getSeconds());
	};
	this.toTimeStringMS = function () {
		if (this.raw == 0)
			return "N/A";
		return sprintf("%02d:%02d:%02d.%03d", this.date.getHours(), this.date.getMinutes(),
			this.date.getSeconds(), this.date.getMilliseconds());
	};
	this.toFullTimeString = function () {
		if (this.raw == 0)
			return "N/A";
		return sprintf("%02d:%02d:%02d.%03d%03d", this.date.getHours(), this.date.getMinutes(),
			this.date.getSeconds(), this.date.getMilliseconds(), this.usec);
	};
	this.addUS = function(t) {
		if (this.raw == 0)
			return;
		this.msec = parseInt(time.substring(0, time.length-3)) - 62167305600000 + Math.round(t/1000);
		this.usec = parseInt(time.substring(time.length-3)) + t%1000;
		this.date = new Date(this.msec);
	};
	this.addMS = function(t) {
		if (this.raw == 0)
			return;
		this.msec = parseInt(time.substring(0, time.length-3)) - 62167305600000 + t;
		this.date = new Date(this.msec);
	};
	this.isValid = function() {
		return (this.raw != 0);
	};
}

function PsyTimeDiffUS(t2, t1) {
	if (!t1.isValid() || !t2.isValid())
		return 0;
	var msec = t2.date.getTime() - t1.date.getTime();
	var usec = (msec * 1000) + (t2.usec - t1.usec);
	return usec;
}

function PrintPsyTimeDiff(t2, t1) {
	if (!t1.isValid() || !t2.isValid())
		return "N/A";
	return PrintTimeDiff(PsyTimeDiffUS(t2, t1));
}

function PrintPsyTimeDiffFull(t2, t1) {
	if (!t1.isValid() || !t2.isValid())
		return "N/A";
	return PrintTimeDiffFull(PsyTimeDiffUS(t2, t1));
}

function PrintPsyTimeDiffApprox(t2, t1) {
	if (!t1.isValid() || !t2.isValid())
		return "N/A";
	return PrintTimeDiffApprox(PsyTimeDiffUS(t2, t1));
}

function PrintTimeDiff(t) {
	var sign = "";
	if (t < 0)
		sign = "-";
	if (t < 1000)
		return sign + t + "us";
	else if (t < 1000000)
		return sign + (t/1000.0) + "ms";
	else if (t < 60000000)
		return sign + (t/1000000.0).toFixed(3) + "sec";
	else {
		var usec = t%1000000;
		var msec = Math.round(t/1000);
		var hh = Math.floor(msec / 1000 / 60 / 60);
		msec -= hh * 1000 * 60 * 60;
		var mm = Math.floor(msec / 1000 / 60);
		msec -= mm * 1000 * 60;
		var ss = Math.round(msec / 1000);
		return sprintf("%s%02d:%02d:%02d", sign, hh, mm, ss);
	}
}

function PrintTimeDiffFull(t) {
	var sign = "";
	if (t < 0)
		sign = "-";
	if (t < 1000)
		return sign + t + "us";
	else if (t < 1000000)
		return sign + (t/1000.0) + "ms";
	else if (t < 60000000)
		return sign + (t/1000000.0).toFixed(3) + "sec";
	else {
		var usec = t%1000000;
		var msec = Math.round(t/1000);
		var hh = Math.floor(msec / 1000 / 60 / 60);
		msec -= hh * 1000 * 60 * 60;
		var mm = Math.floor(msec / 1000 / 60);
		msec -= mm * 1000 * 60;
		var ss = Math.floor(msec / 1000);
		// msec -= ss * 1000;
		return sprintf("%s%02d:%02d:%02d.%06d", sign, hh, mm, ss, usec);
	}
}

function PrintTimeDiffApprox(t) {
	var sign = "";
	if (t < 0)
		sign = "-";
	if (t < 1000) // 1ms
		return sign + t + "us";
	else if (t < 1000000) // 1sec
		return sign + (t/1000.0) + "ms";
	else if (t < 180000000) { // 3min
		var m = Math.floor(t/60000000.0);
		var s = Math.round((t - (m*60000000.0))/1000000.0)
		if (m < 1)
			return "~" + sign + Math.round(t/1000000.0) + "sec";
		else
			return "~" + sign + m + ":" + s + "min";
	}
	else {
		var msec = Math.round(t/1000);
		var hh = Math.floor(msec / 1000 / 60 / 60);
		if (hh > 1)
			return "~" + sign + hh + "hours";
		msec -= hh * 1000 * 60 * 60;
		var mm = Math.floor(msec / 1000 / 60);
		if (hh == 1)
			return sprintf("~%s1:%02dhour", sign, mm);
		else
			return "~" + sign + mm + "min";
	}
}

function loadURL(url) {
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}

function loadURLJSON(url) {
	var data = $.ajax({
		type: "GET",
		url: url,
		cache: false,
		async: false
	}).responseText;
	if ((typeof data == "undefined") || !data.length)
		return undefined;
	return $.parseJSON(data);
}

function loadTemplate(template, $target, templateName, compName, compType) {
    $fullXML = $(latestXML);
    console.log("Loading template for " + compName);
    var html = loadURL(template);
    html = html.replace(/Template/g, compName + "_" + templateName);
    $target.html(html);

    var loadFunc = "load" + compName + "_" + templateName;
    var updateFunc = "update" + compName + "_" + templateName;

    $compxml = $fullXML.find(compType.toLowerCase() + "[name='" + compName + "']");
    try {
        eval(loadFunc + "($target, compName, compType, $compxml, $fullXML)");
        eval(updateFunc + "($target, compName, compType, $compxml, $fullXML)");
        updateFunctionInfo[updateFunc] = { func: updateFunc, comp: compName, type: compType, target: $target.attr('id') };
        if (updateFunctions.indexOf(updateFunc) < 0)
            updateFunctions.push(updateFunc);
    } catch (err) {
    }
}

function SetActivityImage(img, val) {
	var imgFile = "images/active_0.gif";
	if (val > 0.95)
		imgFile = "images/active_100.gif";
	else if (val > 0.85)
		imgFile = "images/active_90.gif";
	else if (val > 0.75)
		imgFile = "images/active_80.gif";
	else if (val > 0.65)
		imgFile = "images/active_70.gif";
	else if (val > 0.55)
		imgFile = "images/active_60.gif";
	else if (val > 0.45)
		imgFile = "images/active_50.gif";
	else if (val > 0.35)
		imgFile = "images/active_40.gif";
	else if (val > 0.25)
		imgFile = "images/active_30.gif";
	else if (val > 0.15)
		imgFile = "images/active_20.gif";
	else if (val > 0.05)
		imgFile = "images/active_10.gif";
	img.attr("src", imgFile);
}

$.fn.pressEnter = function(fn) {  
	return this.each(function() {  
		$(this).bind('enterPress', fn);
		$(this).keyup(function(e){
			if(e.keyCode == 13) {
				$(this).trigger("enterPress");
			}
		})
	});  
 }; 









/**
 * Copyright (c) 2010 Jakob Westhoff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
(function( window ) {
    var sprintf = function( format ) {
        // Check for format definition
        if ( typeof format != 'string' ) {
            throw "sprintf: The first arguments need to be a valid format string.";
        }
        
        /**
         * Define the regex to match a formating string
         * The regex consists of the following parts:
         * percent sign to indicate the start
         * (optional) sign specifier
         * (optional) padding specifier
         * (optional) alignment specifier
         * (optional) width specifier
         * (optional) precision specifier
         * type specifier:
         *  % - literal percent sign
         *  b - binary number
         *  c - ASCII character represented by the given value
         *  d - signed decimal number
         *  f - floating point value
         *  o - octal number
         *  s - string
         *  x - hexadecimal number (lowercase characters)
         *  X - hexadecimal number (uppercase characters)
         */
        var r = new RegExp( /%(\+)?([0 ]|'(.))?(-)?([0-9]+)?(\.([0-9]+))?([%bcdfosxX])/g );

        /**
         * Each format string is splitted into the following parts:
         * 0: Full format string
         * 1: sign specifier (+)
         * 2: padding specifier (0/<space>/'<any char>)
         * 3: if the padding character starts with a ' this will be the real 
         *    padding character
         * 4: alignment specifier
         * 5: width specifier
         * 6: precision specifier including the dot
         * 7: precision specifier without the dot
         * 8: type specifier
         */
        var parts      = [];
        var paramIndex = 1;
        while ( part = r.exec( format ) ) {
            // Check if an input value has been provided, for the current
            // format string (no argument needed for %%)
            if ( ( paramIndex >= arguments.length ) && ( part[8] != '%' ) ) {
                throw "sprintf: At least one argument was missing.";
            }

            parts[parts.length] = {
                /* beginning of the part in the string */
                begin: part.index,
                /* end of the part in the string */
                end: part.index + part[0].length,
                /* force sign */
                sign: ( part[1] == '+' ),
                /* is the given data negative */
                negative: ( parseFloat( arguments[paramIndex] ) < 0 ) ? true : false,
                /* padding character (default: <space>) */
                padding: ( part[2] == undefined )
                         ? ( ' ' ) /* default */
                         : ( ( part[2].substring( 0, 1 ) == "'" ) 
                             ? ( part[3] ) /* use special char */
                             : ( part[2] ) /* use normal <space> or zero */
                           ),
                /* should the output be aligned left?*/
                alignLeft: ( part[4] == '-' ),
                /* width specifier (number or false) */
                width: ( part[5] != undefined ) ? part[5] : false,
                /* precision specifier (number or false) */
                precision: ( part[7] != undefined ) ? part[7] : false,
                /* type specifier */
                type: part[8],
                /* the given data associated with this part converted to a string */
                data: ( part[8] != '%' ) ? String ( arguments[paramIndex++] ) : false
            };
        }

        var newString = "";
        var start = 0;
        // Generate our new formated string
        for( var i=0; i<parts.length; ++i ) {
            // Add first unformated string part
            newString += format.substring( start, parts[i].begin );
            
            // Mark the new string start
            start = parts[i].end;

            // Create the appropriate preformat substitution
            // This substitution is only the correct type conversion. All the
            // different options and flags haven't been applied to it at this
            // point
            var preSubstitution = "";
            switch ( parts[i].type ) {
                case '%':
                    preSubstitution = "%";
                break;
                case 'b':
                    preSubstitution = Math.abs( parseInt( parts[i].data ) ).toString( 2 );
                break;
                case 'c':
                    preSubstitution = String.fromCharCode( Math.abs( parseInt( parts[i].data ) ) );
                break;
                case 'd':
                    preSubstitution = String( Math.abs( parseInt( parts[i].data ) ) );
                break;
                case 'f':
                    preSubstitution = ( parts[i].precision === false )
                                      ? ( String( ( Math.abs( parseFloat( parts[i].data ) ) ) ) )
                                      : ( Math.abs( parseFloat( parts[i].data ) ).toFixed( parts[i].precision ) );
                break;
                case 'o':
                    preSubstitution = Math.abs( parseInt( parts[i].data ) ).toString( 8 );
                break;
                case 's':
                    preSubstitution = parts[i].data.substring( 0, parts[i].precision ? parts[i].precision : parts[i].data.length ); /* Cut if precision is defined */
                break;
                case 'x':
                    preSubstitution = Math.abs( parseInt( parts[i].data ) ).toString( 16 ).toLowerCase();
                break;
                case 'X':
                    preSubstitution = Math.abs( parseInt( parts[i].data ) ).toString( 16 ).toUpperCase();
                break;
                default:
                    throw 'sprintf: Unknown type "' + parts[i].type + '" detected. This should never happen. Maybe the regex is wrong.';
            }

            // The % character is a special type and does not need further processing
            if ( parts[i].type ==  "%" ) {
                newString += preSubstitution;
                continue;
            }

            // Modify the preSubstitution by taking sign, padding and width
            // into account

            // Pad the string based on the given width
            if ( parts[i].width != false ) {
                // Padding needed?
                if ( parts[i].width > preSubstitution.length ) 
                {
                    var origLength = preSubstitution.length;
                    for( var j = 0; j < parts[i].width - origLength; ++j ) 
                    {
                        preSubstitution = ( parts[i].alignLeft == true ) 
                                          ? ( preSubstitution + parts[i].padding )
                                          : ( parts[i].padding + preSubstitution );
                    }
                }
            }

            // Add a sign symbol if neccessary or enforced, but only if we are
            // not handling a string
            if ( parts[i].type == 'b' 
              || parts[i].type == 'd' 
              || parts[i].type == 'o' 
              || parts[i].type == 'f' 
              || parts[i].type == 'x' 
              || parts[i].type == 'X' ) {
                if ( parts[i].negative == true ) {
                    preSubstitution = "-" + preSubstitution;
                }
                else if ( parts[i].sign == true ) {
                    preSubstitution = "+" + preSubstitution;
                }
            }

            // Add the substitution to the new string
            newString += preSubstitution;
        }

        // Add the last part of the given format string, which may still be there
        newString += format.substring( start, format.length );

        return newString;
    };

    // Register the new sprintf function as a global function, as well as a
    // method to the String object.
    window.sprintf = sprintf;
    String.prototype.printf = function() {
        var newArguments = Array.prototype.slice.call( arguments );
        newArguments.unshift( String( this ) );
        return sprintf.apply( undefined, newArguments );
    }
})( window );


function Generator() { };
Generator.prototype.rand = Math.floor(Math.random() * 26) + Date.now();
Generator.prototype.getId = function () {
	return this.rand++;
};
var idGen = new Generator();

function postData(opType, uri, formData, mimeTypeData, replyFunc, id) {
	var start_time = new Date();
	postInProgress = true;

	if (mimeTypeData == "multipart/form-data")
		mimeTypeData = false;

	$.ajax({
		url: uri,
		data: formData,
		processData: false,
		type: opType,
		cache: false,
		async: true,

		// This will override the content type header,
		// regardless of whether content is actually sent.
		// Defaults to 'application/x-www-form-urlencoded'
		contentType: mimeTypeData,

		//Before 1.5.1 you had to do this:
		beforeSend: function (request) {
			if (request && request.overrideMimeType) {
				request.overrideMimeType(mimeTypeData);
				//request.setRequestHeader("Authorization", currentAuthentication);
			}
		},
		// Now you should be able to do this:
		mimeType: mimeTypeData,    //Property added in 1.5.1
		timeout: 30000,

		success: function (data) {
			var now = new Date();
			if (typeof data == "undefined") {
				console.log("Got no reply from: " + uri);
			}
			else if (typeof data == "object") {
				//console.log("Reply(" + uri + "):\n" + JSON.stringify(data));
				//var response = JSON && JSON.parse(data) || $.parseJSON(data);
				var response = data;
				replyFunc(id, true, opType, uri, formData, response.text, data, now - start_time);
			}
			else {
				console.log("Reply(" + uri + "):\n" + data);
				replyFunc(id, false, opType, uri, formData, "No JSON in reply", data, now - start_time);
			}
			postInProgress = false;
		},
		error: function (x, errorText, errorThrown) {
			console.log("Error(" + uri + "): " + errorText + "," + errorThrown);
			replyFunc(id, false, opType, uri, formData, response.text, errorText, now - start_time);
			postInProgress = false;
		}
	});
}


function postDataBinary(opType, uri, formData, mimeTypeData, replyFunc, id) {
	var start_time = new Date();
	postInProgress = true;

	if (mimeTypeData == "multipart/form-data")
		mimeTypeData = false;

	//formData.append('AUTHENTICATION', currentAuthentication);
	console.log("PostData cur auth: " + currentAuthentication);
	$.ajax({
		url: '/fa6_api/' + uri,
		data: formData,
		processData: false,
		type: opType,
		cache: false,
		async: true,

		// This will override the content type header,
		// regardless of whether content is actually sent.
		// Defaults to 'application/x-www-form-urlencoded'
		contentType: mimeTypeData,

		//Before 1.5.1 you had to do this:
		beforeSend: function (request) {
			if (request && request.overrideMimeType) {
				//request.overrideMimeType(mimeTypeData);
				request.setRequestHeader("Authorization", currentAuthentication);
			}
		},
		// Now you should be able to do this:
		//mimeType: mimeTypeData,    //Property added in 1.5.1
		timeout: 30000,

		success: function (data) {
			var now = new Date();
			if (typeof data == "undefined") {
				console.log("Got no reply from: " + uri);
				replyFunc(id, false, opType, uri, formData, "No DATA in reply", data, now - start_time);
			}
			else if (typeof data == "object") {
				console.log("Reply(" + uri + "):\n" + data);
				replyFunc(id, false, opType, uri, formData, "Got unexpected object back", data, now - start_time);
			}
			else {
				//console.log("Reply(" + uri + "):\n" + data);
				replyFunc(id, true, opType, uri, formData, "Got binary data back", data, now - start_time);
			}
			postInProgress = false;
		},
		error: function (x, errorText, errorThrown) {
			console.log("Error(" + uri + "): " + errorText + "," + errorThrown);
			replyFunc(id, false, opType, uri, formData, "Error", errorText, now - start_time);
			postInProgress = false;
		}
	});
}


function xmlToString(xmlData) { // this functions waits jQuery XML

	var xmlString = undefined;

	if (window.ActiveXObject) {
		xmlString = xmlData[0].xml;
	}

	if (xmlString === undefined) {
		var oSerializer = new XMLSerializer();
		xmlString = oSerializer.serializeToString(xmlData[0]);
	}

	return xmlString;
}

function syntaxHighlight(json) {
	json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
	return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
		var cls = 'number';
		if (/^"/.test(match)) {
			if (/:$/.test(match)) {
				cls = 'key';
			} else {
				cls = 'string';
			}
		} else if (/true|false/.test(match)) {
			cls = 'boolean';
		} else if (/null/.test(match)) {
			cls = 'null';
		}
		return '<span class="' + cls + '">' + match + '</span>';
	});
}

function getTypeGroup(type) {
	var str = "" + type;
	var p = str.indexOf('.', 0);
	if (p < 0) return type;
	p = str.indexOf('.', p + 1);
	if (p < 0) return type;
	return str.substr(0, p);
}

function getNColors(num_colors) {
	var colors = new Array();
	for (i = 0; i < 360; i += 360 / num_colors) {
		var h = i;
		var s = 90 + Math.random() * 10;
		var l = 50 + Math.random() * 10;
		colors.push('hsl(' + h + ',' + s + '%,' + l + '%)');
	}
	return colors;
}

function ArrayContains(array, obj) {
	var i = array.length;
	while (i--) {
		if (array[i] === obj) {
			return true;
		}
	}
	return false;
}

function wordWrap(str, maxWidth) {
	var newLineStr = "\n"; done = false; res = '';
	do {
		found = false;
		// Inserts new line at first whitespace of the line
		for (i = maxWidth - 1; i >= 0; i--) {
			if (testWhite(str.charAt(i))) {
				res = res + [str.slice(0, i), newLineStr].join('');
				str = str.slice(i + 1);
				found = true;
				break;
			}
		}
		// Inserts new line at maxWidth position, the word is too long to wrap
		if (!found) {
			res += [str.slice(0, maxWidth), newLineStr].join('');
			str = str.slice(maxWidth);
		}

		if (str.length < maxWidth)
			done = true;
	} while (!done);

	return res + str;
}

function testWhite(x) {
	var white = new RegExp(/^\s$/);
	return white.test(x.charAt(0));
}

