///////////////////////////////////////////////////////////////////////////////
//
// Name:         synforce.js
//
// Description:  Implements real-time syntax enforcement for input text boxes
//               in HTML forms.
//
// Author:       Stephen Collyer <scollyer@netspinner.co.uk>
//
// Copyright:    (C) Netspinner Ltd 2004, 2005
//
// License:      This product is licensed under the LGPL. 
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
//
// You should have received a copy of the GNU General Public License along 
// with this program; if not, write to the 
//
// Free Software Foundation, Inc., 
// 59 Temple Place, Suite 330, 
// Boston, MA 02111-1307 
// USA 
//
///////////////////////////////////////////////////////////////////////////////

// validaters

function validate_date_list(el, ev, warn_timeout)
{
   var warn_text = ' : you must specify a comma or space separated list of dates, like 1,16,31 or 1 16 31';
   var min_match_list    = [ /^[1-9]\d?([ ,]([1-9]\d?)?)*$/ ];
   var min_no_match_list = [ /,,/, /  /, /, ,/, /[4-9]\d/, /3[2-9]/ ];
   return validate_field(el, ev, warn_timeout, min_match_list, min_no_match_list, warn_text);
}

function validate_email(el, ev, warn_timeout)
{
    var warn_text = ' No debe dejar espacios en Blanco!! Ingrese Su Email Correctamente...';
    return validate_field(el, ev, warn_timeout, [ /^([a-zA-Z])[-\.\w]*(@(((\w[-\w]*)\.?)*)?)?$/ ], [], warn_text);
}

function validate_identifier(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in an identifier name field';
    return validate_field(el, ev, warn_timeout, [ /^\w+$/ ], [ ], warn_text);
}

function validate_integer(el, ev, warn_timeout)
{
    var warn_text = ' No Es un Digito de Cédula, Ingrese solo Números...';
    return validate_field(el, ev, warn_timeout, [ /^\d+$/ ], [ /^0\d/ ], warn_text);
}

function validate_minute_list(el, ev, warn_timeout)
{
   var warn_text = ' : you must specify a comma or space separated list of minutes, like 15,23,45 or 15 23 45';
   var min_match_list    = [ /^[0-9]\d?([ ,]([1-9]\d?)?)*$/ ];
   var min_no_match_list = [ /,,/, /  /, /, ,/, /[6-9]\d/ ];
   return validate_field(el, ev, warn_timeout, min_match_list, min_no_match_list, warn_text);
}

function validate_name(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a name field';
    return validate_field(el, ev, warn_timeout, [ /^[a-zA-Z][-a-zA-Z ]*$/ ], [ /--/ ], warn_text);
}

function validate_price(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a price field';
    return validate_field(el, ev, warn_timeout, [ /^\d+(\.(\d{0,2})?)?$/ ], [], warn_text);
}

function validate_signed_integer(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a signed whole number';
    return validate_field(el, ev, warn_timeout, [ /^[+-]?(\d+)?$/ ], [ /^0\d/, /^[+-]0/ ], warn_text);
}

function validate_telno(el, ev, warn_timeout)
{
    var warn_text = ' No es Digito Número Telefónico Valido..';
    return validate_field(el, ev, warn_timeout, [ /^\d[\d ]*$/, /^\+?(\d+( \((\d\)?)?)?)?(\d[\d ]*)?$/ ], [ /  /, /\(\d\d/ ], warn_text);
}

function validate_title(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a title field';
    return validate_field(el, ev, warn_timeout, [ /^[a-zA-Z][-a-zA-Z ]*$/ ], [], warn_text);
}

function validate_UK_postcode(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a UK postcode field';
    return validate_field(el, ev, warn_timeout, [ /^[A-Z][A-Z\d ]*$/ ], [ /  / ], warn_text);
}

function validate_USA_state_abbrev(el, ev, warn_timeout)
{
    var warn_text = ' is not allowed here in a state abbreviation';
    var match_re = /^(AK?|AL?|AR?|AZ?|CA?|CO?|CT?|DC?|DE?|FL?|GA?|HI?|IA?|ID?|IL?|IN?|KS?|KY?|LA?|MA?|MD?|ME?|MI?|MN?|MO?|MS?|MT?|NC?|ND?|NE?|NH?|NJ?|NM?|NV?|NY?|OH?|OK?|OR?|PA?|RI?|SC?|SD?|TN?|TX?|UT?|VA?|VT?|WA?|WI?|WV?|WY?)$/;
    return validate_field(el, ev, warn_timeout, [ match_re ], [ ], warn_text);
}

// finalizers

function id_finalize_email(id)
{
    return finalize_email(document.getElementById(id));
}

function finalize_email(el, domain_finalizer)
{
    var value = el.value;
    var tlds = '[^a-zA-Z](af|al|dz|as|ad|ao|ai|aq|ag|ar|am|aw|ac|au|at|az|bh|bd|bb|by|be|bz|bj|bm|bt|bo|ba|bw|bv|br|io|bn|bg|bf|bi|kh|cm|ca|cv|cf|td|gg|je|cl|cn|cx|cc|co|km|cg|cd|ck|cr|ci|hr|cu|cy|cz|dk|dj|dm|do|tp|ec|eg|sv|gq|er|ee|et|fk|fo|fj|fi|fr|gf|pf|tf|fx|ga|gm|ge|de|gh|gi|gr|gl|gd|gp|gu|gt|gn|gw|gy|ht|hm|hn|hk|hu|is|in|id|ir|iq|ie|im|il|it|jm|jp|jo|kz|ke|ki|kp|kr|kw|kg|la|lv|lb|ls|lr|ly|li|lt|lu|mo|mk|mg|mw|my|mv|ml|mt|mh|mq|mr|mu|yt|mx|fm|md|mc|mn|ms|ma|mz|mm|na|nr|np|nl|an|nc|nz|ni|ne|ng|nu|nf|mp|no|om|pk|pw|pa|pg|py|pe|ph|pn|pl|pt|pr|qa|re|ro|ru|rw|kn|lc|vc|ws|sm|st|sa|sn|sc|sl|sg|sk|si|sb|so|za|gs|es|lk|sh|pm|sd|sr|sj|sz|se|ch|sy|tw|tj|tz|th|bs|ky|tg|tk|to|tt|tn|tr|tm|tc|tv|ug|ua|ae|uk|us|um|uy|uz|vu|va|ve|vn|vg|vi|wf|eh|ye|yu|zm|zw|aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro)';

    var tlds_re = RegExp(tlds + '\.?$');

    if (value.match(/^[-\.\w]+$/))
    {
        el.value += '@domain' + '.' + domain_finalizer;
    }
    else if (value.match(/^[-\.\w]+@$/))
    {
        el.value += 'domain' + '.' + domain_finalizer;
    }
    else if (value.match(/^[-\.\w]+@([-\w]+\.)*[-\w]+$/))
    {
        if (! value.match(tlds_re)) el.value += '.' + domain_finalizer;
    }
    else if (value.match(/^[-\.\w]+@([-\w]+\.)+$/))
    {
        if (! value.match(tlds_re)) el.value += domain_finalizer;
    }
}

function id_finalize_price(id)
{
    return finalize_price(document.getElementById(id));
}

function finalize_price(el)
{
    var value = el.value;

    if (value.match(/^\d+$/))
    {
        el.value += '.00';
    }
    else if (value.match(/^\d+.$/))
    {
        el.value += '00';
    }
    else if (value.match(/^\d+.\d$/))
    {
        el.value += '0';
    }
}

function finalize_ucfirst(el)
{
    el.value = el.value.toLowerCase();
    el.value = el.value.replace(/\b(\w)/g, 
                                function(word) 
                                  { return word.substring(0,1).toUpperCase() +
                                           word.substring(1); } 
                               );
}

// implementation functions

function validate_field(el, ev, warn_timeout, match_list, no_match_list, warn_text)
{
    if (!ev) ev = window.event;

    // preserve the start value for replacement in IE if the
    // user enters an invalid character

    var start_text = el.value;

    // allow backspace, deletion and arrow keys

    var keycode = get_keycode(ev);
    if (keycode == 8 || keycode == 9)
    {
        return true;
    }

    // the following unusual code correctly distinguises Mozilla 
    // char key presses from non-char keypresses (and also detects
    // when we get an IE keypress) which allows us to process chars
    // locally, but allow the browser to handle arrow/delete keys etc
    // (For some reason, trying to refer to ev.keyCode in the test
    // below breaks the Moz test, so we use document.selection instead
    // which is IE only)

    if (ev.which)
    {
        // it's a Mozilla keypress - process it here

        ;
    }
    else if (document.selection)
    {
        // it's an IE keypress - process it here

        ;
    }
    else
    {
        return true;
    }
 
    var key = String.fromCharCode(keycode);
    var cur_pos = cursor_pos(el);

    var new_value = insert_at_cursor(el, key);

    // does the new string match one of the 'must match' list ?

    var no_match = true;
    for (var i = 0; i < match_list.length; i++)
    {
        var regex = match_list[i];

        if (new_value.match(regex))
        {
            no_match = false;
            break;
        }
    }
    
    if (no_match)
    {
        if (ev.keyCode) 
        {
            // IE support - replace original value to erase illegal char
            // and reset cursor position
    
            el.value = start_text;
            set_cursor_pos(el, cur_pos);
    
            ev.returnValue = false;
        }
    
        // illegal character - show the user a warning and prevent the
        // character from being shown
    
        display_warning(el, warn_timeout, key + warn_text);
    
        return false;
    }

    // does the new string fail to match the 'must not match' list ?

    for (var i = 0; i < no_match_list.length; i++)
    {
        var regex = no_match_list[i];

        if (new_value.match(regex))
        {
            if (ev.keyCode) 
            {
                // IE support - replace original value to erase illegal char
                // and reset cursor position
    
                el.value = start_text;
                set_cursor_pos(el, cur_pos);
    
                ev.returnValue = false;
            }
    
            // illegal character - show the user a warning and prevent the
            // character from being shown
    
            display_warning(el, warn_timeout, key + warn_text);
    
            return false;
        }
    }

    if (ev.keyCode)
    {
        // IE support - ensure we don't insert the valid character again

        ev.returnValue = false;
    }
    else
    {
        // Mozilla - just let the browser insert the valid character

        return true;
    }

}

function get_keycode(ev)
{
    if (ev.keyCode)
    {
        return ev.keyCode;
    }
    else
    {
        return ev.which;
    }
}

// insert_at_cursor is based on code released under the GPL in the
// PHPMySQLAdmin project.

function insert_at_cursor(el, str) {

    // IE support

    var new_value;
    if (document.selection) 
    {
        el.focus();
        var sel = document.selection.createRange();
        sel.text = str;
        new_value = el.value;
    }

    // Mozilla/Netscape support
 
    else if (el.selectionStart || el.selectionStart == '0') 
    {
        var start_pos = el.selectionStart;
        var end_pos = el.selectionEnd;
        new_value = el.value.substring(0, start_pos)
                        + str
                        + el.value.substring(end_pos, el.value.length);
    } 
    else 
    {
        new_value += str;
    }

    return new_value;
}

function set_cursor_pos(el, cur_pos)
{
    if (cur_pos == -1) return;
    el.focus();
    var rng = el.createTextRange();
    rng.move("Character", cur_pos - 1)
    rng.select();
}

function cursor_pos(el)
{
    var i = el.value.length + 1;

    if (el.createTextRange)
    {
        var cursor = document.selection.createRange().duplicate();
        while (cursor.parentElement() == el
               && cursor.move("character", 1) == 1)
        {
             --i; 
        }
    } 
    return (i == el.value.length+1) ? -1 : i;
}

function display_warning(el, timeout, text)
{
    var geom_obj = getGeom(el);

    var warning_box_HTML = get_warning_box_HTML(text);

    var warning_box = document.getElementById('warning_box');
    if (! warning_box)
    {
        add_warning_box();
    }

    warning_box.innerHTML = warning_box_HTML;
    warning_box.style.left = geom_obj.x;
    warning_box.style.top  = geom_obj.y - 65;
    warning_box.style.display = 'inline';

    setTimeout("remove_warning()", (1000 * timeout));
}

function add_warning_box()
{
    var warning_box_HTML = get_warning_box_HTML('dummy');

    document.body.innerHTML += 
        '<span id="warning_box" style="position:absolute; display: none;">' +
         warning_box_HTML                                                   +
        '</span>';
}

function get_warning_box_HTML(text)
{
    return '<table class="Warning_table" id="info_table_1" cellspacing="2" width="350px">' +
           '<tr class="Warning_table_row">'                                                +
           '<td class="Warning_table_data">'                                               +
           text                                                                            +
           '</td>'                                                                         +
           '</tr>'                                                                         +
           '</table>'; 
}

function remove_warning()
{
    var warning_box = document.getElementById('warning_box');
    warning_box.style.display = 'none';
}

function getGeom(el)
{
    var object   = new Object();
    object.x     = el.offsetLeft;
    object.y     = el.offsetTop;
    var parent    = el.offsetParent;
    object.width  = el.offsetWidth;
    object.height = el.offsetHeight;
    while(parent != null)
    {
        object.x += parent.offsetLeft;
        object.y += parent.offsetTop;
        parent   = parent.offsetParent;
    }
    return object;
}

