!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Alec Flett <alecf@netscape.com>
24 * Seth Spitzer <sspitzer@netscape.com>
25 * David Bienvenu <bienvenu@nventure.com>
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 var gTotalSearchTerms=0;
42 var gSearchTermList;
43 var gSearchTerms = new Array;
44 var gSearchRemovedTerms = new Array;
45 var gSearchScope;
46 var gSearchBooleanRadiogroup;
47
48 var gUniqueSearchTermCounter = 0; // gets bumped every time we add a search term so we can always
49 // dynamically generate unique IDs for the terms.
50
51 // cache these so we don't have to hit the string bundle for them
52 var gMoreButtonTooltipText;
53 var gLessButtonTooltipText;
54 var gLoading = true;
55
56
searchTermContainer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
57 function searchTermContainer() {}
58
59 searchTermContainer.prototype = {
60 internalSearchTerm : '',
61 internalBooleanAnd : '',
62
63 // this.searchTerm: the actual nsIMsgSearchTerm object
get_searchTerm
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
64 get searchTerm() { return this.internalSearchTerm; },
set_searchTerm
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
65 set searchTerm(val) {
66 this.internalSearchTerm = val;
67
68 var term = val;
69 // val is a nsIMsgSearchTerm
70 var searchAttribute=this.searchattribute;
71 var searchOperator=this.searchoperator;
72 var searchValue=this.searchvalue;
73
74 // now reflect all attributes of the searchterm into the widgets
75 if (searchAttribute) searchAttribute.value = term.attrib;
76 if (searchOperator) searchOperator.value = val.op;
77 if (searchValue) searchValue.value = term.value;
78
79 this.booleanAnd = val.booleanAnd;
80 return val;
81 },
82
83 // searchscope - just forward to the searchattribute
get_searchScope
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
84 get searchScope() {
85 if (this.searchattribute)
86 return this.searchattribute.searchScope;
87 return undefined;
88 },
set_searchScope
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
89 set searchScope(val) {
90 var searchAttribute = this.searchattribute;
91 if (searchAttribute) searchAttribute.searchScope=val;
92 return val;
93 },
94
saveId
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
95 saveId: function (element, slot) {
96 this[slot] = element.id;
97 },
98
getElement
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
99 getElement: function (slot) {
100 return document.getElementById(this[slot]);
101 },
102
103 // three well-defined properties:
104 // searchattribute, searchoperator, searchvalue
105 // the trick going on here is that we're storing the Element's Id,
106 // not the element itself, because the XBL object may change out
107 // from underneath us
get_searchattribute
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
108 get searchattribute() { return this.getElement("internalSearchAttributeId"); },
set_searchattribute
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
109 set searchattribute(val) {
110 this.saveId(val, "internalSearchAttributeId");
111 return val;
112 },
get_searchoperator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
113 get searchoperator() { return this.getElement("internalSearchOperatorId"); },
set_searchoperator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
114 set searchoperator(val) {
115 this.saveId(val, "internalSearchOperatorId");
116 return val;
117 },
get_searchvalue
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
118 get searchvalue() { return this.getElement("internalSearchValueId"); },
set_searchvalue
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
119 set searchvalue(val) {
120 this.saveId(val, "internalSearchValueId");
121 return val;
122 },
123
124 booleanNodes: null,
get_booleanAnd
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
125 get booleanAnd() { return this.internalBooleanAnd; },
set_booleanAnd
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
126 set booleanAnd(val) {
127 this.internalBooleanAnd = val;
128 return val;
129 },
130
save
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
131 save: function () {
132 var searchTerm = this.searchTerm;
133
134 searchTerm.attrib = this.searchattribute.value;
135 var nsMsgSearchAttrib = Components.interfaces.nsMsgSearchAttrib;
136 if (this.searchattribute.value > nsMsgSearchAttrib.OtherHeader && this.searchattribute.value < nsMsgSearchAttrib.kNumMsgSearchAttributes)
137 searchTerm.arbitraryHeader = this.searchattribute.label;
138 searchTerm.op = this.searchoperator.value;
139 if (this.searchvalue.value)
140 this.searchvalue.save();
141 else
142 this.searchvalue.saveTo(searchTerm.value);
143 searchTerm.value = this.searchvalue.value;
144 searchTerm.booleanAnd = this.booleanAnd;
145 searchTerm.matchAll = this.matchAll;
146 },
147 // if you have a search term element with no search term
saveTo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
148 saveTo: function(searchTerm) {
149 this.internalSearchTerm = searchTerm;
150 this.save();
151 }
152 }
153
154 var nsIMsgSearchTerm = Components.interfaces.nsIMsgSearchTerm;
155
initializeSearchWidgets
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
156 function initializeSearchWidgets()
157 {
158 gSearchBooleanRadiogroup = document.getElementById("booleanAndGroup");
159 gSearchTermList = document.getElementById("searchTermList");
160
161 // initialize some strings
162 var bundle = document.getElementById('bundle_search');
163 gMoreButtonTooltipText = bundle.getString('moreButtonTooltipText');
164 gLessButtonTooltipText = bundle.getString('lessButtonTooltipText');
165 }
166
initializeBooleanWidgets
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
167 function initializeBooleanWidgets()
168 {
169 var booleanAnd = true;
170 var matchAll = false;
171 // get the boolean value from the first term
172 var firstTerm = gSearchTerms[0].searchTerm;
173 if (firstTerm)
174 {
175 booleanAnd = firstTerm.booleanAnd;
176 matchAll = firstTerm.matchAll;
177 }
178 // target radio items have value="and" or value="or" or "all"
179 gSearchBooleanRadiogroup.value = matchAll
180 ? "matchAll"
181 : (booleanAnd ? "and" : "or")
182 var searchTerms = document.getElementById("searchTermList");
183 if (searchTerms)
184 updateSearchTermsListbox(matchAll);
185 }
186
initializeSearchRows
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
187 function initializeSearchRows(scope, searchTerms)
188 {
189 for (var i = 0; i < searchTerms.Count(); i++) {
190 var searchTerm = searchTerms.QueryElementAt(i, nsIMsgSearchTerm);
191 createSearchRow(i, scope, searchTerm);
192 gTotalSearchTerms++;
193 }
194 initializeBooleanWidgets();
195 updateRemoveRowButton();
196 }
197
198 /**
199 * Enables/disables all the visible elements inside the search terms listbox.
200 *
201 * @param matchAllValue boolean value from the first search term
202 */
updateSearchTermsListbox
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
203 function updateSearchTermsListbox(matchAllValue)
204 {
205 var searchTerms = document.getElementById("searchTermList");
206 searchTerms.setAttribute("disabled", matchAllValue);
207 var searchAttributeList = searchTerms.getElementsByTagName("searchattribute");
208 var searchOperatorList = searchTerms.getElementsByTagName("searchoperator");
209 var searchValueList = searchTerms.getElementsByTagName("searchvalue");
210 for (var i = 0; i < searchAttributeList.length; i++) {
211 searchAttributeList[i].setAttribute("disabled", matchAllValue);
212 searchOperatorList[i].setAttribute("disabled", matchAllValue);
213 searchValueList[i].setAttribute("disabled", matchAllValue);
214 if (!matchAllValue)
215 searchValueList[i].removeAttribute("disabled");
216 }
217 var moreOrLessButtonsList = searchTerms.getElementsByTagName("button");
218 for (var i = 0; i < moreOrLessButtonsList.length; i++) {
219 moreOrLessButtonsList[i].setAttribute("disabled", matchAllValue);
220 }
221 if (!matchAllValue)
222 updateRemoveRowButton();
223 }
224
225 // enables/disables the less button for the first row of search terms.
updateRemoveRowButton
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
226 function updateRemoveRowButton()
227 {
228 var firstListItem = gSearchTermList.getItemAtIndex(0);
229 if (firstListItem)
230 firstListItem.lastChild.lastChild.lastChild.setAttribute("disabled", gTotalSearchTerms == 1);
231 }
232
233 // Returns the actual list item row index in the list of search rows
234 // that contains the passed in element id.
getSearchRowIndexForElement
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
235 function getSearchRowIndexForElement(aElement)
236 {
237 var listItem = aElement;
238
239 while (listItem && listItem.localName != "listitem")
240 listItem = listItem.parentNode;
241
242 return gSearchTermList.getIndexOfItem(listItem);
243 }
244
onMore
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
245 function onMore(event)
246 {
247 // if we have an event, extract the list row index and use that as the row number
248 // for our insertion point. If there is no event, append to the end....
249 var rowIndex;
250
251 if (event)
252 rowIndex = getSearchRowIndexForElement(event.target) + 1;
253 else
254 rowIndex = gSearchTermList.getRowCount();
255
256 createSearchRow(rowIndex, gSearchScope, null);
257 gTotalSearchTerms++;
258 updateRemoveRowButton();
259
260 // the user just added a term, so scroll to it
261 gSearchTermList.ensureIndexIsVisible(rowIndex);
262 }
263
onLess
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
264 function onLess(event)
265 {
266 if (event && gTotalSearchTerms > 1)
267 {
268 removeSearchRow(getSearchRowIndexForElement(event.target));
269 --gTotalSearchTerms;
270 }
271
272 updateRemoveRowButton();
273 }
274
275 // set scope on all visible searchattribute tags
setSearchScope
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
276 function setSearchScope(scope)
277 {
278 gSearchScope = scope;
279 for (var i=0; i<gSearchTerms.length; i++) {
280 gSearchTerms[i].obj.searchattribute.searchScope = scope;
281 gSearchTerms[i].scope = scope;
282 // act like the user "selected" this, see bug #202848
283 gSearchTerms[i].obj.searchattribute.onSelect(null /* no event */);
284 }
285 }
286
updateSearchAttributes
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
287 function updateSearchAttributes()
288 {
289 for (var i=0; i<gSearchTerms.length; i++)
290 gSearchTerms[i].obj.searchattribute.refreshList();
291 }
292
booleanChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
293 function booleanChanged(event) {
294 // when boolean changes, we have to update all the attributes on the search terms
295 var newBoolValue = (event.target.getAttribute("value") == "and") ? true : false;
296 var matchAllValue = (event.target.getAttribute("value") == "matchAll") ? true : false;
297 if (document.getElementById("abPopup")) {
298 var selectedAB = document.getElementById("abPopup").selectedItem.id;
299 setSearchScope(GetScopeForDirectoryURI(selectedAB));
300 }
301 for (var i=0; i<gSearchTerms.length; i++) {
302 var searchTerm = gSearchTerms[i].obj;
303 searchTerm.booleanAnd = newBoolValue;
304 searchTerm.matchAll = matchAllValue;
305 }
306 var searchTerms = document.getElementById("searchTermList");
307 if (searchTerms)
308 {
309 if (!matchAllValue && searchTerms.hidden && !gTotalSearchTerms)
310 onMore(null); // fake to get empty row.
311 updateSearchTermsListbox(matchAllValue);
312 }
313 }
314
createSearchRow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
315 function createSearchRow(index, scope, searchTerm)
316 {
317 var searchAttr = document.createElement("searchattribute");
318 var searchOp = document.createElement("searchoperator");
319 var searchVal = document.createElement("searchvalue");
320 var enclosingBox = document.createElement('vbox');
321
322 var buttonBox = document.createElement("hbox");
323 buttonBox.setAttribute("align", "start");
324 var moreButton = document.createElement("button");
325 var lessButton = document.createElement("button");
326 moreButton.setAttribute("class", "small-button");
327 moreButton.setAttribute("oncommand", "onMore(event);");
328 moreButton.setAttribute('label', '+');
329 moreButton.setAttribute('tooltiptext', gMoreButtonTooltipText);
330 lessButton.setAttribute("class", "small-button");
331 lessButton.setAttribute("oncommand", "onLess(event);");
332 lessButton.setAttribute('label', '\u2212');
333 lessButton.setAttribute('tooltiptext', gLessButtonTooltipText);
334
335 enclosingBox.setAttribute('align', 'right');
336
337 // now set up ids:
338 searchAttr.id = "searchAttr" + gUniqueSearchTermCounter;
339 searchOp.id = "searchOp" + gUniqueSearchTermCounter;
340 searchVal.id = "searchVal" + gUniqueSearchTermCounter;
341
342 buttonBox.appendChild(moreButton);
343 buttonBox.appendChild(lessButton);
344
345 searchAttr.setAttribute("for", searchOp.id + "," + searchVal.id);
346 searchOp.setAttribute("opfor", searchVal.id);
347
348 var rowdata = new Array(enclosingBox, searchAttr,
349 null, searchOp,
350 null, searchVal,
351 null, buttonBox);
352 var searchrow = constructRow(rowdata);
353 searchrow.id = "searchRow" + gUniqueSearchTermCounter;
354
355 var searchTermObj = new searchTermContainer;
356 searchTermObj.searchattribute = searchAttr;
357 searchTermObj.searchoperator = searchOp;
358 searchTermObj.searchvalue = searchVal;
359
360 // now insert the new search term into our list of terms
361 gSearchTerms.splice(index, 0, {obj:searchTermObj, scope:scope, searchTerm:searchTerm, initialized:false});
362
363 // and/or string handling:
364 // this is scary - basically we want to take every other
365 // listcell, (note the i+=2) which will be a text label,
366 // and set the searchTermObj's
367 // booleanNodes to that
368 var stringNodes = new Array;
369 var listcells = searchrow.childNodes;
370 var j=0;
371 for (var i=0; i<listcells.length; i+=2) {
372 stringNodes[j++] = listcells[i];
373
374 // see bug #183994 for why these cells are hidden
375 listcells[i].hidden = true;
376 }
377 searchTermObj.booleanNodes = stringNodes;
378
379 var editFilter = null;
380 try { editFilter = gFilter; } catch(e) { }
381
382 var editMailView = null;
383 try { editMailView = gMailView; } catch(e) { }
384
385 if ((!editFilter && !editMailView) ||
386 (editFilter && index == gTotalSearchTerms) ||
387 (editMailView && index == gTotalSearchTerms))
388 gLoading = false;
389
390 // index is index of new row
391 // gTotalSearchTerms has not been updated yet
392 if (gLoading || index == gTotalSearchTerms) {
393 gSearchTermList.appendChild(searchrow);
394 }
395 else {
396 var currentItem = gSearchTermList.getItemAtIndex(index);
397 gSearchTermList.insertBefore(searchrow, currentItem);
398 }
399
400 // bump our unique search term counter
401 gUniqueSearchTermCounter++;
402 }
403
initializeTermFromId
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
404 function initializeTermFromId(id)
405 {
406 initializeTermFromIndex(getSearchRowIndexForElement(document.getElementById(id)));
407 }
408
initializeTermFromIndex
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
409 function initializeTermFromIndex(index)
410 {
411 var searchTermObj = gSearchTerms[index].obj;
412
413 searchTermObj.searchScope = gSearchTerms[index].scope;
414 // the search term will initialize the searchTerm element, including
415 // .booleanAnd
416 if (gSearchTerms[index].searchTerm)
417 searchTermObj.searchTerm = gSearchTerms[index].searchTerm;
418 // here, we don't have a searchTerm, so it's probably a new element -
419 // we'll initialize the .booleanAnd from the existing setting in
420 // the UI
421 else
422 {
423 searchTermObj.booleanAnd = (gSearchBooleanRadiogroup.value == "and");
424 if (index)
425 {
426 // if we weren't pre-initialized with a searchTerm then steal the search attribute from the
427 // previous row.
428 searchTermObj.searchattribute.value = gSearchTerms[index - 1].obj.searchattribute.value;
429 }
430 }
431
432 gSearchTerms[index].initialized = true;
433 }
434
435 // creates a <listitem> using the array children as
436 // the children of each listcell
constructRow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
437 function constructRow(children)
438 {
439 var listitem = document.createElement("listitem");
440 listitem.setAttribute("allowevents", "true");
441 for (var i = 0; i < children.length; i++) {
442 var listcell = document.createElement("listcell");
443
444 // it's ok to have empty cells
445 if (children[i]) {
446 children[i].setAttribute("flex", "1");
447 listcell.appendChild(children[i]);
448 }
449 listitem.appendChild(listcell);
450 }
451 return listitem;
452 }
453
removeSearchRow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
454 function removeSearchRow(index)
455 {
456 var searchTermObj = gSearchTerms[index].obj;
457 if (!searchTermObj) {
458 return;
459 }
460
461 // if it is an existing (but offscreen) term,
462 // make sure it is initialized before we remove it.
463 if (!gSearchTerms[index].searchTerm && !gSearchTerms[index].initialized)
464 initializeTermFromIndex(index);
465
466 // need to remove row from list, so walk upwards from the
467 // searchattribute to find the first <listitem>
468 var listitem = searchTermObj.searchattribute;
469
470 while (listitem) {
471 if (listitem.localName == "listitem") break;
472 listitem = listitem.parentNode;
473 }
474
475 if (!listitem) {
476 dump("Error: couldn't find parent listitem!\n");
477 return;
478 }
479
480
481 if (searchTermObj.searchTerm) {
482 gSearchRemovedTerms[gSearchRemovedTerms.length] = searchTermObj.searchTerm;
483 } else {
484 //dump("That wasn't real. ignoring \n");
485 }
486
487 listitem.parentNode.removeChild(listitem);
488
489 // now remove the item from our list of terms
490 gSearchTerms.splice(index, 1);
491 }
492
493 // save the search terms from the UI back to the actual search terms
494 // searchTerms: nsISupportsArray of terms
495 // termOwner: object which can contain and create the terms
496 // (will be unnecessary if we just make terms creatable
497 // via XPCOM)
saveSearchTerms
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
498 function saveSearchTerms(searchTerms, termOwner)
499 {
500 var matchAll = gSearchBooleanRadiogroup.value == 'matchAll';
501 var i;
502 for (i = 0; i<gSearchTerms.length; i++) {
503 try {
504 var searchTerm = gSearchTerms[i].obj.searchTerm;
505
506 // the term might be an offscreen one we haven't initialized yet
507 // if so, don't bother saving it.
508 if (!searchTerm && !gSearchTerms[i].initialized) {
509 // is an existing term, but not initialize, so skip saving
510 continue;
511 }
512 searchTerm.matchAll = matchAll;
513 if (searchTerm)
514 gSearchTerms[i].obj.save();
515 else {
516 // need to create a new searchTerm, and somehow save it to that
517 searchTerm = termOwner.createTerm();
518 gSearchTerms[i].obj.saveTo(searchTerm);
519 termOwner.appendTerm(searchTerm);
520 }
521 } catch (ex) {
522 dump("** Error saving element " + i + ": " + ex + "\n");
523 }
524 }
525
526 // now remove the queued elements
527 for (i=0; i<gSearchRemovedTerms.length; i++) {
528 // this is so nasty, we have to iterate through
529 // because GetIndexOf is acting funny
530 var searchTermSupports =
531 gSearchRemovedTerms[i].QueryInterface(Components.interfaces.nsISupports);
532 searchTerms.RemoveElement(searchTermSupports);
533 }
534 }
535
onReset
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
536 function onReset(event)
537 {
538 while (gTotalSearchTerms>0)
539 removeSearchRow(--gTotalSearchTerms);
540 onMore(null);
541 }
542
hideMatchAllItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
543 function hideMatchAllItem()
544 {
545 var allItems = document.getElementById('matchAllItem');
546 if (allItems)
547 allItems.hidden = true;
548 }