/*----------------------------------------

 *  scrollbar.class.js Version 1.0             _____ _______ ____
 *  Built at 2011/03/xx xx:xx GMT + 1:00      /  __ \__   __/ __ \
 *                                            | |__) | | | | |  | |
 *  © RTO GmbH 2011                           |  _  /  | | | |  | |
 *  All rights reserved.                      | | \ \  | | | |__| |
 *                                            |_|  \_\ |_|  \____/  GmbH
 *  http://www.rto.de
 *  http://www.rto-webservice.de

----------------------------------------*/
/*global window: false*/

var ScrollbarOverride = function () {
			// Private Variablen
	var win = window,                  // window Objekt
	    doc = win.document,            // document Objekt
			nav = win.navigator,           // navigator Objekt
			addEvent = win.addEvent,       // addEvent Funktion
			getStyle = win.getStyle,       // getStyle Funktion
			JSTweener = win.JSTweener,     // JSTweener Klasse
			self = this,                   // Referenz auf die Scrollbars
			jumpCaller = null,             // Referenz auf Slider für aktuellen Scroll-Sprung
			sliderToDrag = null,           // Referenz auf Slider, der aktuell per Drag 'n' Drop verschoben wird
			scrollTweener = null,          // Instanz der JSTweener-Klasse
			scrollContent = null,          // Container für Inhalt
	    scrollContainer = null,        // Container für Scrollbars
		  verticalScrollbar = null,      // Referenz auf vertikale Scrollbar
			horizontalScrollbar = null,    // Referenz auf horizontale Scrollbar
			allowAnyScroll = true,         // Status, ob gescrollt werden darf
			allowScrollJump = true,        // Status, ob Scroll-Sprünge ausgeführt werden dürfen (wird bei Drag 'n' Drop deaktiviert)
			containerName = '',            // Name des Containers, zum Prüfen auf Body-Tag
			instance = 0,                  // Aktuelle Scrollbar-Instanz Nummer
			scrollBarWidth = 32,           // Minimale Breite der horizontalen Scrollbar
			scrollBarHeight = 32,          // Minimale Höhe der vertikalen Scrollbar
			dragData = {},                 // Objekt zum Speichern der Drag 'n' Drop Werte
			anchorTags = {},               // Beinhaltet alle Anker der Seite
			scrollFrame = {},              // Beinhaltet Referenzen auf die Scrollbar-Slider
			setScrollbar = {},             // Beinhaltet Informationen, welche Scrollbars benötigt werden
			defaultValues = {},            // Standardwerte (für Berechnungen)
			scrollArrowCorner = {},        // Referenz auf die unteren rechten Scrollbar-Abschlüsse
			scrollMax = {                  // Maximale Anzahl an Pixeln, um die Scrollbar-Slider verschoben werden können
				x: 0,                        //   Wert für die Breite
				y: 0                         //   Wert für die Höhe
			},
			scrollStep = {                 // Minimale Anzahl an Pixeln, die durch die Pfeile gesprungen wird
				x: 16,                       //   Wert für horizontale Sprünge
				y: 16                        //   Wert für vertikale Sprünge
			},
			scrollJumper = {               // Referenz auf die Bereiche um den Slider, für große Scroll-Sprünge
				top: null,                   //   vertikal oben
				left: null,                  //   horizontal links
				right: null,                 //   horizontal rechts
				bottom: null                 //   vertikal unten
			},
			smoothInterval = {             // Speichert den Verweis auf setInterval bei Smooth-Scrolling
				x: null,                     //   bei horizontalem Scrolling
				y: null                      //   bei vertikalem Scrolling
			},
			smoothDuration = {             // Speichert den Startwert zum Berechnen der Scrolling-Dauer
				x: 0,                        //   für horizontales Scrolling
				y: 0                         //   für vertikales Scrolling
			},
			scrollMaxOffset = {            // Speichert den Offset der Scrollbars (Padding) für Berechnungen
				x: 0,                        //   Padding-Left
				y: 0                         //   Padding-Top
			},

	    config = {                     // Konfiguration der Scrollbars
				warnings: false,             //   Sollen Warnungen (alerts) erscheinen             (true/false)
				hScrollbar: true,            //   Soll eine horizontale Scrollbar angezeigt werden (true/false)
				vScrollbar: true,            //   Soll eine vertikale Scrollbar angezeigt werden   (true/false)
				smoothScroll: false,         //   Wert, ob Smooth-Scrolling aktiviert werden soll  (true/false)
				smoothFunction: '',          //   Welche Funktion wird fürs Scrolling verwendet    (function)
				scrollSpeed: 2.0,            //   Geschwindigkeit für Smooth-Scrolling             (float; langsamer > schneller)
				globalId: ''
			},

			// Private Funktionen          // Deklaration der Privaten Funktionen
			setGlobalConfig,
			makeNoSelection,               // Funktion zum verhindern von Textselektierung auf der Seite
			resetAllValues,                // Funktion zum Aufheben aller gesetzten Einschränkungen
			getDefaultValues,              // Funktion zum Auslesen von Standardwerten
			checkForResize,                // Funktion zum Prüfen, ob bei Änderung der Fesntergröße die Scrollbar angezeigt / versteckt werden soll
			createScrollbar,               // Funktion zum Erstellen der Scrollbars
			getScrollSteps,                // Funktion zur Berechnung der Schritte beim klicken auf die Pfeile
			scroll,                        // Funktion zum Scrollen des Inhalts
			setSliderDistance,             // Funktion zum Scrollen des Sliders
			setDistanceByAnchor,           // Funktion zum Berechnen der Position von Ankern
			smoothScrolling,               // Funktion zum Ermitteln der neuen Sliderposition für Smooth-Scrolling
			distanceCorrection,            // Funktion zum Korrigieren der Slider-Position bei Änderung der Fenstergröße
			jumpScroll,                    // Funktion zum Starten der Slider-Sprünge
			jumpStop,                      // Funktion zum Stoppen der Slider-Sprünge
			wheelScroll,                   // Funktion zum Bewegen des Sliders mit Hilfe des Mouse-Rades
			startDragSlider,               // Funktion zum Starten des Drag 'n' Drops
			dragSlider,                    // Funktiom zum Ausführen des Dragging
			stopDragSlider,                // Funktion zum Stoppen des Drag 'n' Drops
			arrowScroll,                   // Funktion zum Starten des Pfeil-Scrollens
			arrowStop,                     // Funktiom zum Stoppen des Pfeil-Scrollens

			// Konstanten                  // Deklaration von Konstanten
			VPOSITION = 'vertical',        // Prefix für Vertikal
			HPOSITION = 'horizontal',      // Prefix für Horizontal
			BACKWARD = -1,                 // Scroll-Richtung, nach oben oder nach links
			FORWARD = 1;                   // Scroll-Richtung, nach unten oder nach rechts


	self.init = function (scroller, userConfig) {
		var index = 0,
			elementCount = 0;

		SO_instances.push(self);
		instance = SO_instances.length;

		if (typeof(userConfig) !== 'undefined') {
			setGlobalConfig(userConfig);
		}

		if (typeof(scroller) !== 'undefined') {
			if (typeof(scroller) !== 'object') {
				scrollContainer = doc.getElementById(scroller);
			}
			else {
				scrollContainer = scroller;
			}

			if (scrollContainer) {
				containerName = scrollContainer.nodeName.toLowerCase();

				if (scrollContainer.id === '') {
					scrollContainer.id = scrollContainer.nodeName.toLowerCase();
				}

				if (JSTweener) {
					scrollTweener = JSTweener.easingFunctions;
				}

				scrollContent = doc.createElement('div');
				scrollContent.style.position = 'relative';
				scrollContent.style.overflow = 'hidden';
				scrollContent.style.marginRight = '16px';
				scrollContent.id = containerName + '_content';

				if (containerName === 'body') {
					 scrollContainer.setAttribute('scroll', 'no');
				}

				scrollContainer.style.position = 'relative';
				scrollContainer.style.overflow = 'hidden';

				scrollContent = scrollContainer.appendChild(scrollContent);

				scrollContent.setAttribute('override', instance);

				index = scrollContainer.children.length;
				for (; index > 0;) {
					index -= 1;
					
					if (scrollContainer.children[index].nodeType === 1 && scrollContainer.children[index] !== scrollContent) {
						scrollContent.insertBefore(scrollContainer.children[index], scrollContent.firstChild);
					}
				}

				if (config.hScrollbar && !horizontalScrollbar) {
					horizontalScrollbar = createScrollbar(HPOSITION);
				}

				if (config.vScrollbar && !verticalScrollbar) {
					verticalScrollbar = createScrollbar(VPOSITION);
				}

				addEvent(scrollContainer, 'DOMMouseScroll', wheelScroll); // Für FF
				addEvent(scrollContainer, 'mousewheel', wheelScroll); // Für andere Browser

				addEvent(win, 'resize', function () {
					win.setTimeout(function () {
						self.showScrollbar();
						distanceCorrection();
					}, 0);
				});

				self.getAllAnchors();

				getDefaultValues();
				win.setTimeout(function () {
					self.showScrollbar();
				}, 0);
			}
			else if (config.warnings) {
				win.alert('ScrollbarOverride::Init\n\nScrollbar kann nicht initialisiert werden: Element existiert nicht.');
			}
		}
		else if (config.warnings) {
			win.alert('ScrollbarOverride::Init\n\nScrollbar kann nicht initialisiert werden: undefiniertes Element.');
		}
	};

	self.showScrollbar = function () {
		var index = 0,
		    maxSize = 0,
				displaySize = 0,
				sizeRelation = 0,
				newScrollSize = 0,
				scrollOffset = 0;

		setScrollbar = checkForResize();

		if (setScrollbar.horizontal && setScrollbar.vertical) {
			scrollMaxOffset.x = defaultValues.scrollbarRight;
			if (defaultValues.scrollbarRight < 24) {
				horizontalScrollbar.style.right = '24px';
				scrollMaxOffset.x = 24;
			}

			scrollMaxOffset.y = defaultValues.scrollbarBottom;
			if (defaultValues.scrollbarBottom < 24) {
				verticalScrollbar.style.bottom  = '24px';
				scrollMaxOffset.y = 24;
			}
		}
		else {
			if (setScrollbar.horizontal) {
				horizontalScrollbar.style.right = defaultValues.scrollbarRight + 'px';
			}

			if (setScrollbar.vertical) {
				verticalScrollbar.style.bottom = defaultValues.scrollbarBottom + 'px';
			}

			scrollMaxOffset.x = defaultValues.scrollbarRight;
			scrollMaxOffset.y = defaultValues.scrollbarBottom;
		}
		
		if (setScrollbar.horizontal) {
			horizontalScrollbar.style.display = 'block';

			if (setScrollbar.vertical) {
				scrollArrowCorner[VPOSITION].style.display = 'none';
			}

			scrollContent.style.width = (scrollContainer.offsetWidth - (setScrollbar.vertical ? 16 : 0)) + 'px';
			scrollContent.style.paddingBottom = '16px';

			maxSize = scrollContent.scrollWidth;
			displaySize = scrollContent.offsetWidth;
			sizeRelation = maxSize / displaySize;

			newScrollSize = Math.ceil(horizontalScrollbar.offsetWidth / sizeRelation) - 16;
			scrollFrame[HPOSITION].style.width = newScrollSize + 'px';

			scrollMax.x  = horizontalScrollbar.offsetWidth - newScrollSize + 8;

			scrollJumper.left.style.width = '16px';
			scrollJumper.right.style.width = (scrollMax.x + 16) + 'px';
		}

		if (setScrollbar.vertical) {
			verticalScrollbar.style.display = 'block';
			
			if(setScrollbar.horizontal) {
				scrollArrowCorner[HPOSITION].style.display = 'block';
			}

			scrollContent.style.height = (scrollContainer.offsetHeight - (setScrollbar.horizontal ? 16 : 0)) + 'px';
			scrollContent.style.marginRight = '16px';

			maxSize = scrollContent.scrollHeight;
			displaySize = scrollContainer.offsetHeight;
			sizeRelation = maxSize / displaySize;

			newScrollSize = Math.ceil(verticalScrollbar.offsetHeight / sizeRelation) - 16;
			scrollFrame[VPOSITION].style.height = newScrollSize + 'px';

			scrollMax.y  = verticalScrollbar.offsetHeight - newScrollSize + 8;

			for (index = 0; index < scrollContent.children.length;) {
				if (scrollContent.children[index].nodeType === 1 && scrollContent.children[index] !== scrollContent) {
					scrollContent.children[index].style.paddingRight = '0px';
				}

				index += 1;
			}

			scrollJumper.top.style.height = '16px';
			scrollJumper.bottom.style.height = (scrollMax.y + 16) + 'px';
		}

		if (!setScrollbar.horizontal) {
			if (horizontalScrollbar) {
				horizontalScrollbar.style.display = 'none';
			}
			
			if (scrollArrowCorner[VPOSITION]) {
				scrollArrowCorner[VPOSITION].style.display = 'none';
			}

			scrollContent.style.width = '';
			scrollContent.style.paddingBottom = '';

			scrollMax.y -= 16;
		}

		if (!setScrollbar.vertical) {
			if (verticalScrollbar) {
				verticalScrollbar.style.display = 'none';
			}
			
			if (scrollArrowCorner[HPOSITION]) {
				scrollArrowCorner[HPOSITION].style.display = 'none';
			}

			for (index = 0; index < scrollContent.children.length;) {
				if (scrollContent.children[index].nodeType === 1 && scrollContent.children[index] !== scrollContent) {
					scrollContent.children[index].style.paddingRight = '0px';
				}

				index += 1;
			}

			scrollContent.style.height = '';
			scrollContent.style.paddingRight = '';

			scrollMax.x -= 16;
		}

		if (scrollContent.scrollLeft > 0) {
			scrollOffset = scrollContent.scrollLeft / (scrollContent.scrollWidth - scrollContainer.offsetWidth) * 100;
			scrollOffset = (scrollMax.x - scrollMaxOffset.x) / 100 * scrollOffset;

			setSliderDistance(HPOSITION, scrollOffset);
		}

		if (scrollContent.scrollTop > 0) {
			scrollOffset = scrollContent.scrollTop / (scrollContent.scrollHeight - scrollContainer.offsetHeight) * 100;
			scrollOffset = (scrollMax.y - scrollMaxOffset.y) / 100 * scrollOffset;

			setSliderDistance(VPOSITION, scrollOffset);
		}
	};

	self.getAllAnchors = function () {
		var links = null,
		    index = 0,
				anchorFunction = null;

		links = doc.getElementsByTagName('a');

		for (index in links) {
			if (links[index].hash || (links[index].href && links[index].href.indexOf('#') >= 0 && links[index].href.length > 1)) {
				anchorFunction = function (anchorEvent) {
					var anchorLink = null;

					if (!anchorEvent) {
						anchorEvent = win.event;
					}

					if (anchorEvent.preventDefault) {
						anchorEvent.preventDefault();
					}

					anchorLink = anchorEvent.srcElement;
					if (!anchorLink) {
						anchorLink = this;
					}

					setDistanceByAnchor(anchorLink);

					return false;
				};

				removeEvent(links[index], 'click', anchorFunction);
				addEvent(links[index], 'click', anchorFunction);
			}

			if (links[index].nodeType === 1) {
				if (links[index].name !== '') {
					anchorTags[links[index].name] = links[index];
				}
			}
		}
	};

	self.jumpTo = function (positionX, positionY) {
		var timer = null,
		    relativePosition = 0,
		    currentPosition = 0,
			currentPositionX = 0,
			currentPositionY = 0,
		    newPosition = 0,
			newPositionX = 0,
			newPositionY = 0,
			innerWidth = 0,
			innerHeight = 0;

		timer = new Date();

		if (smoothInterval.x) {
			win.clearInterval(smoothInterval.x);
		}

		if (smoothInterval.y) {
			win.clearInterval(smoothInterval.y);
		}
		
		if (typeof(win.innerHeight) !== 'undefined') {
			innerWidth = win.innerWidth;
			innerHeight = win.innerHeight;
		}
		else if (doc.documentElement) {
			innerWidth = doc.documentElement.clientWidth;
			innerHeight = doc.documentElement.clientWidth;
		}
		else if (doc.body) {
			innerWidth = doc.body.clientWidth;
			innerHeight = doc.body.clientHeight;
		}
		
		if (positionX >= 0) {
			currentPosition = getStyle(scrollFrame[HPOSITION], 'left', true);

			if (positionX > scrollContent.scrollWidth - innerWidth) {
				positionX = scrollContent.scrollWidth - innerWidth;
			}

			relativePosition = (positionX / scrollContent.scrollWidth - innerWidth) * 100;
			newPosition = Math.round((scrollMax.x / 100) * relativePosition);

			if (config.smoothScroll && typeof(JSTweener) !== 'undefined' && currentPosition !== newPosition && JSTweener.easingFunctions[config.smoothFunction]) {
				currentPositionX = currentPosition;
				newPositionX = newPosition;

				allowAnyScroll = false;
				smoothDuration.x = timer.getTime();

				smoothInterval.x = win.setInterval(function () {
					smoothScrolling(HPOSITION, currentPositionX, newPositionX);
				}, 10);
			}
			else {
				setSliderDistance(HPOSITION, newPosition);
			}
		}

		if (positionY >= 0) {
			currentPosition = getStyle(scrollFrame[VPOSITION], 'top', true);

			if (positionY > scrollContent.scrollHeight - innerHeight) {
				positionY = scrollContent.scrollHeight - innerHeight;
			}
			
			relativePosition = (positionY / (scrollContent.scrollHeight - innerHeight)) * 100;
			newPosition = Math.round((scrollMax.y / 100) * relativePosition);

			if (config.smoothScroll && typeof(JSTweener) !== 'undefined' && currentPosition !== newPosition && JSTweener.easingFunctions[config.smoothFunction]) {
				currentPositionY = currentPosition;
				newPositionY = newPosition;

				allowAnyScroll = false;
				smoothDuration.y = timer.getTime();

				smoothInterval.y = setInterval(function () {
					smoothScrolling(VPOSITION, currentPositionY, newPositionY);
				}, 10);
			}
			else {
				setSliderDistance(VPOSITION, newPosition);
			}
		}
	};

	setGlobalConfig = function (userConfig) {
		var index = 0,
				configError = [];

		for (index in userConfig) {
			if (typeof(userConfig[index]) !== 'undefined') {
				if (typeof(config[index]) !== 'undefined') {
					config[index] = userConfig[index];
				}
				else {
					configError.push(index);
				}
			}
		}

		if (configError.length > 0 && config.warnings) {
			alert('ScrollbarOverride::setGlobalConfig\n\nDiese Einstellungen existieren nicht!:\n- ' + configError.join('\n- '));
		}
	};

	makeNoSelection = function () {
		doc.body.onselectstart = function () {return false;};
		doc.body.ondrag        = function () {return false;};
		doc.onmousedown        = function () {return false;};
	};

	resetAllValues = function () {
		doc.body.onselectstart = function () {return true;};
		doc.body.ondrag        = function () {return true;};
		doc.onmousedown        = function () {return true;};

		win.clearTimeout(self.arrowTimeout);
		stopDragSlider();
	};

	getDefaultValues = function () {
		defaultValues.paddingRight    = getStyle(scrollContainer, 'paddingRight', true);
		defaultValues.paddingBottom   = getStyle(scrollContainer, 'paddingBottom', true);
		defaultValues.scrollbarRight  = getStyle(horizontalScrollbar, 'right', true);
		defaultValues.scrollbarBottom = getStyle(verticalScrollbar, 'bottom', true);

		if (isNaN(defaultValues.paddingRight)) {
			defaultValues.paddingRight = 0;
		}

		if (isNaN(defaultValues.paddingBottom)) {
			defaultValues.paddingBottom = 0;
		}

		if (isNaN(defaultValues.scrollbarRight)) {
			defaultValues.scrollbarRight = 0;
		}

		if (isNaN(defaultValues.scrollbarBottom)) {
			defaultValues.scrollbarBottom = 0;
		}
	};

	checkForResize = function () {
		var index = 0,
		    scrollWidth = 0,
				scrollHeight = 0,
		    topOffset = 0,
		    leftOffset = 0,
				realWidth = 0,
				realHeight = 0,
				checkWidth = false,
				checkHeight = false,
				sizeChildren = [];
		
		if (config.hScrollbar) {
			realWidth = scrollContainer.offsetWidth;
			leftOffset = getStyle(scrollContainer, 'marginLeft', true);

			if (isNaN(leftOffset)) {
				leftOffset = 0;
			}

			sizeChildren = scrollContent.children;
			for (index = 0; index < sizeChildren.length;) {
				if (scrollWidth < sizeChildren[index].offsetWidth) {
					scrollWidth = sizeChildren[index].offsetWidth;
				}

				index += 1;
			}
		}

		checkWidth = scrollWidth > (realWidth + leftOffset);

		if (config.vScrollbar) {
			realHeight = scrollContainer.offsetHeight;
			topOffset = getStyle(scrollContainer, 'marginTop', true);

			if (isNaN(topOffset)) {
				topOffset = 0;
			}

			sizeChildren = scrollContent.children;
			for (index = 0; index < sizeChildren.length;) {
				if (scrollHeight < sizeChildren[index].offsetHeight) {
					scrollHeight = sizeChildren[index].offsetHeight;
				}

				index += 1;
			}
		}

		checkHeight = scrollHeight > (realHeight + topOffset);

		return {horizontal: checkWidth, vertical: checkHeight};
	};

	createScrollbar = function (position) {
		var scrollStylePrefix = '',
		    scrollContainerDiv = null,
		    scrollArrowBack = null,
				scrollArrowForward = null,
				scrollArrowCornerDiv = null,
				scrollArrowTerminator = null,
				scrollBarBack = null,
				scrollBarForward = null,
				scrollMoveBack = null,
				scrollMoveForward = null,
		    scrollFrameDiv = null,
				scrollSliderStartDiv = null,
				scrollSliderDiv = null,
				scrollSliderEndDiv = null,
				scrollDetailDiv = null;

		scrollStylePrefix = scrollContainer.id + '_';

		if (typeof(position) !== 'undefined') {
			// CONTAINER
			scrollContainerDiv = doc.createElement('div');
			if (config.globalId) {
				scrollContainerDiv.className = 'SO_' + config.globalId + '_' + position + '_container';
			}
			scrollContainerDiv.id = 'SO_' + scrollStylePrefix + position + '_container';

			scrollContainerDiv = scrollContainer.appendChild(scrollContainerDiv);

			// PFEIL LINKS / OBEN
			scrollArrowBack = doc.createElement('div');
			if (config.globalId) {
				scrollArrowBack.className = 'SO_' + config.globalId + '_' + position + '_backarrow';
			}
			scrollArrowBack.id = 'SO_' + scrollStylePrefix + position + '_backarrow';

			scrollArrowBack = scrollContainerDiv.appendChild(scrollArrowBack);

			addEvent(scrollArrowBack, 'mousedown', function () {
				if (config.globalId) {
					this.className = this.className + '_active';
				}
				this.id = this.id + '_active';
				arrowScroll(position, BACKWARD, 300);
			});

			addEvent(scrollArrowBack, 'mouseup', arrowStop);
			addEvent(scrollArrowBack, 'mouseout', arrowStop);

			// PFEIL RECHTS / UNTEN
			scrollArrowForward = doc.createElement('div');
			if (config.globalId) {
				scrollArrowForward.className = 'SO_' + config.globalId + '_' + position + '_forwardarrow';
			}
			scrollArrowForward.id = 'SO_' + scrollStylePrefix + position + '_forwardarrow';

			scrollArrowForward = scrollContainerDiv.appendChild(scrollArrowForward);

			addEvent(scrollArrowForward, 'mousedown', function () {
				if (config.globalId) {
					this.className = this.className + '_active';
				}
				this.id = this.id + '_active';
				arrowScroll(position, FORWARD, 300);
			});

			addEvent(scrollArrowForward, 'mouseup', arrowStop);
			addEvent(scrollArrowForward, 'mouseout', arrowStop);

			// SCROLLBAR ABSCHLUSS LINKS / OBEN
			scrollArrowTerminator = doc.createElement('div');
			if (config.globalId) {
				scrollArrowTerminator.className = 'SO_' + config.globalId + '_' + position + '_terminate';
			}
			scrollArrowTerminator.id = 'SO_' + scrollStylePrefix + position + '_terminate';

			scrollContainerDiv.appendChild(scrollArrowTerminator);

			// SCROLLBAR ABSCHLUSS RECHTS / UNTEN
			scrollArrowCornerDiv = doc.createElement('div');
			if (config.globalId) {
				scrollArrowCornerDiv.className = 'SO_' + config.globalId + '_' + position + '_corner';
			}
			scrollArrowCornerDiv.id = 'SO_' + scrollStylePrefix + position + '_corner';

			scrollArrowCornerDiv = scrollContainerDiv.appendChild(scrollArrowCornerDiv);
			scrollArrowCorner[position] = scrollArrowCornerDiv;

			// KLICKBARER SCROLLBAR BEREICH LINKS / OBEN
			scrollBarBack = doc.createElement('div');
			if (config.globalId) {
				scrollBarBack.className = 'SO_' + config.globalId + '_' + position + '_backbar';
			}
			scrollBarBack.id = 'SO_' + scrollStylePrefix + position + '_backbar';

			scrollBarBack = scrollContainerDiv.appendChild(scrollBarBack);

			if (position === HPOSITION) {
				scrollJumper.left = scrollBarBack;

				addEvent(scrollJumper.left, 'mousedown', function () {
					jumpCaller = this;
					
					if (config.globalId) {
						jumpCaller.className = jumpCaller.className + '_active';
					}
					jumpCaller.id = jumpCaller.id + '_active';

					jumpScroll(position, BACKWARD, 300);
				});

				addEvent(scrollJumper.left, 'mouseup', jumpStop);
				addEvent(scrollJumper.left, 'mouseout', jumpStop);
			}
			else if (position === VPOSITION) {
				scrollJumper.top = scrollBarBack;

				addEvent(scrollJumper.top, 'mousedown', function () {
					jumpCaller = this;
					
					if (config.globalId) {
						jumpCaller.className = jumpCaller.className + '_active';
					}
					jumpCaller.id = jumpCaller.id + '_active';

					jumpScroll(position, BACKWARD, 300);
				});

				addEvent(scrollJumper.top, 'mouseup', jumpStop);
				addEvent(scrollJumper.top, 'mouseout', jumpStop);
			}

			// KLICKBARER SCROLLBAR BEREICH RECHTS / UNTEN
			scrollBarForward = doc.createElement('div');
			if (config.globalId) {
				scrollBarForward.className = 'SO_' + config.globalId + '_' + position + '_forwardbar';
			}
			scrollBarForward.id = 'SO_' + scrollStylePrefix + position + '_forwardbar';

			scrollBarForward = scrollContainerDiv.appendChild(scrollBarForward);

			if (position === HPOSITION) {
				scrollJumper.right = scrollBarForward;

				addEvent(scrollJumper.right, 'mousedown', function () {
					jumpCaller = this;
					
					if (config.globalId) {
						jumpCaller.className = jumpCaller.className + '_active';
					}
					jumpCaller.id = jumpCaller.id + '_active';

					jumpScroll(position, FORWARD, 300);
				});

				addEvent(scrollJumper.right, 'mouseup', jumpStop);
				addEvent(scrollJumper.right, 'mouseout', jumpStop);
			}
			else if (position === VPOSITION) {
				scrollJumper.bottom = scrollBarForward;

				addEvent(scrollJumper.bottom, 'mousedown', function () {
					jumpCaller = this;

					if (config.globalId) {
						jumpCaller.className = jumpCaller.className + '_active';
					}
					jumpCaller.id = jumpCaller.id + '_active';

					jumpScroll(position, FORWARD, 300);
				});

				addEvent(scrollJumper.bottom, 'mouseup', jumpStop);
				addEvent(scrollJumper.bottom, 'mouseout', jumpStop);
			}

			// SCROLLBAR ABSCHLUSS LINKS / OBEN
			scrollMoveBack = doc.createElement('div');
			if (config.globalId) {
				scrollMoveBack.className = 'SO_' + config.globalId + '_' + position + '_backclosure';
			}
			scrollMoveBack.id = 'SO_' + scrollStylePrefix + position + '_backclosure';

			scrollBarBack.appendChild(scrollMoveBack);

			// SCROLLBAR ABSCHLUSS RECHTS / UNTEN
			scrollMoveForward = doc.createElement('div');
			if (config.globalId) {
				scrollMoveForward.className = 'SO_' + config.globalId + '_' + position + '_forwardclosure';
			}
			scrollMoveForward.id = 'SO_' + scrollStylePrefix + position + '_forwardclosure';

			scrollBarForward.appendChild(scrollMoveForward);

			// SCROLLER RAHMEN
			scrollFrameDiv = doc.createElement('div');
			if (config.globalId) {
				scrollFrameDiv.className = 'SO_' + config.globalId + '_' + position + '_frame';
			}
			scrollFrameDiv.id = 'SO_' + scrollStylePrefix + position + '_frame';

			scrollFrameDiv = scrollContainerDiv.appendChild(scrollFrameDiv);
			scrollFrame[position] = scrollFrameDiv;

			scrollFrame[position].direction = position;
			addEvent(scrollFrame[position], 'mousedown', function (clickEvent) {
				var caller = null,
				    direction = '';

				if (!clickEvent) {
					clickEvent = win.event;
				}

				caller = scrollFrame[position];
				direction = position;

				startDragSlider(caller, direction, clickEvent);
			});

			// SCROLLER
			scrollSliderStartDiv = doc.createElement('div');
			if (config.globalId) {
				scrollSliderStartDiv.className = 'SO_' + config.globalId + '_' + position + '_sliderstart';
			}
			scrollSliderStartDiv.id = 'SO_' + scrollStylePrefix + position + '_sliderstart';

			scrollFrameDiv.appendChild(scrollSliderStartDiv);

			scrollSliderDiv = doc.createElement('div');
			if (config.globalId) {
				scrollSliderDiv.className = 'SO_' + config.globalId + '_' + position + '_slider';
			}
			scrollSliderDiv.id = 'SO_' + scrollStylePrefix + position + '_slider';

			scrollSliderDiv = scrollFrameDiv.appendChild(scrollSliderDiv);

			scrollSliderEndDiv = doc.createElement('div');
			if (config.globalId) {
				scrollSliderEndDiv.className = 'SO_' + config.globalId + '_' + position + '_sliderend';
			}
			scrollSliderEndDiv.id = 'SO_' + scrollStylePrefix + position + '_sliderend';

			scrollFrameDiv.appendChild(scrollSliderEndDiv);

			// SCROLLER DETAIL (3-FACH STRICHE, ETC.)
			scrollDetailDiv = doc.createElement('div');
			if (config.globalId) {
				scrollDetailDiv.className = 'SO_' + config.globalId + '_' + position + '_detail';
			}
			scrollDetailDiv.id = 'SO_' + scrollStylePrefix + position + '_detail';

			scrollSliderDiv.appendChild(scrollDetailDiv);

			if (position === HPOSITION) {
				defaultValues.right  = getStyle(scrollContainerDiv, 'right', true);

				if (isNaN(defaultValues.right)) {
					defaultValues.right = 0;
				}

				scrollFrameDiv.style.width = scrollBarWidth + 'px';
			}

			if (position === VPOSITION) {
				defaultValues.bottom = getStyle(scrollContainerDiv, 'bottom', true);

				if (isNaN(defaultValues.bottom)) {
					defaultValues.bottom = 0;
				}

				scrollFrameDiv.style.height = scrollBarHeight + 'px';
			}

			addEvent(doc, 'mousemove', dragSlider);
			addEvent(doc, 'mouseup', resetAllValues);
		}
		else {
			scrollContainerDiv = false;

			if (config.warnings) {
				win.alert('ScrollbarOverride::createScrollbar\n\nEs wurde keine Scrollbar zum Erstellen angegeben!');
			}
		}

		return scrollContainerDiv;
	};

	getScrollSteps = function () {
		if (scrollFrame[HPOSITION]) {
			scrollStep.x = Math.floor(scrollFrame[HPOSITION].offsetWidth / 10);
			if(scrollStep.x < 16) {
				scrollStep.x = 16;
			}
		}

		if (scrollFrame[VPOSITION]) {
			scrollStep.y = Math.floor(scrollFrame[VPOSITION].offsetHeight / 10);
			if(scrollStep.y < 16) {
				scrollStep.y = 16;
			}
		}
	};

	scroll = function (position) {
		var scrollRelation = 0,
		    scrollOffset = 0,
		    contentRelation = 0;

		makeNoSelection();

		if (position === HPOSITION) {
			scrollRelation = getStyle(scrollFrame[position], 'left', true) / (scrollMax.x - scrollMaxOffset.x) * 100;
			contentRelation = ((scrollContent.scrollWidth - scrollContainer.offsetWidth) / 100);

			scrollOffset = contentRelation * scrollRelation;

			scrollContent.scrollLeft = Math.ceil(scrollOffset);
			
			if (containerName === 'body') {
				doc.documentElement.scrollTop = 0;
			}
		}
		else if (position === VPOSITION) {
			scrollRelation = getStyle(scrollFrame[position], 'top', true) / (scrollMax.y - scrollMaxOffset.y) * 100;
			contentRelation = ((scrollContent.scrollHeight - scrollContainer.offsetHeight) / 100);

			scrollOffset = contentRelation * scrollRelation;

			scrollContent.scrollTop = Math.ceil(scrollOffset);

			if (containerName === 'body') {
				doc.documentElement.scrollTop = 0;
			}
		}
	};

	setSliderDistance = function (position, distance) {
		var newDistance = 0,
		    reachedMaxDistance = false;

		if (typeof(distance) !== 'undefined' && !isNaN(distance)) {
			newDistance = distance;
		}
		else {
			distance = 0;
		}

		if (position === HPOSITION) {
			if (newDistance <= 0) {
				newDistance = 0;
			}

			if (newDistance > scrollMax.x - scrollMaxOffset.x) {
				newDistance = scrollMax.x - scrollMaxOffset.x;
			}

			if(scrollFrame[position]) {
				scrollFrame[position].style.left = newDistance + 'px';

				scrollJumper.left.style.width = (newDistance + 16) + 'px';
				scrollJumper.right.style.width = (scrollMax.x - newDistance + 16) + 'px';
			}

			if (newDistance === 0 || newDistance === scrollMax.x - scrollMaxOffset.x) {
				reachedMaxDistance = true;
			}
		}
		else if (position === VPOSITION) {
			if (newDistance < 0) {
				newDistance = 0;
			}

			if (newDistance > scrollMax.y - scrollMaxOffset.y) {
				newDistance = scrollMax.y - scrollMaxOffset.y;
			}

			if(scrollFrame[position]) {
				scrollFrame[position].style.top = newDistance + 'px';

				scrollJumper.top.style.height = (newDistance + 16) + 'px';
				scrollJumper.bottom.style.height = (scrollMax.y - newDistance + 16) + 'px';
			}

			if (newDistance === 0 || newDistance === scrollMax.y - scrollMaxOffset.y) {
				reachedMaxDistance = true;
			}
		}

		scroll(position);

		return reachedMaxDistance;
	};

	setDistanceByAnchor = function (anchorLink) {
		var anchorId = '',
		    locationHref = '',
				locationIndex = 0,
				scrollElement = null,
				anchorElement = null,
				anchorParent = null,
				anchorPosition = {
					x: 0,
					y: 0
				};

		anchorId = anchorLink.hash;
		if (typeof(anchorId) === 'undefined') {
			anchorId = anchorLink.parentNode.hash;
		}

		if (!anchorId && anchorLink.location) {
			locationHref = anchorLink.location.toString();
			locationIndex = locationHref.indexOf('#');

			anchorId = locationHref.substr(locationIndex);
		}
		
		if(anchorId) {
			anchorId = anchorId.replace('#', '');
		}

		if (anchorTags[anchorId]) {
			anchorElement = anchorTags[anchorId];
		}
		else if (typeof(anchorId) === 'string' && anchorId !== '') {
			anchorElement = doc.getElementById(anchorId);
		}

		if (anchorElement) {
			anchorParent = anchorElement;

			do {
				anchorPosition.x += anchorParent.offsetLeft;
				anchorPosition.y += anchorParent.offsetTop;

				scrollElement = anchorParent.getAttribute('override');
				if (scrollElement) {
					scrollElement = SO_instances[scrollElement - 1];

					if (scrollElement && scrollElement !== self) {
						scrollElement.jumpTo(anchorPosition.x, anchorPosition.y);

						anchorPosition.x = 0;
						anchorPosition.y = 0;
					}
				}

				anchorParent = anchorParent.offsetParent;
			}
			while (anchorParent);

			self.jumpTo(anchorPosition.x, anchorPosition.y);
		}

		return false;
	};

	smoothScrolling = function (position, startPosition, endPosition) {
		// PARAMETER: easeInOutCubic(t, b, c, d);
		//   t = Verstrichene Zeit in Millisekunden
		//   b = Startwert in Pixel
		//   c = Startwert-Endwert-Differenz in Pixel
		//   d = Maximale Dauer in Millisekunden

		var tweenTimer = 0,
		    tweenDuration = 0,
				positionDifference = 0,
				timerDifference = 0,
		    newPosition = 0;

		tweenTimer = config.scrollSpeed * 1000;
		tweenDuration = new Date();

		if (position === HPOSITION) {
			positionDifference = endPosition - startPosition;
			timerDifference = tweenDuration.getTime() - smoothDuration.x;

			if (timerDifference > tweenTimer) {
				timerDifference = tweenTimer;
			}

			newPosition = scrollTweener[config.smoothFunction](timerDifference, startPosition, positionDifference, tweenTimer);

			if (positionDifference > 0) {
				setSliderDistance(position, Math.ceil(startPosition + newPosition));
			}
			else if (positionDifference < 0) {
				setSliderDistance(position, Math.floor(endPosition + newPosition));
			}

			if (endPosition === newPosition) {
				allowAnyScroll = true;
				clearInterval(smoothInterval.x);
			}
		}
		else if (position === VPOSITION) {
			positionDifference = endPosition - startPosition;
			timerDifference = tweenDuration.getTime() - smoothDuration.y;

			if (timerDifference > tweenTimer) {
				timerDifference = tweenTimer;
			}

			newPosition = scrollTweener[config.smoothFunction](timerDifference, startPosition, positionDifference, tweenTimer);
			
			if (positionDifference > 0) {
				setSliderDistance(position, Math.ceil(startPosition + newPosition));
			}
			else if (positionDifference < 0) {
				setSliderDistance(position, Math.floor(endPosition + newPosition));
			}
				
			if (endPosition === newPosition) {
				allowAnyScroll = true;
				clearInterval(smoothInterval.y);
			}
		}
	};

	distanceCorrection = function () {
		var scrollOffset = 0;

		if (setScrollbar.horizontal) {
			scrollOffset = getStyle(scrollFrame[HPOSITION], 'left', true);
			setSliderDistance(HPOSITION, scrollOffset);
		}

		if (setScrollbar.vertical) {
			scrollOffset = getStyle(scrollFrame[VPOSITION], 'top', true);
			setSliderDistance(VPOSITION, scrollOffset);
		}
	};

	jumpScroll = function (position, direction, holdTime) {
		var startDistance = 0,
		    newDistance = 0;

		if (allowAnyScroll && allowScrollJump) {
			if (typeof(holdTime) === 'undefined') {
				holdTime = 50;
			}
			
			win.clearTimeout(self.jumpTimeout);

			if (position === HPOSITION) {
				startDistance = getStyle(scrollFrame[position], 'left', true);
				newDistance = startDistance + Math.round(direction * (scrollFrame[position].offsetWidth / 2.5));

				setSliderDistance(position, newDistance);
			}
			else if (position === VPOSITION) {
				startDistance = getStyle(scrollFrame[position], 'top', true);
				newDistance = startDistance + Math.round(direction * (scrollFrame[position].offsetHeight / 2.5));

				setSliderDistance(position, newDistance);
			}

			self.jumpTimeout = win.setTimeout(function () {
				jumpScroll(position, direction);
			}, holdTime);
		}
	};

	jumpStop = function () {
		win.clearTimeout(self.jumpTimeout);

		if (this.id) {
			if (config.globalId) {
				this.className = this.className.replace('_active', '');
			}
			this.id = this.id.replace('_active', '');
		}
	};

	wheelScroll = function (wheelEvent) {
		var delta = 0,
		    direction = 0,
				startDistance = 0,
				newDistance = 0,
				executeDefault = false;

		if (!wheelEvent) {
			wheelEvent = win.event;
		}

		if (allowAnyScroll && allowScrollJump) {
			if (wheelEvent.wheelDelta) {
				delta = wheelEvent.wheelDelta / 100;

				if (win.opera && parseFloat(nav.appVersion) < 9.8) {
					delta = -delta;
				}
				if (nav.userAgent.toLowerCase().indexOf('chrome') >= 0) {
					delta *= 2;
				}
			}
			else if (wheelEvent.detail) {
				delta = -wheelEvent.detail;
			}

			if (delta > 0) {
				direction = -1;
			}
			else {
				direction = 1;
			}

			delta = Math.round(Math.abs(delta));

			if ((wheelEvent.axis && wheelEvent.axis === wheelEvent.HORIZONTAL_AXIS) || !setScrollbar.vertical) {
				startDistance = getStyle(scrollFrame[HPOSITION], 'left', true);
				newDistance = startDistance + (direction * delta * scrollStep.x);

				executeDefault = setSliderDistance(HPOSITION, newDistance);
			}
			else {
				startDistance = getStyle(scrollFrame[VPOSITION], 'top', true);
				newDistance = startDistance + (direction * delta * scrollStep.y);

				executeDefault = setSliderDistance(VPOSITION, newDistance);
			}
		}

		if (wheelEvent.stopPropagation) {
			wheelEvent.stopPropagation();
		}
		else {
			wheelEvent.cancelBubble = true;
		}

		if (!executeDefault) {
			if (wheelEvent.preventDefault) {
				wheelEvent.preventDefault();
			}
		}
		
		return false;
	};

	startDragSlider = function (caller, direction, clickEvent) {
		if (!clickEvent) {
			clickEvent = win.event;
		}

		if (allowAnyScroll) {
			sliderToDrag = caller;
			allowScrollJump = false;

			makeNoSelection();

			dragData.direction = direction;
			dragData.dragFrame = scrollFrame[direction];

			if (config.globalId) {
				dragData.dragFrame.className = dragData.dragFrame.className + '_active';
			}
			dragData.dragFrame.id = dragData.dragFrame.id + '_active';

			if (direction === HPOSITION) {
				dragData.mouseStart = clickEvent.clientX;
				dragData.sliderStart = getStyle(caller, 'left', true);
			}
			else if (direction === VPOSITION) {
				dragData.mouseStart = clickEvent.clientY;
				dragData.sliderStart = getStyle(caller, 'top', true);
			}
		}
	};

	dragSlider = function (mouseEvent) {
		var newDistance = 0;

		if (!mouseEvent) {
			mouseEvent = win.event;
		}

		if (typeof(sliderToDrag) === 'object' && sliderToDrag !== null) {
			if (dragData.direction === HPOSITION) {
				newDistance = dragData.sliderStart + (mouseEvent.clientX - dragData.mouseStart);
			}
			else if (dragData.direction === VPOSITION) {
				newDistance = dragData.sliderStart + (mouseEvent.clientY - dragData.mouseStart);
			}

			setSliderDistance(dragData.direction, newDistance);
		}
	};

	stopDragSlider = function () {
		if (typeof(sliderToDrag) === 'object' && sliderToDrag !== null) {
			if (config.globalId) {
				dragData.dragFrame.className = dragData.dragFrame.className.replace('_active', '');
			}
			dragData.dragFrame.id = dragData.dragFrame.id.replace('_active', '');

			sliderToDrag = null;
			dragData = {};
		}

		allowScrollJump = true;
	};

	arrowScroll = function (position, direction, holdTime) {
		var startDistance = 0,
		    newDistance = 0;

		if (allowAnyScroll && allowScrollJump) {
			if (typeof(holdTime) === 'undefined') {
				holdTime = 50;
			}

			win.clearTimeout(self.arrowTimeout);

			getScrollSteps();

			if (position === HPOSITION) {
				startDistance = getStyle(scrollFrame[position], 'left', true);
				newDistance = startDistance + (direction * scrollStep.x);

				setSliderDistance(position, newDistance);
			}
			else if (position === VPOSITION) {
				startDistance = getStyle(scrollFrame[position], 'top', true);
				newDistance = startDistance + (direction * scrollStep.y);

				setSliderDistance(position, newDistance);
			}

			self.arrowTimeout = win.setTimeout(function () {
				arrowScroll(position, direction);
			}, holdTime);
		}
	};

	arrowStop = function () {
		win.clearTimeout(self.arrowTimeout);

		if (config.globalId) {
			this.className = this.className.replace('_active', '');
		}
		this.id = this.id.replace('_active', '');
	};
},

SO_instances = [];

