~ubuntu-branches/ubuntu/oneiric/bugzilla/oneiric

« back to all changes in this revision

Viewing changes to js/productform.js

  • Committer: Bazaar Package Importer
  • Author(s): Raphael Bossek
  • Date: 2008-06-27 22:34:34 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20080627223434-0ib57vstn43bb4a3
Tags: 3.0.4.1-1
* Update of French, Russian and German translations. (closes: #488251)
* Added Bulgarian and Belarusian translations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* The contents of this file are subject to the Mozilla Public
2
 
 * License Version 1.1 (the "License"); you may not use this file
3
 
 * except in compliance with the License. You may obtain a copy of
4
 
 * the License at http://www.mozilla.org/MPL/
5
 
 * 
6
 
 * Software distributed under the License is distributed on an "AS
7
 
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
8
 
 * implied. See the License for the specific language governing
9
 
 * rights and limitations under the License.
10
 
 * 
11
 
 * The Original Code is the Bugzilla Bug Tracking System.
12
 
 * 
13
 
 * The Initial Developer of the Original Code is Netscape Communications
14
 
 * Corporation. Portions created by Netscape are
15
 
 * Copyright (C) 1998 Netscape Communications Corporation. All
16
 
 * Rights Reserved.
17
 
 * 
18
 
 * Contributor(s): Christian Reis <kiko@async.com.br>
19
 
 */
20
 
 
21
 
/* this file contains functions to update form controls based on a
22
 
 * collection of javascript arrays containing strings */
23
 
 
24
 
/* selectClassification reads the selection from f.classification and updates
25
 
 * f.product accordingly
26
 
 *     - f: a form containing classification, product, component, varsion and
27
 
 *       target_milestone select boxes.
28
 
 * globals (3vil!):
29
 
 *     - prods, indexed by classification name
30
 
 *     - first_load: boolean, specifying if it is the first time we load
31
 
 *       the query page.
32
 
 *     - last_sel: saves our last selection list so we know what has
33
 
 *       changed, and optimize for additions.
34
 
 */
35
 
function selectClassification(classfield, product, component, version, milestone) {
36
 
    /* this is to avoid handling events that occur before the form
37
 
     * itself is ready, which could happen in buggy browsers.
38
 
     */
39
 
    if (!classfield) {
40
 
        return;
41
 
    }
42
 
 
43
 
    /* if this is the first load and nothing is selected, no need to
44
 
     * merge and sort all components; perl gives it to us sorted.
45
 
     */
46
 
    if ((first_load) && (classfield.selectedIndex == -1)) {
47
 
        first_load = false;
48
 
        return;
49
 
    }
50
 
    
51
 
    /* don't reset first_load as done in selectProduct.  That's because we
52
 
       want selectProduct to handle the first_load attribute
53
 
    */
54
 
 
55
 
    /* - sel keeps the array of classifications we are selected. 
56
 
     * - merging says if it is a full list or just a list of classifications 
57
 
     *   that were added to the current selection.
58
 
     */
59
 
    var merging = false;
60
 
    var sel = Array();
61
 
 
62
 
    /* if nothing selected, pick all */
63
 
    var findall = classfield.selectedIndex == -1;
64
 
    sel = get_selection(classfield, findall, false);
65
 
    if (!findall) {
66
 
        /* save sel for the next invocation of selectClassification() */
67
 
        var tmp = sel;
68
 
    
69
 
        /* this is an optimization: if we have just added classifications to an
70
 
         * existing selection, no need to clear the form controls and add 
71
 
         * everybody again; just merge the new ones with the existing 
72
 
         * options.
73
 
         */
74
 
        if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
75
 
            sel = fake_diff_array(sel, last_sel);
76
 
            merging = true;
77
 
        }
78
 
        last_sel = tmp;
79
 
    }
80
 
    /* save original options selected */
81
 
    var saved_prods = get_selection(product, false, true);
82
 
 
83
 
    /* do the actual fill/update, reselect originally selected options */
84
 
    updateSelect(prods, sel, product, merging);
85
 
    restoreSelection(product, saved_prods);
86
 
    selectProduct(product, component, version, milestone);
87
 
}
88
 
 
89
 
 
90
 
/* selectProduct reads the selection from the product control and
91
 
 * updates version, component and milestone controls accordingly.
92
 
 * 
93
 
 *     - product, component, version and milestone: form controls
94
 
 *
95
 
 * globals (3vil!):
96
 
 *     - cpts, vers, tms: array of arrays, indexed by product name. the
97
 
 *       subarrays contain a list of names to be fed to the respective
98
 
 *       selectboxes. For bugzilla, these are generated with perl code
99
 
 *       at page start.
100
 
 *     - first_load: boolean, specifying if it is the first time we load
101
 
 *       the query page.
102
 
 *     - last_sel: saves our last selection list so we know what has
103
 
 *       changed, and optimize for additions. 
104
 
 */
105
 
function selectProduct(product, component, version, milestone) {
106
 
 
107
 
    if (!product) {
108
 
        /* this is to avoid handling events that occur before the form
109
 
         * itself is ready, which could happen in buggy browsers. */
110
 
        return;
111
 
    }
112
 
 
113
 
    /* if this is the first load and nothing is selected, no need to
114
 
     * merge and sort all components; perl gives it to us sorted. */
115
 
    if ((first_load) && (product.selectedIndex == -1)) {
116
 
        first_load = false;
117
 
        return;
118
 
    }
119
 
 
120
 
    /* turn first_load off. this is tricky, since it seems to be
121
 
     * redundant with the above clause. It's not: if when we first load
122
 
     * the page there is _one_ element selected, it won't fall into that
123
 
     * clause, and first_load will remain 1. Then, if we unselect that
124
 
     * item, selectProduct will be called but the clause will be valid
125
 
     * (since selectedIndex == -1), and we will return - incorrectly -
126
 
     * without merge/sorting. */
127
 
    first_load = false;
128
 
 
129
 
    /* - sel keeps the array of products we are selected.
130
 
     * - merging says if it is a full list or just a list of products that
131
 
     *   were added to the current selection. */
132
 
    var merging = false;
133
 
    var sel = Array();
134
 
 
135
 
    /* if nothing selected, pick all */
136
 
    var findall = product.selectedIndex == -1;
137
 
    if (useclassification) {
138
 
        /* update index based on the complete product array */
139
 
        sel = get_selection(product, findall, true);
140
 
        for (var i=0; i<sel.length; i++) {
141
 
           sel[i] = prods[sel[i]];
142
 
        }
143
 
    } else {
144
 
        sel = get_selection(product, findall, false);
145
 
    }
146
 
    if (!findall) {
147
 
        /* save sel for the next invocation of selectProduct() */
148
 
        var tmp = sel;
149
 
 
150
 
        /* this is an optimization: if we have just added products to an
151
 
         *  existing selection, no need to clear the form controls and add
152
 
         *  everybody again; just merge the new ones with the existing
153
 
         *  options. */
154
 
        if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
155
 
            sel = fake_diff_array(sel, last_sel);
156
 
            merging = true;
157
 
        }
158
 
        last_sel = tmp;
159
 
    }
160
 
 
161
 
    /* do the actual fill/update */
162
 
    if (component) {
163
 
        var saved_cpts = get_selection(component, false, true);
164
 
        updateSelect(cpts, sel, component, merging);
165
 
        restoreSelection(component, saved_cpts);
166
 
    }
167
 
 
168
 
    if (version) {
169
 
        var saved_vers = get_selection(version, false, true);
170
 
        updateSelect(vers, sel, version, merging);
171
 
        restoreSelection(version, saved_vers);
172
 
    }
173
 
 
174
 
    if (milestone) {
175
 
        var saved_tms = get_selection(milestone, false, true);
176
 
        updateSelect(tms, sel, milestone, merging);
177
 
        restoreSelection(milestone, saved_tms);
178
 
    }
179
 
}
180
 
 
181
 
 
182
 
/* updateSelect(array, sel, target, merging)
183
 
 *
184
 
 * Adds to the target select object all elements in array that
185
 
 * correspond to the elements selected in source.
186
 
 * - array should be a array of arrays, indexed by number. the
187
 
 *   array should contain the elements that correspond to that
188
 
 *   product.
189
 
 * - sel is a list of selected items, either whole or a diff
190
 
 *   depending on merging.
191
 
 * - target should be the target select object.
192
 
 * - merging (boolean) determines if we are mergine in a diff or
193
 
 *   substituting the whole selection. a diff is used to optimize adding
194
 
 *   selections.
195
 
 *
196
 
 * Example (compsel is a select form control)
197
 
 *
198
 
 *     var components = Array();
199
 
 *     components[1] = [ 'ComponentA', 'ComponentB' ];
200
 
 *     components[2] = [ 'ComponentC', 'ComponentD' ];
201
 
 *     source = [ 2 ];
202
 
 *     updateSelect(components, source, compsel, 0, 0);
203
 
 *
204
 
 * would clear compsel and add 'ComponentC' and 'ComponentD' to it.
205
 
 *
206
 
 */
207
 
 
208
 
function updateSelect(array, sel, target, merging) {
209
 
 
210
 
    var i, item;
211
 
 
212
 
    /* If we have no versions/components/milestones */
213
 
    if (array.length < 1) {
214
 
        target.options.length = 0;
215
 
        return false;
216
 
    }
217
 
 
218
 
    if (merging) {
219
 
        /* array merging/sorting in the case of multiple selections */
220
 
        /* merge in the current options with the first selection */
221
 
        item = merge_arrays(array[sel[0]], target.options, 1);
222
 
 
223
 
        /* merge the rest of the selection with the results */
224
 
        for (i = 1 ; i < sel.length ; i++) {
225
 
            item = merge_arrays(array[sel[i]], item, 0);
226
 
        }
227
 
    } else if ( sel.length > 1 ) {
228
 
        /* here we micro-optimize for two arrays to avoid merging with a
229
 
         * null array */
230
 
        item = merge_arrays(array[sel[0]],array[sel[1]], 0);
231
 
 
232
 
        /* merge the arrays. not very good for multiple selections. */
233
 
        for (i = 2; i < sel.length; i++) {
234
 
            item = merge_arrays(item, array[sel[i]], 0);
235
 
        }
236
 
    } else { /* single item in selection, just get me the list */
237
 
        item = array[sel[0]];
238
 
    }
239
 
 
240
 
    /* clear select */
241
 
    target.options.length = 0;
242
 
 
243
 
    /* load elements of list into select */
244
 
    for (i = 0; i < item.length; i++) {
245
 
        target.options[i] = new Option(item[i], item[i]);
246
 
    }
247
 
    return true;
248
 
}
249
 
 
250
 
 
251
 
/* Selects items in control that have index defined in sel
252
 
 *    - control: SELECT control to be restored
253
 
 *    - selnames: array of indexes in select form control */
254
 
function restoreSelection(control, selnames) {
255
 
    /* right. this sucks. but I see no way to avoid going through the
256
 
     * list and comparing to the contents of the control. */
257
 
    for (var j=0; j < selnames.length; j++) {
258
 
        for (var i=0; i < control.options.length; i++) {
259
 
            if (control.options[i].value == selnames[j]) {
260
 
                control.options[i].selected = true;
261
 
            }
262
 
        }
263
 
    }
264
 
}
265
 
 
266
 
 
267
 
/* Returns elements in a that are not in b.
268
 
 * NOT A REAL DIFF: does not check the reverse.
269
 
 *    - a,b: arrays of values to be compare. */
270
 
function fake_diff_array(a, b) {
271
 
    var newsel = new Array();
272
 
    var found = false;
273
 
 
274
 
    /* do a boring array diff to see who's new */
275
 
    for (var ia in a) {
276
 
        for (var ib in b) {
277
 
            if (a[ia] == b[ib]) {
278
 
                found = true;
279
 
            }
280
 
        }
281
 
        if (!found) {
282
 
            newsel[newsel.length] = a[ia];
283
 
        }
284
 
        found = false;
285
 
    }
286
 
    return newsel;
287
 
}
288
 
 
289
 
/* takes two arrays and sorts them by string, returning a new, sorted
290
 
 * array. the merge removes dupes, too.
291
 
 *    - a, b: arrays to be merge.
292
 
 *    - b_is_select: if true, then b is actually an optionitem and as
293
 
 *      such we need to use item.value on it. */
294
 
function merge_arrays(a, b, b_is_select) {
295
 
    var pos_a = 0;
296
 
    var pos_b = 0;
297
 
    var ret = new Array();
298
 
    var bitem, aitem;
299
 
 
300
 
    /* iterate through both arrays and add the larger item to the return
301
 
     * list. remove dupes, too. Use toLowerCase to provide
302
 
     * case-insensitivity. */
303
 
    while ((pos_a < a.length) && (pos_b < b.length)) {
304
 
        if (b_is_select) {
305
 
            bitem = b[pos_b].value;
306
 
        } else {
307
 
            bitem = b[pos_b];
308
 
        }
309
 
        aitem = a[pos_a];
310
 
 
311
 
        /* smaller item in list a */
312
 
        if (aitem.toLowerCase() < bitem.toLowerCase()) {
313
 
            ret[ret.length] = aitem;
314
 
            pos_a++;
315
 
        } else {
316
 
            /* smaller item in list b */
317
 
            if (aitem.toLowerCase() > bitem.toLowerCase()) {
318
 
                ret[ret.length] = bitem;
319
 
                pos_b++;
320
 
            } else {
321
 
                /* list contents are equal, inc both counters. */
322
 
                ret[ret.length] = aitem;
323
 
                pos_a++;
324
 
                pos_b++;
325
 
            }
326
 
        }
327
 
    }
328
 
 
329
 
    /* catch leftovers here. these sections are ugly code-copying. */
330
 
    if (pos_a < a.length) {
331
 
        for (; pos_a < a.length ; pos_a++) {
332
 
            ret[ret.length] = a[pos_a];
333
 
        }
334
 
    }
335
 
 
336
 
    if (pos_b < b.length) {
337
 
        for (; pos_b < b.length; pos_b++) {
338
 
            if (b_is_select) {
339
 
                bitem = b[pos_b].value;
340
 
            } else {
341
 
                bitem = b[pos_b];
342
 
            }
343
 
            ret[ret.length] = bitem;
344
 
        }
345
 
    }
346
 
    return ret;
347
 
}
348
 
 
349
 
/* Returns an array of indexes or values from a select form control.
350
 
 *    - control: select control from which to find selections
351
 
 *    - findall: boolean, store all options when true or just the selected
352
 
 *      indexes
353
 
 *    - want_values: boolean; we store values when true and indexes when
354
 
 *      false */
355
 
function get_selection(control, findall, want_values) {
356
 
    var ret = new Array();
357
 
 
358
 
    if ((!findall) && (control.selectedIndex == -1)) {
359
 
        return ret;
360
 
    }
361
 
 
362
 
    for (var i=0; i<control.length; i++) {
363
 
        if (findall || control.options[i].selected) {
364
 
            ret[ret.length] = want_values ? control.options[i].value : i;
365
 
        }
366
 
    }
367
 
    return ret;
368
 
}
369