<?xml version="1.0"?>
<!--
    ***** BEGIN LICENSE BLOCK *****
    
    Copyright © 2009 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org
    
    This file is part of Zotero.
    
    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    
    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
    
    ***** END LICENSE BLOCK *****
-->
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">

<bindings xmlns="http://www.mozilla.org/xbl"
		  xmlns:xbl="http://www.mozilla.org/xbl">

	<binding id="tag-selector">
		<resources>
			<stylesheet src="chrome://zotero/skin/bindings/tagselector.css"/>
			<stylesheet src="chrome://zotero-platform/content/tagselector.css"/>
		</resources>
		
		<implementation>
			<field name="collectionTreeRow"/>
			<field name="updateScope"/>
			<field name="selection"/>
			<field name="onchange"/>
			
			<field name="_initialized">false</field>
			<field name="_notifierID">false</field>
			<field name="_tags">null</field>
			<field name="_dirty">null</field>
			<field name="_emptyColored">null</field>
			<field name="_emptyRegular">null</field>
			
			<!-- Modes are predefined settings groups for particular tasks -->
			<field name="_mode">"view"</field>
			<property name="mode" onget="return this._mode;">
				<setter>
				<![CDATA[
					this.clickable = false;
					this.editable = false;
					
					switch (val) {
						case 'view':
							break;
						
						case 'edit':
							this.clickable = true;
							this.editable = true;
							//this.clickHandler = this.showEditor;
							//this.blurHandler = this.hideEditor;
							break;
						
						default:
							throw ("Invalid mode '" + val + "' in tagselector.xml");
					}
					
					this._mode = val;
					document.getAnonymousNodes(this)[0].setAttribute('mode', val);
				]]>
				</setter>
			</property>
			
			<field name="_libraryID"/>
			<property name="libraryID" onget="return this._libraryID">
				<setter>
				<![CDATA[
					if (this._libraryID != val) {
						this._dirty = true;
					}
					this._libraryID = val;
				]]>
				</setter>
			</property>
			
			<property name="showAutomatic" onget="return this.getAttribute('showAutomatic') != 'false'"/>
			<property name="_types">
				<getter>
					<![CDATA[
					var types = [0];
					if (this.showAutomatic) {
						types.push(1);
					}
					return types;
					]]>
				</getter>
			</property>
			
			<field name="_search">null</field>
			<method name="setSearch">
				<parameter name="val"/>
				<parameter name="skipRefresh"/>
				<body>
				<![CDATA[
					this._search = val
						? val.toLowerCase().normalize()
						: false;
					
					if (!skipRefresh) {
						this.refresh();
					}
				]]>
				</body>
			</method>
			
			<field name="_hasScope">false</field>
			<field name="_scope">null</field>
			<property name="scope" onget="return this._scope">
				<setter>
				<![CDATA[
					if (val.length) {
						this._hasScope = true;
						this._scope = {};
						for (let i=0; i<val.length; i++) {
							let tag = val[i];
							if (!this._scope[tag.tag]) {
								this._scope[tag.tag] = [];
							}
							this._scope[tag.tag].push(tag.type);
						}
					}
					else {
						this._hasScope = false;
						this._scope = {};
					}
					
					this.refresh();
				]]>
				</setter>
			</property>
			
			<property name="filterToScope">
				<getter>
				<![CDATA[
					// Default is to filter, so test for explicit 'false'
					return this.getAttribute('filterToScope') != 'false';
				]]>
				</getter>
				<setter>
				<![CDATA[
					this.setAttribute('filterToScope', val);
					this.refresh();
				]]>
				</setter>
			</property>
			
			<constructor>
				<![CDATA[
					this.id('show-automatic').setAttribute('checked', this.showAutomatic);
					this.id('display-all-tags').setAttribute('checked', !this.filterToScope);
					this.dragObserver = new this._dragObserverConstructor;
				]]>
			</constructor>
			
			
			<method name="init">
				<body>
				<![CDATA[
					this._initialized = true;
					this.selection = {};
					this._notifierID = Zotero.Notifier.registerObserver(this, ['collection-item', 'item-tag', 'tag', 'setting'], 'tagSelector');
				]]>
				</body>
			</method>
			
			
			<method name="uninit">
				<body>
				<![CDATA[
					if (!this._initialized) {
						return;
					}
					
					this._initialized = false;
					this.unregister();
					this.selection = {};
					if (this.onchange) {
						this.onchange();
					}
				]]>
				</body>
			</method>
			
			
			<method name="unregister">
				<body>
				<![CDATA[
					if (this._notifierID) {
						Zotero.Notifier.unregisterObserver(this._notifierID);
					}
				]]>
				</body>
			</method>
			
			
			<method name="refresh">
				<parameter name="fetch"/>
				<body>
				<![CDATA[
					Zotero.spawn(function* () {
						Zotero.debug('Refreshing tags selector');
						var t = new Date;
						
						if (!this._initialized) {
							this.init();
							fetch = true;
						}
						
						var emptyColored = true;
						var emptyRegular = true;
						var tagsToggleBox = this.id('tags-toggle');
						
						var tagColors = yield Zotero.Tags.getColors(this.libraryID);
						if (fetch || this._dirty) {
							this._tags = yield Zotero.Tags.getAll(this.libraryID, this._types);
							
							// Remove children
							tagsToggleBox.textContent = "";
							
							// Sort by name
							var collation = Zotero.getLocaleCollation();
							var orderedTags = this._tags.concat();
							orderedTags.sort(function(a, b) {
								return collation.compareString(1, a.tag, b.tag);
							});
							
							var tagColorsLowerCase = {};
							var colorTags = [];
							for (let name in tagColors) {
								colorTags[tagColors[name].position] = name;
								tagColorsLowerCase[name.toLowerCase()] = true;
							}
							var positions = Object.keys(colorTags);
							for (let i=positions.length-1; i>=0; i--) {
								let name = colorTags[positions[i]];
								orderedTags.unshift({
									tag: name,
									type: 0,
									hasColor: true
								});
							}
							
							var lastTag;
							for (let i=0; i<orderedTags.length; i++) {
								let tagData = orderedTags[i];
								
								// Skip colored tags in the regular section,
								// since we add them to the beginning above
								if (!tagData.hasColor && tagColorsLowerCase[tagData.tag.toLowerCase()]) {
									continue;
								}
								
								// Only show tags of different types once
								if (tagData.tag === lastTag) {
									continue;
								}
								lastTag = tagData.tag;
								
								let tagButton = this._makeClickableTag(tagData, this.editable);
								if (tagButton) {
									var self = this;
									tagButton.addEventListener('click', function(event) {
										self.handleTagClick(event, this);
									});
									if (this.editable) {
										tagButton.addEventListener('dragover', this.dragObserver.onDragOver);
										tagButton.addEventListener('dragexit', this.dragObserver.onDragExit);
										tagButton.addEventListener('drop', this.dragObserver.onDrop, true);
									}
									tagsToggleBox.appendChild(tagButton);
								}
							}
							this._dirty = false;
						}
						
						// Set attributes
						var colorTags = {};
						var labels = tagsToggleBox.getElementsByTagName('label');
						for (let i=0; i<labels.length; i++) {
							let name = labels[i].value;
							let lcname = name.toLowerCase();
							
							let colorData = tagColors[name];
							if (colorData) {
								labels[i].setAttribute(
									'style', 'color:' + colorData.color + '; ' + 'font-weight: bold'
								);
							}
							else {
								labels[i].removeAttribute('style');
							}
							
							// Restore selection
							if (this.selection[name]){
								labels[i].setAttribute('selected', 'true');
							}
							else {
								labels[i].setAttribute('selected', 'false');
							}
							
							// Check tags against search
							if (this._search) {
								var inSearch = lcname.indexOf(this._search) != -1;
							}
							
							// Check tags against scope
							if (this._hasScope) {
								var inScope = !!this._scope[name];
							}
							
							// If not in search, hide
							if (this._search && !inSearch) {
								labels[i].setAttribute('hidden', true);
							}
							else if (this.filterToScope) {
								if (this._hasScope && inScope) {
									labels[i].className = 'zotero-clicky';
									labels[i].setAttribute('inScope', true);
									labels[i].setAttribute('hidden', false);
									emptyRegular = false;
								}
								else {
									labels[i].className = '';
									labels[i].setAttribute('hidden', true);
									labels[i].setAttribute('inScope', false);
								}
							}
							// Display all
							else {
								if (this._hasScope && inScope) {
									labels[i].className = 'zotero-clicky';
									labels[i].setAttribute('inScope', true);
								}
								else {
									labels[i].className = '';
									labels[i].setAttribute('inScope', false);
								}
								
								labels[i].setAttribute('hidden', false);
								emptyRegular = false;
							}
							
							// Always show colored tags at top, unless they
							// don't match an active tag search
							if (colorData && (!this._search || inSearch)) {
								labels[i].setAttribute('hidden', false);
								labels[i].setAttribute('hasColor', true);
								emptyColored = false;
							}
							else {
								labels[i].removeAttribute('hasColor');
							}
						}
						
						//start tag cloud code
						
						var tagCloud = Zotero.Prefs.get('tagCloud');
						
						if(tagCloud) {
							var labels = tagsToggleBox.getElementsByTagName('label');
						
							//loop through displayed labels and find number of linked items
							var numlinked= [];
							for (var i=0; i<labels.length; i++){
								if(labels[i].getAttribute("hidden") != 'true') {
									var tagIDs = labels[i].getAttribute('tagID').split('-');
									
									
									//replace getLinkedItems() with function that gets linked items within the current collection
									var linked = this._tags[tagIDs[0]].getLinkedItems();
									
									numlinked.push(parseInt(linked.length));
								}	
							}
							//
							numlinked.sort();
							
							//Get number of attached items from tag with fewest items
							var min = numlinked[0];
							
							//Get number of attached items from tag with most items
							var max = numlinked.pop();
							numlinked.push(max);
							
							//Create array of possible tag text sizes
							var sizes = ["11", "12", "13", "14", "15", "16", "17", "18", "19","20","21","22"];
							
							//Number of possible tag sizes
							var categories = sizes.length;
							
							//inc is the size of each size category of tags, in terms of the number of attached items
							var inc = Math.ceil((max-min)/categories);
							if(inc<1) {
								inc = 1;
							}
							
							for (var i=0; i<labels.length; i++){
								if(labels[i].getAttribute("hidden") != 'true') {
									var tagIDs = labels[i].getAttribute('tagID').split('-');
									
									
									//replace getLinkedItems() with function that gets linked items within the current collection
									var linked = this._tags[tagIDs[0]].getLinkedItems();
									
									numlink = linked.length;
									
									//range is the difference between how many items this tag has and how many items the smallest tag has
									var range=(numlink-min);
									
									//Divide the range by the size of the categories
									s=range/inc;
									
									if(s==categories) {
										s=categories-1;
									}
									var stylestr = 'font-size:'+sizes[s]+'px;';
									labels[i].setAttribute('style',stylestr);
								}
							}
						}
						
						//end tag cloud code
						
						this.updateNumSelected();
						this._emptyColored = emptyColored;
						this._emptyRegular = emptyRegular;
						var empty = emptyColored && emptyRegular;
						this.id('tags-toggle').setAttribute('collapsed', empty);
						this.id('no-tags-box').setAttribute('collapsed', !empty);
						
						if (this.onRefresh) {
							this.onRefresh();
							this.onRefresh = null;
						}
						
						// Clear "Loading tags…" after the first load
						this.id('no-tags-deck').selectedIndex = 1;
						
						Zotero.debug("Loaded tag selector in " + (new Date - t) + " ms");
					}, this);
				]]>
				</body>
			</method>
			
			
			<method name="getNumSelected">
				<body>
				<![CDATA[
					var count = 0;
					for (var i in this.selection) {
						count++;
					}
					return count;
				]]>
				</body>
			</method>
			
			
			<method name="updateNumSelected">
				<body>
				<![CDATA[
					var count = this.getNumSelected();
					
					switch (count) {
						case 0:
							var mod = 'none';
							break;
						case 1:
							var mod = 'singular';
							break;
						default:
							var mod = 'plural';
					}
					
					this.id('num-selected').label = Zotero.getString('pane.tagSelector.numSelected.' + mod, [count]);
					this.id('deselect-all').disabled = count == 0;
				]]>
				</body>
			</method>
			
			<field name="_notified"/>
			<method name="notify">
				<parameter name="event"/>
				<parameter name="type"/>
				<parameter name="ids"/>
				<body><![CDATA[
					return Zotero.spawn(function* () {
						if (type == 'setting') {
							if (ids.some(function (val) val.split("/")[1] == 'tagColors')) {
								this.refresh(true);
							}
							return;
						}
						
						// Ignore anything other than deletes in duplicates view
						if (this.collectionTreeRow.isDuplicates()) {
							switch (event) {
								case 'delete':
								case 'trash':
									break;
								
								default:
									return;
							}
						}
						
						var selectionChanged = false;
						
						// If a selected tag no longer exists, deselect it
						if (event == 'delete' || event == 'modify') {
							// TODO: necessary, or just use notifier value?
							this._tags = yield Zotero.Tags.getAll(this.libraryID, this._types);
							
							for (var tag in this.selection) {
								for each(var tag2 in this._tags) {
									if (tag == tag2) {
										var found = true;
										break;
									}
								}
								if (!found) {
									delete this.selection[tag];
									selectionChanged = true;
								}
							}
						}
						
						// This could be more optimized to insert new/changed tags at the appropriate
						// spot if we cared, but we probably don't
						var t = this.id('tags-search').inputField;
						if (t.value) {
							this.setSearch(t.value, true);
						}
						else {
							this.setSearch(false, true);
						}
						this._dirty = true;
						
						// This is a hack, but set this to run after the refresh,
						// since _emptyRegular isn't set until then
						this.onRefresh = function () {
							// If no regular tags visible after a delete, deselect all.
							// This is necessary so that a selected tag that's removed
							// from its last item doesn't cause all regular tags to
							// disappear without anything being visibly selected.
							if ((event == 'remove' || event == 'delete') &&
									this._emptyRegular && this.getNumSelected()) {
								Zotero.debug('No tags visible after delete -- deselecting all');
								return this.clearAll();
							}
						}.bind(this);
						
						// If the selection changed, update the items list
						if (selectionChanged && this.onchange) {
							return this.onchange();
						}
						
						// Otherwise, just update the tag selector
						return this.updateScope();
					}, this);
				]]>
				</body>
			</method>
			
			
			<!-- Not currently used -->
			<method name="selectVisible">
				<body>
					<![CDATA[
						var tagsToggleBox = this.id('tags-toggle');
						
						var labels = tagsToggleBox.getElementsByTagName('label');
						for (var i=0; i<labels.length; i++){
							if (labels[i].getAttribute('hidden') != 'true'
								 && labels[i].getAttribute('inScope') == 'true') {
								labels[i].setAttribute('selected', 'true');
								this.selection[labels[i].value] = true;
							}
						}
					]]>
				</body>
			</method>
			
			
			<method name="clearVisible">
				<body><![CDATA[
					var tagsToggleBox = this.id('tags-toggle');
					
					var labels = Zotero.Utilities.xpath(tagsToggleBox, 'label[@selected="true"]');
					for (var i=0; i<labels.length; i++){
						var label = labels[i];
						label.setAttribute('selected', 'false');
						delete this.selection[label.value];
					}
					
					if (this.onchange) {
						this.onchange();
					}
				]]></body>
			</method>
			
			
			<method name="clearAll">
				<body><![CDATA[
					this.selection = {};
					return this.clearVisible();
				]]></body>
			</method>
			
			
			<method name="handleKeyPress">
				<parameter name="clear"/>
				<body>
					<![CDATA[
						var textbox = this.id('tags-search');
						var t = textbox.inputField;
						
						if (typeof clear != 'undefined') {
							if (clear){
								t.value = '';
								this.setSearch();
								return false;
							}
							else {
								return true;
							}
						}
						
						if (t.value) {
							this.setSearch(t.value);
						}
						else {
							this.setSearch();
						}
						return true;
					]]>
				</body>
			</method>
			
			
			<method name="handleTagClick">
				<parameter name="event"/>
				<parameter name="label"/>
				<body>
					<![CDATA[
						if (event.button != 0) {
							return;
						}
						
						// Ignore clicks on tags not in scope
						if (label.getAttribute('inScope') == 'false') {
							return;
						}
						
						// Deselect
						if (label.getAttribute('selected')=='true'){
							delete this.selection[label.value];
							label.setAttribute('selected', 'false');
						}
						// Select
						else {
							this.selection[label.value] = true;
							label.setAttribute('selected', 'true');
						}
						
						this.updateNumSelected();
						
						if (this.onchange) {
							this.onchange();
						}
					]]>
				</body>
			</method>
			
			
			<method name="rename">
				<parameter name="oldName"/>
				<body><![CDATA[
					Zotero.spawn(function* () {
						var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
							.getService(Components.interfaces.nsIPromptService);
						
						var newName = { value: oldName };
						var result = promptService.prompt(window,
							Zotero.getString('pane.tagSelector.rename.title'),
							Zotero.getString('pane.tagSelector.rename.message'),
							newName, '', {});
						
						if (!result || !newName.value || oldName == newName.value) {
							return;
						}
						
						if (this.selection[oldName]) {
							var wasSelected = true;
							delete this.selection[oldName];
						}
						
						yield Zotero.Tags.load(this.libraryID);
						if (Zotero.Tags.getID(this.libraryID, oldName)) {
							yield Zotero.Tags.rename(this.libraryID, oldName, newName.value);
						}
						// Colored tags don't need to exist, so in that case
						// just rename the color setting
						else {
							let color = yield Zotero.Tags.getColor(this.libraryID, oldName);
							if (!color) {
								throw new Error("Can't rename missing tag");
							}
							yield Zotero.Tags.setColor(this.libraryID, oldName, false);
							yield Zotero.Tags.setColor(this.libraryID, newName, color);
						}
						
						if (wasSelected) {
							this.selection[newName.value] = true;
						}
					}.bind(this));
				]]>
				</body>
			</method>
			
			
			<method name="delete">
				<parameter name="name"/>
				<body>
				<![CDATA[
					var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
						.getService(Components.interfaces.nsIPromptService);
					
					var confirmed = promptService.confirm(window,
						Zotero.getString('pane.tagSelector.delete.title'),
						Zotero.getString('pane.tagSelector.delete.message'));
					
					if (!confirmed) {
						return;
					}
					
					return Zotero.DB.executeTransaction(function* () {
						yield Zotero.Tags.load(this.libraryID);
						var tagID = Zotero.Tags.getID(this.libraryID, name);
						if (tagID) {
							yield Zotero.Tags.erase(this.libraryID, tagID);
						}
					}.bind(this));
					
					// If only a tag color setting, remove that
					if (!tagID) {
						Zotero.Tags.setColor(this.libraryID, name, false);
					}
				]]>
				</body>
			</method>
			
			<method name="getColor">
				<parameter name="tagIDs"/>
				<body>
				<![CDATA[
					tagIDs = tagIDs.split('-');
					var name = Zotero.Tags.getName(this.libraryID, tagIDs[0]);
					return Zotero.Tags.getColor(this.libraryID, name)
					.then(function (colorData) {
						return colorData ? colorData.color : '#000000';
					});
				]]>
				</body>
			</method>
			
			
			<method name="_makeClickableTag">
				<parameter name="tagObj"/>
				<parameter name="editable"/>
				<body>
				<![CDATA[
					var tagName = tagObj.tag;
					var tagType = tagObj.type;
					
					var label = document.createElement('label');
					
					label.setAttribute('value', tagName);
					label.setAttribute('tagType', tagType);
					if (editable) {
						label.setAttribute('context', 'tag-menu');
					}
					return label;
				]]>
				</body>
			</method>
			
			
			<method name="_openColorPickerWindow">
				<parameter name="name"/>
				<body>
				<![CDATA[
					var io = {
						libraryID: this.libraryID,
						name: name
					};
					
					var self = this;
					Zotero.Tags.getColors(this.libraryID)
					.then(function (tagColors) {
						if (Object.keys(tagColors).length >= Zotero.Tags.MAX_COLORED_TAGS && !tagColors[io.name]) {
							var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
								.getService(Components.interfaces.nsIPromptService);
							ps.alert(null, "", Zotero.getString('pane.tagSelector.maxColoredTags', Zotero.Tags.MAX_COLORED_TAGS));
							return;
						}
						
						// Opening a modal window directly from within this promise handler causes
						// the opened window to block on the first yielded promise until the window
						// is closed.
						setTimeout(function () {
							window.openDialog(
								'chrome://zotero/content/tagColorChooser.xul',
								"zotero-tagSelector-colorChooser",
								"chrome,modal,centerscreen", io
							);
							
							// Dialog cancel
							if (typeof io.color == 'undefined') {
								return;
							}
							
							Zotero.Tags.setColor(self.libraryID, io.name, io.color, io.position);
						}, 0);
					});
				]]>
				</body>
			</method>
			
			
			<method name="focusTextbox">
				<body>
					<![CDATA[
						this.id('tags-search').focus();
					]]>
				</body>
			</method>
			
			
			<property name="dragObserver"/>
			<method name="_dragObserverConstructor">
				<body>
				<![CDATA[
					this.onDragOver = function (event) {
						//Zotero.debug(event.dataTransfer.mozTypesAt(0));
						if (!event.dataTransfer.getData('zotero/item')) {
							return;
						}
						
						/*
						// TODO: get drop data
						var ids = dropData.data.split(',');
						var items = Zotero.Items.get(ids);
						for (var i=0; i<items.length; i++) {
							if (!Zotero.Items.isEditable(items[i])) {
								return true;
							}
						}
						*/
						
						event.target.setAttribute('draggedOver', true);
						event.preventDefault();
						event.effectAllowed = event.dropEffect = "link";
					}
					
					this.onDragExit = function (event) {
						event.target.setAttribute('draggedOver', false);
					}
					
					
					this.onDrop = Zotero.Promise.method(function (event) {
						var node = event.target;
						node.setAttribute('draggedOver', false);
						
						var dt = event.dataTransfer;
						var ids = dt.getData('zotero/item');
						if (!ids) {
							return;
						}
						
						return Zotero.DB.executeTransaction(function* () {
							ids = ids.split(',');
							var items = Zotero.Items.get(ids);
							var value = node.getAttribute('value')
							
							for (let i=0; i<items.length; i++) {
								let item = items[i];
								item.addTag(value);
								yield item.save();
							}
						}.bind(this));
					});
				]]>
				</body>
			</method>
			
			
			<method name="id">
				<parameter name="id"/>
				<body>
					<![CDATA[
						return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0];
					]]>
				</body>
			</method>
		</implementation>
		
		<content>
			<groupbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1">
				<menupopup id="tag-menu">
					<menuitem label="&zotero.tagSelector.assignColor;"
						oncommand="_openColorPickerWindow(document.popupNode.getAttribute('value')); event.stopPropagation()"/>
					<menuitem label="&zotero.tagSelector.renameTag;"
						oncommand="document.getBindingParent(this).rename(document.popupNode.getAttribute('value')); event.stopPropagation()"/>
					<menuitem label="&zotero.tagSelector.deleteTag;"
						oncommand="document.getBindingParent(this).delete(document.popupNode.getAttribute('value')); event.stopPropagation()"/>
				</menupopup>
				
				<vbox id="no-tags-box" align="center" pack="center" flex="1">
					<deck id="no-tags-deck">
						<label value="&zotero.tagSelector.loadingTags;"/>
						<label value="&zotero.tagSelector.noTagsToDisplay;"/>
					</deck>
				</vbox>
				
				<vbox id="tags-toggle" flex="1"/>
				
				<vbox id="tag-controls">
					<hbox>
						<!-- TODO: &zotero.tagSelector.filter; is now unused -->
						<textbox id="tags-search" flex="1" type="search" timeout="250" dir="reverse"
							oncommand="document.getBindingParent(this).handleKeyPress(); event.stopPropagation()"
							onkeypress="if (event.keyCode == event.DOM_VK_ESCAPE) { document.getBindingParent(this).handleKeyPress(true); }"/>
						<toolbarbutton id="view-settings-menu" tooltiptext="&zotero.toolbar.actions.label;"
								image="chrome://zotero/skin/tag-selector-menu.png" type="menu">
							<menupopup id="view-settings-popup"
									onpopupshown="/*
											This is necessary to fix a bug with Display All Tags not
											being checked if enabled before menuu is shown (OS X only?)
										*/
										document.getElementById('show-automatic').setAttribute('checked', document.getBindingParent(this).showAutomatic);
										document.getElementById('display-all-tags').setAttribute('checked', !document.getBindingParent(this).filterToScope);">
								<menuitem id="num-selected" disabled="true"/>
								<menuitem id="deselect-all" label="&zotero.tagSelector.clearAll;"
									oncommand="document.getBindingParent(this).clearAll(); event.stopPropagation();"/>
								<menuseparator/>
								<menuitem id="show-automatic" label="&zotero.tagSelector.showAutomatic;" type="checkbox"
									oncommand="var ts = document.getBindingParent(this);
										ts._dirty = true;
										var showAutomatic = this.getAttribute('checked') == 'true';
										ts.setAttribute('showAutomatic', showAutomatic);
										this.setAttribute('checked', showAutomatic);"/>
								<menuitem id="display-all-tags" label="&zotero.tagSelector.displayAllInLibrary;" type="checkbox"
									oncommand="var displayAll = this.getAttribute('checked') == 'true';
										this.setAttribute('checked', !displayAll);
										document.getBindingParent(this).filterToScope = !displayAll;
										event.stopPropagation();"/>
							</menupopup>
						</toolbarbutton>
					</hbox>
				</vbox>
			</groupbox>
		</content>
	</binding>
</bindings>
