// Basic JavaScript functions
// --------------------------


// -- Helper functions ----------------------------------------------------------------------------------

// Quick IE detection; works IE > 4.0
var isIE = false;
/*@cc_on
  isIE = true;
@*/

// Finds the index of an item in an array, returns -1 on failure
// extends the Array prototype (JavaScript feature since 1.5, not implemented in IE up to 7)
function arrayIndexOf(val)
{
	if (!val || !this) return -1;
	var i;
	for (i = 0; i < this.length; i++) if (this[i] == val) return i;
	return -1;
}
Array.prototype.indexOf = arrayIndexOf;

// Returns true if the element given is an Array
function isArray(o) { return toString.call(o) === "[object Array]"; }

// Appends items from array 2 to the end of array 1; a new copy is created
function arrayCombine(a1, a2) {
  for (var result = [], i = 0, len = a1.length; i < len; i++) result[i] = a1[i];
	for (j = 0, len = a2.length; j < len; j++) result[i + j] = a2[j];
	return result;
}

// Merges two objects and returns the merged object - Note: no deep copy
function merge(o1, o2)
{
  var target = {}
  if (typeof o1 == 'object') for (var i in o1) target[i] = o1[i];
  if (typeof o2 == 'object') for (var i in o2) if (o2[i] !== undefined) target[i] = o2[i];
  return target;
}


// -- String functions ----------------------------------------------------------------------------------

// Pads a string with a char (default "0") if necessary (z specifies the number of digits required, defaults to 2)
function padString(n, z, pad)
{
	z = z || 2, n = n.toString(), pad = pad || "0", res = [];
	for (var i = n.length; i < z; i ++) res[res.length] = pad;
	return res.join("") + n;
}
function leadingZero(n) { return padString(n); }

// Strips leading zeroes
function cleanLeadingZeroes(s)
{
	return s.replace(/^0/, "");
}

// Filters the value of an object to contain only digits.
// Sets the new value only if it is different from the old value. This prevents
// the selection from disappearing in unconvenient circumstances
function numericFilter(o, keepZero)
{
  var newValue = cleanLeadingZeroes(o.value.replace(/\D/g, ""));
  if (newValue == "" && keepZero) newValue = "0";
  if (newValue != o.value) o.value = newValue;
}

// Trims white-space from the beginning and end of a string
function trim(s) { return s.replace(/^\s+|\s+$/g,''); }

// Truncates a date, i. e. omits the time portion
function trunc(d, h)
{
	d.setHours(!h ? 0 : h),	d.setMinutes(0), d.setSeconds(0),	d.setMilliseconds(0);
	return d;
}

// Reads YYYYMMDD date string and returns it as a date object
function readDate(s, def)
{
	var parts = /\s*([0-9]{4})([0-9]{2})([0-9]{2})\s*/.exec(s);
	return !parts ? def : new Date(parts[1], parts[2] - 1, parts[3]);
}

// Formats a YYYYMMDD date string
function formatDate(d)
{
	return d.getFullYear() + padString(d.getMonth() + 1, null) + padString(d.getDate());
}

// Formats a floating point number
// The function can take a number or a string and removes optionally a part of the string.
// The number will be formatted with precis digits after the decimal point and will have
// thousand separators inserted
function formatFloat(num, precis, remove)
{
  num = num.toString();                                                     // Make sure it's a string
  if (!!remove && remove != "")
    num.replace(new RegExp(remove, "g"), "");                               // replace substring remove from number if there
  if (isNaN(num)) return num;                                               // return unchanged if not a number

  var number, frac = "", round = "", neg = false;
	if (!!precis && precis > 0) {
	  number = new Number(num).toFixed(precis).toString().split('.');
	  if (number.length > 1) frac = number[1];
	} else number = num.split('.');

  if (number[0].length == 0) number[0] = "0";                               // Start with 0, if the initial string was empty
  if (number[0].charAt(0) == '-') { neg = true; number[0] = number[0].substring(1); }      // Record sign and remove it
  var startC = 3 - (number[0].length % 3), i;                               // Put in thousand separators
  for (i = 0; i < number[0].length; i++, startC++) {
    if (startC > 2) { startC = 0; if (i) round += ','; }
    round += number[0].charAt(i);
  }

  return (neg ? '-' : '') + round + (precis > 0 ? ('.' + frac) : "");       // Put all the parts together again
}


// -- Object detection ----------------------------------------------------------------------------------

// Find an object with its ID
function findObject(oid, doc)
{
	var doc = (doc || window.document);
	return (doc.getElementById ? doc.getElementById(oid) : null);
}

// Determines if o2 is a parent of o
function isParentOf(o, o2)
{
  while (o = o.parentNode) if (o == o2) return true;
  return false;
}

// Gets all elements of a certain type; tagname = "" -> all elements
// Elements under a certain object can be found by specifying an object
function getAllElements(tagName, o)
{
	o = (o || window.document);
	if (o.getElementsByTagName) return o.getElementsByTagName(tagName == "" ? "*" : tagName);
  return null;
}

// Get all elements of a specific class
function getElementsByClass(o, className)
{
	o = (o || window.document);
	if (o.getElementsByClassName) return o.getElementsByClassName(className);
	var result = [], list = o.getElementsByTagName("*"), listLen = list.length, srch = new RegExp("(^|\\s)" + className + "(\\s|$)");
	for (i = 0, j = 0; i < listLen; i++) if (srch.test(list[i].className)) result[j++] = list[i];
	return result;
}


// -- Object visibility -----------------------------------------------------------------

// Show or hide an object
function showObject(o, doShow)
{
	if (o && o.style) o.style.display = (doShow ? "" : "none");
}

// Make an object visible or invisible
function makeObjectVisible(o, doShow)
{
	if (o && o.style) o.style.visibility = (doShow ? "visible" : "hidden");
}

// Shortcuts
function show(o) { showObject(o, true); }
function hide(o) { showObject(o, false); }
function appear(o) { makeObjectVisible(o, true); }
function disappear(o) { makeObjectVisible(o, false); }

// Determines if an element is visible
function isVisible(o)
{
  while (o && o.style) {
    if (o.style.display == "none" || o.style.visibility == "hidden") return false;
    o = o.parentNode;
  }
  return true;
}

// Shows of hides all elements of the given class
function showElements(classname, doShow)
{
	var e = getAllElements("");
	if (!e) return;
	for (var i = 0; i < e.length; i++)
		if (e[i].className == classname) showObject(e[i], doShow);
}


// -- Object positioning and styling -------------------------------------------------------------------------

// Returns the requested border size of the given object
function getBorderSize(o, dim)
{
  var style;
  if (o.currentStyle) { style = o.currentStyle[dim];  }
  else if (window.getComputedStyle) style = document.defaultView.getComputedStyle(o, null).getPropertyValue(dim);
  style = parseInt(style);
  return isNaN(style) ? 0 : style;
}

// Returns the objects position style setting
function getObjectPositioning(o)
{
	if (!o) return null;
	if (o.currentStyle) return o.currentStyle["position"];
	else if (window.getComputedStyle) return document.defaultView.getComputedStyle(o, null).getPropertyValue("position");
}

// Gets the horizontal position of an element
function getX(o)
{
  if (!o) return 0;
	var x = 0;
  if (o.offsetParent) {
    do {
      x += o.offsetLeft;
      if (isIE && o.offsetParent && o.offsetParent.tagName.toLowerCase() != "table") x += getBorderSize(o.offsetParent, "borderLeftWidth");
    } while ((o = o.offsetParent) && getObjectPositioning(o) != "relative");
  } else x = o.x;
	return x;
}

// Gets the horizontal position of an element
function getY(o)
{
	if (!o) return 0;
	var y = 0;
  if (o.offsetParent) {
    do {
      y += o.offsetTop;
      if (isIE && o.offsetParent && o.offsetParent.tagName.toLowerCase() != "table") y += getBorderSize(o.offsetParent, "borderTopWidth");
    } while ((o = o.offsetParent) && getObjectPositioning(o) != "relative");
  } else y = o.y;
	return y;
}

// Gets the horizontal width of an element
// Contains code to subtract border-widths from element for IE, as IE doesn't count the borders as width. Needs to be removed once IE gets served
// Standards mode triggering pages
function getWidth(o)
{
	if (!o) return 0;
	if (isIE) return o.offsetWidth - getBorderSize(o, "borderLeftWidth") - getBorderSize(o, "borderRightWidth");
	return o.offsetWidth;
}

// Gets the height of an element
// Contains code to subtract border-widths from element for IE, as IE doesn't count the borders as width. Needs to be removed once IE gets served
// Standards mode triggering pages
function getHeight(o)
{
	if (!o) return 0;
	if (isIE) return o.offsetHeight - getBorderSize(o, "borderTopWidth") - getBorderSize(o, "borderBottomWidth");
	return o.offsetHeight;
}

// Sets an element's position
function moveTo(o, x, y)
{
	if (o && o.style) {
		o.style.left = x + 'px';
		o.style.top = y + 'px';
	}
}

// Sets an elements width and height
function resizeTo(o, w, h)
{
	if (o && o.style) {
	  o.style.width = w + 'px';
	  o.style.height = h + 'px';
	}
}

// Sets the opacity of an element; opacity ranges from 0 to 100
function setOpacity(o, opacity)
{
	if (o && o.style) {
	  if (o.style.opacity !== undefined) o.style.opacity = opacity / 100.0;
	  if (o.style.filter !== undefined) o.style.filter = "alpha(opacity=" + opacity + ")";
	}
}

function buttonClassSwitch(t, OnText)
{
	var c = t.className;
	if (c == "ButtonDisabled") return;
	if (c.substr(c.length - OnText.length, OnText.length) == OnText) c = t["savedClass"];
	else {
		t["savedClass"] = c;
		c += OnText;
	}
	t.className = c;
}

// Enables or disables a button
// The code expects that buttons consist of an input element which is given
// as parameter and an element around it. The input element will be disabled, and
// style or the parent element changed to disabled
// This regards to the current implementation of buttons in the site.
function enableButton(b, enable)
{
  if (!b) return;
  b.disabled = enable ? false : true;
  b = b.parentNode;
  if (!b) return;
  var c = b.className;
  if (!enable && c != "ButtonDisabled") {
    b["savedClass"] = c;
    c = "ButtonDisabled";
  } else if (enable && c == "ButtonDisabled") c = b["savedClass"];
  b.className = c;
}

// Determines if the element is a "button". A "button" in this sense is an element
// which is surrounded by a div whose classname starts with "Button"
// If defaultable check is turned on, it will be checked additionally if the button
// has a default frame
function isButton(o, checkDefaultable)
{
  var p = (!o ? null : o.parentNode);
  if (!p || p.className.substr(0, "Button".length) != "Button") return false;
  return (!checkDefaultable || (p.parentNode && p.parentNode.className.substr(0, "DefaultFrame".length) == "DefaultFrame"));
}

// Changes the default visuals of a button
// Depends on the current implementation of button: input element -> div for button visuals -> div for default visuals (-> means "is child node of")
function setDefaultVisuals(o, OnOff)
{
  var p = (!o ? null : o.parentNode);

  var defaultFrame = (!p ? null : p.parentNode);
  if (!defaultFrame || defaultFrame.className.substr(0, "DefaultFrame".length) != "DefaultFrame") return;
  defaultFrame.className = (!OnOff ? "DefaultFrame" : "DefaultFrameOn");
}


// -- Event handling ---------------------------------------------------------------------------------------

// Events to run when document structure has finished loading
var _DOMLoadEventHandlers = [],
    _DOMLoaded = false,
    _CustomEvents = {};
    
// Subscribes to a published custom event; if event cannot be registered, the function tries it later
// again. Therefore, the event should come to existence at some point to prevent the function from trying indefinitely.
_CustomEvents._subscribeTo = function(type, handler) {
		try {
		this[type][this[type].length] = handler;
	} catch(e) { setTimeout(function() { _CustomEvents._subscribeTo(type, handler); }, 100); }
	return true;
}

// Fires a custom event, i. e. calls all registered handlers
function _fireCustomEvent(type, event)
{
	if (_CustomEvents[type] !== undefined)
	  for (var i in _CustomEvents[type])
		  _CustomEvents[type][i](event, _CustomEvents[type][i].data);
	return !event.eventStopped;
}

// Adds an event handler for the "DOMContentLoaded" event
// If the event has fired already, the handler is called right aways
function _addDOMLoadedEventHandler(handler)
{
  if (_DOMLoaded) return handler();
  _DOMLoadEventHandlers[_DOMLoadEventHandlers.length] = handler;
}
    
// Fixes IE events (only to be called for IE)
function _fixIEEvent(event)
{
  event.preventDefault = function() { this.returnValue = false; }
  event.stopPropagation = function() { this.cancelBubble = true; }
  event.target = event.srcElement;
  event.which = event.keyCode;
  return event;
}

// Handles an event for an element
function _handleEvent(event)
{
	event = event || _fixIEEvent(window.event);
  var handlers = this.handlers[event.type];
  if (handlers) for (var i in handlers) {
    if (handlers[i].call(this, event, handlers[i].data) === false) event.preventDefault();
  }
}
    
// Adds an event handler to an element
// The function handles all event types - JavaScript events, DOMContentLoaded, and custom events.
// The latter are indicated by the first parameter being null.
function addEventHandler(o, type, handler, data)
{
	if (!handler || !type || type == "") return false;
	var isDOMLoadedEvent = (type == "DOMContentLoaded" || type == "domready");

  // Attach handler data to the handler (make a copy first)
  if (data !== undefined) {
    var fn = handler;
    handler = function() { return fn.apply(this, arguments); };
    handler.data = data;
  }

  // The event specifying page structure being complete is handled as a special case
  if (isDOMLoadedEvent) return _addDOMLoadedEventHandler(handler);

  // If it is a custom event, the event is handled by the Custom Event Manager
	if (!o) return _CustomEvents._subscribeTo(type, handler);

  // Store all handlers in a list sorted by event type and add an event listener for a global event handling function
  // calling all those handlers in case of an event
  if (!o.handlers) o.handlers = {};
  var handlers = o.handlers[type];
  if (!handlers) {
    handlers = o.handlers[type] = {};
    if (o["on" + type]) handlers[0] = o["on" + type];
    if (o.addEventListener) o.addEventListener(type, _handleEvent, false);
    else if (o.attachEvent) o.attachEvent("on" + type, function() { _handleEvent.call(o); });
    else o["on" + type] = _handleEvent;
  }
  handlers[addEventHandler.handlerID++] = handler;
  return true;
}
addEventHandler.handlerID = 1;                   // Handler ID to assign to each handler

// Determines if an element has already at least 1 handler for the given event type
function hasEventHandler(o, type)
{
	return (!o || !o.handlers || !o.handlers[type]) ? false : true;
}

// Adds an event handler only if there is no handler for the given type already
function addEventHandlerOnce(o, type, handler, data)
{
	return hasEventHandler(o, type) ? true : addEventHandler(o, type, handler, data);
}

// Fires an event, calling all event handlers, of the given event type on the given object
// If the object is not given, a custom event is dispatch.
function fireEvent(o, type, param)
{
	// Manually create an event object to be used for triggering custom and built-in events. Note that this in no way replicates the browsers
	// event model, there's no bubbling, etc. Just a way to uniformly use those events
	var event = { type : type,
								target : o,
								param : param,
								preventDefault : function() { this.eventStopped = true; },
								stopPropagation : function() { this.eventStopped = true; },
								_eventStopped : false };

	// Fire custom event if the object is not given
	if (!o) return _fireCustomEvent(type, event);

	// Call handlers for this event
	_handleEvent.call(o, event);
	return !event.eventStopped;
}

// Removes all event handlers from an element
function removeEventHandlers(o)
{
	var i;
	if (o && o.handlers) for (i in o.handlers) {
		if (o.removeEventListener) o.removeEventListener(i, _handleEvent, false);
		else if (o.detachEvent) o.detachEvent("on" + i, _handleEvent);
		else o["on" + i] = null;
	}
	o.handlers = null;
}

// Removes all events from all elements
// Note: This function is useful due to a bug in Firefox when the browser does not disable all elements' events
//       after destroying all user variables. This results in script errors. This behaviour does not occur on IE.
//       On IE, it prevents memory leaks.
function removeAllEventHandlers(tagName)
{
	var allElements = getAllElements(tagName), i;
	for (i = 0; i < allElements.length; i++) removeEventHandlers(allElements[i]);
}

// Publishes a custom event, i. e. makes an event accessible to a potential audience
function publishEvent(type) { if (!_CustomEvents[type]) _CustomEvents[type] = []; }

// Calls all event handlers for the "DOMContentLoaded" event
function _DOMContentLoaded()
{
	if (_DOMLoaded) return;
  _DOMLoaded = true;
  for (var i = 0; i < _DOMLoadEventHandlers.length; i++) _DOMLoadEventHandlers[i]();
}

// Delayed test for DOM readiness
function _DOMContentLoadedScheduled()
{
  if (_DOMLoaded) return;
  if (/KHTML|WebKit/i.test(navigator.userAgent)) {
    if (/loaded|complete/.test(document.readyState)) _DOMContentLoaded();
    else setTimeout("_DOMContentLoadedScheduled()", 250);
  } else if (findObject("__ie_onload")) return;
}

// Event initialisation
if (document.addEventListener) document.addEventListener("DOMContentLoaded", _DOMContentLoaded, false); // W3C browsers
_DOMContentLoadedScheduled();                                                                           // Periodic checking (KHTML, WebKit-based browsers)
addEventHandler(window, "load", _DOMContentLoaded);                                                     // Fall back
/*@cc_on                                                                                                // For IE
	@if (@_win32 || @_win64)
	document.write("<script id=\"__ie_onload\" defer src=\"//:\"><\/script>");
	var script = findObject("__ie_onload");
  script.onreadystatechange = function() {
		if (this.readyState == "complete") _DOMContentLoaded();
	};
	@end
@*/


// -- And whatever else is there -------------------------------------------------------------------------------

// Opens a popup window
var winPop = { first: true };
function ContentPopper(sContent,name,ileft,itop,iWidth,iHeight)
{
	name = name || "Default";
	if (winPop.first) addEventHandler(window, "unload", function() { for (var i in winPop) if (winPop[i].close) winPop[i].close(); });
  winPop[name] = window.open(sContent,name,'scrollbars,resizable=yes,location=no,status=no,menubar=no,toolbar=no,directories=no,left=' + ileft + ',top=' + itop + ',width=' + iWidth + ',height=' + iHeight);
  winPop[name].focus();
  winPop.first = false;
}

// Selects a list entry by value
function selectByValue(o, v)
{
	if (!o) return -1;
	for (var i = 0; i < o.options.length; i++)
		if (o.options[i].value == v) return o.options.selectedIndex = i;
	return -1;
}

// Selects a list entry by text
function selectByText(o, text)
{
	if (!o) return -1;
	for (var i = 0; i < o.options.length; i++)
		if (o.options[i].text == text) return o.options.selectedIndex = i;
	return -1;
}

// Selects a list entry by text, ignoring case
function selectByTextIgnoreCase(o, text)
{
	if (!o) return -1;
	for (var i = 0; i < o.options.length; i++)
		if (o.options[i].text.toLowerCase() == text.toLowerCase()) return o.options.selectedIndex = i;
	return -1;
}

// Fix problems with the non-transparent PNG shadows on a few IE versions
// Uses the Conditional Compilation feature of IE4 or later; code inside will only
// be executed in Internet Explorer JScript implementations; moreover, code is only run on IE 6, as IE 7 & up support transparent PNGs.
function fixPNGs(BlankImage)
{
  /*@cc_on
		if (@_jscript_version == 5.6 || (@_jscript_version == 5.7 && navigator.userAgent.toLowerCase().indexOf("msie 6.") != -1)) {
			var imgs = getAllElements("img"), i, realSrc;
			for (i = 0; i < imgs.length; i++) {
				if (imgs[i].className == "PNGClass" && typeof imgs[i].runtimeStyle != 'undefined' ) {
					realSrc = imgs[i].src;
					imgs[i].src = BlankImage;
					imgs[i].runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + realSrc + "', sizingMethod='scale')";
				}
			}
		}
	@*/
}

