/*
** Copyright 2004-2005 by Pixta, Inc.,
** 926A Diablo Ave., No. 542, Novato, CA 94947 U.S.A.
** All rights reserved.
**
** This software is the confidential and proprietary information
** of Pixta, Inc.
**
** util.js
**
** Various utility routines, including unit conversion
**
** Notes:
**
*/

/*
** Unit conversions: US (ft/in) => metric (meters)
**                   metric => US
*/

var kMetersPerFoot      = 0.3048;

// Returns MMM.mm
function feet_to_meters(feet, inches)
{
    var meters;
    
//alert('before: ' + feet + ' ' + inches);


    // Make sure they're numbers
    feet = Number(feet);
    inches = Number(inches);
    
//alert('after: ' + feet + ' ' + inches);
    
    if (isNaN(feet))
        feet = 0;
    if (isNaN(inches))
        inches = 0;    

    // total value in feet
    feet += inches / 12;

    // Convert, round to 3 decimal places (1 mm precision)
    meters = feet * kMetersPerFoot;
    return Math.round(meters * 1000) / 1000;
}   

// Returns array of [feet, inches]
function meters_to_feet(meters)
{
    var feet, parts;
    
    // Make sure it's a number
    meters = Number(meters);
    if (isNaN(meters))
        return null;
        
    feet = meters / kMetersPerFoot;
    
    // Convert fractional part to inches
    parts = Array(2);
    parts[0] = Math.floor(feet);
    parts[1] = (feet - parts[0]) * 12;
    
    // Round inches to 2 decimal places
    parts[1] = Math.round(parts[1] * 100) / 100;
    
    
//alert(parts[0] + ' ' + parts[1]);
    
    return parts;
}    

// Given values for feet and inches, with any values, convert them to canonical
// format (feet as int, inches < 12)
function normalize_US_units(feet, inches)
{
    var parts;
    
    // Dear god they come in as strings...
    feet = +feet;
    inches = +inches;
    
    inches += feet * 12;
    feet = Math.floor(inches / 12);
    inches = inches % 12;
    
    // Round inches to 2 decimal places
    parts = Array(2);
    parts[0] = feet;
    parts[1] = Math.round(inches * 100) / 100;
    
    return parts;
}

/*
** Validate form fields for proper input
** Called from onkeypress()
*/
function validate_float_field(field, form_name)
{
    // Lame way to do it - need to figure out event model...
    
    var f, value, decpos, substr;
    
    f = document[form_name];
    value = f[field].value;
    
//alert('before: ' + value);
    
    value = value.replace(/[^0-9.]*/g, '');
    //value = value.replace(/[^0-9.]*/g, '');
    
    // Make sure there's only one decimal point
    decpos = value.indexOf('.');
    if (decpos >= 0 && decpos != value.lastIndexOf('.'))
    {
        substr = value.substring(decpos + 1);

// alert(value + ' ' + substr + ' ' + value.substring(0, decpos + 1));
        
        substr = substr.replace(/[.]+/g, '');
        value = value.substring(0, decpos + 1) + substr;
    }
    
    f[field].value = value;
}    

function validate_int_field(field, form_name)
{
    // Lame way to do it - need to figure out event model...
    
    var f, value, decpos, substr;
    
//    f = document.<?php echo $form_name ?>;

    f = document[form_name];
    value = f[field].value;
    
    value = value.replace(/[^0-9]*/g, '');
    
    f[field].value = value;
}    

// Format a floating point number with the requested number of decimal places
function format_number(num, num_places)
{
    var dec_part, frac_part;

    // Don't mess with non-numeric input
    // Beware of silent conversions from null/'' => 0, hence the '===' operator!
    if (num === null || num === '' || isNaN(num))
        return num;
    
    // Make sure this is reasonable
    if (!num_places || isNaN(num_places))
        num_places = 0;

    // See if the JS v1.5 function is available
    if (num.toFixed)
        return num.toFixed(num_places);

    // No, do it the hard way
    dec_part = Math.floor(num);
    
    if (num_places == 0)
        return dec_part;
    
    // Get fractional part, convert to int of right size, jam two parts together
    frac_part = num - dec_part;
    frac_part = Math.round(frac_part * Math.pow(10, num_places));

    // Special case for the right number of trailing zeroes
    if (frac_part == 0)
    {
        frac_part = '';
        for (var i = 0; i < num_places; i++)
            frac_part += '0';
    }
    
    num = dec_part + '.' + frac_part;
    
    return num;
}    

// Get the full date from a Date object, in YYYY-MM-DD format
/* This must change to allow for configurable format, separator */
function get_date_string(date_obj)
{
    var year, month, day;
    
    if (!date_obj)
        return null;
    
    // getFullYear() only exists in Javascript v 1.2, JScript (MS) v 3.0
    // getYear() is deprecated as ECMAScript v 3.0
    
    if (date_obj.getFullYear)
    {
        // returns full year
        year = date_obj.getFullYear();
    }
    else
    {
        // returns year - 1900
        year = date_obj.getYear() + 1900;
    }
    
    // Returns 0 - 11!
    month = date_obj.getMonth() + 1;
    
    // returns 1 - 31
    day = date_obj.getDate();
    
    // alert('get_date_string: ' + date_obj.toString() + ' ' + year + '-' + month + '-' + day);
    
    // Force leading zeros for month and day
    
    return year + '-' + month + '-' + day;
        
}

// escape() and unescape() are deprecated as of Javascript v1.5, but 
// encodeURI()/encodeURIComponent() and decodeURI()/decodeURIComponent()
// don't arrive on the scene until v1.5, either.
// In particular, IE < 5.5 does not support JS v1.5.
// Gotta love web development.
//
function encodeString(str)
{
    if (encodeURIComponent)
        return encodeURIComponent(str);
    else    
        return escape(str);
}

function decodeString(str)
{
    if (decodeURIComponent)
        return decodeURIComponent(str);
    else
        return unescape(str);
}

// Returns all fields, except for buttons, on page as 'f1=v1&f2=v2...'
// Includes hidden fields
function get_form_fields_as_params(form_id)
{
    var f, fields, params, i;
    
    // Get the form and go through all fields
    // Use first form on page if ID not specified
    if (form_id)    
        f = document.getElementById(form_id);
    else
        f = document.forms[0];
            
    fields = f.getElementsByTagName('*');
    
    params = '';
    for (i = 0; i < fields.length; i++)
    {
        var field, tagName;
        
        field = fields[i];
        
        // Only looking for input fields
        // tagName is always uppercase, they say
        tagName = field.tagName;
        if (tagName != 'INPUT' && tagName != 'SELECT' && tagName != 'TEXTAREA')
            continue;
        
        // Skip buttons
        if (field.type == 'button' || field.type == 'submit')
            continue;
        
        if (params)
            params += '&';
        params += field.name + '=' + field.value;
    }
    
    // alert('fields: ' + params);
    
    return params;
}

// Assume it's an id if it's not an HTML element
function selectedItem(list)
{
    if (!list)
        return null;
 
    if (typeof list == 'number' || typeof list == 'string')
        list = document.getElementById(list);

    if (typeof list != 'object')
        return null;
        
    return list.options[list.selectedIndex].value;
}

// Sets the selected item (selectedIndex) to the index of the
// given item, if it's there
function selectItem(list, value)
{
    var idx;
    
    if (!list)
        return false;
 
    if (typeof list == 'number' || typeof list == 'string')
        list = document.getElementById(list);

    if (typeof list != 'object')
        return false;
    
    // Find the position of 'value' in the options, if it's there
    idx = options_idx(list.options, value);
    if (idx < 0)
        return false;
    
    list.selectedIndex = idx;

    return true;
}

// Remove the specifed option from the given list and return it
function removeItem(list, idx)
{
    var opts, opt;
    
    opts = list.options;
    opt = opts[idx];
    opts[idx] = null;
    
    return opt;
}

function addItem(list, opt)
{
    var opts;
    
    opts = list.options;
    opts[opts.length] = opt;
}

// Return the first index of 'val' in 'array', or -1 if it is not there
function arr_index(array, val)
{
    var len, i;
    

    len = array.length;
    for (i = 0; i < len; i++)
    {
        if (array[i] == val)
            return i;
    }

    // nada
    return -1;
}    

// Return true if 'val' is in 'array', false otherwise
function arr_contains(array, val)
{
    if (array == null || typeof array != "object") // no specific "array" type
        return false;
        
    index = arr_index(array, val);
        
    return (index >= 0) ? true : false;
}

// Return index of 'val' in the given <options> list if it's there, -1 otherwise
// Clumsy but necessary, as array elements are not real strings
//
// is_text: if true, look in 'text' field, otherwise in 'value' (default)
//
function options_idx(opts, val, is_text)
{
    var field, len, i;

    if (opts == null || typeof opts != "object") // no specific "array" type
        return -1;
    
    field = is_text ? 'text' : 'value';
    len = opts.length;
    
    for (i = 0; i < len; i++)
    {
        if (opts[i][field] == val)
            return i;
    }
    
    return -1;
}    

// Return true if 'val' is in the given <options> list, false otherwise
// Clumsy but necessary, as array elements are not real strings
function options_contains(opts, val)
{
    var idx;
    
    // p3 = true: look in 'text' field of options
    idx = options_idx(opts, val, true);
    
    return (idx >= 0);
}

// Split a string into parts.  If not specified, regex will be /, /
function split_list(str, regex)
{
    if (!str)
        return [];
        
    if (!regex)
        regex = /[, ]+/;
    
    return str.split(regex);    
}

function isReasonableEmail(address)
{
    var disallowed, regexp;
    var parts;
    
    // Does this look like a valid email address?
    // Note that real checking is quite complicated, and we're
    // not even going to try - all we want to do is try to catch
    // typos and dumb stuff.  Remember, this is not a public site.
    
    // so saith rfc 822
    // although that disallows '.', and people are using them in 
    // email names now

    // JS bug in Mozilla?  First regex will not work, second will
    // (note reversed order of '[]' => '][' in second)
    // disallowed = '[()<>"@,;:\\\[\] ]+';
    disallowed = '[()<>"@,;:\\\]\[ ]+';
    
    // The regexp is proving troublesome, so split into two parts
    parts = address.split('@');
    
    // Just 2 parts, each with some length, right?
    // Should we attempt to enforce at least a few characters for the 
    // domain part (at least 4 - x.to), or will that just get us in big
    // trouble at some point down the road?
    if (parts.length != 2 || parts[0].length < 1 || parts[1].length < 4)
        return false;
    
    // Test for nonexistence of any of these bad characters
    regexp = new RegExp(disallowed);
    
    return (!regexp.test(parts[0]) && !regexp.test(parts[1]));
}    

// Attempt to ensure that the given string is a lsit of *reasonable*
// (see above) looking email addresses, separated by the given regex,
// which, by default, will be /, '.
//
// Returns a string containing the suspicious addresses, separated by newlines.
function validate_email_list(str, regex)
{
    var addr_list, bad_addrs, i;
    
    addr_list = split_list(str, regex);
    bad_addrs = [];
    for (i in addr_list)
    {
        var addr;
        
        addr = addr_list[i];
        if (!isReasonableEmail(addr))
        {
            bad_addrs[bad_addrs.length] = addr;
        }
    }
    
    if (bad_addrs.length > 0)
        return bad_addrs.join("\n");
    
    return undefined;    
}

/*
** Clean undesirable characters from a text (or textarea) field.
** type is 'int', 'float', etc.
*/

function clean_text_field(type, field)
{
    if (!field)
        return;
        
    if (type == 'int')
        regex = /[^0-9]/g;
    else
    if (type == 'float')
        regex = /[^0-9.]/g;
    else
    {
        // More types to be determined later
        return;
    }
    
    // alert(type + ' : ' + regex + ' : ' + field.value);
    
    field.value = field.value.replace(regex, '');
    
    // alert('now: ' + field.value);
}

function strtrim(str)
{
    var left_re, right_re;
    
    left_re = /^\s+/;
    right_re = /\s+$/;
    
    str = str.replace(left_re, '');
    str = str.replace(right_re, '');
    
    return str;
}