
var tekt = {};

var eventFunctionNames = ["onclick","onmouseover","onmouseout","onchange"];

Object.addBehavior = function(obj, behavior) {
    for (i in behavior) {
        if (i != "_init") {
            if (eventFunctionNames.indexOf(i) == -1) {
                obj[i] = behavior[i].bind(obj);
            }
            else {
                Event.observe(obj, i.substring(2), behavior[i].bindAsEventListener(obj));
            }
        }
    }
    if (behavior["_init"]) {
        behavior["_init"].apply(obj);
    }
}

function walkDOM(root, callback, data) {
    if (root) {
        var doTraverseChildren = callback(root, data); 
        if (doTraverseChildren) {
            var children = root.childNodes;
            for (var i=0; i<children.length; i++) {
                walkDOM(children[i], callback, data);
            }
        }
    }
}

tekt.init = function(bindings) {
    bindings = bindings || tekt.bindings || {};
    walkDOM(document.body, initElement, bindings);
}

function initElement(element, bindings) {
	for (var selector in bindings) {
		if (selector.charAt(0) == '.') { // class selector
			if (element.className && element.className.search("(?:^|\\s)" + selector.substring(1) + "(?:$|\\s)") > -1) {
				var behavior = bindings[selector];
				Object.addBehavior(element, behavior);
			}
		}
		else if (selector.charAt(0) == '#') { // id selector
			if (element.id && element.id == selector.substring(1)) {
				var behavior = bindings[selector];
				Object.addBehavior(element, behavior);
			}
		}
	}
	return true;
}

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(object) {
        for (var i = 0; i < this.length; i++)
            if (this[i] == object) return i;
        return -1;
    }
}


function cumulativeOffset(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
}


String.prototype.trim = function() {
    return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}

function getInitialHeight(element) {
    var height = element.offsetHeight;
    if (!height) {
        var style = element.style;
        if (window.getComputedStyle) {
            style = window.getComputedStyle(element,"");
        }
        else if (element.currentStyle) {
           style = element.currentStyle;
        }
        
        var heightStr = style.height;
        if (heightStr.indexOf("px")>0) {
            height = parseFloat(heightStr.substring(0,heightStr.indexOf("px")));
        }
    }
    return height;
}

Function.prototype.bind = function(object) {
  var __method = this;
  return function() {
    return __method.apply(object, arguments);
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this;
  return function(event) {
    return __method.call(object, event || window.event);
  }
}

function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1)
      return element;

    elements.push(element);
  }

  return elements;
}

function getQualifiedElements(tagName, className, parentElement) {
  var children = ($(parentElement) || document.body).getElementsByTagName(tagName || '*');
  if (!className) return children;
  var result = [];
  for (var i=0; i<children.length; i++) {
    if (children[i].className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) {
      result.push(children[i]);
    }
  }
  return result;
}

function getFirstQualifiedElement(tagName, className, parentElement) {
    var arr = getQualifiedElements(tagName, className, parentElement);
    if (arr && arr.length > 0) return arr[0];
    return null;
}

function addClassName(el, className) {
    if (!el.className) {
        el.className = className;
        return;
    }
    var classes = el.className.split(/\s+/);
    if (classes.indexOf(className) > -1) return;
    el.className = classes.concat(className).join(' ');
}

function removeClassName(el, className) {
    if (!el.className) return;
    var classes = el.className.split(/\s+/);
    var index = classes.indexOf(className);
    if (index == -1) return;
    classes.splice(index,1);
    el.className = classes.join(' ');
}

function hasClassName(el, className) {
    var classes = el.className.split(/\s+/);
    return (classes.indexOf(className) > -1);
}


var Event = {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX +
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY +
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } else {
      event.returnValue = false;
      event.cancelBubble = true;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },

  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0; i < Event.observers.length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    this._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.detachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      element.detachEvent('on' + name, observer);
    }
  }
};


Event.observe(window, 'unload', Event.unloadCache, false);



var bindings = {
    ".item" : {
        onmouseover: function(event) {
            var el = getFirstQualifiedElement(null, "details", this);
            if (el) {
                var links = getQualifiedElements("a", null, el);
                this.others = [];
                for (var i=0; i<links.length; i++) {
                    if (links[i].hash) {
                        var other = $(links[i].hash.substring(1));
                        if (other) {
                            this.others.push(other);
                            addClassName(other, "activeItem");
                        }
                    }
                }
                if (this.others.length > 0) {
                    connectWithArrows(this, this.others, 4);
                }
            }
            addClassName(this, "itemHover");
        },
        onmouseout: function(event) {
            $("arrows").innerHTML = "";
            if (this.others) {
                for (var i=0; i<this.others.length; i++) {
                    removeClassName(this.others[i], "activeItem");
                }
            }
            removeClassName(this, "itemHover");
        }
    }    
}

function init() {
    tekt.init(bindings);
}

function connectWithArrows(el, others, spacing) {
    if (!others.length) others = [others];
    var quadrants = [[],[],[],[],[],[]];
    var pos = cumulativeOffset(el);
    var size = [el.offsetWidth, el.offsetHeight];
    for (var i=0; i<others.length; i++) {
    
        // categorize others according to position in 6 quadrants: LT, LB, T, B, RT, RB
    
        var posOther = cumulativeOffset(others[i]);
        others[i]._pos = posOther;
        
        var horizontalOffset = 0;
        if (posOther[1] + others[i].offsetHeight / 2 > pos[1] + size[1] / 2 - 2) {
            // other is below el's midpoint
            horizontalOffset = 1;
        }
               
        if (posOther[0] + others[i].offsetWidth < pos[0]) {
            // other is left to el
            quadrants[0 + horizontalOffset].push(others[i]);
        }
        else if (posOther[0] > pos[0] + el.offsetWidth) {
            // other is right to el
            quadrants[4 + horizontalOffset].push(others[i]);
        }
        else {
            // other is above / below el
            quadrants[2 + horizontalOffset].push(others[i]);
        }   
    }
    
    for (var i=0; i<quadrants.length; i++) {
        quadrants[i].sort(function(a,b){return (a._pos[1] + a.offsetHeight / 2) - (b._pos[1] + b.offsetHeight / 2)});
    }
    
    for (var i=0; i<quadrants[0].length; i++) {
        var x1 = pos[0];
        var y1 = pos[1] + size[1] / 2 - (quadrants[0].length + quadrants[1].length) / 2 * spacing + i * spacing;
        var x2 = quadrants[0][i]._pos[0] + quadrants[0][i].offsetWidth;
        var y2 = quadrants[0][i]._pos[1] + quadrants[0][i].offsetHeight / 2;
        var col = pos[0] - 20 + spacing * quadrants[0].length / 2 - spacing * i;
        makeArrowH(x1, y1, x2, y2, col);
    }
    for (var i=0; i<quadrants[1].length; i++) {
        var x1 = pos[0];
        var y1 = pos[1] + size[1] / 2 - (quadrants[0].length + quadrants[1].length) / 2 * spacing + (i + quadrants[0].length) * spacing;
        var x2 = quadrants[1][i]._pos[0] + quadrants[1][i].offsetWidth;
        var y2 = quadrants[1][i]._pos[1] + quadrants[1][i].offsetHeight / 2;
        var col = pos[0] - 20 - spacing * quadrants[1].length / 2 + spacing * (i+1);
        makeArrowH(x1, y1, x2, y2, col);
    }
    for (var i=0; i<quadrants[4].length; i++) {
        var x1 = pos[0] + size[0];
        var y1 = pos[1] + size[1] / 2 - (quadrants[4].length + quadrants[5].length) / 2 * spacing + i * spacing;
        var x2 = quadrants[4][i]._pos[0];
        var y2 = quadrants[4][i]._pos[1] + quadrants[4][i].offsetHeight / 2;
        var col = pos[0] + size[0] + 20 - spacing * quadrants[4].length / 2 + spacing * i;
        makeArrowH(x1, y1, x2, y2, col);
    }
    for (var i=0; i<quadrants[5].length; i++) {
        var x1 = pos[0] + size[0];
        var y1 = pos[1] + size[1] / 2 - (quadrants[4].length + quadrants[5].length) / 2 * spacing + (i + quadrants[4].length) * spacing;
        var x2 = quadrants[5][i]._pos[0];
        var y2 = quadrants[5][i]._pos[1] + quadrants[5][i].offsetHeight / 2;
        var col = pos[0] + size[0] + 20 + spacing * quadrants[5].length / 2 - spacing * (i+1);
        makeArrowH(x1, y1, x2, y2, col);
    }
    
}

function makeArrowH(x1, y1, x2, y2, col) {

    if (x1 > x2) {
        // swap points
        var tx = x1, ty = y1;
        x1 = x2;
        y1 = y2;
        x2 = tx;
        y2 = ty;
    }

    if (!col || col < x1 || col > x2) col = (x1 + x2) / 2;
    
    if (Math.abs(y1 - y2) < 4) {
        makeArrowPart("TR", x1, y1, x2 - x1, 0);
    }
    else if (y1 < y2) {
        makeArrowPart("TR", x1, y1, col - x1, y2 - y1);
        makeArrowPart("TR", col, y2, x2 - col, 0);
    }
    else {
        makeArrowPart("BR", x1, y2, col - x1, y1 - y2);
        makeArrowPart("TR", col, y2, x2 - col, 0);
    }
}

function makeArrowV(x1, y1, x2, y2, row) {
}

function makeArrowPart(orientation, left, top, width, height) {
    var part = document.createElement("div");
    part.className = "arrowPart arrow" + orientation;
    part.style.top = top + "px";
    part.style.left = left + "px";
    part.style.width = width + "px";
    part.style.height = height + "px";
    $("arrows").appendChild(part);
}

function safeMail(el) {
    if (el.href.indexOf("mailto:") == 0) return true;
    el.innerHTML = ["mail","floledermann.com"].join("@");
    el.href = ["mailto:mail","floledermann.com"].join("@");
    el.title = "";
    return false;
}