//
// Copyright (C) 2004 Center of Air Pollution Impact and Trend Analysis 
//
// package: datafed.js
//

function ver(condition, message)
{
    if (!condition)
        Panic(message);
}

function ver_st(st, message)
{
    if (typeof(st) != "string" || st == "")
        Panic(message);
    return st;
}

function ver_num(number)
{
    if (typeof(number) != "number" || isNaN(number))
        Panic("NaN found, check your code.");
    return number;
}

function ver_positive(number)
{
    if (typeof(number) != "number" || isNaN(number))
        Panic("NaN found, check your code.");
    ver(number > 0, "positive required");
    return number;
}

function ver_ref(obj)
{
    if (typeof(obj) == "undefined")
        Panic("object cannot be null, check your code");
}

function Panic(message)
{
    alert("Assert failure: " + message);
    debug();
}

var debug_is_called = false;

function debug()
{
    if (!debug_is_called)
    {
        debug_is_called = true;
        enter_debugger(); // this usually let's you debug the script.
    }
}

function parse_cgi(href, initial)
{
    var params = typeof(initial) == "undefined" ? {} : initial;
    var param_names = new Array();
    var cgip;
    var idxq = href.indexOf("?");
    if (idxq < 0)
    {
        cgip = {service:href, names:param_names, params:params};
    }
    else
    {
        var service = href.substring(0, idxq);
        var query = href.substring(idxq+1, href.length);
        cgip = parse_query(query);
        cgip.service = service;
    }    
    return cgip;
}

function parse_query(query)
{
    var params = typeof(initial) == "undefined" ? {} : initial;
    var param_names = new Array();
    var namevals;
    if (query.indexOf("&") == -1) // opening a file://C:\\ results like this
        namevals = query.split("%26");
    else
        namevals = query.split("&");
    for (var inv = 0; inv < namevals.length; ++inv)
    {
        var nval = trim(namevals[inv]);
        if (nval.length > 0)
        {
            var par = nval.split("=");
            ver(par.length == 2, "bad cgi url:" + query);
            param_names[param_names.length] = par[0];
            params[par[0]] = decode_url_parameter(par[1]);
        }
    }
    return {names:param_names, params:params};
}

function find_any_opt(doc, id)
{
    if (typeof(doc.getElementById) != "undefined")
    {
        var try_elem = doc.getElementById(id);
        if (typeof(try_elem) != "undefined" && try_elem != null)
        {
            return try_elem;
        }
    }
    var arrays = Array(doc.anchors, doc.embeds, doc.images, doc.links);
    
    if (typeof(doc.forms) != "undefined")
    {
        for (var ifrm = 0; ifrm < doc.forms.length; ++ifrm)
        {
            arrays[arrays.length] = doc.forms[ifrm].elements;
            arrays[arrays.length] = doc.forms[ifrm].all;
        }
    }
    
    if (typeof(doc.all) != "undefined")
        array_append(arrays, doc.all);
    
    for (var ia = 0; ia < arrays.length; ++ia)
    {
        var elem = find_by_id_opt(arrays[ia], id);
        if (elem != null)
            return elem;
    }
    return null;
}

function find_any(doc, id)
{
    var elem = find_any_opt(doc, id);
    ver(typeof(elem) == "object" && elem != null, "could not find elem " + id);
    return elem;
}

function remove_newlines(text)
{
    text = strrepl(text, "\r", "");
    text = strrepl(text, "\n", " ");
    return text
}

function float_to_string(num)
{
    return truncate_decimals(num, 3).toString();
}

function truncate_decimals(num, max_dec_count)
{
    var text = num.toString();
    var dot = text.indexOf(".");
    return (dot >= 0) ? text.substring(0, dot+1+max_dec_count) : num;
}

function to_quoted_string(val)
{
    return "\"" + val.toString() + "\"";
}

function remove_item(arr, item)
{
    for (var ii = 0; ii < arr.length; ++ii)
    {
        if (arr[ii] == item)
        {
            arr[ii] = arr[arr.length-1];
            arr.length--;
            return;
        }
    }
    Panic("could not find item " + item.toString());
}

function strrepl(body, pattern, replval)
{
    // netscape does not work with replace.
    var accu = "";
    var patlen = pattern.length;
    var idx = body.indexOf(pattern);
    while (idx >= 0)
    {
        var start = body.substring(0, idx);
        accu = accu + start + replval;
        body = body.substring(idx+patlen, body.length);
        idx = body.indexOf(pattern);
    }
    return accu + body;
}

function isnumeric(chr)
{
    return chr >= '0' && chr <= "9";
}

function parse_int(str)
{
    return ver_num(parseInt(str, 10));
}

function parse_float(str)
{
    return ver_num(parseFloat(str));
}

function new_date(year, month, day)
{
    ver(month >= 0 && month <= 11, "bad month.");
    ver(day >= 1 && month <= 31, "bad day.");
    var ret = new Date();
    ret.setTime(Date.UTC(year, month, day));
    return ret;
}

function parse_date_opt(str)
{
    var parts1 = str.split("-");
    if (parts1.length != 3)
        return NaN;
    var year = parse_int(parts1[0]);
    var month = parse_int(parts1[1]);
    isep = 0;
    while (isep < parts1[2].length && isnumeric(parts1[2].substring(isep, isep+1)))
        ++isep;
    var day = parse_int(parts1[2].substring(0, isep));
    var ret = new Date();
    if (isep == parts1[2].length)
        ret.setTime(Date.UTC(year, month-1, day));
    else
    {
        var parts2 = parts1[2].substring(isep+1, parts1[2].length).split(":");
        var hour = parse_int(parts2[0]);
        var minute = parse_int(parts2[1]);
        var second = parse_int(parts2[2]);
        ret.setTime(Date.UTC(year, month-1, day, hour, minute, second));
    }
    return ret;
}

function parse_date(str)
{
    var ret = parse_date_opt(str);
    ver(!isNaN(ret), "date in unrecognized format:" + str);
    return ret;
}

function is_valid_date(str)
{
    return !isNaN(parse_date_opt(str));
}

function is_date(obj)
{
    return typeof(obj) == "object" && typeof(obj.getTime) == "function";
}

function lzero(num)
{
    return (ver_num(num) <= 9 ? "0" : "") + num.toString(); 
}

var month_map = {Jan:0, Feb:1, Mar:2, Apr:3, May:4, Jun:5, Jul:6, Aug:7, Sep:8, Oct:9, Nov:10, Dec:11};
var month_arr = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(",");

function to_string(obj)
{
    var ret;
    if (is_date(obj))
    {
        var GMTtime = obj.toGMTString(); //Fri, 1 Jan 1999 00:00:00 UTC
        var wo_weekday = GMTtime.split(",")[1]; // 1 Jan 1999 00:00:00 UTC
        var parts = wo_weekday.split(" ");
        var day = parse_int(parts[1]);
        var month = month_map[parts[2]];
        var year = parse_int(parts[3]);
        var times = parts[4];
        ver(parts[5] == "UTC" || parts[5] == "GMT", "strange UTC string " + GMTtime);
        ret = year.toString() + "-" + lzero(1+month) + "-" + lzero(day);
        if (times != "00:00:00")
            ret += "T" + times;
    }
    else if (typeof(obj) == "boolean")
        ret = obj ? "true" : "false";
    else
        ret = obj.toString();
    return ret;
}

function get_year(date)
{
    var str = to_string(date);
    return parse_int(str.substring(0,4));
}

function get_month(date)
{
    var str = to_string(date);
    return parse_int(str.substring(5,7))-1;
}

function get_date(date)
{
    var str = to_string(date);
    return parse_int(str.substring(8,10));
}

function get_hours(date)
{
    var str = to_string(date);
    var ts = str.substring(11,13);
    return (ts == "") ? 0 : parse_int(ts);
}

function get_minutes(date)
{
    var str = to_string(date);
    var ts = str.substring(14,16);
    return (ts == "") ? 0 : parse_int(ts);
}

function get_seconds(date)
{
    var str = to_string(date);
    var ts = str.substring(17,19);
    return (ts == "") ? 0 : parse_int(ts);
}

function str_eq(s1, s2)
{
    return s1.toString().toUpperCase() == s2.toString().toUpperCase();
}

function date_eq(d1, d2)
{
    return d1.getTime() == d2.getTime();
}

function eps_eq(f1, f2)
{
    return Math.abs(f1-f2) < 0.0001;
}

function triml(str)
{
    return str.replace( /^\s*/, "" ); 
}

function trimr(str)
{
    return str.replace( /\s*$/, "" ); 
}

function trim(str)
{
    return trimr(triml(str));
}

function is_empty(str)
{
    return ((str == null) || (trim(str).length == 0)); 
}

function trim_array(aa)
{
    for (var ii = 0; ii < aa.length; ++ ii)
    {
        aa[ii] = trim(aa[ii]);
    }
}

function shallow_array_copy(arr)
{
    var ret = Array();
    for (var ii = 0, len = arr.length; ii < len; ++ii)
    {
        ret[ii] = arr[ii];
    }
    return ret;
}

function concat_arrays(target, arr1)
{
    for (var iapp = 0; iapp < arr1.length; ++iapp)
        array_append(target, arr1[iapp]);
}

function array_append(target, obj)
{
    target[target.length] = obj;
}

function array_has_elem(arr, elem)
{
    return array_indexof_opt(arr, elem) >= 0;
}

function array_indexof_opt(arr, elem)
{
    for (var iord = 0; iord < arr.length; ++iord)
    {
        if (elem == arr[iord])
            return iord;
    }
    return -1;
}

function array_indexof(arr, elem)
{
    var ret = array_indexof_opt(arr, elem);
    ver(ret >= 0);
    return ret;
}

function is_array_a_set(arr)
{
    var is_set = true;
    for (var ii = 0; ii < arr.length; ++ii)
    {
        for (var tail = ii+1; tail < arr.length; ++tail)
        {
            if (arr[ii] == arr[tail])
                is_set = false;
        }
    }
    return is_set;
}

function find_by_id_opt(arr, id)
{
    if (typeof(arr) != "undefined")
    {
        for (var ii = 0; ii < arr.length; ++ii)
        {
            var elem = arr[ii];
            if (typeof(elem) == "object" && 
                (typeof(elem.id) == "string" || typeof(elem.name) == "string") && 
                (elem.id == id || elem.name == id))
            {
                return elem;
            }
        }
    }
    return null;
}

function get_listbox_value(lb)
{
    return lb.options[lb.selectedIndex].value;
}

function get_listbox_text(lb)
{
    return lb.options[lb.selectedIndex].text;
}

function select_listbox_value(lb, v)
{
    for (var ii = 0; ii < lb.options.length; ++ii)
    {
        if (lb.options[ii].value == v)
        {
            lb.selectedIndex = ii;
            return true;
        }
    }
    lb.selectedIndex = 0;
    return false;
}

function parse_csv(csv_text)
{
    var ret = {fields:new Array(), rows:new Array()};
    var csv_lines = csv_text.replace("\r", "").split("\n");
    trim_array(csv_lines);
    var irow = 0; 
    while (irow < csv_lines.length && csv_lines[irow] == "")
    {
        ++irow;
    }
    if (irow < csv_lines.length)
    {
        ret.rows = Array(csv_lines.length-irow-1);
        ret.fields = csv_lines[irow].split(",");
        var ifinal = 0;
        ++irow;
        while (irow < csv_lines.length)
        {
            var parts = csv_lines[irow].split(",");
            var row = {};
            for (var ifld = 0; ifld < ret.fields.length; ++ifld)
            {
                row[ret.fields[ifld]] = parts[ifld];
            }
            ret.rows[ifinal] = row;
            ++ifinal;
            ++irow;
        }
    }
    return ret;
}

function time_dimension(
    current_time, current_index, 
    sample_periodicity_unit, sample_periodicity_multiplier, time_min, time_max)
{
    ver(typeof(current_time) == "string", "time_dimension:current_time must be date formatted as string, 'unknown' or empty"); 
    ver(typeof(current_index) == "string", "time_dimension:current_index must be string, not " + typeof(current_index)); 
    ver_num(sample_periodicity_multiplier);
    ver(typeof(time_min.getTime) == "function", "time_dimension:time_min must be Date"); 
    ver(typeof(time_max) == "string", "time_dimension:time_max must be 'now' or date formatted as string"); 
    
    this.current_time = current_time;
    this.current_index = current_index;
    this.sample_periodicity_unit = sample_periodicity_unit;
    this.sample_periodicity_multiplier = sample_periodicity_multiplier;
    this.time_min = time_min;
    this.time_max = time_max;
    
    this.ensure_and_get_current_time();
    this.default_time = this.current_time;
}

time_dimension.prototype.goto_first = function()
{
    this.current_time = "unknown";
    this.current_index = 1;
    this.ensure_and_get_current_time();
}

time_dimension.prototype.goto_prev = function()
{
    if (this.current_index > 1)
    {
        --this.current_index;
        this.current_time = "unknown";
        this.ensure_and_get_current_time();
    }
}

time_dimension.prototype.goto_next = function()
{
    if (this.current_index < this.last_index())
    {
        ++this.current_index;
        this.current_time = "unknown";
        this.ensure_and_get_current_time();
    }
}

time_dimension.prototype.goto_last = function()
{
    this.current_index = "last";
    this.current_time = "unknown";
    this.ensure_and_get_current_time();
}

time_dimension.prototype.change_time = function(new_datetime)
{
    this.current_index = "unknown";
    this.current_time = to_string(new_datetime);
    this.ensure_and_get_current_time();
}

time_dimension.prototype.change_index = function(new_index)
{
    this.current_index = new_index;
    this.current_time = "unknown";
    this.ensure_and_get_current_time();
}

time_dimension.prototype.time_to_index = function(index_time)
{
    var ret;

    if (this.sample_periodicity_unit == "year")
    {
        ret = (get_year(index_time) - get_year(this.time_min)) / this.sample_periodicity_multiplier + 1;
    }
    else if (this.sample_periodicity_unit == "month")
    {
        var years = get_year(index_time) - get_year(this.time_min);
        var months = get_month(index_time) - get_month(this.time_min);
        ret = (years * 12 + months) / this.sample_periodicity_multiplier + 1;
    }
    else 
    {
        var span = index_time.getTime() - this.time_min.getTime();
        if (this.sample_periodicity_unit == "week")
            span /= (7*24*3600*1000);
        else if (this.sample_periodicity_unit == "day")
            span /= (24*3600*1000);
        else if (this.sample_periodicity_unit == "hour")
            span /= (3600*1000);
        else if (this.sample_periodicity_unit == "minute")
            span /= (60*1000);
        else if (this.sample_periodicity_unit == "second")
            span /= 1000;
        else
            Panic("unknown sample_periodicity_unit " + this.sample_periodicity_unit);
        ret = Math.floor(span / this.sample_periodicity_multiplier) + 1;
    }
    return ret;
}

time_dimension.prototype.index_to_start_time = function(index)
{
    index = (index-1) * this.sample_periodicity_multiplier;
    var ret;
    if (this.sample_periodicity_unit == "year")
        ret = new_date(get_year(this.time_min) + index, 0, 1);
    else if (this.sample_periodicity_unit == "month")
    {
        var month = get_month(this.time_min) + index;
        ret = new_date(get_year(this.time_min) + month / 12, month % 12, 1);
    }
    else if (this.sample_periodicity_unit == "week")
    {
        var mstime = this.time_min.getTime() + index*7*24*3600*1000;
        ret = new Date();
        ret.setTime(mstime);
    }
    else if (this.sample_periodicity_unit == "day")
    {
        var mstime = this.time_min.getTime() + index*24*3600*1000;
        ret = new Date();
        ret.setTime(mstime);
    }
    else if (this.sample_periodicity_unit == "hour")
    {
        var mstime = this.time_min.getTime() + index*3600*1000;
        ret = new Date();
        ret.setTime(mstime);
    }
    else if (this.sample_periodicity_unit == "minute")
    {
        var mstime = this.time_min.getTime() + index*60*1000;
        ret = new Date();
        ret.setTime(mstime);
    }
    else if (this.sample_periodicity_unit == "second")
    {
        var mstime = this.time_min.getTime() + index*1000;
        ret = new Date();
        ret.setTime(mstime);
    }
    else
    {
        ret = DateTime.Now;
        Panic("wrong periodicity_unit " + this.sample_periodicity_unit);
    }
    return ret;
}

time_dimension.prototype.get_time_from_text = function(timetext)
{
    if (timetext.indexOf("now") == -1)
        return parse_date(timetext);
    else
    {
        timetext = trim(strrepl(timetext, "now", ""));
        var currtime = new Date();
        var mscurr = currtime.getTime();
        mscurr -= 60.0*1000.0*currtime.getTimezoneOffset();
        currtime.setTime(mscurr);
        var idx = this.time_to_index(currtime);
        if (timetext != "")
            idx += parse_int(timetext);
        return this.index_to_start_time(idx);
    }
}

time_dimension.prototype.last_index = function()
{
    return this.time_to_index(this.get_time_from_text(this.time_max));
}

time_dimension.prototype.ensure_and_get_current_time = function()
{
    var last_index = this.last_index();
    
    var curr_time = "";
    if (this.current_time != "")
    {
        if (str_eq(this.current_time, "unknown"))
            curr_time = "";
        else
            curr_time = to_string(this.get_time_from_text(this.current_time));
    }
    var curr_idx = "";
    if (this.current_index != "")
    {
        curr_idx = this.current_index;
        if (str_eq(curr_idx, "unknown"))
            curr_idx = "";
        else if (str_eq(curr_idx, "last"))
            curr_idx = last_index.toString();
    }

    var retidx;
    if (curr_time != "")
    {
        var given_time = parse_date(curr_time);
        if (curr_idx != "")
        {
            retidx = parse_int(curr_idx);
            if (retidx != this.time_to_index(given_time))
                Panic("time '" + curr_time + "' and index '" + curr_idx + "' are inconsistent. Try to replace other with 'UNKNOWN'.");
        }
        else
        {
            curr_idx = this.time_to_index(given_time).toString();
            retidx = parse_int(curr_idx);
        }
    }
    else
    {
        retidx = curr_idx != "" ? parse_int(curr_idx) : 1;
    }
    ver_num(retidx);
    // don't make this restriction
    //retidx = Math.max(1, Math.min(retidx, last_index));
    this.current_index = retidx.toString();
    var ret = this.index_to_start_time(retidx);
    this.current_time = to_string(ret);
    return ret;
}

function encode_url_parameter(param)
{
    var ret = "";
    for (var ii = 0; ii < param.length; ++ii)
    {
        ret += find_enc(param.substring(ii, ii+1));
    }
    return ret;
}

function decode_url_parameter(param)
{
    var ret = "";
    for (var ii = 0; ii < param.length; ++ii)
    {
        var ch = param.substring(ii, ii+1);
        if (ch == "+")
            ret += " ";
        else if (ch == "%")
        {
            ret += find_dec(param.substring(ii, ii+3));
            ii += 2;
        }
        else
            ret += ch;
    }
    return ret;
}

function find_enc(chr)
{
    for (var ichr = 0; ichr < url_xlate.length; ++ichr)
    {
        if (url_xlate[ichr].chr == chr)
            return url_xlate[ichr].enc;
    }
    return chr;
}

function find_dec(enc)
{
    for (var ichr = 0; ichr < url_xlate.length; ++ichr)
    {
        if (url_xlate[ichr].enc == enc)
            return url_xlate[ichr].chr;
    }
    return enc;
}

var url_xlate = Array(
{idx:9, chr:'\t', enc:'%09'},
{idx:10, chr:'\n', enc:'%0a'},
{idx:13, chr:'\r', enc:'%0d'},
{idx:32, chr:' ', enc:'+'},
{idx:32, chr:' ', enc:'%20'},
{idx:34, chr:'"', enc:'%22'},
{idx:35, chr:'#', enc:'%23'},
{idx:36, chr:'$', enc:'%24'},
{idx:37, chr:'%', enc:'%25'},
{idx:38, chr:'&', enc:'%26'},
{idx:43, chr:'+', enc:'%2b'},
{idx:44, chr:',', enc:'%2c'},
{idx:47, chr:'/', enc:'%2f'},
{idx:58, chr:':', enc:'%3a'},
{idx:59, chr:';', enc:'%3b'},
{idx:60, chr:'<', enc:'%3c'},
{idx:61, chr:'=', enc:'%3d'},
{idx:62, chr:'>', enc:'%3e'},
{idx:63, chr:'?', enc:'%3f'},
{idx:64, chr:'@', enc:'%40'},
{idx:91, chr:'[', enc:'%5b'},
{idx:92, chr:'\\', enc:'%5c'},
{idx:93, chr:']', enc:'%5d'},
{idx:94, chr:'^', enc:'%5e'},
{idx:96, chr:'`', enc:'%60'},
{idx:123, chr:'{', enc:'%7b'},
{idx:124, chr:'|', enc:'%7c'},
{idx:125, chr:'}', enc:'%7d'},
{idx:126, chr:'~', enc:'%7e'});

var c_active_color = "#0000FF";
var c_inactive_color = "#FFFFFF";

function validate_type(type_name, ctrl_id)
{
}

function server_url()
{
    var top_loc = top.location.href.toLowerCase();
    var http_prefix = "http://";
    ver(top_loc.indexOf(http_prefix) == 0, "url does not start with http://");
    
    var dvs = "/dvoy_services/";
    var idx_dvs = top_loc.indexOf(dvs);
    if (idx_dvs >= 0)
        return top_loc.substring(0, idx_dvs + dvs.length);
    else
    {
        var idx_slash = top_loc.indexOf("/", http_prefix.length);
        ver(idx_slash > 0, "strange server url " + top_loc);
        return top_loc.substring(0,  idx_slash+1);
    }
}

function box_has_text(box)
{
    return box.value != "" && box.value != " ";
}

function join_path(part0, part1)
{
    part0 = strrepl(part0, "\\", "/")
    part1 = strrepl(part1, "\\", "/")
    if (part0 == "")
        return part1
    if (part1 == "")
        return part0
    return strrepl(part0 + "/" + part1, "//", "/")
}

function boot_xml_explorer(sfr, form_obj)
{
    if (typeof(sfr) != "undefined" && 
        typeof(sfr.global_data_port) != "undefined" && 
        sfr.global_data_port != null)
    {
        if (form_obj.submit_cmd.value == "get_xml")
        {
            var text = sfr.global_data_port.get_data();
            form_obj.data_xml_text.value = remove_newlines(text);
            form_obj.submit();
            return false;
        }
        else if (form_obj.submit_cmd.value == "set_xml")
        {
            var text = form_obj.data_xml_text.value;
            sfr.global_data_port.set_data(text);
            return true;
        }
        else if (form_obj.submit_cmd.value == "done_edit")
        {
            var text = form_obj.data_xml_text.value;
            sfr.global_data_port.done_edit(text);
            return true;
        }
        else if (form_obj.submit_cmd.value == "select")
        {
            var text = form_obj.data_xml_text.value;
            sfr.global_data_port.select(text);
            return true;
        }
    }
    return false;
}
