/*  Miya Validator Realtime 0.1 (2007-11-16)
 *  (c) 2007 Goonoo Kim
 *
 *  Miya Validator is under the terms of a GPL(General Public License).
 *
 *  Miya Validator required for this library!
 */

MiyaValidator.REALTIME_MESSAGE_DELAY = 500; // ms

MiyaValidator.prototype.enableRealtimeValidation = function(rtOptionsObj) {
    if (!document.getElementById
            || !document.createElement
            || !document.createTextNode)
        return;

    this.realtimeOptions = {
        delay: (rtOptionsObj && rtOptionsObj.delay
                ? rtOptionsObj.delay
                : MiyaValidator.REALTIME_MESSAGE_DELAY)
    };

    var miya = this;

    for (var i=0; i<this.conditions.length; i++) {
        var element = this.conditions[i].element;
        var elementType = MiyaValidator.getElementType(element);
        var optionsObj = this.conditions[i].optionsObj;

        var targetElements = [element];
        if (elementType == MiyaValidator.CHECK || elementType == MiyaValidator.RADIO) {
            if (element.form && element.name) {
                var targetElements = element.form.elements[element.name];
            };
        };

        if (optionsObj.option && optionsObj.span) {
            var span = parseInt(optionsObj.span, 10);
            for (var j = 1; j < span; j++) {
                var nextIndex = targetElements.length;
                targetElements[nextIndex] = MiyaValidator.getNextElement(targetElements[nextIndex - 1]);
            };
        };
        if (optionsObj.match) {
            var nextIndex = targetElements.length;
            targetElements[nextIndex] = MiyaValidator.getElement(optionsObj.match, this.form);
        };
        if (optionsObj.requiregroup) {
            var requiregroup = optionsObj.requiregroup;
            for (var j = 0; j < requiregroup.length; j++) {
                if (MiyaValidator.getElement(requiregroup[j], this.form) != element) {
                    var nextIndex = targetElements.length;
                    targetElements[nextIndex] = MiyaValidator.getElement(requiregroup[j], this.form);
                };
            };
        };

        for (var j = 0; j < targetElements.length; j++) {
            var targetElement = targetElements[j];
            if (targetElement.parentElements)
                targetElement.parentElements[targetElement.parentElements.length] = element;
            else
                targetElement.parentElements = [element];

            if (MiyaValidator.getElementType(element) == MiyaValidator.RADIO
                    || MiyaValidator.getElementType(element) == MiyaValidator.CHECK) {
                addEvent(targetElement, "change", function(){
                    miya.registerRealtimeEvent(this.parentElements);
                });
                addEvent(targetElement, "click", function(){
                    miya.registerRealtimeEvent(this.parentElements);
                });
            } else {
                addEvent(targetElement, "keyup", function(e){
                    var keycode;
                    if (window.event) 
                        keycode = window.event.keyCode;
                    else if (e) 
                        keycode = e.which;
    
                    // ignore tab & shift key
                    if (!keycode || keycode == 9 || keycode == 16) 
                        return;
    
                    miya.registerRealtimeEvent(this.parentElements);
                });
                addEvent(targetElement, "blur", function(){
                    miya.registerRealtimeEvent(this.parentElements);
                });
            };
        };
    };
};

MiyaValidator.prototype.registerRealtimeEvent = function(elements) {
    var miya = this;

    for (var i = 0; i < elements.length; i++) {
        if (elements[i].miyatimer) {
            clearTimeout(elements[i].miyatimer);
            elements[i].miyatimer = null;
        };
        var reserveRef = this.reserveRealtimeValidation(elements[i]);
        elements[i].miyatimer = setTimeout(reserveRef, this.realtimeOptions.delay);
    };
};

MiyaValidator.prototype.reserveRealtimeValidation = function(element) {
    var miya = this;

    return (function() {
        miya.realtimeValidate(element);
    });
};

MiyaValidator.prototype.realtimeValidate = function(element) {
    // get condition
    var condition = null;
    var _element = MiyaValidator.getElement(element, this.form);
    for (var i=0, l=this.conditions.length; i<l; i++) {
        if (this.conditions[i].element == _element) {
            condition = this.conditions[i];
            break;
        };
    };
    if (!condition) return; // ignore

    var result = this.validateCondition(condition);
    this.hideRealtimeMessage(condition);
    if (!result) {
        this.showRealtimeMessage(condition);
    };
};

MiyaValidator.prototype.hideRealtimeMessage = function(condition) {
    var blockParent = MiyaValidator.getBlockParent(condition.element);
    var childElements = blockParent.childNodes;
    
    for (var i=0; i<childElements.length; i++) {
        if (childElements[i].tagName && childElements[i].className == "miya-error-wrapper") {
            blockParent.removeChild(childElements[i]);
            break;
        };
    };
};

MiyaValidator.prototype.showRealtimeMessage = function(condition) {
    var blockParent = MiyaValidator.getBlockParent(condition.element);

    var errorSpan = document.createElement("span");
    errorSpan.className = "miya-error-message";
    var errorSpanWrapper = document.createElement("span");
    errorSpanWrapper.className = "miya-error-wrapper";
    var errorSpanMsgWrapper = document.createElement("span");
    errorSpanMsgWrapper.className = "miya-error-message-wrapper";
    var errorSpanDecoBefore = document.createElement("span");
    errorSpanDecoBefore.className = "miya-error-message-before";
    var errorSpanDecoAfter = document.createElement("span");
    errorSpanDecoAfter.className = "miya-error-message-after";

    errorSpan.appendChild(document.createTextNode(this.getErrorMessage("{message}")));
    errorSpanMsgWrapper.appendChild(errorSpan);

    errorSpanWrapper.appendChild(errorSpanDecoBefore);
    errorSpanWrapper.appendChild(errorSpanMsgWrapper);
    errorSpanWrapper.appendChild(errorSpanDecoAfter);

    // ...
    if (blockParent.getElementsByTagName
            && blockParent.getElementsByTagName("br")) {
        if (blockParent.all) {
            var blockNodes = blockParent.all;
        } else {
            var blockNodes = blockParent.getElementsByTagName("*");
        };
        var flagElement = false;
        var flagBr = false;
        var idxBr;
        for (var i=0; i<blockNodes.length; i++) {
            if (flagElement && blockNodes[i].tagName
                    && blockNodes[i].tagName.toLowerCase() == "br") {
                flagBr = true;
                idxBr = i;
            } else if (!flagElement && blockNodes[i] == condition.element) {
                flagElement = true;
            };
        };
        if (flagBr && idxBr) {
            blockParent.insertBefore(errorSpanWrapper, blockNodes[idxBr]);
        } else {
            blockParent.appendChild(errorSpanWrapper);
        };
    } else {
        blockParent.appendChild(errorSpanWrapper);
    };
};

MiyaValidator.getBlockParent = function(element) {
    var _element = element;
    var blockDisplayProperties = ["block", "list-item", "table-cell"];

    while (true) {
        if (_element.parentNode.tagName.toLowerCase() == "body") {
            return element.parentNode; // for ie5 (has bug that currentStyle.display always return inline)
        };

        _element = _element.parentNode;
        var value = _element.style["display"];
        if (!value && document.defaultView) {
            var css = document.defaultView.getComputedStyle(_element, null);
            value = css ? css["display"] : null;
        } else if (!value && _element.currentStyle) {
            value = _element.currentStyle.display;
        };

        for (var i = 0; i < blockDisplayProperties.length; i++) {
            if (value.toLowerCase() == blockDisplayProperties[i]) {
                return _element;
            };
        };
    };
};

/*
AddEvent Manager (c) 2005-2006 Angus Turnbull http://www.twinhelix.com
Free usage permitted as long as this credit notice remains intact.
*/
if (typeof addEvent != 'function')
{
    var addEvent = function(o, t, f, l)
    {
        var d = 'addEventListener', n = 'on' + t, rO = o, rT = t, rF = f, rL = l;
        if (o[d] && !l) return o[d](t, f, false);
        if (!o._evts) o._evts = {};
        if (!o._evts[t])
        {
            o._evts[t] = o[n] ? { b: o[n] } : {};
            o[n] = new Function('e',
            'var r = true, o = this, a = o._evts["' + t + '"], i; for (i in a) {' +
            'o._f = a[i]; r = o._f(e||window.event) != false && r; o._f = null;' +
            '} return r');
            if (t != 'unload') addEvent(window, 'unload', function() {
                removeEvent(rO, rT, rF, rL);
            });
        }
        if (!f._i) f._i = addEvent._i++;
        o._evts[t][f._i] = f;
    };
    addEvent._i = 1;
    var removeEvent = function(o, t, f, l)
    {
        var d = 'removeEventListener';
        if (o[d] && !l) return o[d](t, f, false);
        if (o._evts && o._evts[t] && f._i) delete o._evts[t][f._i];
    };
};
