/* 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.id = null; 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 = '('; 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 = ')'; 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; }