// init_pso einhaengen
var priv_Event = {  
    add: function(obj, etype, fp, cap) {
        cap = cap || false;
        if (obj.addEventListener) obj.addEventListener(etype, fp, cap);
        else if (obj.attachEvent) obj.attachEvent("on" + etype, fp);
    }
}

// horizId nur fuer horizontal scrolling
function pso(outerID, innerID, horizId) {
    var outer = document.getElementById(outerID);
    this.id = outerID; pso.col[this.id] = this;
    this.animString = "pso.col." + this.id;
    this.load(innerID, horizId);
    
    if (outer.addEventListener) {
        outer.addEventListener('DOMMouseScroll', pso.doOnMouseWheel, false);
    } 
    outer.onmousewheel = pso.doOnMouseWheel;
}

// taugt der Browser fuer unser Vorhaben?
pso.isSupported = function () {
    if ( document.getElementById && document.getElementsByTagName 
         && document.addEventListener || document.attachEvent ) {
        return true;
    }
    return false;
}

pso.col = {}; // collect instances
pso.defaultSpeed = pso.prototype.speed = 100; // default for mouseover or mousedown scrolling
pso.defaultSlideDur = pso.prototype.slideDur = 500; // default duration of glide onclick

// pseudo events 
pso.prototype.on_load = function() {} // when pso initialized or new layer loaded
pso.prototype.on_scroll = function() {}
pso.prototype.on_scroll_start = function() {}
pso.prototype.on_scroll_stop = function() {} // when scrolling has ceased (mouseout/up)
pso.prototype.on_scroll_end = function() {} // reached end
pso.prototype.on_update = function() {} // called in updateDims

pso.prototype.on_glidescroll = function() {}
pso.prototype.on_glidescroll_start = function() {}
pso.prototype.on_glidescroll_stop = function() {} // destination (to/by) reached
pso.prototype.on_glidescroll_end = function() {} // reached end

pso.prototype.load = function(innerID, horizId) {
    var outer, inner;
    if (this.innerID) {
        inner = document.getElementById(this.innerID);
        inner.style.visibility = "hidden";
    }
    this.inner = inner = document.getElementById(innerID); 
    this.inner.style.position = 'absolute'; 
    this.inner.style.width = '100%';
    this.innerID = innerID; 
    this.horizId = horizId || null; 
    outer = document.getElementById(this.id);
    this.y = 0; this.x = 0; this.shiftTo(0,0);
    this.getDims(outer, inner); 
    inner.style.visibility = "visible";
    this.ready = true; this.on_load(); 
}

pso.prototype.shiftTo = function(x, y) {
    if (this.inner) {
        this.inner.style.left = (this.x = x) + "px"; 
        this.inner.style.top = (this.y = y) + "px";
    }
}

pso.prototype.getX = function() { return this.x; }
pso.prototype.getY = function() { return this.y; }

pso.prototype.getDims = function(outer, inner) { 
    this.wd = this.horizId? document.getElementById( this.horizId ).offsetWidth: inner.offsetWidth;
    this.maxX = (this.wd - outer.offsetWidth > 0)? this.wd - outer.offsetWidth: 0;
    this.maxY = (inner.offsetHeight - outer.offsetHeight > 0)? inner.offsetHeight - outer.offsetHeight: 0;
}


pso.prototype.initScrollByVals = function(dx, dy, dur) {
    if ( !this.ready || this.sliding ) return;
    this.startX = this.x; this.startY = this.y;
    this.destX = this.destY = this.distX = this.distY = 0;
    if (dy < 0) {
        this.distY = (this.startY + dy >= -this.maxY)? dy: -(this.startY  + this.maxY);
    } else if (dy > 0) {
        this.distY = (this.startY + dy <= 0)? dy: -this.startY;
    }
    if (dx < 0) {
        this.distX = (this.startX + dx >= -this.maxX)? dx: -(this.startX + this.maxX);
    } else if (dx > 0) {
        this.distX = (this.startX + dx <= 0)? dx: -this.startX;
    }
    this.destX = this.startX + this.distX; this.destY = this.startY + this.distY;
    this.glideScrollPrep(this.destX, this.destY, dur);
}

pso.prototype.glideScrollPrep = function(destX, destY, dur) {
    this.slideDur = (typeof dur == 'number')? dur: pso.defaultSlideDur;
    this.per = Math.PI/(2 * this.slideDur); this.sliding = true;
    this.inner = document.getElementById(this.innerID); 
    this.startTime = new Date().getTime();
    this.timerId = setInterval(this.animString + ".doGlideScroll()",10);
    this.on_glidescroll_start(this.startX, this.startY);
}

pso.prototype.doGlideScroll = function() {
    var elapsed = new Date().getTime() - this.startTime;
    if (elapsed < this.slideDur) {
        var x = this.startX + Math.round( this.distX * Math.sin(this.per*elapsed) );
        var y = this.startY + Math.round( this.distY * Math.sin(this.per*elapsed) );
        this.shiftTo(x, y); 
        this.on_glidescroll(x, y);
    } else {	// if time's up
        clearInterval(this.timerId); this.timerId = 0; this.sliding = false;
        this.shiftTo(this.destX, this.destY);
        this.on_glidescroll(this.destX, this.destY);
        this.on_glidescroll_stop(this.destX, this.destY);
        // end of axis reached ? 
        if ( this.distX && (this.destX == 0 || this.destX == -this.maxX) 
          || this.distY && (this.destY == 0 || this.destY == -this.maxY) ) { 
            this.on_glidescroll_end(this.destX, this.destY);
        } 
    }
}

pso.handleMouseWheel = function(id, delta) {
    var outer = pso.col[id];
    var x = outer.x;
    var y = outer.y;
    outer.on_scroll_start(x,y);
    var ny;
    ny = 36  * delta + y
    ny = (ny < 0 && ny >= -outer.maxY)? ny: (ny < -outer.maxY)? -outer.maxY: 0;
    outer.shiftTo(x, ny);
    outer.on_scroll(x, ny);
}

pso.doOnMouseWheel = function(e) {
    var delta = 0;
    if (!e) e = window.event;
    if (e.wheelDelta) { 
        delta = e.wheelDelta/120;
        if (window.opera) delta = -delta;
    } else if (e.detail) { // Mozilla 
        delta = -e.detail/3;
    }
    if (delta) { // > 0 up, < 0 down
        pso.handleMouseWheel(this.id, delta);
    }
    if (e.preventDefault) e.preventDefault();
    e.returnValue = false;
}

pso.prototype.setUpPSO = function(controlsId, autoHide, axis) {
    var outerID = this.id; 
    var el = document.getElementById(controlsId); 
    var links = el.getElementsByTagName('a'), cls, eType;
    for (var i=0; links[i]; i++) { 
        cls = pso.get_DelimitedClass( links[i].className );
        eType = pso.getEv_FnType( cls.slice(0, cls.indexOf('_') ) );
        switch ( eType ) {
            case 'click': 
                pso.handleClick(links[i], outerID, cls) ;
                break;
        }
    }
}

pso.handleClick = function (linkEl, outerID, cls) {
    var outer = pso.col[outerID];
    var parts = cls.split('_'); var eType = parts[0]; 
    var dur_re = /^([\d]+)$/; var fn, re, x, y, dur;
    
    switch (eType) {
        case 'scrollTo' :
            fn = 'scrollTo';  re = /^(null|end|[\d]+)$/;
            x = re.test( parts[1] )? parts[1]: '';
            y = re.test( parts[2] )? parts[2]: '';
            dur = ( parts[3] && dur_re.test(parts[3]) )? parts[3]: null;
            break;
        case 'scrollBy': // scrollBy_m30_m40, scrollBy_null_m100, scrollBy_100_null
            fn = 'scrollBy';  re = /^(([m]?[\d]+)|null)$/;
            x = re.test( parts[1] )? parts[1]: '';
            y = re.test( parts[2] )? parts[2]: '';
            
            // negate numbers (m not - but vice versa) 
            if ( !isNaN( parseInt(x) ) ) {
                x = -parseInt(x);
            } else if ( typeof x == 'string' ) {
                x = x.indexOf('m') !=-1 ? x.replace('m', ''): x;
            }
            if ( !isNaN( parseInt(y) ) ) {
                y = -parseInt(y);
            } else if ( typeof y == 'string' ) {
                y = y.indexOf('m') !=-1 ? y.replace('m', ''): y;
            }
            
            dur = ( parts[3] && dur_re.test(parts[3]) )? parts[3]: null;
            break;
        
        case 'click': 
            var o = pso.getClickParts(cls);
            fn = o.fn; x = o.x; y = o.y; dur = o.dur;
            break;
    }
    if ( x !== '' && y !== '' ) {
        if (x == 'end') { x = outer.maxX; }
        if (y == 'end') { y = outer.maxY; }
        if (x === 'null' || x === null) { x = outer.x; }
        if (y === 'null' || y === null) { y = outer.y; }
        
        x = parseInt(x); y = parseInt(y);  
        dur = !isNaN( parseInt(dur) )? parseInt(dur): null;
        
        if (fn == 'scrollBy') {
            priv_Event.add( linkEl, 'click', function (e) {
                    pso.col[outerID].initScrollByVals(x, y, dur);
                    if (e && e.preventDefault) e.preventDefault();
                    return false;
                } );
        } else if (fn == 'scrollTo') {
            priv_Event.add( linkEl, 'click', function (e) {
                    pso.col[outerID].initScrollToVals(x, y, dur);
                    if (e && e.preventDefault) e.preventDefault();
                    return false;
                } );
        }
    }
}

// Klassennamen geschickt auswerten (z.B. click_down_by_100)
pso.getClickParts = function(cls) {
    var parts = cls.split('_');
    var re = /^(up|down|left|right)$/;
    var dir, fn = '', dur, ar, val, x = '', y = '';
    
    if ( parts.length >= 4 ) {
        ar = parts[1].match(re);
        dir = ar? ar[1]: null;
            
        re = /^(to|by)$/; 
        ar = parts[2].match(re);
        if (ar) {
            fn = (ar[0] == 'to')? 'scrollTo': 'scrollBy';
        } 
    
        val = parts[3]; // value on x or y axis
        re = /^([\d]+)$/;
        dur = ( parts[4] && re.test(parts[4]) )? parts[4]: null;
    
        switch (fn) {
            case 'scrollBy' :
                if ( !re.test( val ) ) {
                    x = ''; y = ''; break;
                }
                switch (dir) { // 0 for unspecified axis 
                    case 'up' : x = 0; y = val; break;
                    case 'down' : x = 0; y = -val; break;
                    case 'left' : x = val; y = 0; break;
                    case 'right' : x = -val; y = 0;
                 }
                break;
            case 'scrollTo' :
                re = /^(end|[\d]+)$/;
                if ( !re.test( val ) ) {
                    x = ''; y = ''; break;
                }
                switch (dir) { // null for unspecified axis 
                    case 'up' : x = null; y = val; break;
                    case 'down' : x = null; y = (val == 'end')? val: -val; break;
                    case 'left' : x = val; y = null; break;
                    case 'right' : x = (val == 'end')? val: -val; y = null;
                 } 
                break;
         }
    }
    return { fn: fn, x: x, y: y, dur: dur }
}

pso.getEv_FnType = function(str) {
    var re = /^(mouseover|mousedown|scrollBy|scrollTo|scrollToId|click)$/;
    if (re.test(str) ) {
        return str;
    }
    return '';
}

// Klassennamen mit Unterstrich liefern 
pso.get_DelimitedClass = function(cls) {
    if ( cls.indexOf('_') == -1 ) {
        return '';
    }
    var whitespace = /\s+/;
    if ( !whitespace.test(cls) ) {
        return cls;
    } else {
        var classes = cls.split(whitespace); 
        for(var i = 0; classes[i]; i++) { 
            if ( classes[i].indexOf('_') != -1 ) {
                return classes[i];
            }
        }
    }
}
