<!-- 1.0@bugzilla.org -->
[%# 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): Chris Lahey <clahey@ximian.com> [javascript fixes]
  #                 Christian Reis <kiko@async.com.br> [javascript rewrite]
  #                 Gervase Markham <gerv@gerv.net>
  #%]
  
  [%# 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 = componentsbyproduct.$p %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
  vers[[% n %]] = [ 
    [%- FOREACH item = versionsbyproduct.$p -%]'[%  item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
  [% IF Param('usetargetmilestone') %]
  tms[[% n %]]  = [ 
     [%- FOREACH item = milestonesbyproduct.$p %]'[% 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" },
  { value => "substring", description => "contains the string" },
  { value => "casesubstring", description => "contains the string (exact case)" },
  { value => "allwords", description => "contains all of the words" },
  { value => "anywords", description => "contains any of the words" },
  { value => "regexp", description => "matches the regexp" },
  { value => "notregexp", description => "doesn't match the regexp" } ] %]
     
[%# *** Summary *** %]

<table>
  <tr>
    <th align="right">Summary:</th>
    <td>
      <select name="short_desc_type">
      [% FOREACH qv = query_variants %]
        <option value="[% qv.value %]"
          [% " selected" IF default.short_desc_type.0 == qv.value %]>[% qv.description %]</option>
      [% END %]              
      </select>
    </td>
    <td>
      <input name="short_desc" size="40" value="[% default.short_desc.0 FILTER html %]">
    </td>
    <td>
      <input type="submit" value="[% button_name %]">
    </td>
  </tr>  

[%# *** Product Component Version Target *** %]

  <tr>
    <td colspan="4">
      <table>
        <tr valign="bottom">
          <th align="left">Product:</th>
          <th align="left"><a href="describecomponents.cgi">Component</a>:</th>
          <th align="left">Version:</th>

        [% IF (Param("usetargetmilestone")) %]
          <th align="left">Target:</th>
        [% END %]
        </tr>

        <tr valign="top">

          [%# Can't use the select block here because of onChange and the fact that
              'component' is a toolkit reserved word - we use 'component_' instead. %]
          <td align="left">
            <select name="product" multiple="multiple" size="5" onchange="selectProduct(this.form);">
            [% FOREACH p = product %]
              <option value="[% p FILTER html %]"
                [% " selected" IF lsearch(default.product, p) != -1 %]>
                [% p FILTER html %]</option>
            [% END %]
            </select>
          </td>

          <td align="left">
            <select name="component" multiple="multiple" size="5">
            [% FOREACH c = component_ %]
              <option value="[% c FILTER html %]"
                [% " selected" IF lsearch(default.component, c) != -1 %]>
                [% c FILTER html %]</option>
            [% END %]
            </select>
          </td>

          [% PROCESS select sel = { name => 'version', size => 5 } %]

        [% IF Param('usetargetmilestone') && target_milestone.size > 0 %]
          [% PROCESS select sel = { name => 'target_milestone', size => 5 } %]
        [% END %]
        </tr>
      </table>
    </td>
  </tr>
  
[%# *** Comment URL Whiteboard Keywords *** %]

  [% FOREACH field = [ 
    { name => "long_desc", description => "A&nbsp;comment" },
    { name => "bug_file_loc", description => "The&nbsp;URL" },
    { name => "status_whiteboard", description => "Whiteboard" } ] %]

    [% UNLESS field.name == 'status_whiteboard' AND NOT Param('usestatuswhiteboard') %]
    <tr>
      <th align="right">[% field.description %]:</th>
      <td>
        <select name="[% field.name %]_type">
        [% FOREACH qv = query_variants %]
          [% type = "${field.name}_type" %]                   
          <option value="[% qv.value %]"
            [% " selected" IF default.$type.0 == qv.value %]>[% qv.description %]</option>
        [% END %]              
        </select>
      </td>
      <td><input name="[% field.name %]" size="40" value="
        [% default.${field.name}.0 FILTER html %]">
      </td>
      <td></td>
    </tr>  
    [% END %]
  [% END %]

  [% IF have_keywords %]
    <tr>
      <th align="right"><a href="describekeywords.cgi">Keywords</a>:</th>
      <td>
        <select name="keywords_type">
        [% FOREACH qv = [ 
          { name => "allwords", description => "contains all of the keywords" },
          { name => "anywords", description => "contains any of the keywords" },
          { name => "nowords",  description => "contains none of the keywords" } ] %]

          <option value="[% qv.name %]"
            [% " selected" IF default.keywords_type.0 == qv.name %]>
            [% qv.description %]</option>
        [% END %]
        </select>
      </td>
      <td>
        <input name="keywords" size="40" value="[% default.keywords.0 FILTER html %]">
      </td>
    </tr>
  [% END %]
</table>  

<hr>

[%# *** Status Resolution Severity Priority Hardware OS *** %]

<table>
  <tr>
    <th align="left"><a href="queryhelp.cgi#status">Status</a>:</th>
    <th align="left"><a href="queryhelp.cgi#resolution">Resolution</a>:</th>
    <th align="left"><a href="queryhelp.cgi#severity">Severity</a>:</th>
    <th align="left"><a href="queryhelp.cgi#priority">Priority</a>:</th>
    <th align="left"><a href="queryhelp.cgi#platform">Hardware</a>:</th>
    <th align="left"><a href="queryhelp.cgi#opsys">OS</a>:</th>
  </tr>

  <tr valign="top">
    [% PROCESS select sel = { name => 'bug_status', size => 7 } %]
    [% PROCESS select sel = { name => 'resolution', size => 7 } %]
    [% PROCESS select sel = { name => 'bug_severity', size => 7 } %]    
    [% PROCESS select sel = { name => 'priority', size => 7 } %]    
    [% PROCESS select sel = { name => 'rep_platform', size => 7 } %]
    [% PROCESS select sel = { name => 'op_sys', size => 7 } %]
  </tr>
</table>

<p>

[%# *** Email Numbering Votes *** %]

<table>
  <tr>
    <td>
      <fieldset>
        <legend>
          <strong>
            <a href="queryhelp.cgi#peopleinvolved">Email</a> and Numbering
          </strong>  
        </legend>


<table>
  <tr>
  [% FOREACH n = [1, 2] %]
    <td>


<table cellspacing="0" cellpadding="0">
  <tr>
    <td>
      Any of:
    </td>
  </tr>
  <tr>
    <td>
      <input type="checkbox" name="emailassigned_to[% n %]" 
             id="emailassigned_to[% n %]" value="1"
             [% " checked" IF default.emailassigned_to.$n %]>
      <label for="emailassigned_to[% n %]">
        bug owner
      </label>
    </td>
  </tr>
  <tr>
    <td>
      <input type="checkbox" name="emailreporter[% n %]" 
             id="emailreporter[% n %]" value="1"
             [% " checked" IF default.emailreporter.$n %]>
      <label for="emailreporter[% n %]">
        reporter
      </label>
    </td>
  </tr>
  [% IF Param('useqacontact') %]
  <tr>
    <td>
      <input type="checkbox" name="emailqa_contact[% n %]" 
             id="emailqa_contact[% n %]" value="1"
             [% " checked" IF default.emailqa_contact.$n %]>
      <label for="emailqa_contact[% n %]">
        QA contact
      </label>
    </td>
  </tr>
  [% END %]
  <tr>
    <td>
      <input type="checkbox" name="emailcc[% n %]" 
             id="emailcc[% n %]" value="1"
             [% " checked" IF default.emailcc.$n %]>
      <label for="emailcc[% n %]">
        CC list member
      </label>
    </td>
  </tr>
  <tr>
    <td>
        <input type="checkbox" name="emaillongdesc[% n %]" 
               id="emaillongdesc[% n %]" value="1"
               [% " checked" IF default.emaillongdesc.$n %]>
      <label for="emaillongdesc[% n %]">
        commenter
      </label>
    </td>
  </tr>
  <tr>
    <td>
      <select name="emailtype[% n %]">
      [% FOREACH qv = [ 
        { name => "substring", description => "contains" },
        { name => "exact", description => "is" },
        { name => "regexp", description => "matches regexp" },
        { name => "notregexp", description => "doesn't match regexp" } ] %]
        
        <option value="[% qv.name %]"
          [% " selected" IF default.emailtype.$n == qv.name %]>[% qv.description %]</option>
      [% END %]
      </select>
    </td>
  </tr>
  <tr>
    <td>
      <input name="email[% n %]" size="25" value="[% default.email.$n FILTER html %]">
    </td>
  </tr>
</table>

    
    </td>
  [% END %]
  </tr>
</table>
<hr>
<table>  
  <tr>
    <td>
      <select name="bugidtype">
        <option value="include"[% " selected" IF default.bugidtype.0 == "include" %]>Only include</option>
        <option value="exclude"[% " selected" IF default.bugidtype.0 == "exclude" %]>Exclude</option>
      </select>
      bugs numbered: 
    </td>
    <td>
      <input type="text" name="bug_id" value="[% default.bug_id.0 FILTER html %]" size="20">
    </td>
  </tr>
  <tr>
    <td></td>
    <td>(comma-separated list)</td>
  </tr>
  [% IF Param('usevotes') %]
    <tr>
      <td align="right">
        Only bugs with at least:
      </td>
      <td>
        <input name="votes" size="3" value="[% default.votes.0 FILTER html %]"> votes
      </td>
    </tr>
  [% END %]
</table>


      </fieldset>
    </td>
   
[%# *** Bug Changes *** %]

    <td valign="top">
      <fieldset>
        <legend><strong>Bug Changes</strong></legend>


<dl>
  <dt>Only bugs changed in the last </dt>
  <dd><input name="changedin" size="3" value="[% default.changedin.0 FILTER html %]"> days</dd>
</dl>

<dl>
  <dt>Only bugs where any of the fields</dt>
  <dd>
    <select name="chfield" multiple="multiple" size="4">
    [% FOREACH field = chfield %]
      <option value="[% field FILTER html %]"
        [% " selected" IF lsearch(default.chfield, field) != -1 %]>
        [% field FILTER html %]</option>
    [% END %]
    </select>
  </dd>

  <dt>were changed between</dt>
  <dd>
    <input name="chfieldfrom" size="10" value="[% default.chfieldfrom.0 FILTER html %]">
    and <input name="chfieldto" size="10" value="[% default.chfieldto.0 FILTER html %]">
    <br>(YYYY-MM-DD)
  </dd>
  <dt>to this value: (optional)</dt>
  <dd>
    <input name="chfieldvalue" size="20" value="[% default.chfieldvalue.0 FILTER html %]">
  </dd>
</dl>


       </fieldset>
     </td>
  </tr>
</table>

[%# Note: the <form> tag is unclosed at the end of this template %]

[%############################################################################%]
[%# Block for SELECT fields                                                  #%]
[%############################################################################%]

[% BLOCK select %]
  <td align="left">
    <select name="[% sel.name %]" multiple="multiple" size="[% sel.size %]">
    [% FOREACH name = ${sel.name} %]
      <option value="[% name FILTER html %]"
        [% " selected" IF lsearch(default.${sel.name}, name) != -1 %]>
        [% name FILTER html %]</option>
    [% END %]
    </select>
  </td>
[% END %]
