RegExp.escape = function(str) {
    var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{} \
    return str.replace(specials, "\\$&");
}

function getY( oElement ){
    var iReturnValue = 0;
    while( oElement != null ) {
        iReturnValue += oElement.offsetTop;
        oElement = oElement.offsetParent;
    }
    return iReturnValue;
}

function getX( oElement ){
    var iReturnValue = 0;
    while( oElement != null ) {
        iReturnValue += oElement.offsetLeft;
        oElement = oElement.offsetParent;
    }
    return iReturnValue;
}

var caller = function(options) {
    var caller = this;

    var defaultDict = {password_required: '- Login is required if password specified.',
                       login_required: '- Password is required if login specified.',
                       to_required: '- "Call To" is a required field.',
                       from_required: '- "From" is a required field.',
                       from_format: '- Invalid "From" number specified',
                       init_error: '- Please specify toID, fromID, submitID and suggestorID!',
                       login_format: '- Login should be only email or alfanumeric word.',
                       to_format: '- "To" number should be only alphanumeric'};

    this.dict = options.dict ? options.dict : defaultDict;

    this.showError = function(msg, field) {
        if (caller[field]) {
            caller[field].innerHTML = msg;
            caller[field].style.display = 'block';
            if (field == 'messages' && caller.server_message) {
                caller.server_message.style.display = 'none';
            }
        } else if (field != 'messages') {
            //alert(msg);
        }
    }

    this.hideError = function(field) {
        if (caller[field]) {
            caller[field].style.display = 'none';
            debugger
            if (field == 'messages' && caller.server_message) {
                caller.server_message.style.display = 'none';
            }
        }
    }

    this.messages = options.messages ? document.getElementById(options.messages) : false;
    this.server_message = document.getElementById('server_message');
    this.suggestions_height = options.suggestions_height ? options.suggestions_height : '80px';

    if (!(options.toID && options.fromID && options.submitID && options.suggestorID)) {
        this.showError(caller.dict.init_error, 'messages');
        return false;
    }
    
    function restore_suggestions() {
        return eval('(' + get_cookie('numbers') + ')');
    }

    function get_cookie(check_name) {
        // first we'll split this cookie up into name/value pairs
        // note: document.cookie only returns name=value, not the other components
        var a_all_cookies = document.cookie.split( ';' );
        var a_temp_cookie = '';
        var cookie_name = '';
        var cookie_value = '';
        var b_cookie_found = false; // set boolean t/f default f
        
        for ( i = 0; i < a_all_cookies.length; i++ ) {
            // now we'll split apart each name=value pair
            a_temp_cookie = a_all_cookies[i].split( '=' );
            
            
            // and trim left/right whitespace while we're at it
            cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
            
            // if the extracted name matches passed check_name
            if ( cookie_name == check_name ) {
                b_cookie_found = true;
                // we need to handle case where cookie has no value but exists (no = sign, that is):
                if ( a_temp_cookie.length > 1 ) {
                    cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
                }
                // note that in cases where cookie is initialized but no value, null is returned
                return cookie_value;
                break;
            }
            a_temp_cookie = null;
            cookie_name = '';
        }
        
        if ( !b_cookie_found ) {
            return null;
        }
    }
    
    function set_cookie(name, value, expires, path, domain, secure) {
        // set time, it's in milliseconds
        var today = new Date();
        today.setTime( today.getTime() );
        
        /*
          if the expires variable is set, make the correct
          expires time, the current script below will set
          it for x number of days, to make it for hours,
          delete * 24, for minutes, delete * 60 * 24
        */
        if ( expires ) {
            expires = expires * 1000 * 60 * 60 * 24;
        }
        var expires_date = new Date( today.getTime() + (expires) );
        
        document.cookie = name + "=" + escape( value ) +
        ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
        ( ( path ) ? ";path=" + path : "" ) +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ( ( secure ) ? ";secure" : "" );
    }

    var dt = new Date();
    var current_ts = dt.getTime();

    if (!options.calculatePriority) {
        this.calculatePriority = function(a, b) {
            /*var a_weight = ((current_ts - a.call_time) / a.count);
            var b_weight = ((current_ts - b.call_time) / b.count)
            return (a_weight - b_weight);*/
            return (a.call_time - b.call_time);
        };
    } else {
        this.calculatePriority = options.calculatePriority;
    }

    this.suggestions = restore_suggestions();
    if (this.suggestions && this.suggestions.length) {
        this.suggestions.sort(this.calculatePriority);
    }
    
    this.to_field = document.getElementById(options.toID);
    this.to_field_error = document.getElementById(options.toID + '_error');

    this.to_field.onkeyup = function() {
        caller.showSuggestion(this.value, this, 'to');
    }

    this.from_field = document.getElementById(options.fromID);
    this.from_field_error = document.getElementById(options.fromID + '_error');

    this.validate_to = function() {
        if (caller.to_field.value && !caller.to_field.value.match(/^\+?[a-zA-Z0-9\s\-]+$/)) {
            caller.showError(caller.dict.to_format, 'to_field_error');
            return -1;
        }
    }

    this.validate_from = function() {
        if (caller.from_field.value && !caller.from_field.value.match(/^\+?[\d\s\-]{10,}$/)) {
            caller.showError(caller.dict.from_format, 'from_field_error');
            return -1;
        }
    }

    this.from_field.onchange = this.validate_from;

    this.from_field.onkeyup = function(event) {
        if (!event) event = window.event;
        
        caller.showSuggestion(this.value, this, 'from');
    }

    this.from_field.onclick = function() {
        caller.hideError('from_field_error');
    }

    this.from_field.onblur = this.to_field.onblur = function() {
        setTimeout(function() {caller.suggestor.style.display = 'none';}, 2000);
    }

    this.login_field = false;
    if (options.loginID) {
        this.login_field = document.getElementById(options.loginID);
        if (this.login_field) {
            this.login_field_error = document.getElementById(options.loginID + '_error');

            this.login_field.onclick = function() {
                caller.hideError('login_field_error');
            }

            this.validate_login = function() {
                if (caller.login_field.value && !caller.login_field.value.match(/^([a-z0-9\.]+|[a-z0-9\.]+@[a-z0-9\.]+\.[a-z0-9\.]{2,4})$/i)) {
                    caller.showError(caller.dict.login_format, 'login_field_error');
                    return false;
                }

                return true;
            }

            this.login_field.onblur = this.validate_login;
            this.login_field.onchange = this.validate_login;
        }
    }

    this.password_field = false;
    if (options.passwordID) {
        this.password_field = document.getElementById(options.passwordID);
        if (this.password_field) {
            this.password_field_error = document.getElementById(options.passwordID + '_error');

            this.password_field.onclick = function() {
                caller.hideError('password_field_error');
            }
        }
    }

    this.submitButton = document.getElementById(options.submitID);
    this.suggestor = document.getElementById(options.suggestorID);

    this.to_field.onclick = function() {
        caller.hideError('to_field_error');
    }

    this.suggest_to = document.getElementById('suggest_to');

    if (this.suggest_to) {
        this.suggest_to.onclick = function () {
            if (caller.suggestor.style.display != 'block') {
                caller.showSuggestion('', caller.to_field, 'to', true);
            } else {
                caller.suggestor.style.display = 'none';
            }
        }
    }

    this.showSuggestion = function(number, element, fname, force) {
        if (number.length > 0 && this.suggestions && this.suggestions.length > 0 || force) {
            var re = new RegExp('^(' + RegExp.escape(number) + ').*$', 'g');
            var to_suggest = [];

            for (var key in this.suggestions) {
                if (String(this.suggestions[key].number).match(re) || force) {
                    to_suggest.push(this.suggestions[key]);
                }
            }
            
            if (to_suggest.length > 0) {
                var html = '';
                for (var ind in to_suggest) {
                    if (to_suggest[ind].number) {
                        html += '<div class="pointer" number="'+to_suggest[ind].number+'">'+
                                '<a class="nodecoration" number="'+to_suggest[ind].number.replace(/\++/g, ' ')+'">' +
                                to_suggest[ind].number.replace(/\++/g, ' ')+'</a></div>';
                    }
                }
                this.suggestor.innerHTML = html;
                this.suggestor.style.left = getX(element) + 'px';
                this.suggestor.style.top = getY(element) + element.offsetHeight + 'px';
                this.suggestor.style.width = element.offsetWidth + 'px';
                this.suggestor.style.display = 'block';
                this.suggestor.style.height = caller.suggestions_height;
                
                this.suggestor.setAttribute('field', fname);
            } else {
                this.suggestor.style.display = 'none';
            }
        }
    }

    this.suggestor.onclick = function(event) {
        if (!event) event = window.event;
        
        var originalTarget = event.originalTarget ? event.originalTarget : event.srcElement;
        var top_target = event.currentTarget ? event.currentTarget : originalTarget.parentNode;
        var field = top_target.getAttribute('field');
        var number = originalTarget.getAttribute('number');
        
        if (field && number) {
            caller[field + '_field'].value = number;
            top_target.innerHTML = '';
            top_target.style.display = 'none';
            top_target.removeAttribute('field');
            caller.hideError(field + '_field_error');
        }
    }

    this.submitButton.onclick = function(event) {
        var message = [];

        caller.messages.innerHTML = "";
        if (!caller.to_field.value) {
            var msg = caller.dict.to_required;
            message.push(msg);
            caller.showError(msg, 'to_field_error');
        }
        
        if (!caller.from_field.value) {
            var msg = caller.dict.from_required;
            message.push(msg);
            caller.showError(msg, 'from_field_error');
        }

        if (caller.validate_to() == -1) {
            message.push(caller.dict.to_format);
        }

        if (caller.validate_from() == -1) {
            message.push(caller.dict.from_format);
        }

        if (caller.login_field && caller.password_field && 
            (caller.login_field.value || caller.password_field.value)) {
            var msg = '';
            if (!caller.password_field.value) {
                msg = caller.dict.password_required;
                caller.showError(msg, 'password_field_error');
            }

            if (!caller.login_field.value) {
                msg = caller.dict.login_required;
                caller.showError(msg, 'login_field_error');
            }

            if (!caller.validate_login()) {
                message.push(caller.dict.login_format);
            }
            
            if (msg) {
                message.push(msg);
            }
        }

        if (message.length) {
            caller.showError(message.join("<br />"), 'messages');
            return false;
        }
    }
};
