/* Copyright 2003 Yahoo! Inc. All rights reserved.  */

var opt_sort_col = 'minprice';
var opt_rev_sort = -1;
var opt_min_max = [];
var opt_fltr_min_max = [];
var opt_data = [];
var opt_timer_id;
var opt_page = 1;
var opt_page_size = 10;
var opt_selected = {};
var opt_compare = [];
var opt_state_cookie = new yg_cookie();

function expandData(raw, fields) {
    var raw_len = raw.length;
    var fields_len = fields.length;
    var rec_count = raw_len / fields_len;
    for (var i = 0; i < rec_count; i++) {
        var rec = opt_data[i] = [];
        rec['ranks'] = [];
        rec['top_ranks'] = [];
        for (var j = 0; j < fields_len; j++) {
            rec[fields[j]] = raw[i * fields_len + j];
        }
    }
}

function sortFunc(f1, f2) {
    var v1 = f1[opt_sort_col];
    var v2 = f2[opt_sort_col];
    if (v1 == undefined) { v1 = opt_rev_sort * 1000000; }
    if (v2 == undefined) { v2 = opt_rev_sort * 1000000; }
    if (v1 < v2) { return -1 * opt_rev_sort; }
    if (v1 > v2) { return 1 * opt_rev_sort; }
    return 0;
}

function sortData(data, col, dir) {
    window.status = 'Sorting Data...' + col;
    if (col == opt_sort_col) {
        opt_rev_sort = opt_rev_sort * -1;
    } else {
        opt_rev_sort = 1;
        opt_sort_col = col;
    }
    if (dir) {
        opt_rev_sort = dir;
    }
    return data.sort(sortFunc);
}

function setIndex(data) {
    for (var i = 0; i < data.length; i++) {
        data[i].index = i;
    }
}

function buildResultRow(tr1, tr2, item, next) {
    var tmp_node = make_element('SPAN', { className:'rank' });
    tmp_node.appendChild(document.createTextNode((item.index + 1) + '.'));
    addCell(tr1, tmp_node, 30, { align: 'center' } );

    addCell(tr1, 
        make_link(
            make_image('http://shopping.yahoo.com/optimizer/image.php?id=' + item.id, item.image_width, item.image_height), 
            item.id, 0, 'img', item.index
        ), 70, { height: 70 } );

    var td = make_element('TD', { className:'resultCell', vAlign:'top'});
    td.appendChild(document.createElement('BIG')).appendChild(make_link(item.title, item.id, 1, 'title', item.index));
    td.appendChild(document.createElement('BR'));
    td.appendChild(make_element('SPAN', { className:'helpLink' })).appendChild(make_rating(item.rating, item.rating_count));
    td.appendChild(document.createElement('BR'));
    if (item.summary) {
        td.appendChild(document.createTextNode(item.summary));
    }
    td.appendChild(document.createElement('BR'));
    td.appendChild(make_element('SPAN', { className:'price' })).appendChild(make_price(item));
    td.appendChild(document.createTextNode(' - '));
    td.appendChild(document.createElement('B')).appendChild(make_link('compare prices', item.id, 0, 'compare', item.index));
    tr1.appendChild(td);

    var reason = make_reason(item, next);
    reason = document.createTextNode(reason);
    tmp_node = make_element('SPAN', { className:'naturalLanguage' });
    tmp_node.appendChild(reason);

    addCell(tr1, tmp_node, '40%');

    addCell(tr1, ' ', 20, { className: 'compareBar' } );
    addCell(tr2, 'compare', null , { className: '' , colSpan: 4, align: 'right' });

    var checkbox = make_element('INPUT', { type:'checkbox', name:'item[]', value:item.id });
    opt_checkbox.push(checkbox);
    add_event(checkbox, 'click', slctid);

    addCell(tr2, checkbox, 20 , { className: 'compareCheck' } );
}

function add_event(obj, evnt, hndlr) {
    if (obj.addEventListener) obj.addEventListener(evnt, hndlr, false); 
    else if (obj.attachEvent) obj.attachEvent("on" + evnt, hndlr);
}

function get_event_src(e) {  
    if (!e) e = window.event;
    if (e.target)    return e.target; 
    else if (e.srcElement)    return e.srcElement;
}

function slctid(e) {
    e = get_event_src(e);
    opt_selected[e.value] = e.checked;
    save_state();
}

function make_element(tag, attr) {
    var el = document.createElement(tag);
    if (attr != undefined) {
        for (var prop in attr) { 
            if (prop == 'className') {
                el.setAttribute('class', attr[prop]); // for gecko engines
            }
            el.setAttribute(prop, attr[prop]); 
        }
    }
    return el;
}

function addCell(row, content, width, attr) {
    if (typeof(content) == 'string') {
        content = document.createTextNode(content);
    }

    var cell = document.createElement('TD');
    if (width != undefined) { cell.setAttribute('width', width); }
    cell.setAttribute('vAlign', 'top');
    cell.setAttribute('class', 'resultCell');
    cell.setAttribute('className', 'resultCell');

    if (attr != undefined) {
        for (var prop in attr) { 
            if (prop == 'className') {
                cell.setAttribute('class', attr[prop]); // for gecko engines
            }
            cell.setAttribute(prop, attr[prop]); 
        }
    }

    cell.appendChild(content);
    row.appendChild(cell);
    return cell;
}

function make_display_range(i) {
    var attr = opt_attrs['fields'][i];
    var n = attr.name;
    var el = document.getElementById(n + '_disp');
    if (el == undefined) { return; }
    var min = attr.min_val;
    var max = attr.max_val;
    var pre = attr.pre || '';
    var post = attr.post || '';
    var result = '';
    if (attr.filter == 'checkbox') {
        if (attr.sel_val == undefined || attr.sel_val.length == 0) {
            result += 'All ' + (attr.labels || attr.label);
        } else {
            var count = attr.sel_val.length;
            if (count > 1) {
                result += count  + ' ' + attr.labels + ' selected';
            } else {
                result += '1 ' + attr.label + ' selected';
            }
        }
    } else {
        if (min && max)  { 
            if (min == max) {
                result += '~ ' + pre + min + post;
            } else {
                result += pre + min + post + ' to ' + pre + max + post; 
            }
        }
        else if (min)    { result += 'From ' + pre + min + post;  }
        else if (max)    { result += 'Up To ' + pre + max + post;  }
        else             {  
            result += 'Any ' + (attr.any || attr.label);
        }
    }
    el.innerHTML = result;
}

function make_edit_caption(i) {
    var attr = opt_attrs['fields'][i];
    return 'Select ' + attr.labels;
}

function set_filter(index, min_o, max_o, norefresh) {
    var attr = opt_attrs['fields'][index];
    var name = attr.name;
    var min = parseFloat(gv(min_o));
    var max = parseFloat(gv(max_o));
    var real_min = min;
    var real_max = max;
    if (min && max && (min > max)) {
        real_min = max;
        real_max = min;
    }

    if (min == undefined || isNaN(min)) {
        sv(min_o, 'min');
        attr.min_val = undefined;
    } else {
        if (min == '_ALL_' || min == 'min') { attr.min_val = undefined; sv(min_o, 'min'); } 
        else { 
            if (attr.slider && attr.slider.getPercent() < 5) {
                attr.slider.setPercent(80);
            }
            attr.min_val = real_min; 
            sv(min_o, min);
        }
    }
    if (max == undefined || isNaN(max)) {
        sv(max_o, 'max');
        attr.max_val = undefined;
    } else {
        if (max == '_ALL_' || max == 'max') { attr.max_val = undefined; sv(max_o, 'max'); } 
        else { 
            if (attr.slider && attr.slider.getPercent() < 5) {
                attr.slider.setPercent(80);
            }
            attr.max_val = real_max; 
            sv(max_o, max);
        }
    }
    if (norefresh) {}
    else { 
        // if (opt_timer_id) { clearTimeout(opt_timer_id); }
        //opt_timer_id = setTimeout("adjust_filter()", 250);
        adjust_filter();
    }
}

function set_affinity(index, values, norefresh) {
    var attr = opt_attrs['fields'][index];
    if (values.length == 0 || ((values.length > 0) && (values[0] == '_ALL_'))) {
        attr.sel_val = [];
        attr.sel_code = undefined;
    } else {
        var codes = [];
        for (var i = 0; i < values.length; i++) {
            var code = attr.value_map[values[i]];
            if (code != undefined) { codes.push(code); }
        }
        attr.sel_val  = values;
        attr.sel_code = codes;
        if (attr.slider && attr.slider.getPercent() < 5) {
            attr.slider.setPercent(80);
        }
    }
    nd();
    nd();

    make_display_range(index);

    if (norefresh) {}
    else { adjust_filter(); }
}

function drawTable() {
    opt_checkbox = [];
    var table = document.getElementById('display');
    var tbody = table.getElementsByTagName('TBODY').item(0);
    table.removeChild(tbody);
    var len = tbody.childNodes.length;

    // remove existing rows
    for (var i = 0; i < len; i++) {
        tbody.removeChild(tbody.childNodes.item(0));
    }

    var s = (opt_page - 1) * opt_page_size;
    var e = s + opt_page_size;
    len = Math.min(opt_data.length, e);
    for (var i = s; i < len; i++) {
        var tr1 = document.createElement('TR');
        var tr2 = document.createElement('TR');
        if (i < 8) {
            buildResultRow(tr1, tr2, opt_data[i], opt_data[i+1]);
        } else {
            buildResultRow(tr1, tr2, opt_data[i]);
        }
        tbody.appendChild(tr1);
        tbody.appendChild(tr2);
    }
    table.appendChild(tbody);
    
    // check the previsously selected items
    for (var i = 0; i < opt_checkbox.length; i++) {
        var c = opt_checkbox[i];
        if (opt_selected[c.value]) { 
            c.checked = true; 
        }
    }
    update_nextprev();
    window.status = 'Done.';
}

function page(dir) {
    opt_page += dir;
    drawTable();
}

function update_nextprev() {
    var node = document.getElementById('nextprev');
    if (node == undefined) { return; }

    var len = opt_data.length;

    var links = 'See ';
    var ps = 1 + (opt_page - 2) * opt_page_size;
    var pe = (opt_page - 1 ) *  opt_page_size;
    var ns = 1 + opt_page * opt_page_size;
    var ne = (opt_page + 1) * opt_page_size;
    if (ne > len) { ne = len; }

    if (ps > 0) { // need prev link
        links += '<a class="rank" href="javascript:void(0);" onclick="page(-1);">';
        links += ps + ' to ' + pe + '</a>';
    }
    if (ps > 0 && ns < len) {
        links += ' | ';
    }
    if (ns < len) {
        links += '<a class="rank" href="javascript:void(0);" onclick="page(1);">';
        links += ns + ' to ' + ne + '</a>';
    }
    node.innerHTML = links;
}

function setRank(data, field, key) {
    window.status = 'Computing Ranks...';
    data = sortData(data, field, 1);
    var len = data.length;
    var last = 1000000;
    var rank = 0;
    for (var i = len - 1; i > -1; i--) {
        var v = data[i][field];
        if (v == undefined) {
            continue;
        }
        if (v < last) {
            last = v;
            rank = i;
        }
        data[i][key][field] = rank / len;
    }
}

function adjust_goal() {
    window.status = 'Adjusting goals...';
    compute_scores();
    opt_data = sortData(opt_data, 'score', -1);
    setIndex(opt_data);
    opt_page = 1;

    var opt_subset = [];
    var j = 0;
    for (var i = 0; i < opt_data.length && j < 20; i++) {
         opt_subset.push(opt_data[i]);
         j++;
    }

    for (var i = 0; i < opt_attrs['fields'].length; i++) {
        var attr = opt_attrs['fields'][i];
        if (attr.goal == 'min' || attr.goal == 'max') {
            setRank(opt_subset, attr.name, 'top_ranks');
        }
    }

    document.getElementById('result_header').style.display = 'block';
    document.getElementById('compare_table').style.display = 'block';
    drawTable();
    save_state();
}

function adjust_filter() {
    window.status = 'Adjusting filters...';
    compute_filters();
    adjust_goal();
}

function compute_scores() {
    window.status = 'Computing Scores...';
    // reset scores
    for (var i = 0; i < opt_data.length; i++) { 
        opt_data[i].score = 0;
        opt_data[i].scores = []; 
    }
    // compute scores
    var touched = 0;
    for (var j = 0; j < opt_attrs['fields'].length; j++) {
        var attr = opt_attrs['fields'][j];
        var weight;
        if (attr.slider) {
            weight = attr.slider.getPercent();
        }
        else if (attr.defw) {
            weight = attr.defw;
        }
        else { continue; }

        if (weight > 0.1) {
            touched = 1;
            var name = attr.name;
            var goal = attr.goal;
            if (goal == 'min') {
                for (var k = 0; k < opt_data.length; k++) {
                    var d = opt_data[k];
                    var attr_score = d['ranks'][name];
                    if (attr_score == undefined) {
                        d['scores'][name] = 0;
                        continue;
                    }
                    attr_score = 1 - attr_score;
                    d['scores'][name] = attr_score;
                    var filter = d.filter;
                    d.score += filter * weight * attr_score;
                }
            }
            else if (goal == 'max') {
                for (var k = 0; k < opt_data.length; k++) {
                    var d = opt_data[k];
                    var attr_score = d['ranks'][name];
                    if (attr_score == undefined) {
                        d['scores'][name] = 0;
                        continue;
                    }
                    d['scores'][name] = attr_score;
                    var filter = d.filter;
                    d.score += filter * weight * attr_score;
                }
            }
            else if (goal == 'aff') {
                // if aff is selected...
                if (attr.sel_code) {
                    // set the score to the filter match intensity and slider weight
                    for (var k = 0; k < opt_data.length; k++) {
                        var d = opt_data[k];
                        var attr_filter = d['filters'][name];
                        if (attr_filter != undefined) {
                            d['scores'][name] = attr_filter;
                            d.score += d.filter * weight;
                        }
                    }
                } 
                // else, use the value_pop to assign a score
                else if (attr.value_pop) {
                    var vp = attr.value_pop;
                    var vpc = attr.choices.length;
                    var cache = '_sc_' + name;
                    var multi = attr.multi;
                    for (var k = 0; k < opt_data.length; k++) {
                        var d = opt_data[k];
                        var s = 0;
                        if ((s = d[cache]) != undefined) { 
                        } else {
                            s = 0;
                            var v = d[name];
                            if (multi) {
                                for (var m = 0; m < v.length; m++) {
                                    s += vp[v[m]] || 0;
                                }
                                s = s / vpc;
                            } else {
                                s = vp[v];
                            }
                            d[cache] = s;
                        }
                        if (s) {
                            d['scores'][name] = s;
                            d.score += d.filter * weight * s;
                        }
                    }
                }
            }
            else { 
                // other goal types 
            }
        }
    }
}

function compute_filters() {
    window.status = 'Computing filters...';
    // reset filters
    for (var i = 0; i < opt_data.length; i++) { opt_data[i].filter = 1; opt_data[i].filters = []; }
    // compute filters
    for (var j = 0; j < opt_attrs['fields'].length; j++) {
        var attr = opt_attrs['fields'][j];
        var goal = attr.goal;
        if (goal) {}
        else { continue; }

        var name = attr.name;
        var weight;
        if (attr.slider) {
            weight = attr.slider.getPercent();
        }
        else if (attr.defw) {
            weight = attr.defw;
        }
        else { weight = 0; }
        
        if (goal == 'min' || goal == 'max') {
            var f = 0;
            var g_min = opt_min_max[name].min;
            var g_max = opt_min_max[name].max;
            var f_min = attr.min_val;
            var f_max = attr.max_val;
            if (f_min == undefined) { f_min = g_min; }
            if (f_max == undefined) { f_max = g_max; }
            if (f_min < g_min) { f_min = g_min; }
            if (f_max > g_max) { f_max = g_max; }
            if (f_min == g_min && f_max == g_max) { continue; }

            var avg = (f_min + f_max) / 2;
            var dev = Math.pow(avg, 2);

            for (var k = 0; k < opt_data.length; k++) {
                var v = opt_data[k][name];
                var attr_filter = 1;
                if (v == undefined) {
                    if (weight > 0) {
                        attr_filter = opt_data[k].filters[name] = 0.1 - weight / 1000;
                    }
                }
                else if (v < f_min || v > f_max) {
                    attr_filter = opt_data[k].filters[name] = Math.pow(2, -Math.pow(avg - v, 2) / dev) / 10;
                } else {
                    attr_filter = opt_data[k].filters[name] = 1;
                }
                opt_data[k].filter *= attr_filter;
            }
        }
        else if (goal == 'aff' && attr.sel_code && attr.aff_matrix) {    
            var multi = attr.multi;
            var vpc = attr.choices.length;
            var sel_codes = attr.sel_code;
            if (sel_codes.length == 0) { continue; }
            var len = sel_codes.length;
            var matrix = attr.aff_matrix;
            for (var k = 0; k < opt_data.length; k++) {
                var v = opt_data[k][name];
                if (v == undefined) { 
                    opt_data[k].filters[name] = 0;
                    opt_data[k].filter *= 0.01;
                    continue; 
                }
                var f = 0;
                if (multi) {
                    for (var m = 0; m < v.length; m++) {
                        for (var l = 0; l < len; l++) {
                            var sel_code = sel_codes[l];
                            if (matrix[sel_code] == undefined) { continue; }
                            f += matrix[sel_code][v[m]] || 0;
                        }
                    }
                    f = f / len;
                    if (f > 1) { f = 1; }
                } else {
                    for (var l = 0; l < len; l++) {
                        var sel_code = sel_codes[l];
                        if (matrix[sel_code] == undefined) { continue; }
                        var n = matrix[sel_code][v] || 0;
                        if (n > f) { f = n; }
                        if (f == 1) { break; }
                    }
                }
                opt_data[k].filters[name] = f;
                opt_data[k].filter *= f;
            }
        }
    }
}

function compute_stats() {
    window.status = 'Computing Stats...';
    var result = [];
    for (var i = 0; i < opt_attrs['fields'].length; i++) {
        var attr = opt_attrs['fields'][i];
        if (attr.goal == 'min' || attr.goal == 'max') {
            opt_min_max[attr.name] = find_min_max(opt_data, attr.name);
            setRank(opt_data, attr.name, 'ranks');
        }
    }
    return result;
}

function find_min_max(data, col) {
    var max = -1000000000;
    var min = 1000000000;
    var avg = 0;
    var count = 0;
    for (var i = 0; i < data.length; i++) {
        var v = data[i][col];
        if (v != null) {
            count++;
            max = Math.max(v, max);
            min = Math.min(v, min);
            avg += v;
        }
    }
    avg = avg / count;
    return { min: min, max: max , avg:avg };
}

function make_image(url, w, h) {
    var img = document.createElement('IMG');
    img.setAttribute('src', url);
    img.setAttribute('width', w);
    img.setAttribute('height', h);
    img.setAttribute('border', 0);
    return img;
}

function make_rating(rating, count) {
    if (rating == undefined) { return document.createTextNode('USER RATING: N/A');  }
    if (count == undefined)  { return document.createTextNode('USER RATING: N/A');  }
    var r = rating;
    var result = document.createElement('NOBR');
    result.appendChild(document.createTextNode('USER RATING: '));
    var stars = 0;
    while (r >= 1) { 
        result.appendChild(make_image('http://l.yimg.com/a/i/mb/1s.gif', 10, 9));
        r--;
        stars++;
    }
    if (r > 0.3) {
        result.appendChild(make_image('http://l.yimg.com/a/i/mb/1-5sg.gif', 10, 9));
        stars++;
    }
    while (stars < 5) {
        result.appendChild(make_image('http://l.yimg.com/a/i/mb/1sg.gif', 10, 9));
        stars++;
    }
    if (rating != undefined) {
        result.appendChild(document.createTextNode(' (' + count + ' reviews)'));
    }

    return result;
}

function make_price(data) {
    return document.createTextNode('$' + data.minprice + ' - $' + data.maxprice);
}

function make_link(content, id, new_win, slk, pos) {
    var span = document.createElement('SPAN');
    var link = document.createElement('A');
    var href = 'http://shopping.yahoo.com/shop?d=n&id=' + id;
    link.setAttribute('href', href);
    link['pos'] = pos;
    link['slk'] = slk;
    link.onmousedown = track_click;
    if (typeof(content) == 'string') {
        content = document.createTextNode(content);
    }
    link.appendChild(content);
    span.appendChild(link);
    if (new_win) {
        link = document.createElement('A');
        link.setAttribute('href', href);
        link.setAttribute('target', '_blank');
        link['pos'] = pos;
        link['slk'] = slk;
        link.onmousedown = track_click;
        var img = make_image('http://l.yimg.com/a/i/s/nw.gif', 11, 11);
        link.appendChild(img);
        span.appendChild(document.createTextNode(' '));
        span.appendChild(link);
    }
    return span;
}

function make_reason(data, next) {
    var overall  = [];
    var top      = [];
    var relative = [];
    var filter   = [];

    var fields          = opt_attrs['fields'];
    var top_ranks       = data['top_ranks'];
    var overall_ranks   = data['ranks'];
    var scores          = data['scores'];
    var filters         = data['filters'];

    var indx = data.index;

    var seen     = [];
    var s = '';

    var diff;
    if (next != undefined) {
        diff = [];
        var next_ranks = next['ranks'];
        for (var i = 0; i < fields.length; i++) {
            var name = fields[i].name;
            if (data[name] != undefined && next[name] != undefined && overall_ranks[name]) {
                diff[name] = overall_ranks[name] - next_ranks[name];
            }
        }
    }

    var price_specified = 0;

    for (var i = 0; i < fields.length; i++) {
        var rank;
        var attr = fields[i];
        var name = attr.name
        if (name == 'price' && (attr.min_val != undefined || attr.max_val != undefined)) {
            price_specified = 1;
        }
        var score;
        var nl;

        // overall rank
        score = overall_ranks[name];
        if (attr.goal && attr.goal == 'min') { score = 1 - score; }
        seen[name] = assign_rank(overall, score, attr, 'overall');

        // top rank
        if (seen[name]) {} 
        else {
            score = top_ranks[name];
            if (attr.goal && attr.goal == 'min') { score = 1 - score; }
            assign_rank(top, score, attr, 'top');
        }

        // relative rank
        if (diff && attr.nl && attr.nl['relative']) {
            score = diff[name];
            var nls = attr.nl['relative'];
            assign_relative(relative, score, nls);
        }

        // filter disc
        score = filters[name];
        assign_rank(filter, score, attr, 'filter');
    }  
    
    // collapse all the buckets
    for (var i = 0; i < 11; i++) {
        if (overall[i]) { overall[i] = join_words(overall[i]); }
        if (top[i]) { top[i] = join_words(top[i]); }
        if (relative[i]) { relative[i] = join_words(relative[i]);}
        if (filter[i]) { filter[i] = join_words(filter[i]);}
    }

    var cat_title = opt_attrs['cat_title'];
    var cat_titles = opt_attrs['cat_titles'];

    var pos = ['first', 'second', 'third'];
    var positive = [
        '. Additionally, it', 
        '. Additionally, this ' + cat_title, 
        '. In addition, it',
        '. In addition, this ' + cat_title,
        '. Moreover, it',
        '. Moreover, this ' + cat_title,
        '. Plus, it',
        '. Plus, this ' + cat_title,
        '. It also', 
        '. This ' + cat_title + ' also' 
    ];
    var negative = [
        '. But, it',
        '. However, it',
        '. Unfortunately, it',
    ];

    var bits = [];
    if (overall[0]) {
        bits.push(' has ' + overall[0] + sr([
            ' over all ' + cat_titles, 
            ' compared to other ' + cat_titles
        ]));
    }
    if (filter[0]) {
        bits.push(' ' + filter[0]);
    }
    if (top[0]) {
        if (price_specified == 1) {
            bits.push(' has ' + top[0] + ' within your price range');
        } else {
            bits.push(' has ' + top[0] + ' compared to the others in your top 10 results');
        }
    }

    // s += 'diff: ' + var_dump(diff);
    // s += 'data: ' + var_dump(data);

    if (overall[3]) {
        s += data.title + ' ' + overall[3];
        if (bits.length > 0) { s += '. It'; }
        else { s += '. '; }
    } else {
        s += data.title;
    }

    if (bits.length > 0) { 
        if (pos[indx]) {
            s += ' is ranked ' + pos[indx] + ' because it';
        }
        s += join_words(bits, positive) + '. ';
    }

    if (relative[0]) {
        s += 'This ' + cat_title + ' ' + relative[0] + ' than ' + sr([
        next.title + ' which is displayed next',
        'the next item: ' + next.title,
        next.title + ' shown next',
        next.title
        ]) + '. ';
    }

    return s;
}

function assign_relative(bucket, score, nls) {
    if (score == undefined) { return 0; }
    if (nls == undefined)   { return 0; }
    if (score == 0)     { score = '=';}
    else if (score > 0) { score = '+'; }
    else                { score = '-'; }

    var nl;
    for (var i = 0; i < nls.length; i++) {
        if (score == nls[i].t) {
            nl = nls[i];
        }
    }

    if (nl) {
        if (bucket[nl.p] == undefined) { bucket[nl.p] = []; }
        bucket[nl.p].push(sr(nl.d));
        return 1;
    } else {
        return 0;
    }
}

function assign_rank(bucket, score, attr, nltype) {
    if (score == undefined) { return 0; }
    var natlang;
    if (attr.nl && attr.nl[nltype]) { 
        natlang = attr.nl[nltype];
        for (var i = 0; i < natlang.length; i++) {
            var nl = natlang[i];
            if (score >= nl.t) {
                if (bucket[nl.p] == undefined) { bucket[nl.p] = []; }
                bucket[nl.p].push(sr(nl.d));
                return 1;
            }
        }
    }
    else if (opt_attrs.def.nl[nltype]) {
        natlang = opt_attrs.def.nl[nltype];
        for (var i = 0; i < natlang.length; i++) {
            var nl = natlang[i];
            if (score >= nl.t) {
                if (bucket[nl.p] == undefined) { bucket[nl.p] = [];}
                bucket[nl.p].push(sr(nl.d) + ' ' + attr.label);
                return 1;
            }
        }
    }
    return 0;

}

function sr(a) {
   return a[Math.floor(Math.random() * a.length)];
}

function join_words(a, connector) {  
    if (connector) {
        var first = a.shift();
        if (a.length > 0) { 
            var result = first;
            for (var i = 0; i < a.length; i++) {
                result += sr(connector) + a[i];
            }
            return result;
        } else {
            return first;
        }
    } else {
        var last = a.pop();
        if (a.length > 0) {
            return a.join(', ', a) + ' and ' + last;
        } else {
            return last;
        }
    }
}

function min(a, b) { return (a > b)?b:a; }   
function max(a, b) { return (a > b)?a:b; }   

function buildAffinityMatrix() {
    window.status = 'Building Affinity Matrix...';
    for (var i = 0; i < opt_attrs.fields.length; i++) {
        var attr = opt_attrs.fields[i];
        var goal = attr.goal;
        // only consider aff goal type
        if (goal == undefined || goal != 'aff') { continue; }
        // no need to recompute the affinity matrix
        if (attr.aff_matrix != undefined) { continue; }

        if (attr.affinity && attr.affinity.tiers) {
            var tiers = attr.affinity.tiers;
            var matrix = [];
            for (var j = 0; j < tiers.length; j++) {
                var tier = tiers[j];
                for (var k = 0; k < tier.length; k++) {
                    var src = attr['value_map'][tier[k]] || '__BLAH__';
                    matrix[src] = [];
                    for (var m = 0; m < tier.length; m++) {
                        var tgt = attr['value_map'][tier[m]] || '__BLAH__';
                        if (src == tgt) { matrix[src][tgt] = 1; }
                        else            { matrix[src][tgt] = attr.tieraff; }
                    }
                }
            }
            attr.aff_matrix = matrix;
        }
    }
}

function map_value(index, value) {
    return opt_attrs['fields'][index]['value_map'][value];
}

function opt_init() {
    var restored = restore_state();
    if (restored) { setTimeout('_opt_init(0)', 10); }
    else { setTimeout('_opt_init(1)', 500); }
}

function _opt_init(firstvisit) {
    expandData(opt_raw, opt_fields);
    buildAffinityMatrix();
    opt_raw = null;
    compute_stats();
    if (firstvisit) {
        compute_filters();
    } else {
        adjust_filter();
    }
    window.status = 'Done.';
}

function checkbox_filter(i) {
    var attr = opt_attrs['fields'][i];
    var n = attr.name;
    var p = attr.popup || '';
    var result = '<form name=popsel><span class=setFeatures>';
    // if (p) { result += 'Set your brand preference.';  }

    var grid = [];
    // build a lookup table of currently selected values
    var s = attr.sel_val;
    var all = 'checked';
    var sel = '';
    var lbl = (attr.labels || attr.label);
    lbl = lbl.toLowerCase();
    var sh = [];
    if (s && (s.length > 0) && (s.length < attr.choices.length)) { 
        all = '';
        sel = 'checked';
        for (var m = 0; m < s.length; m++) { sh[s[m]] = 1; } 
    }
    grid.push('<table border=0 cellpadding=0 cellspacing=3 class=setFeatures>');
    grid.push('<tr><td colspan="3" nowrap>');
    grid.push('<input type=radio onclick="modechk(this.form, 0)" name=mode ' + all + '><b>Include all ' + lbl + '</b> (sorted by popularity)');
    grid.push('</td></tr>');
    grid.push('<tr><td colspan="2" nowrap>');
    grid.push('<input type=radio name=mode ' + sel + '><b>Set specific ' + lbl + '</b> ');
    grid.push('<a href="javascript:void(0);" onclick="modechk(document.popsel, 1)">Select All</a> | <a href="javascript:void(0);" onclick="modechk(document.popsel, 0)">Clear All</a>');
    grid.push('</td><td align=right>');
    grid.push(' <input type=button name=click value="Set" class="inputButton" onclick="set_affinity(' + i + ', gcvs(this.form.values));">');
    grid.push('</td></tr>');
    for (var t = 0; t < attr.affinity.tiers.length; t++) {
        grid.push('<tr><td colspan=3 bgcolor=#999999></td></tr>');
        var c = attr.affinity.tiers[t];
        var l = c.length;
        var b = Math.floor(l / 3) + 1;
        for (var i = 0; i < b; i++) {
            grid.push('<tr>');
            for (var j = 0; j < 3 && ((i * 3 + j) < l); j++) {
                var n = i * 3 + j;
                grid.push('<td width=33%><nobr>');
                if (sh[c[n]]) {
                    grid.push('<input checked type=checkbox onclick="itemchk(this.form);" name=values value="' + c[n] + '">' + c[n]);
                } else {
                    grid.push('<input type=checkbox onclick="itemchk(this.form);" name=values value="' + c[n] + '">' + c[n]);
                }
                grid.push('</nobr></td>');
            }
            grid.push('</tr>');
        }
    }
    grid.push('</table>');
    result += grid.join('');

    result += '</span></form>';
    return result;
}

function save_state() {
    var encoded = {}
    for (var i = 0; i < opt_attrs.fields.length; i++) {
        var attr = opt_attrs.fields[i];
        if (attr.goal) {
            var goal = {};
            // we want to save: min_val, max_val, slider.getPercent(), sel_code
            if (attr.min_val != undefined)  { goal.min = attr.min_val; }
            if (attr.max_val != undefined)  { goal.max = attr.max_val; }
            if (attr.sel_code != undefined) { goal.slc = attr.sel_code; }
            if (attr.sel_val  != undefined) { goal.slv = attr.sel_val; }
            if (attr.slider) {
                goal.pos = attr.slider.getPercent();
            }
            encoded[attr.name] = goal;
        }
    }
    encoded['sl'] = opt_selected;
    var state = encoded.toSource();
    opt_state_cookie.set('opt_state_' + opt_attrs.cat_code, state ,null, '/' , 'shopping.yahoo.com', 0);
}

function restore_state() {
    var state = unescape(opt_state_cookie.get('opt_state_' + opt_attrs.cat_code));
    if (state) { 
        state = state.replace(/\+/, ' ');
        eval('state = ' + state);

        var touched = 0;
        
        for (var i = 0; i < opt_attrs.fields.length; i++) {
            var attr = opt_attrs.fields[i];
            if (attr.goal) {
                var name = attr.name;
                var s = state[name];
                if (s == undefined) { continue; }

                if (s.min || s.max || s.slc) {
                    touched = 1;
                    var min_o = document.controls[name + '_from'];
                    sv(min_o, s.min);
                    var max_o = document.controls[name + '_to'];
                    sv(max_o, s.max);
                    set_filter(i, min_o, max_o, 1);
                    if (s.slc || s.slv) {
                        set_affinity(i, s.slv, 1);
                    }
                }
                if (s.pos && attr.slider) {
                    touched = 1;
                    attr.slider.setPercent(s.pos || 0);
                }
            }
        }
        opt_selected = state.sl || {};
        return touched;
    } else {
        return 0;
    }
}

Number.prototype.toSource=function() { return this+'' }
String.prototype.toSource=function() {
    var s=this
    s=s.replace(/\\/g, "\\\\")
    s=s.replace(/\"/g, "\\\"")
    s=s.replace(/\n/g, "\\n")
    s=s.replace(/\r/g, "")
    return '"'+s+'"'
}
Boolean.prototype.toSource=function() {
    return this+''
}
Function.prototype.toSource=function() {
    return this+''
}
Array.prototype.toSource=function() {
    var a=this
    var s1='['
    if (a.length>0) {
        for (var i=0;i<a.length;i++) {
            if ((a[i]+'')=='undefined') {
                s1+=', '
                continue;
            }
            s1+=a[i].toSource()+(i<a.length-1?',':'')
        }
    }
    s1+=']'
    return s1
}

Object.prototype.toSource=function() {
    var o=this
    if (o==null) return 'null'
    var s1=''
    for (var item in o) {
        if (item=="toSource") continue;
        if (o[item]==null) {
            continue;
        }
        s1+=item+':'+o[item].toSource()+','
    }
    s1=s1.substr(0,s1.length-1)
    s1='{'+s1+'}'
    return s1
}

function gv(thing) {
    if (typeof(thing) == 'object') {
        return thing.value;
    } else {
        return thing;
    }
}

function sv(thing, value) {
    if (typeof(thing) == 'object') {
        thing.value = value;
    }
}

function gsv(s) {
    return s.options[s.selectedIndex].value;
}

function gcvs(c) {
    var result = [];
    for (var i = 0; i < c.length; i++) {
        if (c[i].checked) {
            result.push(c[i].value);
        }
    }
    return result;
}

function inputf(thing, def) {
    if (thing.value == def) {
        thing.value = '';
    }
}

function inputb(thing, def) {
    if (thing.value == '') {
        thing.value = def;
    }
}

function keypress(event, index, from, to, nxt) {
    if (event.keyCode == 13) { 
        set_filter(index, from, to);
        if (nxt != undefined) { nxt.focus(); }
        return false;
    } else {
        return true;
    }
}

function modechk(f, s) {
    var l = f['values'];
    if (l) {
        for (var i = 0; i < l.length; i++) {
            l[i].checked = s;
        }
    }
    f['mode'][0].checked = !s;
    f['mode'][1].checked = s;
}

function itemchk(f) {
    var l = f['values'];
    var a = 0;
    if (l) { 
        for (var i = 0; i < l.length; i++) {
            if (l[i].checked) { a = 1; break; }
        }
    }
    f['mode'][0].checked = !a;
    f['mode'][1].checked = a;
}

function opt_reset() {
    for (var i = 0; i < opt_attrs.fields.length; i++) {
        var attr = opt_attrs.fields[i];
        var name = attr.name;
        if (attr.filter && attr.filter == 'range') {
            var min_o = document.controls[name + '_from'];
            sv(min_o, 'min');
            var max_o = document.controls[name + '_to'];
            sv(max_o, 'max');
            set_filter(i, min_o, max_o, 1);
        }
        if (attr.slider) {
            attr.slider.setPercent(0);
        }
        if (attr.sel_code) {
            set_affinity(i, [], 1);
        }
    }
    opt_selected = {};
    adjust_filter();
}

function var_dump(data, indent, max) {
    var result = [];
    if (indent == undefined) {
        indent = 0;
    }
    if (max && indent > max) { return 'stopped recursion' ; }
    var indent_txt = '';
    for (var i = 0; i < indent; i++) {
        indent_txt += '  ';
    }
    if (typeof(data) == 'object') {
        for (var key in data) {
             result.push(key + ' => ' + var_dump(data[key], indent + 1));
        }
        return '{ ' +  result.join(",\n" + indent_txt) + "\n" + indent_txt + " }\n";
    } 
    else if (typeof(data) == 'array') {
        for (var i = 0; i < data.length; i++) {
            result.push(var_dump(data[i], indent + 1));
        }
        return '[ ' +  result.join(",\n" + indent_txt) + "\n" + indent_txt + " ]\n";
    } 
    else if (typeof(data) == 'function') {
        return '<font color=green>function</font>';
    }
    else {
        if (data == undefined) {
            return '<font color=red>undef</font>';
        } else {
            return data;
        }
    }
}

function track_click(event) {
    if (!event) { event = window.event; }
    var src = event.srcElement;
    if (!src) { src = event.currentTarget; }
    if (!src) { return false; }
    if (src.tagName != 'A') {
        src = src.parentNode;
    }
    if (!src) { return false; }

    var url = src.href;
    var ult = src['ult'];
    if (ult) { return false; }
    var pos = src['pos'];
    var slk = src['slk'];
    var itm = opt_data[pos];

    var track = {
        _S  : opt_spaceid,
        _s  : itm.id,
        _p  : pos,
        sec : 'sr',
        slk : slk
    };

    url = yahoo_linktrack_click(url, track);
    src.href = url;
    src['ult'] = 1;
    return false;
}

// ULT tracking code

var YAHOO_ULT_LIBCODE = 2;
var CTRL_C            = '\x03';
var CTRL_D            = '\x04';
var YAHOO_LRD_BEACON  = 'http://us.lrd.yahoo.com/t/b.gif';

var YAHOO_BASE64_STR  =  "ABCDEFGHIJKLMNOP" +
                         "QRSTUVWXYZabcdef" +
                         "ghijklmnopqrstuv" +
                         "wxyz0123456789._-";



/* generate a ylc url */
function yahoo_linktrack_click(u, p) {
    if (u && p) {} else { return u; }

    // hardcode _r to 2 for this library
    p['_r'] = YAHOO_ULT_LIBCODE;

    var ks = []; 
    var i = 0;
    for (var k in p) { 
        var v = p[k];
        if (typeof(v) == undefined) { v = p[k] = ''; }
        // keys must be at least 1 char long
        if (k.length < 1 ) { return u; }           
        // and not longer than 8
        if (k.length > 8) { return u; }
        // keys can't have spaces
        if (k.indexOf(' ') != -1) { return u; }   
        // or ctrl chars
        if (yahoo_has_ctrl_char(k) || yahoo_has_ctrl_char(v)) { return u; }  

        ks[i++] = k; 
    }
    ks = ks.sort(); 

    var f = [];
    for (var i = 0; i < ks.length; i++) {
        f[i] = ks[i] + CTRL_C + p[ks[i]];
    }
    f = f.join(CTRL_D);

    if (f.length < 1 || f.length > 1024) { return u; }

    f = ';_ylc=' + yahoo_encode64(f);

    var i = u.indexOf('/*'); 
    if (i == -1) { i = u.indexOf('/?'); }
    if (i == -1) { i = u.indexOf('?'); }

    if (i == -1) { 
        return u + f;
    } else {
        return u.substr(0, i) + f + u.substr(i);
    }
}

/* register an event on the lrd server by refreshing an img src */
function yahoo_linktrack_beacon(i, p) {
    if (i && p) {
        var url = yahoo_linktrack_click(YAHOO_LRD_BEACON, p);
        url += '?.rand=' . Math.random();
        i.src = url;
    }
}

function yahoo_has_ctrl_char(s) {
    for (i = 0; i < s.length; i++) {
        if( s.charCodeAt(i) < 20 ) {
            return true;
        }
    }
    return false;
}

function yahoo_encode64(input) {
    var output = "";
    var chr1, chr2, chr3 = "";
    var enc1, enc2, enc3, enc4 = "";
    var i = 0;

    do {
        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output = output + 
        YAHOO_BASE64_STR.charAt(enc1) + 
        YAHOO_BASE64_STR.charAt(enc2) + 
        YAHOO_BASE64_STR.charAt(enc3) + 
        YAHOO_BASE64_STR.charAt(enc4);
        chr1 = chr2 = chr3 = "";
        enc1 = enc2 = enc3 = enc4 = "";
    } while (i < input.length);

    return output;
}
