/* The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Bugzilla Bug Tracking System. * * The Initial Developer of the Original Code is BugzillaSource, Inc. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Max Kanat-Alexander <mkanat@bugzilla.org> */ 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_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); fix_query_string(row); return clone; } function custom_search_open_paren() { var row = document.getElementById('custom_search_last_row'); // 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_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 + '" 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 j_top = document.getElementById('j_top'); var any_all = j_top.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'); 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_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) + '" value="CP">'; new_row.parentNode.insertBefore(paren_row, new_row); if (new_margin == 0) { YAHOO.util.Dom.addClass('cp_container', 'bz_default_hidden'); } 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_ids(parent, preserve_values) { // Update the label of the checkbox. var label = YAHOO.util.Dom.getElementBy(function() { return true }, 'label', parent); 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 parent back to their default // and fixes their id. var fields = YAHOO.util.Dom.getElementsByClassName('custom_search_form_field', null, parent); 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 new row. field.name = field.name.replace(/\d+$/, id); field.id = field.name; } return id; } 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; }