// BEHAVIOR LAYER

/**
 * Implements <code>getElementById<code> method for W3C DOM-incapable browsers.
 * For these browsers it returns always <code>null</code>. You do not have
 * to detect the <code>getElementById<code> elsewhere in a script anymore.
 * <p>
 * You should always check, if the returned value is not <code>null</code> to 
 * prevent script errors; <code>getElementById</code> returns null in
 * W3C DOM-capable browsers as well, if there is no match for the given id in
 * the document tree.
 * <p>
 * Usage:
 * <p>
 * <code>var elem = document.getElementById(id);<br>
 * if (elem) {<br>
 *     // process elem<br>
 * }</code>
 *
 * @author klaus.hartl (30.03.2005)
 */
if (!document.getElementById) {
    document.getElementById = function() {
        return null;
    };
}

/**
 * Retrieves the actual document language. Therefore it analyzes the
 * <code>xml:lang</code> and <code>lang</code> attribute of the 
 * <code>html</code> element. The <code>xml:lang</code> attribute takes
 * precedence.
 *
 * @return a string identifying the document's language. If no language
 *         is specified, an empty string is returned.
 *
 * @author klaus.hartl (29.03.2005)
 */
function getDocumentLanguage() {
    var language = '';
    if (document.body.parentNode) {
        var htmlElem = document.body.parentNode;
        if (htmlElem.getAttribute('xml:lang')) {
            language = htmlElem.getAttribute('xml:lang');
        } else if (typeof htmlElem.lang == 'string') {
            language = htmlElem.lang;
        }
    }
    return language;
}

/**
 * Returns an array of element objects from the current document
 * matching the CSS selector. Selectors can contain element names, 
 * class names and ids and can be nested. For example:
 * <p>    
 * <code>elements = document.getElementsBySelect('div#main p a.external')</code>
 * <p>    
 * Will return an array of all '<code>a</code>' elements with
 * '<code>external</code>' in their <code>class</code> attribute
 * that are contained inside '<code>p</code>' elements that are 
 * contained inside the '<code>div</code>' element which has
 * <code>id="main"</code>.
 * <p>
 * Supports CSS2 and CSS3 attribute selectors.
 * <p>
 * Based on: 
 * <a href="http://simon.incutio.com/archive/2003/03/25/getElementsBySelector">getElementsBySelector()</a>
 * <p>
 * Fails in Safari: '.external', because the special case "*" is not
 * supported for the getElementsByTagName() function. Use element
 * selectors (like 'a.external') whenever possible instead.
 */
function getAllChildren(e) {
  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  return e.all ? e.all : e.getElementsByTagName('*');
}

document.getElementsBySelector = function(selector) {
    // Required variables
    var bits, tagName, elements, found, foundCount, currentContextIndex;
    // Attempt to fail gracefully in lesser browsers
    if (!document.getElementsByTagName) {
        return new Array();
    }
    // Split selector in to tokens
    var tokens = selector.split(' ');
    var currentContext = new Array(document);
    for (var i = 0; i < tokens.length; i++) {
        var token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
        if (token.indexOf('#') > -1) {
            // Token is an Id selector
            bits = token.split('#');
            tagName = bits[0];
            var id = bits[1];
            var element = document.getElementById(id);
            if (!element) {
                // Id not found, return false
                return new Array();
            }
            if (tagName && element.nodeName.toLowerCase() != tagName) {
                // Tag with that Id not found, return false
                return new Array();
            }
            // Set currentContext to contain just this element
            currentContext = new Array(element);
            continue; // Skip to next token
        }
        if (token.indexOf('.') > -1) {
            // Token contains a class selector
            bits = token.split('.');
            tagName = bits[0];
            var className = bits[1];
            if (!tagName) {
                tagName = '*';
            }
            // Get elements matching tag, filter them for class selector
            found = new Array;
            foundCount = 0;
            for (var j = 0; j < currentContext.length; j++) {
                if (tagName == '*') {
                    elements = getAllChildren(currentContext[j]);
                } else {
                    elements = currentContext[j].getElementsByTagName(tagName);
                }
                for (var k = 0; k < elements.length; k++) {
                    found[foundCount++] = elements[k];
                }
            }
            currentContext = new Array;
            currentContextIndex = 0;
            for (var m = 0; m < found.length; m++) {
                var classAttr = found[m].className ? found[m].className : found[m].getAttribute('class');
                if (classAttr) {
                    var classNames = classAttr.split(' ');
                    for (var n = 0; n < classNames.length; n++) {
                        if (className == classNames[n]) {
                            currentContext[currentContextIndex++] = found[m];
                            break;
                        }
                    }
                }
                /* Opera 6 does not support \b
                if (classAttr && classAttr.match(new RegExp('\\b'+className+'\\b'))) {
                  currentContext[currentContextIndex++] = found[k];
                } */
            }
            continue; // Skip to next token
        }
        // Code to deal with attribute selectors
        if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
            tagName = RegExp.$1;
            var attrName = RegExp.$2;
            var attrOperator = RegExp.$3;
            var attrValue = RegExp.$4;
            if (!tagName) {
                tagName = '*';
            }
            // Grab all of the tagName elements within current context
            found = new Array;
            foundCount = 0;
            for (var x = 0; x < currentContext.length; x++) {
                if (tagName == '*') {
                    elements = getAllChildren(currentContext[x]);
                } else {
                    elements = currentContext[x].getElementsByTagName(tagName);
                }
                for (var y = 0; y < elements.length; y++) {
                    found[foundCount++] = elements[y];
                }
            }
            currentContext = new Array;
            currentContextIndex = 0;
            var checkFunction; // This function will be used to filter the elements
            switch (attrOperator) {
                case '=': // Equality
                    checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
                    break;
                case '~': // Match one of space seperated words 
                    checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
                    break;
                case '|': // Match start with value followed by optional hyphen
                    checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
                    break;
                case '^': // Match starts with value
                    checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
                    break;
                case '$': // Match ends with value - fails with "Warning" in Opera 7
                    checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
                    break;
                case '*': // Match ends with value
                    checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
                    break;
                default :
                    // Just test for existence of attribute
                    checkFunction = function(e) { return e.getAttribute(attrName); };
            }
            currentContext = new Array;
            currentContextIndex = 0;
            for (var z = 0; z < found.length; z++) {
                if (checkFunction(found[z])) {
                    currentContext[currentContextIndex++] = found[z];
                }
            }
            // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
            continue; // Skip to next token
        }
        // If we get here, token is just an element (not a class or Id selector)
        tagName = token;
        found = new Array;
        foundCount = 0;
        for (var a = 0; a < currentContext.length; a++) {
            elements = currentContext[a].getElementsByTagName(tagName);
            for (var b = 0; b < elements.length; b++) {
                found[foundCount++] = elements[b];
            }
        }
        currentContext = found;
    }
    return currentContext;
}

/**
 * Binds an event handler function to a given node, so that the function executes
 * when an event of the particular, given type arrives at the node either as event
 * target or during event propagation. Existing events, either defined as a tag
 * attribute or binded dynamically, are not affected.
 * <p>
 * Note: If this has to work in IE/Mac with existing events as tag attributes
 * the call to bind a new event has to follow the target node in the source 
 * code.
 *
 * @param target           the node to which the event will be binded
 * @param eventType        a string of one event type (without the "on" prefix)
 *                         known to the browser's object model
 * @param listenerFunction a reference to the function to execute, when the node
 *                         hears the event type
 * @param useCapture       a Boolean value. If <code>true</code>, the node listens
 *                         for the event type only while the event propagates
 *                         toward the target node. If <code>false</code>, the 
 *                         node listens only when the event bubbles outward from
 *                         the event target. The typical setting of this parameter
 *                         is <code>false</code> and if omitted, it falls back to 
 *                         that value.
 * @author                 klaus.hartl (24.03.2005)
 */
function addEventHandler(target, eventType, listenerFunction, useCapture) {
    if (target.addEventListener) {
        // W3C DOM approach
        useCapture = (typeof useCapture == 'boolean') ? useCapture : false;
        target.addEventListener(eventType, listenerFunction, useCapture);
        return true;
    } else if (target.attachEvent) {
        // IE/Win DOM approach
        var r = target.attachEvent('on' + eventType, listenerFunction);
        return r;
    } else {
        // fallback approach (IE/Mac and anything else that gets this far)
        var onEv = 'on' + eventType;
	    // if there's an existing event handler function
        if(typeof target[onEv] == 'function') {
            // store it
            var existing = target[onEv];
            // attach new onload handler
            target[onEv] = function() {
                // call existing function
                existing();
                // call given function
                listenerFunction();
            };
        } else {
            target[onEv] = listenerFunction;
        }
        return true;
    }
}

// convert HTMLCollection object to array
function collectionToArray(collection) {
	var a = new Array();
	for (var i = 0; i < collection.length; i++) {
        a[a.length] = collection[i];
    }
	return a;
}

// create print page link on the fly if key items are supported
/*
var hasPrintFeature = (window.print) ? true : false;
var siteinfoEl = document.getElementById('siteinfo');
if (hasPrintFeature && siteinfoEl && (document.createElementNS || document.createElement) && document.createTextNode && document.body.appendChild  && document.body.insertBefore) {
    var pEl = (document.createElementNS) ? document.createElementNS('http://www.w3.org/1999/xhtml', 'p') : document.createElement('p');
    pEl.setAttribute('id', 'page-print');
    var aPrintEl = (document.createElementNS) ? document.createElementNS('http://www.w3.org/1999/xhtml', 'a') : document.createElement('a');
    aPrintEl.href = 'javascript:window.print();';
    aPrintEl.appendChild(document.createTextNode('Seite drucken'));
    pEl.appendChild(aPrintEl);
    siteinfoEl.insertBefore(pEl, document.getElementById('page-date'));
}
*/
// attach events to certain form elements
var searchInputEl = document.getElementById('search-input');
if (searchInputEl) {
    //searchInputEl.setAttribute('autocomplete', 'off'); // prevents exception occuring with mozilla's autocomplete feature
    // this exception though does not break/impact the script but maybe we want to avoid an error at the console...
    var s = searchInputEl.value;
    searchInputEl.onfocus = searchInputEl.onblur = function(evt) {
        toggleValue(evt, s);
    }
}

function toggleValue(evt, defaultValue) {
    evt = (evt) ? evt : ((event) ? event : null);
    if (evt) {
        var elem = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
        if (elem) {
            if (elem.value == defaultValue) {
                elem.value = '';
            } else if (elem.value == '') {
                elem.value = defaultValue;
            }
        }
    }
}

// To change dynamically the click event and title attribute for links, which
// should open in a new window.
// Compliant to XHTML 1.0 Strict and WCAG/BITV.
// Note: the click event also includes the keypress event in modern browsers.
//
// Insert this JS-file to change your external links. If JS is enabled, all links
// with the class attribute "external" (multiple classes supported), will open in
// a new window

// BEGIN configuration

// Globals

// Default language
var defaultLanguage = 'de';

// The features of the new window
var windowFeatures = 'directories,location,menubar,resizable,scrollbars,status,toolbar';

var titlePrefix = new Array();
// Use iso language codes as array keys as we retrieve the value of the xml:lang attribute as key for the current document language
// english
titlePrefix['en'] = 'New window: ';
// german
titlePrefix['de'] = 'Neues Fenster: ';
// define more if needed

// END configuration


// Helper method to open a new window
function openNewWindow(url, target, windowFeatures) {
    var w = window.open(url, target, windowFeatures);
    w.focus();
}

// Helper method to get the title prefix in the correct document language
function getTitlePrefix(language) {
    var s = titlePrefix[defaultLanguage];
    if (typeof titlePrefix[language] == 'string') {
        s = titlePrefix[language];
    } 
    return s;
}

// Helper method to retrieve only the child text node values of an element node
function getTextNodeValues(elementNode) {
    var text = '';
    if (elementNode.nodeType && elementNode.nodeType == 1 && elementNode.childNodes) {
        var children = elementNode.childNodes;
        for (var i = 0; i < children.length; i++) {
            var child = children[i];
            if (child.nodeType == 3) {
                // text node
                text += child.nodeValue;
            } else if (child.nodeType == 1) {
                // element node
                text += getTextNodeValues(child);
            }
        }
    }
    return text;
}

// Prepare external links
var externalLinks = document.getElementsBySelector('a.external'); // 'a.external' instead of '.external' is required by Safari
for (var i = 0; i < externalLinks.length; i++) {
    var anchor = externalLinks[i];
    
    // Add click event
    
    // Woraround for Safari 1.0, which does not execute preventDefault()
    var isSafari = navigator.userAgent.toLowerCase().indexOf('safari') != -1;
    if (isSafari) {
        var onClick = anchor.onclick; // save existing event
        anchor.onclick = function() {
            return false;
        };
        if (typeof onClick == 'function') {
            addEventHandler(anchor, 'click', onClick);
        }
    }
    
    addEventHandler(anchor, 'click', function(evt) {
        var href;
        if (this == window && event) { // IE/Win does not understand "this", if event is attached other than "a.onclick = '...'"
            var elem = event.srcElement;
            // Source element might be a nested node inside the desired link node
            while (elem.nodeName.toLowerCase() != 'a') {
                elem = elem.parentNode;
            }
            href = elem.href;
        } else {
            href = this.href;
        }
        openNewWindow(href, '', windowFeatures);
        
        if (evt && evt.preventDefault) { // cancle default action of click event (W3C DOM approach)
            evt.preventDefault();
        } else { // cancle default action of click event (IE/Win and anything else that gets this far)
            event.returnValue = false; // IE/Win
        }
    });
    
    // Add title attribute
    var anchorText = getTextNodeValues(anchor);
    var titleAttribute = getTitlePrefix(getDocumentLanguage()) + anchorText;
    anchor.setAttribute('title', titleAttribute);
}

// Add :focus functionality for input and textarea elements in IE/Win
function handleFocus(ev) {
    ev = (ev) ? ev : ((event) ? event : null);
    if (ev) {
        var elem = (ev.target) ? ev.target : ((ev.srcElement) ? ev.srcElement : null);
        if (elem) {
            if (elem.type == 'submit' || elem.type == 'reset') {
                elem.className += ' onhover';
            } else {
                elem.className += ' onfocus';
            }
        }
    }
}

function handleBlur(ev) {
    ev = (ev) ? ev : ((event) ? event : null);
    if (ev) {
        var elem = (ev.target) ? ev.target : ((ev.srcElement) ? ev.srcElement : null);
        if (elem) {
            if (elem.type == 'submit' || elem.type == 'reset') {
                elem.className = elem.className.replace(/onhover/, '');
            } else {
                elem.className = elem.className.replace(/onfocus/, '');
            }
        }
    }
}

// Prepare input and textarea elements if user agent is IE/Win
var ua = navigator.userAgent.toLowerCase();
var uaIsIEWin = (ua.indexOf('msie') != -1) && (ua.indexOf('windows') != -1) && document.execCommand; // Opera spoofs as Internet Explorer
if (uaIsIEWin) {
    var formElems = new Array();
    formElems = formElems.concat(collectionToArray(document.getElementsByTagName('input')), collectionToArray(document.getElementsByTagName('textarea')), collectionToArray(document.getElementsByTagName('select')));
    for (var k = 0; k < formElems.length; k++) {
        var elem = formElems[k];
        var tagName = elem.tagName.toLowerCase();
        if ((tagName == 'input' && (elem.type == 'text' || elem.type == 'password' || elem.type == 'submit' || elem.type == 'reset')) || tagName == 'textarea' || tagName == 'select') {
            addEventHandler(elem, 'focus', handleFocus);
            addEventHandler(elem, 'blur', handleBlur);
        }
        if (tagName == 'input' && (elem.type == 'submit' || elem.type == 'reset')) {
            addEventHandler(elem, 'mouseover', handleFocus);
            addEventHandler(elem, 'mouseout', handleBlur);
        }
    }
}