/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, v. 2.0.
 */

var PAREN_INDENT_EM = 2;
var ANY_ALL_SELECT_CLASS = 'any_all_select';

// When somebody chooses to "Hide Advanced Features" for Custom Search,
// we don't want to hide "Not" checkboxes if they've been checked. We
// accomplish this by removing the custom_search_advanced class from Not
// checkboxes when they are checked.
//
// We never add the custom_search_advanced class back. If we did, TUI
// would get confused in this case: Check Not, Hide Advanced Features,
// Uncheck Not, Show Advanced Features. (It hides "Not" when it shouldn't.)
function custom_search_not_changed(id) {
    var container = document.getElementById('custom_search_not_container_' + id);
    YAHOO.util.Dom.removeClass(container, 'custom_search_advanced');

    fix_query_string(container);
}

function custom_search_new_row() {
    var row = document.getElementById('custom_search_last_row');
    var clone = row.cloneNode(true);
    
    _cs_fix_row_ids(clone);
   
    // We only want one copy of the buttons, in the new row. So the old
    // ones get deleted.
    var op_button = document.getElementById('op_button');
    row.removeChild(op_button);
    var cp_button = document.getElementById('cp_container');
    row.removeChild(cp_button);
    var add_button = document.getElementById('add_button');
    row.removeChild(add_button);
    _remove_any_all(clone);

    // Always make sure there's only one row with this id.
    row.removeAttribute('id');
    row.parentNode.appendChild(clone);
    cs_reconfigure(row);
    fix_query_string(row);
    return clone;
}

var _cs_source_any_all;
function custom_search_open_paren() {
    var row = document.getElementById('custom_search_last_row');

    // create a copy of j_top and use that as the source, so we can modify
    // j_top if required
    if (!_cs_source_any_all) {
        var j_top = document.getElementById('j_top');
        _cs_source_any_all = j_top.cloneNode(true);
    }

    // find the parent any/all select, and remove the grouped option
    var structure = _cs_build_structure(row);
    var old_id = _cs_get_row_id(row);
    var parent_j = document.getElementById(_cs_get_join(structure, 'f' + old_id));
    _cs_remove_and_g(parent_j);

    // If there's an "Any/All" select in this row, it needs to stay as
    // part of the parent paren set.
    var old_any_all = _remove_any_all(row);
    if (old_any_all) {
        var any_all_row = row.cloneNode(false);
        any_all_row.id = null;
        any_all_row.appendChild(old_any_all);
        row.parentNode.insertBefore(any_all_row, row);
    }

    // We also need a "Not" checkbox to stay in the parent paren set.
    var new_not = YAHOO.util.Dom.getElementsByClassName(
        'custom_search_not_container', null, row);
    var not_for_paren = new_not[0].cloneNode(true);

    // Preserve the values when modifying the row.
    var id = _cs_fix_row_ids(row, true);
    var prev_id = id - 1;

    var paren_row = row.cloneNode(false);
    paren_row.id = null;
    paren_row.innerHTML = '(<input type="hidden" name="f' + prev_id
                        + '" id="f' + prev_id + '" value="OP">';
    paren_row.insertBefore(not_for_paren, paren_row.firstChild);
    row.parentNode.insertBefore(paren_row, row);

    // New paren set needs a new "Any/All" select.
    var any_all_container = document.createElement('div');
    YAHOO.util.Dom.addClass(any_all_container, ANY_ALL_SELECT_CLASS);
    var any_all = _cs_source_any_all.cloneNode(true);
    any_all.name = 'j' + prev_id;
    any_all.id = any_all.name;
    any_all_container.appendChild(any_all);
    row.insertBefore(any_all_container, row.firstChild);

    var margin = YAHOO.util.Dom.getStyle(row, 'margin-left');
    var int_match = margin.match(/\d+/);
    var new_margin = parseInt(int_match[0]) + PAREN_INDENT_EM;
    YAHOO.util.Dom.setStyle(row, 'margin-left', new_margin + 'em');
    YAHOO.util.Dom.removeClass('cp_container', 'bz_default_hidden');

    cs_reconfigure(any_all_container);
    fix_query_string(any_all_container);
}

function custom_search_close_paren() {
    var new_row = custom_search_new_row();
    
    // We need to up the new row's id by one more, because we're going
    // to insert a "CP" before it.
    var id = _cs_fix_row_ids(new_row);

    var margin = YAHOO.util.Dom.getStyle(new_row, 'margin-left');
    var int_match = margin.match(/\d+/);
    var new_margin = parseInt(int_match[0]) - PAREN_INDENT_EM;
    YAHOO.util.Dom.setStyle(new_row, 'margin-left', new_margin + 'em');

    var paren_row = new_row.cloneNode(false);
    paren_row.id = null;
    paren_row.innerHTML = ')<input type="hidden" name="f' + (id - 1)
                        + '" id="f' + (id - 1) + '" value="CP">';
  
    new_row.parentNode.insertBefore(paren_row, new_row);

    if (new_margin == 0) {
        YAHOO.util.Dom.addClass('cp_container', 'bz_default_hidden');
    }

    cs_reconfigure(new_row);
    fix_query_string(new_row);
}

// When a user goes Back in their browser after searching, some browsers
// (Chrome, as of September 2011) do not remember the DOM that was created
// by the Custom Search JS. (In other words, their whole entered Custom
// Search disappears.) The solution is to update the History object,
// using the query string, which query.cgi can read to re-create the page
// exactly as the user had it before.
function fix_query_string(form_member) {
    // window.History comes from history.js.
    // It falls back to the normal window.history object for HTML5 browsers.
    if (!(window.History && window.History.replaceState))
        return;

    var form = YAHOO.util.Dom.getAncestorByTagName(form_member, 'form');
    // Disable the token field so setForm doesn't include it
    var reenable_token = false;
    if (form['token'] && !form['token'].disabled) {
      form['token'].disabled = true;
      reenable_token = true;
    }
    var query = YAHOO.util.Connect.setForm(form);
    if (reenable_token)
      form['token'].disabled = false;
    window.History.replaceState(null, document.title, '?' + query);
}

// Browsers that do not support HTML5's history.replaceState gain an emulated
// version via history.js, with the full query_string in the location's hash.
// We rewrite the URL to include the hash and redirect to the new location,
// which causes custom search fields to work.
function redirect_html4_browsers() {
    var hash = document.location.hash;
    if (hash == '') return;
    hash = hash.substring(1);
    if (hash.substring(0, 10) != 'query.cgi?') return;
    var url = document.location + "";
    url = url.substring(0, url.indexOf('query.cgi#')) + hash;
    document.location = url;
}

function _cs_fix_row_ids(row, preserve_values) {
    // Update the label of the checkbox.
    var label = YAHOO.util.Dom.getElementBy(function() { return true }, 'label', row);
    var id_match = label.htmlFor.match(/\d+$/);
    var id = parseInt(id_match[0]) + 1;
    label.htmlFor = label.htmlFor.replace(/\d+$/, id);

    // Sets all the inputs in the row back to their default
    // and fixes their id.
    var fields =
        YAHOO.util.Dom.getElementsByClassName('custom_search_form_field', null, row);
    for (var i = 0; i < fields.length; i++) {
        var field = fields[i];

        if (!preserve_values) {
            if (field.type == "checkbox") {
                field.checked = false;
            }
            else {
                field.value = '';
            }
        }

        // Update the numeric id for the row.
        field.name = field.name.replace(/\d+$/, id);
        field.id = field.name;
    }

    return id;
}

function _cs_build_structure(form_member) {
    // build a map of the structure of the custom fields
    var form = YAHOO.util.Dom.getAncestorByTagName(form_member, 'form');
    var last_id = _get_last_cs_row_id(form);
    var structure = [ 'j_top' ];
    var nested = [ structure ];
    for (var id = 1; id <= last_id; id++) {
        var f = form['f' + id];
        if (!f || !f.parentNode.parentNode) continue;

        if (f.value == 'OP') {
            var j = [ 'j' + id ];
            nested[nested.length - 1].push(j);
            nested.push(j);
            continue;
        } else if (f.value == 'CP') {
            nested.pop();
            continue;
        } else {
            nested[nested.length - 1].push('f' + id);
        }
    }
    return structure;
}

function cs_reconfigure(form_member) {
    var structure = _cs_build_structure(form_member);
    _cs_add_listeners(structure);
    _cs_trigger_j_listeners(structure);
    fix_query_string(form_member);

    var j = _cs_get_join(structure, 'f' + _get_last_cs_row_id());
    document.getElementById('op_button').disabled = document.getElementById(j).value == 'AND_G';
}

function _cs_add_listeners(parents) {
    for (var i = 0, l = parents.length; i < l; i++) {
        if (typeof(parents[i]) == 'object') {
            // nested
            _cs_add_listeners(parents[i]);
        } else if (i == 0) {
            // joiner
            YAHOO.util.Event.removeListener(parents[i], 'change', _cs_j_change);
            YAHOO.util.Event.addListener(parents[i], 'change', _cs_j_change, parents);
        } else {
            // field
            YAHOO.util.Event.removeListener(parents[i], 'change', _cs_f_change);
            YAHOO.util.Event.addListener(parents[i], 'change', _cs_f_change, parents);
        }
    }
}

function _cs_trigger_j_listeners(fields) {
    var has_children = false;
    for (var i = 0, l = fields.length; i < l; i++) {
        if (typeof(fields[i]) == 'undefined') {
            continue;
        } else if (typeof(fields[i]) == 'object') {
            // nested
            _cs_trigger_j_listeners(fields[i]);
            has_children = true;
        } else if (i == 0) {
            _cs_j_change(undefined, fields);
        }
    }
    if (has_children) {
        _cs_remove_and_g(document.getElementById(fields[0]));
    }
}

function _cs_get_join(parents, field) {
    for (var i = 0, l = parents.length; i < l; i++) {
        if (typeof(parents[i]) == 'object') {
            // nested
            var result = _cs_get_join(parents[i], field);
            if (result) return result;
        } else if (parents[i] == field) {
            return parents[0];
        }
    }
    return false;
}

function _cs_remove_and_g(join_field) {
    var index = bz_optionIndex(join_field, 'AND_G');
    join_field.options[index] = null;
    join_field.options[bz_optionIndex(join_field, 'AND')].innerHTML = cs_and_label;
    join_field.options[bz_optionIndex(join_field, 'OR')].innerHTML = cs_or_label;
}

function _cs_j_change(evt, fields, field) {
    var j = document.getElementById(fields[0]);
    if (j && j.value == 'AND_G') {
        for (var i = 1, l = fields.length; i < l; i++) {
            if (typeof(fields[i]) == 'object') continue;
            if (!field) {
                field = document.getElementById(fields[i]).value;
            } else {
                document.getElementById(fields[i]).value = field;
            }
        }
        if (evt) {
            fix_query_string(j);
        }
        if ('f' + _get_last_cs_row_id() == fields[fields.length - 1]) {
            document.getElementById('op_button').style.display = 'none';
        }
    } else {
        document.getElementById('op_button').style.display = '';
    }
}

function _cs_f_change(evt, args) {
    var field = YAHOO.util.Event.getTarget(evt);
    _cs_j_change(evt, args, field.value);
}

function _get_last_cs_row_id() {
    return _cs_get_row_id('custom_search_last_row');
}

function _cs_get_row_id(row) {
    var label = YAHOO.util.Dom.getElementBy(function() { return true }, 'label', row);
    return parseInt(label.htmlFor.match(/\d+$/)[0]);
}

function _remove_any_all(parent) {
    var any_all = YAHOO.util.Dom.getElementsByClassName(
        ANY_ALL_SELECT_CLASS, null, parent);
    if (any_all[0]) {
        parent.removeChild(any_all[0]);
        return any_all[0];
    }
    return null;
}
