/* *
 * mod_fade - Modul zum Faden
 * --------------------------
 * Beispiel:
 * p:-js-action(:hover) {
 *     -js-fade-steps  : 12;
 *     -js-fade-delay  : 100ms;
 * }
 */

var fadeUnits   = new Array();
var timers      = new Array();


modules['mod_fade'] = function (theRules) {
    for (i=0; i<theRules.length; i++) {
        var theSelector = theRules[i].selector;
        if (theSelector.indexOf(':-js-action(')<0) continue; // doesn't concern fading
        var offSel = theSelector.replace(/:-js-action\((.*)\)/, '');
        var  onSel = theSelector.replace(/:-js-action\((.*)\)/, '$1');
        var action = theSelector.replace(/^(.*?):-js-action\((.*)\)(.*?)$/, '$2');
        
        var theOffRule = getRules(offSel);
        var  theOnRule = getRules( onSel);
        if (!theOffRule || !theOnRule) continue;
        var theOffStyles = new Array();
        var  theOnStyles = new Array();
        for (var j in theOnRule.style) {
            if (j*1!=j && j!='cssText' && j!='accelerator' && theOnRule.style[j]!='' && typeof theOnRule.style[j] == 'string') {
                if (j=='background') continue;
                theOnStyles[j] = theOnRule.style[j];
                theOffStyles[j] = theOffRule.style[j];
            }
        }
        removeRules(onSel);
        
        var numSteps = parseInt(theRules[i].properties['-js-fade-steps'].value);
        var delay    = parseInt(theRules[i].properties['-js-fade-delay'].value);
        
        var fUIndex = fadeUnits.length;
        
        fadeUnits[fUIndex] = new FadeUnit(offSel, numSteps, delay, action, theOffStyles, theOnStyles);
        
        switch (action) {
            case ':hover':
                var theElems = cssQuery(offSel);
                for (var c=0; c<theElems.length; c++) {
                    var theElem = theElems[c];
                    if (!theElem) continue;
                    theElem.onmouseover = mod_fade_over;
                    theElem.onmouseout  = mod_fade_out;
                    theElem.fade        = new Array();
                    theElem.fade.fUIndex = fUIndex;
                    theElem.fade.state   = 0;
                    theElem.fade.dir     = 0;
                    theElem.fade.id      = fUIndex+'_'+c;
                }
                break;
        }
    }
}
function FadeUnit(selector, numSteps, delay, action, offstyle, onstyle) {
    this.selector   = selector;
    this.numSteps   = numSteps;
    this.delay      = delay;
    this.action     = action;
    this.propType   = new Array();
    this.offstyle   = new Array();
    this.onstyle    = new Array();
    this.stateStyles= new Array();
    
    for (var i in onstyle) {
        this.propType[i] = cssPropType(onstyle[i]); if (this.propType[i]=='0') this.propType[i] = cssPropType(offstyle[i]);
        this.offstyle[i] = cssPropValue(offstyle[i], this.propType[i]);
        this.onstyle [i] = cssPropValue(onstyle [i], this.propType[i]);
        //debugprint('Property: '+i+', Type: '+this.propType[i]+', Value: '+onstyle[i]);
        this.stateStyles[i] = new Array();
        for (var c=0; c<this.numSteps; c++) {
            var ratio = c / this.numSteps;
            this.stateStyles[i][c] = cssMakeProp(fadeVals(this.offstyle[i], this.onstyle [i], ratio, this.propType[i]), this.propType[i])
            //debugprint('Property: '+i+', Type: '+this.propType[i]+', Value: '+this.stateStyles[i][c]);
        }
    }
}
function mod_fade_over() {
    this.fade.dir = 1;
    var fUIndex = this.fade.fUIndex;
    if (removeTimers) removeTimers(this.fade.id);
    addTimers(this, this.fade.id, fadeUnits[fUIndex].numSteps - this.fade.state, fadeUnits[fUIndex].delay);
}
function mod_fade_out() {
    this.fade.dir = -1;
    var fUIndex = this.fade.fUIndex;
    if (removeTimers) removeTimers(this.fade.id);
    addTimers(this, this.fade.id, this.fade.state, fadeUnits[fUIndex].delay);
}
function removeTimers(fID) {
    if (!timers[fID]) return;
    for (var i=0; i<timers[fID].length; i++) {
        clearTimeout(timers[fID][i]);
    }
}
function addTimers(theElem, fID, numSteps, delay) {
    if (!theElem) return false;
    timers[fID] = new Array();
    for (var i=0; i<numSteps; i++) {
        timers[fID][i] = setTimeout(function(){ fadeStep(theElem); }, i*delay);
    }
}
function fadeStep(theElem) {
    if (!theElem) return false;
    theElem.fade.state += theElem.fade.dir;
    if (theElem.fade.state<0) theElem.fade.state = 0;
    var numSteps = fadeUnits[theElem.fade.fUIndex].numSteps;
    if (theElem.fade.state>=numSteps) theElem.fade.state = numSteps-1;
    applyFadedProperties(theElem, theElem.fade.fUIndex, fadeUnits[theElem.fade.fUIndex]);
}
function applyFadedProperties(theElem, fUIndex, fadeUnit) {
    for (var i in fadeUnit.onstyle) {
        theElem.style[i] = fadeUnit.stateStyles[i][theElem.fade.state]; continue;
        var newVal = fadedProperty(fadeUnit.propType[i], fadeUnit.offstyle[i], fadeUnit.onstyle[i], theElem.fade.state / fadeUnit.numSteps);
        theElem.style[i] = newVal;
    }
}
function fadedProperty(propType, propValueOff, propValueOn, ratio) {
    var fadeVal     = fadeVals(propValueOff, propValueOn, ratio, propType);
    var newProp = cssMakeProp(fadeVal, propType);
    return newProp;
}
function cssPropType(propValue) {
    propValue = propValue.trim().toLowerCase();
    var tests = {
        'num_'      : /^-?[0-9]*\.?[0-9]+$/,
        'num_em'    : /^-?[0-9]*\.?[0-9]+em$/,
        'num_ex'    : /^-?[0-9]*\.?[0-9]+ex$/,
        'num_px'    : /^-?[0-9]*\.?[0-9]+px$/,
        'num_in'    : /^-?[0-9]*\.?[0-9]+in$/,
        'num_cm'    : /^-?[0-9]*\.?[0-9]+cm$/,
        'num_mm'    : /^-?[0-9]*\.?[0-9]+mm$/,
        'num_pt'    : /^-?[0-9]*\.?[0-9]+pt$/,
        'num_pc'    : /^-?[0-9]*\.?[0-9]+pc$/,
        'num_%'     : /^-?[0-9]*\.?[0-9]+%$/,
        'rgb_#RGB'  : /^#[0-9a-f]{3}$/,
        'rgb_#RRGGBB': /^#[0-9a-f]{6}$/,
        'rgb_rgb()' : /^rgb\([0-9]+, [0-9]+, [0-9]+\)$/,
        'multinum'  : /^-?[0-9]*\.?[0-9]+\w*\s+-?[0-9]+.*$/
    }
    for (var i in tests) {
        if (propValue.match(tests[i])) return i;
    }
    return 'rubbish';
}
function cssPropValue(propValue, propType) {
    if (propType=='rubbish') return propValue;
    if (propType.match(/^num_/g)) return parseFloat(propValue);
    if (propType.match(/^rgb_/g)) {
        propValue = propValue.toLowerCase();
        var rc = new Array();
        rc.r = 0;
        rc.g = 0;
        rc.b = 0;
        switch (propType) {
            case 'rgb_#RGB':
                var result = propValue.match(/^#([0-9a-f]{1})([0-9a-f]{1})([0-9a-f]{1})$/);
                if (!result) return rc;
                rc.r = hexToDec(result[1]+''+result[1]);
                rc.g = hexToDec(result[2]+''+result[2]);
                rc.b = hexToDec(result[3]+''+result[3]);
                break;
            case 'rgb_#RRGGBB':
                var result = propValue.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/);
                if (!result) return rc;
                rc.r = hexToDec(result[1]);
                rc.g = hexToDec(result[2]);
                rc.b = hexToDec(result[3]);
                break;
            case 'rgb_rgb()':
                var result = propValue.match(/^rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)$/);
                if (!result) return rc;
                rc.r = parseInt(result[1]);
                rc.g = parseInt(result[2]);
                rc.b = parseInt(result[3]);
                break;
        }
        return rc;
    }
    if (propType=='multinum') {
        var theValues = propValue.split(' ');
        var rc = new Array();
        for (var i=0; i<theValues.length; i++) {
            rc[i] = new Array();
            rc[i].propType  = cssPropType(theValues[i]);
            rc[i].propValue = cssPropValue(theValues[i], rc[i].propType);
            //debugprint('rc['+i+']: { propType: '+rc[i].propType+', propValue: '+rc[i].propValue+' }');
        }
        return rc;
    }
    return 0;
}
function fadeVals(val0, val1, ratio, propType) {
    if (propType=='rubbish') return val0;
    if (propType.match(/^num_/g)) return val0*(1-ratio) + val1*ratio;
    if (propType.match(/^rgb_/g)) {
        var rc = new Array();
        rc.r = Math.round(fadeVals(val0.r, val1.r, ratio, 'num_1'));
        rc.g = Math.round(fadeVals(val0.g, val1.g, ratio, 'num_1'));
        rc.b = Math.round(fadeVals(val0.b, val1.b, ratio, 'num_1'));
        return rc;
    }
    if (propType=='multinum') {
        var rc = new Array();
        for (var i=0; i<val0.length && i<val1.length; i++) {
            rc[i] = new Array(),
            rc[i].propType  = val0[i].propType;
            rc[i].propValue = fadeVals(val0[i].propValue, val1[i].propValue, ratio, val1[i].propType);
        }
        return rc;
    }
}
function cssMakeProp(propVal, propType) {
    if (propType=='rubbish') return propVal;
    var result = propType.match(/^num_(.*)$/);
    if (result) return propVal+result[1];
    if (propType.match(/^rgb_/g)) return 'rgb('+propVal.r+', '+propVal.g+', '+propVal.b+')';
    if (propType=='multinum') {
        var rc = '';
        for (var i=0; i<propVal.length; i++) {
            rc += ' '+cssMakeProp(propVal[i].propValue, propVal[i].propType);
        }
        return rc.trim();
    }
}
function hexToDec(hex) {
    hex = hex.toUpperCase();
    var len = hex.length;
    var result = 0;
    var hex2dec = {
        '0': 0,  '1': 1,  '2': 2,  '3': 3, '4': 4,  '5': 5, 
        '6': 6,  '7': 7,  '8': 8,  '9': 9, 'A': 10, 'B': 11, 
        'C': 12, 'D': 13, 'E': 14, 'F': 15
    }
    for (var i=0; i<len; i++) {
        result += hex2dec[hex.charAt(i)]*Math.pow(16, len-i-1);
    }
    return result;
}
function getRules(selector) {
    var theRules = new Array();
    for (var i in document.styleSheets) {
        if (document.styleSheets[i].disabled) continue;
        if (!document.styleSheets[i].cssRules && !document.styleSheets[i].rules) continue;
        if (!document.styleSheets[i].cssRules) document.styleSheets[i].cssRules = document.styleSheets[i].rules;
        for (var j in document.styleSheets[i].cssRules) {
            if (!document.styleSheets[i].cssRules[j]) continue;
            if (!document.styleSheets[i].cssRules[j].selectorText) continue;
            if (document.styleSheets[i].cssRules[j].selectorText.toLowerCase()==selector.toLowerCase()) theRules[theRules.length] = document.styleSheets[i].cssRules[j];
        }
    }
    return theRules[0];
}
function removeRules(selector) {
    for (var i in document.styleSheets) {
        if (document.styleSheets[i].disabled) continue;
        for (var j in document.styleSheets[i].cssRules) {
            if (!document.styleSheets[i].cssRules[j]) continue;
            if (!document.styleSheets[i].cssRules[j].selectorText) continue;
            if (document.styleSheets[i].cssRules[j].selectorText.toLowerCase()==selector.toLowerCase()) {
                if (document.styleSheets[i].deleteRule) document.styleSheets[i].deleteRule(j);
                if (document.styleSheets[i].removeRule) document.styleSheets[i].removeRule(j);
            }
        }
    }
}
