1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @class
 26  * This class represent a search toolbar. The components are some set of: an input field,
 27  * a search button, a save button, and a button to choose what type of item to search for.
 28  * 
 29  * @param {hash}			params		a hash of parameters:
 30  * @param {DwtComposite}	parent		the parent widget
 31  * @param {string}			id			an explicit ID to use for the control's HTML element
 32  * 
 33  * @extends		DwtComposite
 34  */
 35 ZmSearchToolBar = function(params) {
 36 
 37 	if (arguments.length == 0) { return; }
 38 
 39 	params.className = params.className || "ZmSearchToolbar";
 40 	this._button = {};
 41 	DwtToolBar.apply(this, arguments);
 42 
 43 	this._origin = ZmId.SEARCH;
 44 	this._searchMenu = null;
 45 };
 46 
 47 ZmSearchToolBar.prototype = new DwtToolBar;
 48 ZmSearchToolBar.prototype.constructor = ZmSearchToolBar;
 49 
 50 ZmSearchToolBar.prototype.isZmSearchToolBar = true;
 51 ZmSearchToolBar.prototype.toString = function() { return "ZmSearchToolBar"; };
 52 ZmSearchToolBar.prototype.role = 'toolbar';
 53 
 54 // Consts
 55 
 56 
 57 ZmSearchToolBar.TYPES_BUTTON		= "TYPES";
 58 ZmSearchToolBar.SEARCH_BUTTON 		= "SEARCH";
 59 ZmSearchToolBar.SAVE_BUTTON 		= "SAVE";
 60 ZmSearchToolBar.SEARCH_MENU_BUTTON	= ZmSearchToolBar.TYPES_BUTTON;	// back-compatibility
 61 
 62 ZmSearchToolBar.MENU_ITEMS 			= [];		// list of menu items
 63 ZmSearchToolBar.SETTING 			= {};		// required setting for menu item to appear
 64 ZmSearchToolBar.MSG_KEY 			= {};		// text for menu item
 65 ZmSearchToolBar.TT_MSG_KEY 			= {};		// tooltip text for menu item
 66 ZmSearchToolBar.ICON 				= {};		// icon for menu item
 67 ZmSearchToolBar.SHARE_ICON			= {};		// icon for shared menu item
 68 ZmSearchToolBar.ID 					= {};		// ID for menu item
 69 ZmSearchToolBar.DISABLE_OFFLINE     = {};       // Disable when offline detected
 70 
 71 
 72 // Public static methods
 73 
 74 /**
 75  * Defines a menu item to add when the types menu is created. Static so that it can be called before the
 76  * toolbar has been created.
 77  * 
 78  * @param {string}	id			ID of menu item
 79  * @param {hash}	params		menu item properties
 80  */
 81 ZmSearchToolBar.addMenuItem =
 82 function(id, params) {
 83 
 84 	if (params.msgKey)		   { ZmSearchToolBar.MSG_KEY[id]		 = params.msgKey; }
 85 	if (params.tooltipKey)	   { ZmSearchToolBar.TT_MSG_KEY[id]	 	 = params.tooltipKey; }
 86 	if (params.icon)		   { ZmSearchToolBar.ICON[id]			 = params.icon; }
 87 	if (params.shareIcon)	   { ZmSearchToolBar.SHARE_ICON[id]	 	 = params.shareIcon; }
 88 	if (params.setting)		   { ZmSearchToolBar.SETTING[id]		 = params.setting; }
 89 	if (params.id)			   { ZmSearchToolBar.ID[id]				 = params.id; }
 90 	if (params.disableOffline) { ZmSearchToolBar.DISABLE_OFFLINE[id] = params.disableOffline; }
 91 
 92 	if (params.index == null || params.index < 0 || params.index >= ZmSearchToolBar.MENU_ITEMS.length) {
 93 		ZmSearchToolBar.MENU_ITEMS.push(id);
 94 	} else {
 95 		ZmSearchToolBar.MENU_ITEMS.splice(params.index, 0, id);
 96 	}
 97 };
 98 
 99 
100 // Public methods
101 
102 /**
103  * Removes a menu item from the types menu.
104  * 
105  * @param {string}	id			ID of menu item
106  */
107 ZmSearchToolBar.prototype.removeMenuItem =
108 function(id) {
109 	
110 	var menu = this._searchMenu;
111 	if (menu) {
112 		var mi = menu.getItemById(ZmOperation.MENUITEM_ID, id);
113 		if (mi) {
114 			menu.removeChild(mi);
115 			mi.dispose();
116 		}
117 		this._cleanupSeparators(menu);
118 	}
119 };
120 
121 // Remove unneeded separators
122 ZmSearchToolBar.prototype._cleanupSeparators =
123 function(menu) {
124 
125 	var button = this._button[ZmSearchToolBar.TYPES_BUTTON];
126 	menu = menu || (button && button.getMenu());
127 	if (!menu) { return; }
128 
129 	var items = menu.getItems();
130 	var toRemove = [];
131 	for (var i = 0; i < items.length; i++) {
132 		var mi = items[i];
133 		if (mi.isSeparator() && (i == 0 || i == items.length - 1 || items[i - 1].isSeparator())) {
134 			toRemove.push(mi);
135 		}
136 	}
137 	for (var i = 0; i < toRemove.length; i++) {
138 		var mi = toRemove[i];
139 		menu.removeChild(mi);
140 		mi.dispose();
141 	}
142 };
143 
144 ZmSearchToolBar.prototype.getSearchField =
145 function() {
146 	return this._searchField.getInputElement();
147 };
148 
149 // sets up a function to call when Enter has been pressed
150 ZmSearchToolBar.prototype.registerEnterCallback =
151 function(callback) {
152 	this._enterCallback = callback;
153 };
154 
155 ZmSearchToolBar.prototype.addSelectionListener =
156 function(buttonId, listener) {
157 	var button = this._button[buttonId];
158 	if (button) {
159 		button.addSelectionListener(listener);
160 	}
161 };
162 
163 ZmSearchToolBar.prototype.getButton =
164 function(buttonId) {
165 	return this._button[buttonId];
166 };
167 
168 ZmSearchToolBar.prototype.getButtons =
169 function() {
170 	return AjxUtil.values(this._button);
171 };
172 
173 ZmSearchToolBar.prototype.focus = function(item) {
174 
175     if (item) {
176         // focus is being moved via a shortcut (arrow key)
177         return DwtToolBar.prototype.focus.apply(this, arguments);
178     }
179 	else if (this._searchField) {
180 		this._searchField.focus();
181 		this._searchField.moveCursorToEnd();
182         return this._searchField;
183 	}
184 };
185 
186 ZmSearchToolBar.prototype.blur =
187 function() {
188 	if (this._searchField) {
189 		this._searchField.blur();
190 	}
191 };
192 
193 ZmSearchToolBar.prototype.setEnabled =
194 function(enable) {
195 	if (this._searchField) {
196 		this._searchField.setEnabled(enable);
197 	}
198 	for (var buttonId in this._button) {
199 		this._button[buttonId].setEnabled(enable);
200 	}
201 };
202 ZmSearchToolBar.prototype.setSearchFieldValue =
203 function(value) {
204 	if (this._searchField && value != this.getSearchFieldValue()) {
205 		this._searchField.setValue(value);
206 	}
207 };
208 
209 ZmSearchToolBar.prototype.getSearchFieldValue =
210 function() {
211 	return this._searchField ? this._searchField.getValue() : null;
212 };
213 
214 
215 
216 // Private methods
217 
218 ZmSearchToolBar.prototype._handleKeyDown =
219 function(ev) {
220 	var key = DwtKeyEvent.getCharCode(ev);
221 	if (DwtKeyEvent.IS_RETURN[key]) {
222 		return this._handleEnterKeyPress(ev);
223 	}
224 	return true;
225 };
226 
227 ZmSearchToolBar.prototype._handleEnterKeyPress =
228 function(ev) {
229 	if (this._enterCallback) {
230 		this._enterCallback.run({
231 					ev:				ev,
232 					zimletEvent:	"onKeyPressSearchField",
233 					origin:			this._origin
234 				});
235 	}
236 	return false;
237 };
238 
239 /**
240  * Initializes search autocomplete.
241  */
242 ZmSearchToolBar.prototype.initAutocomplete =
243 function() {
244 	if (!this._acList) {
245 		this._acList = new ZmAutocompleteListView(this._getAutocompleteParams());
246 		this._acList.handle(this.getSearchField());
247 	}
248 };
249 
250 ZmSearchToolBar.prototype._getAutocompleteParams =
251 function() {
252 	var params = {
253 		dataClass:			new ZmSearchAutocomplete(),
254 		matchValue:			"matchText",
255 		delims:				[" ", "\t"],
256 		delimCodes:			[3, 13, 9],
257 		separator:			" ",
258 		keyDownCallback:	this._handleKeyDown.bind(this),
259 		contextId:			this.toString(),
260 		locationCallback:	this._getAcLocation.bind(this)
261 	};
262 	return params;
263 };
264 
265 ZmSearchToolBar.prototype.getAutocompleteListView =
266 function() {
267 	return this._acList;
268 };
269 
270 ZmSearchToolBar.prototype._getAcLocation =
271 function() {
272 	var el = this._searchField.getInputElement();
273 	if (!el) { return {}; }
274 	
275 	var elLoc = Dwt.getLocation(el);
276 	var elSize = Dwt.getSize(el);
277 	var strWidth = AjxStringUtil.getWidth(el.value);
278 	if (AjxEnv.isWindows && (AjxEnv.isFirefox || AjxEnv.isSafari || AjxEnv.isChrome) ){
279 		// FF/Win: fudge factor since string is longer in INPUT than when measured in SPAN
280 		strWidth = strWidth * 1.2;
281 	}
282 	var x = elLoc.x + strWidth;
283 	var y = elLoc.y + elSize.y;
284 	DwtPoint.tmp.set(x, y);
285 	return DwtPoint.tmp;
286 };
287 
288 
289 
290 
291 /**
292  * Adds a types button and support for a custom search menu item to a search toolbar
293  * 
294  * @param params
295  */
296 ZmMainSearchToolBar = function(params) {
297 
298 	if (arguments.length == 0) { return; }
299 
300 	ZmSearchToolBar.apply(this, arguments);
301 
302     this._initialize();
303 
304 	// setup "include shared" menu item
305 	var miParams = {
306 		msgKey:			"searchShared",
307 		tooltipKey:		"searchShared",
308 		icon:			"Group",
309 		setting:		ZmSetting.SHARING_ENABLED,
310 		id:				ZmId.getMenuItemId(ZmId.SEARCH, ZmId.SEARCH_SHARED),
311 		disableOffline: true
312 	};
313 	ZmSearchToolBar.addMenuItem(ZmId.SEARCH_SHARED, miParams);
314 
315 	// setup "all accounts" menu item for multi account
316 	if (appCtxt.multiAccounts) {
317 		var miParams = {
318 			msgKey:	"searchAllAccounts",
319 			icon:	"Globe",
320 			id:		ZmId.getMenuItemId(ZmId.SEARCH, ZmId.SEARCH_ALL_ACCOUNTS)
321 		};
322 		ZmSearchToolBar.addMenuItem(ZmId.SEARCH_ALL_ACCOUNTS, miParams);
323 	}
324 };
325 
326 ZmMainSearchToolBar.prototype = new ZmSearchToolBar;
327 ZmMainSearchToolBar.prototype.constructor = ZmMainSearchToolBar;
328 
329 ZmMainSearchToolBar.prototype.isZmMainSearchToolBar = true;
330 ZmMainSearchToolBar.prototype.toString = function() { return "ZmMainSearchToolBar"; };
331 
332 ZmMainSearchToolBar.CUSTOM_ITEM_ID		= "CustomSearchItem";	// custom search menu item key
333 ZmMainSearchToolBar.CUSTOM_BUTTON 		= "CUSTOM";				// button ID
334 
335 ZmMainSearchToolBar.prototype._initialize = function() {
336 
337     var isExternalAccount = appCtxt.isExternalAccount();
338 
339 	// add "search types" menu
340     var firstItem = ZmSearchToolBar.MENU_ITEMS[0];
341     var buttonId = ZmId.getButtonId(ZmId.SEARCH, ZmId.SEARCH_MENU);
342     var button = this._button[ZmSearchToolBar.TYPES_BUTTON] = new DwtButton({
343         parent:		this,
344         index:		0,
345         id:         buttonId
346     });
347     button.setImage(ZmSearchToolBar.ICON[firstItem]);
348     button.setToolTipContent(ZmMsg[ZmSearchToolBar.TT_MSG_KEY[firstItem]], true);
349 
350     var menu = new AjxCallback(this, this._createSearchMenu);
351     button.setMenu(menu, false, DwtMenuItem.RADIO_STYLE);
352     if (isExternalAccount) {
353         button.setEnabled(false);
354     }
355 
356     // add search box
357     var searchBox = this._searchField = new DwtInputField({
358         parent:     this,
359         hint:       ZmMsg.searchInput,
360         label:      ZmMsg.searchInput,
361         inputId:    ZmId.SEARCH_INPUTFIELD
362     });
363     var inputEl = searchBox.getInputElement();
364     inputEl.className = "search_input";
365     this._searchField._showHint();
366     this._searchField.addListener(DwtEvent.ONFOCUS, this._onInputFocus.bind(this));
367     this._searchField.addListener(DwtEvent.ONBLUR, this._onInputBlur.bind(this));
368     if (isExternalAccount) {
369         this._searchField.setEnabled(false);
370     }
371     searchBox.addListener(DwtEvent.ONFOCUS, this._childFocusListener.bind(this));
372 
373     // add search button
374     button = this._button[ZmSearchToolBar.SEARCH_BUTTON] = new DwtButton({
375         parent:		this,
376         className: 	"ZmSearchButton",
377         id:         ZmId.getButtonId(ZmId.SEARCH, ZmId.SEARCH_SEARCH)
378     });
379     button.setImage("Search2");
380     button.setToolTipContent(ZmMsg.searchTooltip, true);
381 
382     // add save search button if saved-searches enabled
383     if (isExternalAccount) {
384         if (this._button[ZmSearchToolBar.SEARCH_BUTTON]) {
385             this._button[ZmSearchToolBar.SEARCH_BUTTON].setEnabled(false);
386         }
387     }
388 };
389 
390 ZmMainSearchToolBar.prototype._createSearchMenu =
391 function() {
392 
393 	var menu = this._searchMenu = new DwtMenu({
394 				parent:		this._button[ZmSearchToolBar.TYPES_BUTTON],
395 				className:	"ActionMenu",
396 				id:			ZmId.getMenuId(ZmId.SEARCH)
397 			});
398 	var mi;
399 	if (this._customSearchMenuItems) {
400 		for (var i = 0; i < this._customSearchMenuItems.length; i++) {
401 			var csmi = this._customSearchMenuItems[i];
402 			this._createCustomSearchMenuItem(menu, csmi.icon, csmi.text, csmi.listener);
403 		}
404 	}
405 	var params = {
406 		parent:         menu,
407 		enabled:        true,
408 		radioGroupId:   0,
409 		style:          DwtMenuItem.RADIO_STYLE
410 	};
411 	for (var i = 0; i < ZmSearchToolBar.MENU_ITEMS.length; i++) {
412 		var id = ZmSearchToolBar.MENU_ITEMS[i];
413 
414 		// add separator *before* "shared" menu item
415 		if (id == ZmId.SEARCH_SHARED) {
416 			if (ZmSearchToolBar.MENU_ITEMS.length <= 1) { continue; }
417 			mi = new DwtMenuItem({parent:menu, style:DwtMenuItem.SEPARATOR_STYLE});
418 		}
419 
420 		var setting = ZmSearchToolBar.SETTING[id];
421 		if (setting && !appCtxt.get(setting)) { continue; }
422 
423 		var isCheckStyle = (id == ZmId.SEARCH_SHARED || id == ZmId.SEARCH_ALL_ACCOUNTS);
424 		if (isCheckStyle) {
425 			params.style = DwtMenuItem.CHECK_STYLE;
426 		}
427 		params.style = (id == ZmId.SEARCH_SHARED || id == ZmId.SEARCH_ALL_ACCOUNTS)
428 			? DwtMenuItem.CHECK_STYLE : DwtMenuItem.RADIO_STYLE;
429 		params.imageInfo = ZmSearchToolBar.ICON[id];
430 		params.text = ZmMsg[ZmSearchToolBar.MSG_KEY[id]];
431 		params.id = ZmSearchToolBar.ID[id];
432 		mi = DwtMenuItem.create(params);
433 		mi.setData(ZmOperation.MENUITEM_ID, id);
434 		if (!isCheckStyle) {
435 			mi.setAttribute('aria-label', ZmMsg[ZmSearchToolBar.TT_MSG_KEY[id]]);
436 		}
437 	}
438 	
439 	this._checkSharedMenuItem();
440 	appCtxt.getSettings().getSetting(ZmSetting.SEARCH_INCLUDES_SHARED).addChangeListener(this._checkSharedMenuItem.bind(this));
441 
442 	appCtxt.getSearchController()._addMenuListeners(menu);
443 	this._searchMenuCreated = true;
444 
445 	return menu;
446 };
447 
448 ZmMainSearchToolBar.prototype.setOfflineState = function(offline) {
449 	var button   = this._button[ZmSearchToolBar.TYPES_BUTTON];
450 	var menu     = button && button.getMenu();
451 	var numItems = menu.getItemCount();
452 	for (var i = 0; i < numItems; i++) {
453 	    var item = menu.getItem(i);
454 		if (item) {
455 			var id = item.getData(ZmOperation.MENUITEM_ID);
456 			if (id && ZmSearchToolBar.DISABLE_OFFLINE[id])  {
457 				item.setEnabled(!offline);
458 			}
459 		}
460 	}
461 }
462 
463 ZmMainSearchToolBar.prototype.getSearchType =
464 function() {
465 	var button = this._button[ZmSearchToolBar.TYPES_BUTTON];
466 	var menu = button && button.getMenu();
467     var item = menu ? menu.getSelectedItem() || menu.getItems()[0] : null;
468 	var data = item ? item.getData(ZmMainSearchToolBar.CUSTOM_ITEM_ID) || item.getData(ZmOperation.MENUITEM_ID) :
469 					  ZmSearchToolBar.MENU_ITEMS[0];
470 	return data;
471 };
472 
473 ZmMainSearchToolBar.prototype.createCustomSearchBtn =
474 function(icon, text, listener, id) {
475 
476 	if (!this._customSearchListener) {
477 		this._customSearchListener = this._customSearchBtnListener.bind(this);
478 	}
479 
480 	// check if custom search should be a button by checking for the Id against the template
481 	var customSearchBtn = document.getElementById(this._htmlElId + "_customSearchButton");
482 	if (customSearchBtn) {
483 		var data = { icon:icon, text:text, listener:listener };
484 		var button = this._button[ZmSearchToolBar.CUSTOM_BUTTON]
485 		if (!button) {
486 			button = this._button[ZmSearchToolBar.CUSTOM_BUTTON] = ZmToolBar.addButton({
487 						parent:		this,
488 						tdId:		"_customSearchButton",
489 						buttonId:	ZmId.getButtonId(ZmId.SEARCH, ZmId.SEARCH_CUSTOM),
490 						lbl:		text,
491 						icon:		icon
492 					});
493 			button.setData(ZmMainSearchToolBar.CUSTOM_ITEM_ID, data);
494 			button.addSelectionListener(this._customSearchListener);
495 
496 			// show the separator now that we've added a custom search button
497 			var sep = document.getElementById(this._htmlElId + "_customSearchButtonSep");
498 			if (sep) {
499 				Dwt.setVisible(sep, true);
500 			}
501 		} else {
502 			var menu = button && button.getMenu();
503 			var item;
504 			var params = {
505 				parent:			menu,
506 				enabled:		true,
507 				style:			DwtMenuItem.RADIO_STYLE,
508 				radioGroupId:	0,
509 				id:				id
510 			};
511 			if (!menu) {
512 				var btnData = button.getData(ZmMainSearchToolBar.CUSTOM_ITEM_ID);
513 				menu = new DwtMenu({
514 							parent:		button,
515 							className:	"ActionMenu",
516 							id:			ZmId.getMenuId(ZmId.SEARCH, ZmId.SEARCH_CUSTOM)
517 						});
518 				button.setMenu(menu, false, DwtMenuItem.RADIO_STYLE);
519 				params.imageInfo = btnData.icon;
520 				params.text = btnData.text;
521 				item = DwtMenuItem.create(params);
522 				item.setData(ZmMainSearchToolBar.CUSTOM_ITEM_ID, btnData);
523 				item.setData(ZmOperation.MENUITEM_ID, ZmId.SEARCH_CUSTOM);
524 				item.setChecked(true, true);
525 				item.addSelectionListener(this._customSearchListener);
526 			}
527 			params.imageInfo = icon;
528 			params.text = text;
529 			item = DwtMenuItem.create(params);
530 			item.setData(ZmMainSearchToolBar.CUSTOM_ITEM_ID, data);
531 			item.addSelectionListener(this._customSearchListener);
532 		}
533 	} else {
534 		if (this._searchMenuCreated) {
535 			var menu = this._button[ZmSearchToolBar.TYPES_BUTTON].getMenu();
536 			this._createCustomSearchMenuItem(menu, icon, text, listener, id);
537 		} else {
538 			if (!this._customSearchMenuItems) {
539 				this._customSearchMenuItems = [];
540 			}
541 			this._customSearchMenuItems.push({icon:icon, text:text, listener:listener, id:id});
542 		}
543 	}
544 };
545 
546 ZmMainSearchToolBar.prototype._createCustomSearchMenuItem =
547 function(menu, icon, text, listener, id) {
548 	var mi = menu.getItem(0);
549 	var params = {
550 		parent: menu,
551 		imageInfo: icon,
552 		text: text,
553 		enabled: true,
554 		style: DwtMenuItem.RADIO_STYLE,
555 		radioGroupId: 0,
556 		index: 0,
557 		id: id
558 	};
559 	mi = DwtMenuItem.create(params);
560 	var data = { icon:icon, text:text, listener:listener };
561 	mi.setData(ZmMainSearchToolBar.CUSTOM_ITEM_ID, data);
562 	mi.setData(ZmOperation.MENUITEM_ID, ZmId.SEARCH_CUSTOM);
563 	mi.addSelectionListener(this._customSearchListener);
564 
565 	// only add separator if this is the first custom search menu item
566 	if (!(mi && mi.getData(ZmMainSearchToolBar.CUSTOM_ITEM_ID))) {
567 		mi = new DwtMenuItem({parent:menu, style:DwtMenuItem.SEPARATOR_STYLE, index:1});
568 	}
569 };
570 
571 ZmMainSearchToolBar.prototype._customSearchBtnListener = 
572 function(ev) {
573 	var item = ev.item;
574 	if (!item) { return; }
575 	var data = item.getData(ZmMainSearchToolBar.CUSTOM_ITEM_ID);
576 	if (!data) { return; }
577 	if (this._customSearchBtn) {
578 		if (item.isDwtMenuItem) {
579 			if (ev.detail != DwtMenuItem.CHECKED) { return; }
580 			this._customSearchBtn.setToolTipContent(data[1]);
581 			this._customSearchBtn.setData(ZmMainSearchToolBar.CUSTOM_ITEM_ID, data);
582 		}
583 		data.listener.run(ev); // call original listener
584 	} else {
585 		var button = this._button[ZmSearchToolBar.TYPES_BUTTON];
586 		button.setToolTipContent(data.text);
587 
588 		var menu = item.parent;
589 		var shareMenuItem = menu ? menu.getItemById(ZmOperation.MENUITEM_ID, ZmId.SEARCH_SHARED) : null;
590 		if (shareMenuItem) {
591 			shareMenuItem.setChecked(false, true);
592 			shareMenuItem.setEnabled(false);
593 		}
594 
595 		button.setImage(data.icon);
596 		button.setText(data.text);
597 	}
598 };
599 
600 // Expand INPUT when it gets focus
601 ZmMainSearchToolBar.prototype._onInputFocus = function(ev) {
602 
603 	this._setInputExpanded(true);
604 };
605 
606 // Collapse INPUT when it loses focus (unless that was due to a click on a search toolbar button)
607 ZmMainSearchToolBar.prototype._onInputBlur = function(ev) {
608 
609 	var focusObj = appCtxt.getKeyboardMgr().getFocusObj();
610 	if (focusObj !== this._button[ZmSearchToolBar.TYPES_BUTTON] && focusObj !== this._button[ZmSearchToolBar.SEARCH_BUTTON] && !this._movingFocus) {
611 		this._setInputExpanded(false);
612 	}
613     this._movingFocus = false;  // done here since blur handler may be called on a timer
614 };
615 
616 // note when we're moving focus within the toolbar so we don't collapse the INPUT
617 ZmMainSearchToolBar.prototype._moveFocus = function(back) {
618 
619     this._movingFocus = true;
620     ZmSearchToolBar.prototype._moveFocus.apply(this, arguments);
621 };
622 
623 ZmMainSearchToolBar.prototype._setInputExpanded = function(expanded) {
624 
625     // Don't collapse input if user just popped up menu (which causes blur on input)
626     if (!expanded && this._searchMenu && this._searchMenu.isPoppedUp()) {
627         return;
628     }
629 
630 	var input = this._searchField.getInputElement();
631 	var cls = expanded ? "search_input-expanded" : "search_input";
632 
633 	if (AjxEnv.isIE9) {
634 		// bug 83493: IE9 gets the layout wrong on the first try
635 		setTimeout(function() {
636 			input.className = cls;
637 		}, 0);
638 	} else {
639 		input.className = cls;
640 	}
641 };
642 
643 ZmMainSearchToolBar.prototype._checkSharedMenuItem =
644 function() {
645 	var mi = this._searchMenu && this._searchMenu.getItemById(ZmOperation.MENUITEM_ID, ZmId.SEARCH_SHARED);
646 	if (mi) {
647 		mi.setChecked(appCtxt.get(ZmSetting.SEARCH_INCLUDES_SHARED));
648 	}
649 };
650