summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkiko%async.com.br <>2004-07-21 09:58:44 +0200
committerkiko%async.com.br <>2004-07-21 09:58:44 +0200
commitcb2d1a0a94cf690f2f29044010324b3d22505991 (patch)
tree5f4e29aad8a258bb90a5bb140a3d9a506714ecb2
parent7bdd1cbe564883cd12abee3657e671e97e85a8e5 (diff)
downloadbugzilla-cb2d1a0a94cf690f2f29044010324b3d22505991.tar.gz
bugzilla-cb2d1a0a94cf690f2f29044010324b3d22505991.tar.xz
Fix for bug 123030: Move query.cgi javascript to separate file. Create
productform.js that contains functions to handle the various select boxes in the advanced search page, and update the callsites to use it. r,a=myk.
-rw-r--r--js/productform.js295
-rw-r--r--template/en/default/search/form.html.tmpl298
-rw-r--r--template/en/default/search/search-advanced.html.tmpl40
3 files changed, 335 insertions, 298 deletions
diff --git a/js/productform.js b/js/productform.js
new file mode 100644
index 000000000..0be0d4971
--- /dev/null
+++ b/js/productform.js
@@ -0,0 +1,295 @@
+/* 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 Netscape Communications
+ * Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s): Christian Reis <kiko@async.com.br>
+ */
+
+/* this file contains functions to update form controls based on a
+ * collection of javascript arrays containing strings */
+
+/* selectProduct reads the selection from the product control and
+ * updates version, component and milestone controls accordingly.
+ *
+ * - product, component, version and milestone: form controls
+ *
+ * globals (3vil!):
+ * - cpts, vers, tms: array of arrays, indexed by product name. the
+ * subarrays contain a list of names to be fed to the respective
+ * selectboxes. For bugzilla, these are generated with perl code
+ * at page start.
+ * - first_load: boolean, specifying if it is the first time we load
+ * the query page.
+ * - last_sel: saves our last selection list so we know what has
+ * changed, and optimize for additions.
+ */
+function selectProduct(product, component, version, milestone) {
+
+ if (!product) {
+ /* this is to avoid handling events that occur before the form
+ * itself is ready, which could happen in buggy browsers. */
+ return;
+ }
+
+ /* if this is the first load and nothing is selected, no need to
+ * merge and sort all components; perl gives it to us sorted. */
+ if ((first_load) && (product.selectedIndex == -1)) {
+ first_load = false;
+ return;
+ }
+
+ /* turn first_load off. this is tricky, since it seems to be
+ * redundant with the above clause. It's not: if when we first load
+ * the page there is _one_ element selected, it won't fall into that
+ * clause, and first_load will remain 1. Then, if we unselect that
+ * item, selectProduct will be called but the clause will be valid
+ * (since selectedIndex == -1), and we will return - incorrectly -
+ * without merge/sorting. */
+ first_load = false;
+
+ /* - sel keeps the array of products we are selected.
+ * - merging says if it is a full list or just a list of products that
+ * were added to the current selection. */
+ var merging = false;
+ var sel = Array();
+
+ /* if nothing selected, pick all */
+ var findall = product.selectedIndex == -1;
+ sel = get_selection(product, findall, false);
+ if (!findall) {
+ /* save sel for the next invocation of selectProduct() */
+ var tmp = sel;
+
+ /* this is an optimization: if we have just added products to an
+ * existing selection, no need to clear the form controls and add
+ * everybody again; just merge the new ones with the existing
+ * options. */
+ if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
+ sel = fake_diff_array(sel, last_sel);
+ merging = true;
+ }
+ last_sel = tmp;
+ }
+
+ /* do the actual fill/update */
+ if (component) {
+ var saved_cpts = get_selection(component, false, true);
+ updateSelect(cpts, sel, component, merging);
+ restoreSelection(component, saved_cpts);
+ }
+
+ if (version) {
+ var saved_vers = get_selection(version, false, true);
+ updateSelect(vers, sel, version, merging);
+ restoreSelection(version, saved_vers);
+ }
+
+ if (milestone) {
+ var saved_tms = get_selection(milestone, false, true);
+ updateSelect(tms, sel, milestone, merging);
+ restoreSelection(milestone, saved_tms);
+ }
+}
+
+
+/* updateSelect(array, sel, target, merging)
+ *
+ * Adds to the target select object all elements in array that
+ * correspond to the elements selected in source.
+ * - array should be a array of arrays, indexed by number. the
+ * array should contain the elements that correspond to that
+ * product.
+ * - sel is a list of selected items, either whole or a diff
+ * depending on merging.
+ * - target should be the target select object.
+ * - merging (boolean) determines if we are mergine in a diff or
+ * substituting the whole selection. a diff is used to optimize adding
+ * selections.
+ *
+ * Example (compsel is a select form control)
+ *
+ * var components = Array();
+ * components[1] = [ 'ComponentA', 'ComponentB' ];
+ * components[2] = [ 'ComponentC', 'ComponentD' ];
+ * source = [ 2 ];
+ * updateSelect(components, source, compsel, 0, 0);
+ *
+ * would clear compsel and add 'ComponentC' and 'ComponentD' to it.
+ *
+ */
+
+function updateSelect(array, sel, target, merging) {
+
+ var i, item;
+
+ /* If we have no versions/components/milestones */
+ if (array.length < 1) {
+ target.options.length = 0;
+ return false;
+ }
+
+ if (merging) {
+ /* array merging/sorting in the case of multiple selections */
+ /* merge in the current options with the first selection */
+ item = merge_arrays(array[sel[0]], target.options, 1);
+
+ /* merge the rest of the selection with the results */
+ for (i = 1 ; i < sel.length ; i++) {
+ item = merge_arrays(array[sel[i]], item, 0);
+ }
+ } else if ( sel.length > 1 ) {
+ /* here we micro-optimize for two arrays to avoid merging with a
+ * null array */
+ item = merge_arrays(array[sel[0]],array[sel[1]], 0);
+
+ /* merge the arrays. not very good for multiple selections. */
+ for (i = 2; i < sel.length; i++) {
+ item = merge_arrays(item, array[sel[i]], 0);
+ }
+ } else { /* single item in selection, just get me the list */
+ item = array[sel[0]];
+ }
+
+ /* clear select */
+ target.options.length = 0;
+
+ /* load elements of list into select */
+ for (i = 0; i < item.length; i++) {
+ target.options[i] = new Option(item[i], item[i]);
+ }
+ return true;
+}
+
+
+/* Selects items in control that have index defined in sel
+ * - control: SELECT control to be restored
+ * - selnames: array of indexes in select form control */
+function restoreSelection(control, selnames) {
+ /* right. this sucks. but I see no way to avoid going through the
+ * list and comparing to the contents of the control. */
+ for (var j=0; j < selnames.length; j++) {
+ for (var i=0; i < control.options.length; i++) {
+ if (control.options[i].value == selnames[j]) {
+ control.options[i].selected = true;
+ }
+ }
+ }
+}
+
+
+/* Returns elements in a that are not in b.
+ * NOT A REAL DIFF: does not check the reverse.
+ * - a,b: arrays of values to be compare. */
+function fake_diff_array(a, b) {
+ var newsel = new Array();
+ var found = false;
+
+ /* do a boring array diff to see who's new */
+ for (var ia in a) {
+ for (var ib in b) {
+ if (a[ia] == b[ib]) {
+ found = true;
+ }
+ }
+ if (!found) {
+ newsel[newsel.length] = a[ia];
+ }
+ found = false;
+ }
+ return newsel;
+}
+
+/* takes two arrays and sorts them by string, returning a new, sorted
+ * array. the merge removes dupes, too.
+ * - a, b: arrays to be merge.
+ * - b_is_select: if true, then b is actually an optionitem and as
+ * such we need to use item.value on it. */
+function merge_arrays(a, b, b_is_select) {
+ var pos_a = 0;
+ var pos_b = 0;
+ var ret = new Array();
+ var bitem, aitem;
+
+ /* iterate through both arrays and add the larger item to the return
+ * list. remove dupes, too. Use toLowerCase to provide
+ * case-insensitivity. */
+ while ((pos_a < a.length) && (pos_b < b.length)) {
+ if (b_is_select) {
+ bitem = b[pos_b].value;
+ } else {
+ bitem = b[pos_b];
+ }
+ aitem = a[pos_a];
+
+ /* smaller item in list a */
+ if (aitem.toLowerCase() < bitem.toLowerCase()) {
+ ret[ret.length] = aitem;
+ pos_a++;
+ } else {
+ /* smaller item in list b */
+ if (aitem.toLowerCase() > bitem.toLowerCase()) {
+ ret[ret.length] = bitem;
+ pos_b++;
+ } else {
+ /* list contents are equal, inc both counters. */
+ ret[ret.length] = aitem;
+ pos_a++;
+ pos_b++;
+ }
+ }
+ }
+
+ /* catch leftovers here. these sections are ugly code-copying. */
+ if (pos_a < a.length) {
+ for (; pos_a < a.length ; pos_a++) {
+ ret[ret.length] = a[pos_a];
+ }
+ }
+
+ if (pos_b < b.length) {
+ for (; pos_b < b.length; pos_b++) {
+ if (b_is_select) {
+ bitem = b[pos_b].value;
+ } else {
+ bitem = b[pos_b];
+ }
+ ret[ret.length] = bitem;
+ }
+ }
+ return ret;
+}
+
+/* Returns an array of indexes or values from a select form control.
+ * - control: select control from which to find selections
+ * - findall: boolean, store all options when true or just the selected
+ * indexes
+ * - want_values: boolean; we store values when true and indexes when
+ * false */
+function get_selection(control, findall, want_values) {
+ var ret = new Array();
+
+ if ((!findall) && (control.selectedIndex == -1)) {
+ return ret;
+ }
+
+ for (var i=0; i<control.length; i++) {
+ if (findall || control.options[i].selected) {
+ ret[ret.length] = want_values ? control.options[i].value : i;
+ }
+ }
+ return ret;
+}
+
diff --git a/template/en/default/search/form.html.tmpl b/template/en/default/search/form.html.tmpl
index 2fd458148..f875f3541 100644
--- a/template/en/default/search/form.html.tmpl
+++ b/template/en/default/search/form.html.tmpl
@@ -23,302 +23,6 @@
[% PROCESS global/variables.none.tmpl %]
- [%# Note: use Template comments and not JS ones here, to avoid bloating
- what we actually send to the browser %]
-
-<script language="JavaScript" type="text/javascript"> <!--
-
-var first_load = true; [%# is this the first time we load the page? %]
-var last_sel = new Array(); [%# caches last selection %]
-
-var cpts = new Array();
-var vers = new Array();
-[% IF Param('usetargetmilestone') %]
-var tms = new Array();
-[% END %]
-
-[%# Create three arrays of components, versions and target milestones, indexed
- # numerically according to the product they refer to. #%]
-
-[% n = 0 %]
-[% FOREACH p = product %]
- cpts[[% n %]] = [
- [%- FOREACH item = p.components %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
- vers[[% n %]] = [
- [%- FOREACH item = p.versions -%]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
- [% IF Param('usetargetmilestone') %]
- tms[[% n %]] = [
- [%- FOREACH item = p.milestones %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
- [% END %]
- [% n = n+1 %]
-[% END %]
-
-[%# updateSelect(array, sel, target, merging)
- #
- # Adds to the target select object all elements in array that
- # correspond to the elements selected in source.
- # - array should be a array of arrays, indexed by number. the
- # array should contain the elements that correspond to that
- # product.
- # - sel is a list of selected items, either whole or a diff
- # depending on merging.
- # - target should be the target select object.
- # - merging (boolean) determines if we are mergine in a diff or
- # substituting the whole selection. a diff is used to optimize adding
- # selections.
- #
- # Example (compsel is a select form control)
- #
- # var components = Array();
- # components[1] = [ 'ComponentA', 'ComponentB' ];
- # components[2] = [ 'ComponentC', 'ComponentD' ];
- # source = [ 2 ];
- # updateSelect(components, source, compsel, 0, 0);
- #
- # would clear compsel and add 'ComponentC' and 'ComponentD' to it.
- #
- %]
-
-function updateSelect(array, sel, target, merging) {
-
- var i, item;
-
- [%# If we have no versions/components/milestones %]
- if (array.length < 1) {
- target.options.length = 0;
- return false;
- }
-
- if (merging) {
- [%# array merging/sorting in the case of multiple selections %]
- [%# merge in the current options with the first selection %]
- item = merge_arrays(array[sel[0]], target.options, 1);
-
- [%# merge the rest of the selection with the results %]
- for (i = 1 ; i < sel.length ; i++) {
- item = merge_arrays(array[sel[i]], item, 0);
- }
- } else if ( sel.length > 1 ) {
- [%# here we micro-optimize for two arrays to avoid merging with a
- null array %]
- item = merge_arrays(array[sel[0]],array[sel[1]], 0);
-
- [%# merge the arrays. not very good for multiple selections. %]
- for (i = 2; i < sel.length; i++) {
- item = merge_arrays(item, array[sel[i]], 0);
- }
- } else { [%# single item in selection, just get me the list %]
- item = array[sel[0]];
- }
-
- [%# clear select %]
- target.options.length = 0;
-
- [%# load elements of list into select %]
- for (i = 0; i < item.length; i++) {
- target.options[i] = new Option(item[i], item[i]);
- }
- return true;
-}
-
-[%# Returns elements in a that are not in b.
- # NOT A REAL DIFF: does not check the reverse.
- # - a,b: arrays of values to be compare. %]
-function fake_diff_array(a, b) {
- var newsel = new Array();
- var found = false;
-
- [%# do a boring array diff to see who's new %]
- for (var ia in a) {
- for (var ib in b) {
- if (a[ia] == b[ib]) {
- found = true;
- }
- }
- if (!found) {
- newsel[newsel.length] = a[ia];
- }
- found = false;
- }
- return newsel;
-}
-
-[%# takes two arrays and sorts them by string, returning a new, sorted
- # array. the merge removes dupes, too.
- # - a, b: arrays to be merge.
- # - b_is_select: if true, then b is actually an optionitem and as
- # such we need to use item.value on it. %]
-function merge_arrays(a, b, b_is_select) {
- var pos_a = 0;
- var pos_b = 0;
- var ret = new Array();
- var bitem, aitem;
-
- [%# iterate through both arrays and add the larger item to the return
- list. remove dupes, too. Use toLowerCase to provide
- case-insensitivity. %]
- while ((pos_a < a.length) && (pos_b < b.length)) {
- if (b_is_select) {
- bitem = b[pos_b].value;
- } else {
- bitem = b[pos_b];
- }
- aitem = a[pos_a];
-
- [%# smaller item in list a %]
- if (aitem.toLowerCase() < bitem.toLowerCase()) {
- ret[ret.length] = aitem;
- pos_a++;
- } else {
- [%# smaller item in list b %]
- if (aitem.toLowerCase() > bitem.toLowerCase()) {
- ret[ret.length] = bitem;
- pos_b++;
- } else {
- [%# list contents are equal, inc both counters. %]
- ret[ret.length] = aitem;
- pos_a++;
- pos_b++;
- }
- }
- }
-
- [%# catch leftovers here. these sections are ugly code-copying. %]
- if (pos_a < a.length) {
- for (; pos_a < a.length ; pos_a++) {
- ret[ret.length] = a[pos_a];
- }
- }
-
- if (pos_b < b.length) {
- for (; pos_b < b.length; pos_b++) {
- if (b_is_select) {
- bitem = b[pos_b].value;
- } else {
- bitem = b[pos_b];
- }
- ret[ret.length] = bitem;
- }
- }
- return ret;
-}
-
-[%# Returns an array of indexes or values from a select form control.
- # - control: select control from which to find selections
- # - findall: boolean, store all options when true or just the selected
- # indexes
- # - want_values: boolean; we store values when true and indexes when
- # false %]
-function get_selection(control, findall, want_values) {
- var ret = new Array();
-
- if ((!findall) && (control.selectedIndex == -1)) {
- return ret;
- }
-
- for (var i=0; i<control.length; i++) {
- if (findall || control.options[i].selected) {
- ret[ret.length] = want_values ? control.options[i].value : i;
- }
- }
- return ret;
-}
-
-[%# Selects items in control that have index defined in sel
- # - control: SELECT control to be restored
- # - selnames: array of indexes in select form control %]
-function restoreSelection(control, selnames) {
- [%# right. this sucks. but I see no way to avoid going through the
- # list and comparing to the contents of the control. %]
- for (var j=0; j < selnames.length; j++) {
- for (var i=0; i < control.options.length; i++) {
- if (control.options[i].value == selnames[j]) {
- control.options[i].selected = true;
- }
- }
- }
-}
-
-[%# selectProduct reads the selection from f.product and updates
- # f.version, component and target_milestone accordingly.
- # - f: a form containing product, component, varsion and
- # target_milestone select boxes.
- # globals (3vil!):
- # - cpts, vers, tms: array of arrays, indexed by product name. the
- # subarrays contain a list of names to be fed to the respective
- # selectboxes. For bugzilla, these are generated with perl code
- # at page start.
- # - first_load: boolean, specifying if it is the first time we load
- # the query page.
- # - last_sel: saves our last selection list so we know what has
- # changed, and optimize for additions. %]
-function selectProduct(f) {
- [%# this is to avoid handling events that occur before the form
- itself is ready, which could happen in buggy browsers. %]
- if ((!f) || (!f.product)) {
- return;
- }
-
- [%# if this is the first load and nothing is selected, no need to
- merge and sort all components; perl gives it to us sorted. %]
- if ((first_load) && (f.product.selectedIndex == -1)) {
- first_load = false;
- return;
- }
-
- [%# turn first_load off. this is tricky, since it seems to be
- redundant with the above clause. It's not: if when we first load
- the page there is _one_ element selected, it won't fall into that
- clause, and first_load will remain 1. Then, if we unselect that
- item, selectProduct will be called but the clause will be valid
- (since selectedIndex == -1), and we will return - incorrectly -
- without merge/sorting. %]
- first_load = false;
-
- [%# - sel keeps the array of products we are selected.
- - merging says if it is a full list or just a list of products that
- were added to the current selection. %]
- var merging = false;
- var sel = Array();
-
- [%# if nothing selected, pick all %]
- var findall = f.product.selectedIndex == -1;
- sel = get_selection(f.product, findall, false);
- if (!findall) {
- [%# save sel for the next invocation of selectProduct() %]
- var tmp = sel;
-
- [%# this is an optimization: if we have just added products to an
- existing selection, no need to clear the form controls and add
- everybody again; just merge the new ones with the existing
- options. %]
- if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
- sel = fake_diff_array(sel, last_sel);
- merging = true;
- }
- last_sel = tmp;
- }
- [%# save original options selected %]
- var saved_cpts = get_selection(f.component, false, true);
- var saved_vers = get_selection(f.version, false, true);
- [% IF Param('usetargetmilestone') %]
- var saved_tms = get_selection(f.target_milestone, false, true);
- [% END %]
-
- [%# do the actual fill/update, reselect originally selected options %]
- updateSelect(cpts, sel, f.component, merging);
- restoreSelection(f.component, saved_cpts);
- updateSelect(vers, sel, f.version, merging);
- restoreSelection(f.version, saved_vers);
- [% IF Param('usetargetmilestone') %]
- updateSelect(tms, sel, f.target_milestone, merging);
- restoreSelection(f.target_milestone, saved_tms);
- [% END %]
-}
-
-// -->
-</script>
-
[% query_variants = [
{ value => "allwordssubstr", description => "contains all of the words/strings" },
{ value => "anywordssubstr", description => "contains any of the words/strings" },
@@ -379,7 +83,7 @@ function selectProduct(f) {
<td align="left">
<label for="product" accesskey="p">
<select name="product" multiple="multiple" size="5" id="product"
- onchange="selectProduct(this.form);">
+ onchange="doOnSelectProduct();">
[% FOREACH p = product %]
<option value="[% p.name FILTER html %]"
[% " selected" IF lsearch(default.product, p.name) != -1 %]>
diff --git a/template/en/default/search/search-advanced.html.tmpl b/template/en/default/search/search-advanced.html.tmpl
index bbed6c2d6..89938adbe 100644
--- a/template/en/default/search/search-advanced.html.tmpl
+++ b/template/en/default/search/search-advanced.html.tmpl
@@ -30,10 +30,48 @@
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
+
+[% js_data = BLOCK %]
+function doOnSelectProduct() {
+ var f = document.forms['queryform'];
+ milestone = (typeof(f.target_milestone) == "undefined" ?
+ null : f.target_milestone);
+ selectProduct(f.product, f.component, f.version, milestone);
+}
+
+var first_load = true; [%# is this the first time we load the page? %]
+var last_sel = new Array(); [%# caches last selection %]
+
+var cpts = new Array();
+var vers = new Array();
+[% IF Param('usetargetmilestone') %]
+var tms = new Array();
+[% END %]
+
+[%# Create three arrays of components, versions and target milestones, indexed
+ # numerically according to the product they refer to. #%]
+
+[% n = 0 %]
+[% FOREACH p = product %]
+ cpts[[% n %]] = [
+ [%- FOREACH item = p.components %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
+ vers[[% n %]] = [
+ [%- FOREACH item = p.versions -%]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
+ [% IF Param('usetargetmilestone') %]
+ tms[[% n %]] = [
+ [%- FOREACH item = p.milestones %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
+ [% END %]
+ [% n = n+1 %]
+[% END %]
+
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = "Search for $terms.bugs"
h1 = ""
- onload = "selectProduct(document.forms['queryform']);initHelp();"
+ onload = "doOnSelectProduct(); initHelp();"
+ javascript = js_data
+ javascript_urls = [ "js/productform.js" ]
style = "td.selected_tab {
border-width: 2px 2px 0px;
border-style: solid;