<commit_before>/*
    ***** BEGIN LICENSE BLOCK *****
    
    Copyright (c) 2006  Center for History and New Media
                        George Mason University, Fairfax, Virginia, USA
                        http://chnm.gmu.edu
    
    Licensed under the Educational Community License, Version 1.0 (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.opensource.org/licenses/ecl1.php
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    
    ***** END LICENSE BLOCK *****
*/

/*
 * This object contains the various functions for the interface
 */
var ZoteroPane = new function()
{
	this.collectionsView = false;
	this.itemsView = false;
	
	//Privileged methods
	this.onLoad = onLoad;
	this.onUnload = onUnload;
	this.toggleDisplay = toggleDisplay;
	this.isShowing = isShowing;
	this.fullScreen = fullScreen;
	this.isFullScreen = isFullScreen;
	this.handleKeyDown = handleKeyDown;
	this.handleKeyUp = handleKeyUp;
	this.setHighlightedRowsCallback = setHighlightedRowsCallback;
	this.handleKeyPress = handleKeyPress;
	this.newItem = newItem;
	this.newCollection = newCollection;
	this.newSearch = newSearch;
	this.openAdvancedSearchWindow = openAdvancedSearchWindow;
	this.toggleTagSelector = toggleTagSelector;
	this.updateTagSelectorSize = updateTagSelectorSize;
	this.getTagSelection = getTagSelection;
	this.clearTagSelection = clearTagSelection;
	this.updateTagFilter = updateTagFilter;
	this.onCollectionSelected = onCollectionSelected;
	this.itemSelected = itemSelected;
	this.updateItemIndexedState = updateItemIndexedState;
	this.reindexItem = reindexItem;
	this.duplicateSelectedItem = duplicateSelectedItem;
	this.deleteSelectedItem = deleteSelectedItem;
	this.deleteSelectedCollection = deleteSelectedCollection;
	this.editSelectedCollection = editSelectedCollection;
	this.copySelectedItemsToClipboard = copySelectedItemsToClipboard;
	this.clearQuicksearch = clearQuicksearch;
	this.handleSearchKeypress = handleSearchKeypress;
	this.handleSearchInput = handleSearchInput;
	this.search = search;
	this.selectItem = selectItem;
	this.getSelectedCollection = getSelectedCollection;
	this.getSelectedSavedSearch = getSelectedSavedSearch;
	this.getSelectedItems = getSelectedItems;
	this.getSortedItems = getSortedItems;
	this.getSortField = getSortField;
	this.getSortDirection = getSortDirection;
	this.buildCollectionContextMenu = buildCollectionContextMenu;
	this.buildItemContextMenu = buildItemContextMenu;
	this.onDoubleClick = onDoubleClick;
	this.loadURI = loadURI;
	this.setItemsPaneMessage = setItemsPaneMessage;
	this.clearItemsPaneMessage = clearItemsPaneMessage;
	this.contextPopupShowing = contextPopupShowing;
	this.openNoteWindow = openNoteWindow;
	this.newNote = newNote;
	this.addTextToNote = addTextToNote;
	this.addItemFromPage = addItemFromPage;
	this.addAttachmentFromDialog = addAttachmentFromDialog;
	this.addAttachmentFromPage = addAttachmentFromPage;
	this.viewAttachment = viewAttachment;
	this.viewSelectedAttachment = viewSelectedAttachment;
	this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem;
	this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog;
	this.relinkAttachment = relinkAttachment;
	this.reportErrors = reportErrors;
	this.displayErrorMessage = displayErrorMessage;
	
	const DEFAULT_ZPANE_HEIGHT = 300;
	const COLLECTIONS_HEIGHT = 125; // minimum height of the collections pane and toolbar
	
	var self = this;
	
	/*
	 * Called when the window is open
	 */
	function onLoad()
	{
		if (!Zotero || !Zotero.initialized) {
			return;
		}
		
		if(Zotero.Prefs.get("zoteroPaneOnTop"))
		{
			var oldPane = document.getElementById('zotero-pane');
			var oldSplitter = document.getElementById('zotero-splitter');
			var appContent = document.getElementById('appcontent');
			
			var newPane = document.createElement('hbox');
			newPane.setAttribute('id','zotero-pane');
			newPane.setAttribute('persist','savedHeight');
			newPane.setAttribute('hidden', true);
			newPane.setAttribute('onkeydown', 'ZoteroPane.handleKeyDown(event, this.id)');
			newPane.setAttribute('onkeyup', 'ZoteroPane.handleKeyUp(event, this.id)');
			newPane.setAttribute('chromedir', '&locale.dir;');
			
			newPane.height = oldPane.height;
			while(oldPane.hasChildNodes())
				newPane.appendChild(oldPane.firstChild);
			appContent.removeChild(oldPane);
			appContent.insertBefore(newPane, document.getElementById('content'));
			
			var newSplitter = document.createElement('splitter');
			newSplitter.setAttribute('id','zotero-splitter');
			newSplitter.setAttribute('hidden', true);
			newSplitter.setAttribute('resizebefore','closest');
			newSplitter.setAttribute('resizeafter','closest');
			newSplitter.setAttribute('onmouseup', 'ZoteroPane.updateTagSelectorSize()');
			appContent.removeChild(oldSplitter);
			appContent.insertBefore(newSplitter, document.getElementById('content'));
			
			document.getElementById('zotero-tb-fullscreen').setAttribute('zoterotop','true');
		}
		
		Zotero.setFontSize(document.getElementById('zotero-pane'))
		
		if (Zotero.isMac) {
			document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false);
		}
		
		//Initialize collections view
		this.collectionsView = new Zotero.CollectionTreeView();
		var collectionsTree = document.getElementById('zotero-collections-tree');
		collectionsTree.view = this.collectionsView;
		collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree));
		
		var itemsTree = document.getElementById('zotero-items-tree');
		itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree));
		
		// Create the New Item (+) menu with each item type
		var addMenu = document.getElementById('zotero-tb-add').firstChild;
		var separator = document.getElementById('zotero-tb-add').firstChild.firstChild;
		var moreMenu = document.getElementById('zotero-tb-add-more');
		var itemTypes = Zotero.ItemTypes.getPrimaryTypes();
		for(var i = 0; i<itemTypes.length; i++)
		{
			var menuitem = document.createElement("menuitem");
			menuitem.setAttribute("label", Zotero.getString("itemTypes."+itemTypes[i]['name']));
			menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")");
			menuitem.setAttribute("tooltiptext", "");
			addMenu.insertBefore(menuitem, separator);
		}
		// Create submenu for secondary item types
		var itemTypes = Zotero.ItemTypes.getSecondaryTypes();
		for(var i = 0; i<itemTypes.length; i++)
		{
			var menuitem = document.createElement("menuitem");
			menuitem.setAttribute("label", Zotero.getString("itemTypes."+itemTypes[i]['name']));
			menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")");
			menuitem.setAttribute("tooltiptext", "");
			moreMenu.appendChild(menuitem);
		}
		
		var menu = document.getElementById("contentAreaContextMenu");
		menu.addEventListener("popupshowing", ZoteroPane.contextPopupShowing, false);
		
		Zotero.Keys.windowInit(document);
		
		// If the database was initialized and Zotero hasn't been run before
		// in this profile, display the Quick Start Guide -- this way the guide
		// won't be displayed they sync their DB to another profile or if
		// they the DB is initialized erroneously (e.g. while switching data
		// directory locations)
		if (Zotero.Schema.dbInitialized && Zotero.Prefs.get('firstRun')) {
			setTimeout(function () {
				gBrowser.selectedTab = gBrowser.addTab('http://www.zotero.org/documentation/quick_start_guide');
			}, 400);
			Zotero.Prefs.set('firstRun', false);
		}
	}
	
	
	/*
	 * Called when the window closes
	 */
	function onUnload()
	{
		if (!Zotero || !Zotero.initialized) {
			return;
		}
		
		var tagSelector = document.getElementById('zotero-tag-selector');
		tagSelector.unregister();
		
		this.collectionsView.unregister();
		if (this.itemsView)
			this.itemsView.unregister();
	}

	/*
	 * Hides/displays the Zotero interface
	 */
	function toggleDisplay()
	{
		var zoteroPane = document.getElementById('zotero-pane');
		var zoteroSplitter = document.getElementById('zotero-splitter')
		
		if (zoteroPane.getAttribute('hidden') == 'true') {
			var isHidden = true;
		}
		else if (zoteroPane.getAttribute('collapsed') == 'true') {
			var isCollapsed = true;
		}
		
		if (isHidden || isCollapsed) {
			var makeVisible = true;
		}
		
		// If Zotero not initialized, try to get the error handler
		// or load the default error page
		if (makeVisible && (!Zotero || !Zotero.initialized)) {
			if (Zotero) {
				var errMsg = Zotero.startupError;
				var errFunc = Zotero.startupErrorHandler;
			}
			
			if (!errMsg) {
				// Get the stringbundle manually
				var src = 'chrome://zotero/locale/zotero.properties';
				var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
						getService(Components.interfaces.nsILocaleService);
				var appLocale = localeService.getApplicationLocale();
				var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
					.getService(Components.interfaces.nsIStringBundleService);
				var stringBundle = stringBundleService.createBundle(src, appLocale);
				
				var errMsg = stringBundle.GetStringFromName('startupError');
			}
			
			if (errFunc) {
				errFunc();
			}
			else {
				// TODO: Add a better error page/window here with reporting
				// instructions
				// window.loadURI('chrome://zotero/content/error.xul');
				alert(errMsg);
			}
			
			return;
		}
		
		zoteroSplitter.setAttribute('hidden', !makeVisible);
		
		// Restore fullscreen mode if necessary
		var fullScreenMode = document.getElementById('zotero-tb-fullscreen').getAttribute('fullscreenmode') == 'true';
		if (makeVisible && fullScreenMode) {
			this.fullScreen(true);
		}
		
		if (zoteroPane.hasAttribute('savedHeight')) {
			var savedHeight = zoteroPane.getAttribute('savedHeight');
		}
		else {
			var savedHeight = DEFAULT_ZPANE_HEIGHT;
		}
		
		/*
		Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
		Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));
		Zotero.debug("zoteroPane.getAttribute('minheight'): " + zoteroPane.getAttribute('minheight'));
		Zotero.debug("savedHeight: " + savedHeight);
		*/
		
		if (makeVisible) {
			this.updateTagSelectorSize();
			
			var max = document.getElementById('appcontent').boxObject.height
						- zoteroSplitter.boxObject.height;
			
			if (isHidden) {
				zoteroPane.setAttribute('height', Math.min(savedHeight, max));
				zoteroPane.setAttribute('hidden', false);
			}
			else if (isCollapsed) {
				zoteroPane.setAttribute('height', Math.min(savedHeight, max));
				zoteroPane.setAttribute('collapsed', false);
			}
			
			// Focus the quicksearch on pane open
			setTimeout("document.getElementById('zotero-tb-search').inputField.select();", 1);
			
			if (Zotero.Prefs.get('sync.autoSync') && Zotero.Sync.Server.enabled) {
				setTimeout(function () {
					Zotero.Sync.Runner.sync();
				}, 1000);
			}
		}
		else {
			zoteroPane.setAttribute('collapsed', true);
			zoteroPane.height = 0;
			
			document.getElementById('content').setAttribute('collapsed', false);
			
			// Return focus to the browser content pane
			window.content.window.focus();
		}
	}
	
	
	function isShowing() {
		var zoteroPane = document.getElementById('zotero-pane');
		return zoteroPane.getAttribute('hidden') != 'true' &&
				zoteroPane.getAttribute('collapsed') != 'true';
	}
	
	
	function fullScreen(set)
	{
		var fs = document.getElementById('zotero-tb-fullscreen');
		
		if (set != undefined) {
			var makeFullScreen = set;
		}
		else {
			var makeFullScreen = fs.getAttribute('fullscreenmode') != 'true';
		}
		
		// Turn Z-pane flex on to stretch to window in full-screen, but off otherwise so persist works
		document.getElementById('zotero-pane').setAttribute('flex', makeFullScreen ? "1" : "0");
		document.getElementById('content').setAttribute('collapsed', makeFullScreen);
		document.getElementById('zotero-splitter').setAttribute('hidden', makeFullScreen);
		fs.setAttribute('fullscreenmode', makeFullScreen);
	}
	
	
	function isFullScreen() {
		var fs = document.getElementById('zotero-tb-fullscreen');
		return fs.getAttribute('fullscreenmode') == 'true'
	}
	
	
	/*
	 * Trigger actions based on keyboard shortcuts
	 */
	function handleKeyDown(event, from) {
		try {
			// Ignore keystrokes outside of Zotero pane
			if (!(event.originalTarget.ownerDocument instanceof XULDocument)) {
				return;
			}
		}
		catch (e) {
			Zotero.debug(e);
		}
		
		if (from == 'zotero-pane') {
			// Highlight collections containing selected items
			//
			// We use Control (17) on Windows because Alt triggers the menubar;
			// 	otherwise we use Alt/Option (18)
			if ((Zotero.isWin && event.keyCode == 17 && !event.altKey) ||
					(!Zotero.isWin && event.keyCode == 18 && !event.ctrlKey)
					&& !event.shiftKey && !event.metaKey) {
				
				this.highlightTimer = Components.classes["@mozilla.org/timer;1"].
					createInstance(Components.interfaces.nsITimer);
				// {} implements nsITimerCallback
				this.highlightTimer.initWithCallback({
					notify: ZoteroPane.setHighlightedRowsCallback
				}, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
			}
			else if ((Zotero.isWin && event.ctrlKey) ||
					(!Zotero.isWin && event.altKey)) {
				if (this.highlightTimer) {
					this.highlightTimer.cancel();
					this.highlightTimer = null;
				}
				ZoteroPane.collectionsView.setHighlightedRows();
			}
			
			return;
		}
		
		// Ignore keystrokes if Zotero pane is closed
		var zoteroPane = document.getElementById('zotero-pane');
		if (zoteroPane.getAttribute('hidden') == 'true' ||
				zoteroPane.getAttribute('collapsed') == 'true') {
			return;
		}
		
		var useShift = Zotero.isMac;
		
		var key = String.fromCharCode(event.which);
		if (!key) {
			Zotero.debug('No key');
			return;
		}
		
		// Ignore modifiers other than Ctrl-Alt or Cmd-Shift
		if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) &&
				(useShift ? event.shiftKey : event.altKey))) {
			return;
		}
		
		var command = Zotero.Keys.getCommand(key);
		if (!command) {
			return;
		}
		
		Zotero.debug(command);
		
		switch (command) {
			case 'openZotero':
				try {
					// Ignore Cmd-Shift-Z keystroke in text areas
					if (Zotero.isMac && key == 'Z' &&
							event.originalTarget.localName == 'textarea') {
						Zotero.debug('Ignoring keystroke in text area');
						return;
					}
				}
				catch (e) {
					Zotero.debug(e);
				}
				ZoteroPane.toggleDisplay()
				break;
			case 'library':
				document.getElementById('zotero-collections-tree').focus();
				ZoteroPane.collectionsView.selection.select(0);
				break;
			case 'quicksearch':
				document.getElementById('zotero-tb-search').select();
				break;
			case 'newItem':
				ZoteroPane.newItem(2); // book
				document.getElementById('zotero-editpane-type-menu').focus();
				break;
			case 'newNote':
				// Use key that's not the modifier as the popup toggle
				ZoteroPane.newNote(useShift ? event.altKey : event.shiftKey);
				break;
			case 'toggleTagSelector':
				ZoteroPane.toggleTagSelector();
				break;
			case 'toggleFullscreen':
				ZoteroPane.fullScreen();
				break;
			case 'copySelectedItemCitationsToClipboard':
				ZoteroPane.copySelectedItemsToClipboard(true)
				break;
			case 'copySelectedItemsToClipboard':
				ZoteroPane.copySelectedItemsToClipboard();
				break;
			default:
				throw ('Command "' + command + '" not found in ZoteroPane.handleKeyDown()');
		}
		
		event.preventDefault();
	}
	
	
	function handleKeyUp(event, from) {
		if (from == 'zotero-pane') {
			if ((Zotero.isWin && event.keyCode == 17) ||
					(!Zotero.isWin && event.keyCode == 18)) {
				if (this.highlightTimer) {
					this.highlightTimer.cancel();
					this.highlightTimer = null;
				}
				ZoteroPane.collectionsView.setHighlightedRows();
			}
		}
	}
	
	
	/*
	 * Highlights collections containing selected items on Ctrl (Win) or
	 * Option/Alt (Mac/Linux) press
	 */
	function setHighlightedRowsCallback() {
		var itemIDs = ZoteroPane.getSelectedItems(true);
		if (itemIDs && itemIDs.length) {
			var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true);
			if (collectionIDs) {
				ZoteroPane.collectionsView.setHighlightedRows(collectionIDs);
			}
		}
	}
	
	
	function handleKeyPress(event, from) {
		if (from == 'zotero-collections-tree') {
			if (event.keyCode == event.DOM_VK_BACK_SPACE ||
					event.keyCode == event.DOM_VK_DELETE) {
				ZoteroPane.deleteSelectedCollection();
				event.preventDefault();
				return;
			}
		}
		else if (from == 'zotero-items-tree') {
			if (event.keyCode == event.DOM_VK_BACK_SPACE ||
					event.keyCode == event.DOM_VK_DELETE) {
				// If Cmd or Ctrl delete, delete from Library (with prompt)
				var fromDB = event.metaKey || (!Zotero.isMac && event.ctrlKey);
				ZoteroPane.deleteSelectedItem(fromDB);
				event.preventDefault();
				return;
			}
		}
	}
	
	
	/*
	 * Create a new item
	 *
	 * _data_ is an optional object with field:value for itemData
	 */
	function newItem(typeID, data)
	{
		if (!Zotero.stateCheck()) {
			this.displayErrorMessage(true);
			return false;
		}
		
		var item = new Zotero.Item(false, typeID);
		
		for (var i in data)
		{
			item.setField(i, data[i]);
		}
		
		item.save();
		
		if (this.itemsView && this.itemsView._itemGroup.isCollection()) {
			this.itemsView._itemGroup.ref.addItem(item.id);
		}
		
		//set to Info tab
		document.getElementById('zotero-view-item').selectedIndex = 0;
		
		this.selectItem(item.id);
		
		return item;
	}
	
	function newCollection(parent)
	{
		var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
								.getService(Components.interfaces.nsIPromptService);
		
		var untitled = Zotero.DB.getNextName('collections', 'collectionName',
			Zotero.getString('pane.collections.untitled'));
		
		var newName = { value: untitled };
		var result = promptService.prompt(window,
			Zotero.getString('pane.collections.newCollection'),
			Zotero.getString('pane.collections.name'), newName, "", {});
		
		if (!result)
		{
			return;
		}
		
		if (!newName.value)
		{
			newName.value = untitled;
		}
		
		var collection = new Zotero.Collection;
		collection.name = newName.value;
		collection.parent = parent;
		collection.save();
	}
	
	function newSearch()
	{
		var s = new Zotero.Search();
		s.addCondition('title', 'contains', '');
		
		var untitled = Zotero.getString('pane.collections.untitled');
		untitled = Zotero.DB.getNextName('savedSearches', 'savedSearchName',
			Zotero.getString('pane.collections.untitled'));
		var io = {dataIn: {search: s, name: untitled}, dataOut: null};
		window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io);
	}
	
	
	function openAdvancedSearchWindow() {
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
					.getService(Components.interfaces.nsIWindowMediator);
		var enumerator = wm.getEnumerator('zotero:search');
		while (enumerator.hasMoreElements()) {
			var win = enumerator.getNext();
		}
		
		if (win) {
			win.focus();
			return;
		}
		
		var s = new Zotero.Search();
		s.addCondition('title', 'contains', '');
		var io = {dataIn: {search: s}, dataOut: null};
		window.openDialog('chrome://zotero/content/advancedSearch.xul', '', 'chrome,dialog=no,centerscreen', io);
	}
	
	
	function toggleTagSelector(){
		var zoteroPane = document.getElementById('zotero-pane');
		var splitter = document.getElementById('zotero-tags-splitter');
		var tagSelector = document.getElementById('zotero-tag-selector');
		
		var showing = tagSelector.getAttribute('collapsed') == 'true';
		tagSelector.setAttribute('collapsed', !showing);
		splitter.setAttribute('collapsed', !showing);
		this.updateTagSelectorSize();
		
		// If showing, set scope to items in current view
		// and focus filter textbox
		if (showing) {
			_setTagScope();
			tagSelector.focusTextbox();
		}
		// If hiding, clear selection
		else {
			tagSelector.uninit();
		}
	}
	
	
	function updateTagSelectorSize() {
		//Zotero.debug('Updating tag selector size');
		var zoteroPane = document.getElementById('zotero-pane');
		var splitter = document.getElementById('zotero-tags-splitter');
		var tagSelector = document.getElementById('zotero-tag-selector');
		
		// Nothing should be bigger than appcontent's height
		var max = document.getElementById('appcontent').boxObject.height
					- splitter.boxObject.height;
		
		// Shrink tag selector to appcontent's height
		var maxTS = max - COLLECTIONS_HEIGHT;
		if (parseInt(tagSelector.getAttribute("height")) > maxTS) {
			//Zotero.debug("Limiting tag selector height to appcontent");
			tagSelector.setAttribute('height', maxTS);
		}
		
		var height = tagSelector.boxObject.height;
		
		/*
		Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height);
		Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height'));
		Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
		Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));
		*/
		
		// Don't let the Z-pane jump back down to its previous height
		// (if shrinking or hiding the tag selector let it clear the min-height)
		if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) {
			//Zotero.debug("Setting Zotero pane height attribute to " +  zoteroPane.boxObject.height);
			zoteroPane.setAttribute('height', zoteroPane.boxObject.height);
		}
		
		if (tagSelector.getAttribute('collapsed') == 'true') {
			// 32px is the default Z pane min-height in overlay.css
			height = 32;
		}
		else {
			// tS.boxObject.height doesn't exist at startup, so get from attribute
			if (!height) {
				height = parseInt(tagSelector.getAttribute('height'));
			}
			// 121px seems to be enough room for the toolbar and collections
			// tree at minimum height
			height = height + 125;
		}
		
		//Zotero.debug('Setting Zotero pane minheight to ' + height);
		zoteroPane.setAttribute('minheight', height);
		
		if (this.isShowing() && !this.isFullScreen()) {
			zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height);
		}
		
		// Fix bug whereby resizing the Z pane downward after resizing
		// the tag selector up and then down sometimes caused the Z pane to
		// stay at a fixed size and get pushed below the bottom
		tagSelector.height++;
		tagSelector.height--;
	}
	
	
	function getTagSelection(){
		var tagSelector = document.getElementById('zotero-tag-selector');
		return tagSelector.selection ? tagSelector.selection : {};
	}
	
	
	function clearTagSelection() {
		if (Zotero.hasValues(this.getTagSelection())) {
			var tagSelector = document.getElementById('zotero-tag-selector');
			tagSelector.clearAll();
		}
	}
	
	
	/*
	 * Sets the tag filter on the items view
	 */
	function updateTagFilter(){
		this.itemsView.setFilter('tags', getTagSelection());
	}
	
	
	/*
	 * Set the tags scope to the items in the current view
	 *
	 * Passed to the items tree to trigger on changes
	 */
	function _setTagScope() {
		var itemgroup = self.collectionsView.
			_getItemAtRow(self.collectionsView.selection.currentIndex);
		var tagSelector = document.getElementById('zotero-tag-selector');
		if (!tagSelector.getAttribute('collapsed') ||
				tagSelector.getAttribute('collapsed') == 'false') {
			Zotero.debug('Updating tag selector with current tags');
			tagSelector.scope = itemgroup.getChildTags();
		}
	}
	
	
	function onCollectionSelected()
	{
		if (this.itemsView)
		{
			this.itemsView.unregister();
		}
		
		document.getElementById('zotero-tb-search').value = ""; 
		
		if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) {
			var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
			itemgroup.setSearch('');
			itemgroup.setTags(getTagSelection());
			
			try {
				Zotero.UnresponsiveScriptIndicator.disable();
				this.itemsView = new Zotero.ItemTreeView(itemgroup);
				this.itemsView.addCallback(_setTagScope);
				document.getElementById('zotero-items-tree').view = this.itemsView;
				this.itemsView.selection.clearSelection();
			}
			finally {
				Zotero.UnresponsiveScriptIndicator.enable();
			}
			
			if (itemgroup.isLibrary()) {
				Zotero.Prefs.set('lastViewedFolder', 'L');
			}
			if (itemgroup.isCollection()) {
				Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id);
			}
			else if (itemgroup.isSearch()) {
				Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id);
			}
		}
		else
		{
			document.getElementById('zotero-items-tree').view = this.itemsView = null;
		}
	}
	
	function itemSelected()
	{
		if (!Zotero.stateCheck()) {
			this.displayErrorMessage();
			return;
		}
		
		if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1)
		{
			var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
			
			if(item.ref.isNote()) {
				var noteEditor = document.getElementById('zotero-note-editor');
				if (this.itemsView.readOnly) {
					noteEditor.mode = 'view';
				}
				else {
					noteEditor.mode = 'edit';
				}
				
				// If loading new or different note, disable undo while we repopulate the text field
				// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
				// undo content from another note into the current one.
				if (!noteEditor.item || noteEditor.item.id != item.ref.id) {
					noteEditor.disableUndo();
				}
				noteEditor.parent = null;
				noteEditor.item = item.ref;
				
				noteEditor.enableUndo();
				
				document.getElementById('zotero-view-note-button').setAttribute('noteID',item.ref.id);
				if(item.ref.getSource())
				{
					document.getElementById('zotero-view-note-button').setAttribute('sourceID',item.ref.getSource());
				}
				else
				{
					document.getElementById('zotero-view-note-button').removeAttribute('sourceID');
				}
				document.getElementById('zotero-item-pane-content').selectedIndex = 2;
			}
			
			else if(item.ref.isAttachment()) {
				// DEBUG: this is annoying -- we really want to use an abstracted
				// version of createValueElement() from itemPane.js
				// (ideally in an XBL binding)
				
				// Wrap title to multiple lines if necessary
				var label = document.getElementById('zotero-attachment-label');
				while (label.hasChildNodes())
				{
					label.removeChild(label.firstChild);
				}
				var val = item.getField('title');
				
				var firstSpace = val.indexOf(" ");
				// Crop long uninterrupted text
				if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29)
				{
					label.setAttribute('crop', 'end');
					label.setAttribute('value', val);
				}
				// Create a <description> element, essentially
				else
				{
					label.removeAttribute('value');
					label.appendChild(document.createTextNode(val));
				}
				
				// For the time being, use a silly little popup
				label.className = 'zotero-clicky';
				label.onclick = function(event){
					var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
							.getService(Components.interfaces.nsIPromptService);
					
					var newTitle = { value: val };
					var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') };
					
					while (true) {
						// Don't show "Rename associated file" option for
						// linked URLs
						if (item.ref.getAttachmentLinkMode() ==
								Zotero.Attachments.LINK_MODE_LINKED_URL) {
							var result = nsIPS.prompt(
								window,
								'',
								Zotero.getString('pane.item.attachments.rename.title'),
								newTitle,
								null,
								{}
							);
							
							// If they hit cancel or left it blank
							if (!result || !newTitle.value) {
								return;
							}
							
							break;
						}
						
						var result = nsIPS.prompt(
							window,
							'',
							Zotero.getString('pane.item.attachments.rename.title'),
							newTitle,
							Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'),
							checkState
						);
						
						// If they hit cancel or left it blank
						if (!result || !newTitle.value) {
							return;
						}
						
						Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value);
						
						// Rename associated file
						if (checkState.value) {
							var renamed = item.ref.renameAttachmentFile(newTitle.value);
							if (renamed == -1) {
								var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?');
								if (confirmed) {
									item.ref.renameAttachmentFile(newTitle.value, true);
									break;
								}
								// If they said not to overwrite existing file,
								// start again
								continue;
							}
							else if (renamed == -2 || !renamed) {
								alert(Zotero.getString('pane.item.attachments.rename.error'));
								return;
							}
						}
						
						break;
					}
					
					if (newTitle.value != val) {
						item.ref.setField('title', newTitle.value);
						item.ref.save();
					}
				}
				
				
				var isImportedURL = item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_IMPORTED_URL;
				
				// Metadata for URL's
				if (item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL
					|| isImportedURL)
				{
					// "View Page"/"View Snapshot" label
					if (isImportedURL)
					{
						var str = Zotero.getString('pane.item.attachments.view.snapshot');
					}
					else
					{
						var str = Zotero.getString('pane.item.attachments.view.link');
					}
					
					document.getElementById('zotero-attachment-show').setAttribute('hidden', !isImportedURL);
					
					// URL
					document.getElementById('zotero-attachment-url').setAttribute('value', item.getField('url'));
					document.getElementById('zotero-attachment-url').setAttribute('hidden', false);
					// Access date
					document.getElementById('zotero-attachment-accessed').setAttribute('value',
						Zotero.getString('itemFields.accessDate') + ': '
						+ Zotero.Date.sqlToDate(item.getField('accessDate'), true).toLocaleString());
					document.getElementById('zotero-attachment-accessed').setAttribute('hidden', false);
				}
				// Metadata for files
				else
				{
					var str = Zotero.getString('pane.item.attachments.view.file');
					document.getElementById('zotero-attachment-show').setAttribute('hidden', false);
					document.getElementById('zotero-attachment-url').setAttribute('hidden', true);
					document.getElementById('zotero-attachment-accessed').setAttribute('hidden', true);
				}
				
				document.getElementById('zotero-attachment-view').setAttribute('label', str);
				
				// Display page count
				var pages = Zotero.Fulltext.getPages(item.ref.id);
				var pages = pages ? pages.total : null;
				var pagesRow = document.getElementById('zotero-attachment-pages');
				if (pages) {
					var str = Zotero.getString('itemFields.pages') + ': ' + pages;
					pagesRow.setAttribute('value', str);
					pagesRow.setAttribute('hidden', false);
				}
				else {
					pagesRow.setAttribute('hidden', true);
				}
				
				this.updateItemIndexedState();
				
				var noteEditor = document.getElementById('zotero-attachment-note-editor');
				noteEditor.mode = 'edit';
				noteEditor.parent = null;
				noteEditor.item = item.ref;
				
				document.getElementById('zotero-item-pane-content').selectedIndex = 3;
			}
			
			// Regular item
			else
			{
				ZoteroItemPane.viewItem(item.ref, this.itemsView.readOnly ? 'view' : false);
				document.getElementById('zotero-item-pane-content').selectedIndex = 1;
			}
		}
		else
		{
			document.getElementById('zotero-item-pane-content').selectedIndex = 0;
			
			var label = document.getElementById('zotero-view-selected-label');
			
			if (this.itemsView && this.itemsView.selection.count) {
				label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count);
			}
			else {
				label.value = Zotero.getString('pane.item.selected.zero');
			}
		}

	}
	
	
	/*
	 * Update Indexed: (Yes|No|Partial) line in attachment info pane
	 */
	function updateItemIndexedState() {
		var indexBox = document.getElementById('zotero-attachment-index-box');
		var indexStatus = document.getElementById('zotero-attachment-index-status');
		var reindexButton = document.getElementById('zotero-attachment-reindex');
		
		var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
		var status = Zotero.Fulltext.getIndexedState(item.ref.id);
		var str = 'fulltext.indexState.';
		switch (status) {
			case Zotero.Fulltext.INDEX_STATE_UNAVAILABLE:
				str += 'unavailable';
				break;
			case Zotero.Fulltext.INDEX_STATE_UNINDEXED:
				str = 'general.no';
				break;
			case Zotero.Fulltext.INDEX_STATE_PARTIAL:
				str += 'partial';
				break;
			case Zotero.Fulltext.INDEX_STATE_INDEXED:
				str = 'general.yes';
				break;
		}
		str = Zotero.getString('fulltext.indexState.indexed') + ': ' +
			Zotero.getString(str);
		indexStatus.setAttribute('value', str);
		
		// Reindex button tooltip (string stored in zotero.properties)
		var str = Zotero.getString('pane.items.menu.reindexItem');
		reindexButton.setAttribute('tooltiptext', str);
		
		if (Zotero.Fulltext.canReindex(item.ref.id)) {
			reindexButton.setAttribute('hidden', false);
		}
		else {
			reindexButton.setAttribute('hidden', true);
		}
	}
	
	
	function reindexItem() {
		var items = this.getSelectedItems();
		if (!items) {
			return;
		}
		
		for (var i=0; i<items.length; i++) {
			if (!items[i].isAttachment()) {
				continue;
			}
			var itemID = items[i].id;
			Zotero.Fulltext.indexItems(itemID, true);
		}
		this.updateItemIndexedState();
	}
	
	
	function duplicateSelectedItem() {
		var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
										.getService(Components.interfaces.nsIPromptService);
		ps.alert(null, "Error", "Item duplication is not available in this Zotero release.");
		return;
		
		var newItem = this.getSelectedItems()[0].clone();
		var newItemID = newItem.save()<commit_msg>Disable duplication only for tagged items, not all items<commit_after>/*
    ***** BEGIN LICENSE BLOCK *****
    
    Copyright (c) 2006  Center for History and New Media
                        George Mason University, Fairfax, Virginia, USA
                        http://chnm.gmu.edu
    
    Licensed under the Educational Community License, Version 1.0 (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.opensource.org/licenses/ecl1.php
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    
    ***** END LICENSE BLOCK *****
*/

/*
 * This object contains the various functions for the interface
 */
var ZoteroPane = new function()
{
	this.collectionsView = false;
	this.itemsView = false;
	
	//Privileged methods
	this.onLoad = onLoad;
	this.onUnload = onUnload;
	this.toggleDisplay = toggleDisplay;
	this.isShowing = isShowing;
	this.fullScreen = fullScreen;
	this.isFullScreen = isFullScreen;
	this.handleKeyDown = handleKeyDown;
	this.handleKeyUp = handleKeyUp;
	this.setHighlightedRowsCallback = setHighlightedRowsCallback;
	this.handleKeyPress = handleKeyPress;
	this.newItem = newItem;
	this.newCollection = newCollection;
	this.newSearch = newSearch;
	this.openAdvancedSearchWindow = openAdvancedSearchWindow;
	this.toggleTagSelector = toggleTagSelector;
	this.updateTagSelectorSize = updateTagSelectorSize;
	this.getTagSelection = getTagSelection;
	this.clearTagSelection = clearTagSelection;
	this.updateTagFilter = updateTagFilter;
	this.onCollectionSelected = onCollectionSelected;
	this.itemSelected = itemSelected;
	this.updateItemIndexedState = updateItemIndexedState;
	this.reindexItem = reindexItem;
	this.duplicateSelectedItem = duplicateSelectedItem;
	this.deleteSelectedItem = deleteSelectedItem;
	this.deleteSelectedCollection = deleteSelectedCollection;
	this.editSelectedCollection = editSelectedCollection;
	this.copySelectedItemsToClipboard = copySelectedItemsToClipboard;
	this.clearQuicksearch = clearQuicksearch;
	this.handleSearchKeypress = handleSearchKeypress;
	this.handleSearchInput = handleSearchInput;
	this.search = search;
	this.selectItem = selectItem;
	this.getSelectedCollection = getSelectedCollection;
	this.getSelectedSavedSearch = getSelectedSavedSearch;
	this.getSelectedItems = getSelectedItems;
	this.getSortedItems = getSortedItems;
	this.getSortField = getSortField;
	this.getSortDirection = getSortDirection;
	this.buildCollectionContextMenu = buildCollectionContextMenu;
	this.buildItemContextMenu = buildItemContextMenu;
	this.onDoubleClick = onDoubleClick;
	this.loadURI = loadURI;
	this.setItemsPaneMessage = setItemsPaneMessage;
	this.clearItemsPaneMessage = clearItemsPaneMessage;
	this.contextPopupShowing = contextPopupShowing;
	this.openNoteWindow = openNoteWindow;
	this.newNote = newNote;
	this.addTextToNote = addTextToNote;
	this.addItemFromPage = addItemFromPage;
	this.addAttachmentFromDialog = addAttachmentFromDialog;
	this.addAttachmentFromPage = addAttachmentFromPage;
	this.viewAttachment = viewAttachment;
	this.viewSelectedAttachment = viewSelectedAttachment;
	this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem;
	this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog;
	this.relinkAttachment = relinkAttachment;
	this.reportErrors = reportErrors;
	this.displayErrorMessage = displayErrorMessage;
	
	const DEFAULT_ZPANE_HEIGHT = 300;
	const COLLECTIONS_HEIGHT = 125; // minimum height of the collections pane and toolbar
	
	var self = this;
	
	/*
	 * Called when the window is open
	 */
	function onLoad()
	{
		if (!Zotero || !Zotero.initialized) {
			return;
		}
		
		if(Zotero.Prefs.get("zoteroPaneOnTop"))
		{
			var oldPane = document.getElementById('zotero-pane');
			var oldSplitter = document.getElementById('zotero-splitter');
			var appContent = document.getElementById('appcontent');
			
			var newPane = document.createElement('hbox');
			newPane.setAttribute('id','zotero-pane');
			newPane.setAttribute('persist','savedHeight');
			newPane.setAttribute('hidden', true);
			newPane.setAttribute('onkeydown', 'ZoteroPane.handleKeyDown(event, this.id)');
			newPane.setAttribute('onkeyup', 'ZoteroPane.handleKeyUp(event, this.id)');
			newPane.setAttribute('chromedir', '&locale.dir;');
			
			newPane.height = oldPane.height;
			while(oldPane.hasChildNodes())
				newPane.appendChild(oldPane.firstChild);
			appContent.removeChild(oldPane);
			appContent.insertBefore(newPane, document.getElementById('content'));
			
			var newSplitter = document.createElement('splitter');
			newSplitter.setAttribute('id','zotero-splitter');
			newSplitter.setAttribute('hidden', true);
			newSplitter.setAttribute('resizebefore','closest');
			newSplitter.setAttribute('resizeafter','closest');
			newSplitter.setAttribute('onmouseup', 'ZoteroPane.updateTagSelectorSize()');
			appContent.removeChild(oldSplitter);
			appContent.insertBefore(newSplitter, document.getElementById('content'));
			
			document.getElementById('zotero-tb-fullscreen').setAttribute('zoterotop','true');
		}
		
		Zotero.setFontSize(document.getElementById('zotero-pane'))
		
		if (Zotero.isMac) {
			document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false);
		}
		
		//Initialize collections view
		this.collectionsView = new Zotero.CollectionTreeView();
		var collectionsTree = document.getElementById('zotero-collections-tree');
		collectionsTree.view = this.collectionsView;
		collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree));
		
		var itemsTree = document.getElementById('zotero-items-tree');
		itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree));
		
		// Create the New Item (+) menu with each item type
		var addMenu = document.getElementById('zotero-tb-add').firstChild;
		var separator = document.getElementById('zotero-tb-add').firstChild.firstChild;
		var moreMenu = document.getElementById('zotero-tb-add-more');
		var itemTypes = Zotero.ItemTypes.getPrimaryTypes();
		for(var i = 0; i<itemTypes.length; i++)
		{
			var menuitem = document.createElement("menuitem");
			menuitem.setAttribute("label", Zotero.getString("itemTypes."+itemTypes[i]['name']));
			menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")");
			menuitem.setAttribute("tooltiptext", "");
			addMenu.insertBefore(menuitem, separator);
		}
		// Create submenu for secondary item types
		var itemTypes = Zotero.ItemTypes.getSecondaryTypes();
		for(var i = 0; i<itemTypes.length; i++)
		{
			var menuitem = document.createElement("menuitem");
			menuitem.setAttribute("label", Zotero.getString("itemTypes."+itemTypes[i]['name']));
			menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")");
			menuitem.setAttribute("tooltiptext", "");
			moreMenu.appendChild(menuitem);
		}
		
		var menu = document.getElementById("contentAreaContextMenu");
		menu.addEventListener("popupshowing", ZoteroPane.contextPopupShowing, false);
		
		Zotero.Keys.windowInit(document);
		
		// If the database was initialized and Zotero hasn't been run before
		// in this profile, display the Quick Start Guide -- this way the guide
		// won't be displayed they sync their DB to another profile or if
		// they the DB is initialized erroneously (e.g. while switching data
		// directory locations)
		if (Zotero.Schema.dbInitialized && Zotero.Prefs.get('firstRun')) {
			setTimeout(function () {
				gBrowser.selectedTab = gBrowser.addTab('http://www.zotero.org/documentation/quick_start_guide');
			}, 400);
			Zotero.Prefs.set('firstRun', false);
		}
	}
	
	
	/*
	 * Called when the window closes
	 */
	function onUnload()
	{
		if (!Zotero || !Zotero.initialized) {
			return;
		}
		
		var tagSelector = document.getElementById('zotero-tag-selector');
		tagSelector.unregister();
		
		this.collectionsView.unregister();
		if (this.itemsView)
			this.itemsView.unregister();
	}

	/*
	 * Hides/displays the Zotero interface
	 */
	function toggleDisplay()
	{
		var zoteroPane = document.getElementById('zotero-pane');
		var zoteroSplitter = document.getElementById('zotero-splitter')
		
		if (zoteroPane.getAttribute('hidden') == 'true') {
			var isHidden = true;
		}
		else if (zoteroPane.getAttribute('collapsed') == 'true') {
			var isCollapsed = true;
		}
		
		if (isHidden || isCollapsed) {
			var makeVisible = true;
		}
		
		// If Zotero not initialized, try to get the error handler
		// or load the default error page
		if (makeVisible && (!Zotero || !Zotero.initialized)) {
			if (Zotero) {
				var errMsg = Zotero.startupError;
				var errFunc = Zotero.startupErrorHandler;
			}
			
			if (!errMsg) {
				// Get the stringbundle manually
				var src = 'chrome://zotero/locale/zotero.properties';
				var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
						getService(Components.interfaces.nsILocaleService);
				var appLocale = localeService.getApplicationLocale();
				var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
					.getService(Components.interfaces.nsIStringBundleService);
				var stringBundle = stringBundleService.createBundle(src, appLocale);
				
				var errMsg = stringBundle.GetStringFromName('startupError');
			}
			
			if (errFunc) {
				errFunc();
			}
			else {
				// TODO: Add a better error page/window here with reporting
				// instructions
				// window.loadURI('chrome://zotero/content/error.xul');
				alert(errMsg);
			}
			
			return;
		}
		
		zoteroSplitter.setAttribute('hidden', !makeVisible);
		
		// Restore fullscreen mode if necessary
		var fullScreenMode = document.getElementById('zotero-tb-fullscreen').getAttribute('fullscreenmode') == 'true';
		if (makeVisible && fullScreenMode) {
			this.fullScreen(true);
		}
		
		if (zoteroPane.hasAttribute('savedHeight')) {
			var savedHeight = zoteroPane.getAttribute('savedHeight');
		}
		else {
			var savedHeight = DEFAULT_ZPANE_HEIGHT;
		}
		
		/*
		Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
		Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));
		Zotero.debug("zoteroPane.getAttribute('minheight'): " + zoteroPane.getAttribute('minheight'));
		Zotero.debug("savedHeight: " + savedHeight);
		*/
		
		if (makeVisible) {
			this.updateTagSelectorSize();
			
			var max = document.getElementById('appcontent').boxObject.height
						- zoteroSplitter.boxObject.height;
			
			if (isHidden) {
				zoteroPane.setAttribute('height', Math.min(savedHeight, max));
				zoteroPane.setAttribute('hidden', false);
			}
			else if (isCollapsed) {
				zoteroPane.setAttribute('height', Math.min(savedHeight, max));
				zoteroPane.setAttribute('collapsed', false);
			}
			
			// Focus the quicksearch on pane open
			setTimeout("document.getElementById('zotero-tb-search').inputField.select();", 1);
			
			if (Zotero.Prefs.get('sync.autoSync') && Zotero.Sync.Server.enabled) {
				setTimeout(function () {
					Zotero.Sync.Runner.sync();
				}, 1000);
			}
		}
		else {
			zoteroPane.setAttribute('collapsed', true);
			zoteroPane.height = 0;
			
			document.getElementById('content').setAttribute('collapsed', false);
			
			// Return focus to the browser content pane
			window.content.window.focus();
		}
	}
	
	
	function isShowing() {
		var zoteroPane = document.getElementById('zotero-pane');
		return zoteroPane.getAttribute('hidden') != 'true' &&
				zoteroPane.getAttribute('collapsed') != 'true';
	}
	
	
	function fullScreen(set)
	{
		var fs = document.getElementById('zotero-tb-fullscreen');
		
		if (set != undefined) {
			var makeFullScreen = set;
		}
		else {
			var makeFullScreen = fs.getAttribute('fullscreenmode') != 'true';
		}
		
		// Turn Z-pane flex on to stretch to window in full-screen, but off otherwise so persist works
		document.getElementById('zotero-pane').setAttribute('flex', makeFullScreen ? "1" : "0");
		document.getElementById('content').setAttribute('collapsed', makeFullScreen);
		document.getElementById('zotero-splitter').setAttribute('hidden', makeFullScreen);
		fs.setAttribute('fullscreenmode', makeFullScreen);
	}
	
	
	function isFullScreen() {
		var fs = document.getElementById('zotero-tb-fullscreen');
		return fs.getAttribute('fullscreenmode') == 'true'
	}
	
	
	/*
	 * Trigger actions based on keyboard shortcuts
	 */
	function handleKeyDown(event, from) {
		try {
			// Ignore keystrokes outside of Zotero pane
			if (!(event.originalTarget.ownerDocument instanceof XULDocument)) {
				return;
			}
		}
		catch (e) {
			Zotero.debug(e);
		}
		
		if (from == 'zotero-pane') {
			// Highlight collections containing selected items
			//
			// We use Control (17) on Windows because Alt triggers the menubar;
			// 	otherwise we use Alt/Option (18)
			if ((Zotero.isWin && event.keyCode == 17 && !event.altKey) ||
					(!Zotero.isWin && event.keyCode == 18 && !event.ctrlKey)
					&& !event.shiftKey && !event.metaKey) {
				
				this.highlightTimer = Components.classes["@mozilla.org/timer;1"].
					createInstance(Components.interfaces.nsITimer);
				// {} implements nsITimerCallback
				this.highlightTimer.initWithCallback({
					notify: ZoteroPane.setHighlightedRowsCallback
				}, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
			}
			else if ((Zotero.isWin && event.ctrlKey) ||
					(!Zotero.isWin && event.altKey)) {
				if (this.highlightTimer) {
					this.highlightTimer.cancel();
					this.highlightTimer = null;
				}
				ZoteroPane.collectionsView.setHighlightedRows();
			}
			
			return;
		}
		
		// Ignore keystrokes if Zotero pane is closed
		var zoteroPane = document.getElementById('zotero-pane');
		if (zoteroPane.getAttribute('hidden') == 'true' ||
				zoteroPane.getAttribute('collapsed') == 'true') {
			return;
		}
		
		var useShift = Zotero.isMac;
		
		var key = String.fromCharCode(event.which);
		if (!key) {
			Zotero.debug('No key');
			return;
		}
		
		// Ignore modifiers other than Ctrl-Alt or Cmd-Shift
		if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) &&
				(useShift ? event.shiftKey : event.altKey))) {
			return;
		}
		
		var command = Zotero.Keys.getCommand(key);
		if (!command) {
			return;
		}
		
		Zotero.debug(command);
		
		switch (command) {
			case 'openZotero':
				try {
					// Ignore Cmd-Shift-Z keystroke in text areas
					if (Zotero.isMac && key == 'Z' &&
							event.originalTarget.localName == 'textarea') {
						Zotero.debug('Ignoring keystroke in text area');
						return;
					}
				}
				catch (e) {
					Zotero.debug(e);
				}
				ZoteroPane.toggleDisplay()
				break;
			case 'library':
				document.getElementById('zotero-collections-tree').focus();
				ZoteroPane.collectionsView.selection.select(0);
				break;
			case 'quicksearch':
				document.getElementById('zotero-tb-search').select();
				break;
			case 'newItem':
				ZoteroPane.newItem(2); // book
				document.getElementById('zotero-editpane-type-menu').focus();
				break;
			case 'newNote':
				// Use key that's not the modifier as the popup toggle
				ZoteroPane.newNote(useShift ? event.altKey : event.shiftKey);
				break;
			case 'toggleTagSelector':
				ZoteroPane.toggleTagSelector();
				break;
			case 'toggleFullscreen':
				ZoteroPane.fullScreen();
				break;
			case 'copySelectedItemCitationsToClipboard':
				ZoteroPane.copySelectedItemsToClipboard(true)
				break;
			case 'copySelectedItemsToClipboard':
				ZoteroPane.copySelectedItemsToClipboard();
				break;
			default:
				throw ('Command "' + command + '" not found in ZoteroPane.handleKeyDown()');
		}
		
		event.preventDefault();
	}
	
	
	function handleKeyUp(event, from) {
		if (from == 'zotero-pane') {
			if ((Zotero.isWin && event.keyCode == 17) ||
					(!Zotero.isWin && event.keyCode == 18)) {
				if (this.highlightTimer) {
					this.highlightTimer.cancel();
					this.highlightTimer = null;
				}
				ZoteroPane.collectionsView.setHighlightedRows();
			}
		}
	}
	
	
	/*
	 * Highlights collections containing selected items on Ctrl (Win) or
	 * Option/Alt (Mac/Linux) press
	 */
	function setHighlightedRowsCallback() {
		var itemIDs = ZoteroPane.getSelectedItems(true);
		if (itemIDs && itemIDs.length) {
			var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true);
			if (collectionIDs) {
				ZoteroPane.collectionsView.setHighlightedRows(collectionIDs);
			}
		}
	}
	
	
	function handleKeyPress(event, from) {
		if (from == 'zotero-collections-tree') {
			if (event.keyCode == event.DOM_VK_BACK_SPACE ||
					event.keyCode == event.DOM_VK_DELETE) {
				ZoteroPane.deleteSelectedCollection();
				event.preventDefault();
				return;
			}
		}
		else if (from == 'zotero-items-tree') {
			if (event.keyCode == event.DOM_VK_BACK_SPACE ||
					event.keyCode == event.DOM_VK_DELETE) {
				// If Cmd or Ctrl delete, delete from Library (with prompt)
				var fromDB = event.metaKey || (!Zotero.isMac && event.ctrlKey);
				ZoteroPane.deleteSelectedItem(fromDB);
				event.preventDefault();
				return;
			}
		}
	}
	
	
	/*
	 * Create a new item
	 *
	 * _data_ is an optional object with field:value for itemData
	 */
	function newItem(typeID, data)
	{
		if (!Zotero.stateCheck()) {
			this.displayErrorMessage(true);
			return false;
		}
		
		var item = new Zotero.Item(false, typeID);
		
		for (var i in data)
		{
			item.setField(i, data[i]);
		}
		
		item.save();
		
		if (this.itemsView && this.itemsView._itemGroup.isCollection()) {
			this.itemsView._itemGroup.ref.addItem(item.id);
		}
		
		//set to Info tab
		document.getElementById('zotero-view-item').selectedIndex = 0;
		
		this.selectItem(item.id);
		
		return item;
	}
	
	function newCollection(parent)
	{
		var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
								.getService(Components.interfaces.nsIPromptService);
		
		var untitled = Zotero.DB.getNextName('collections', 'collectionName',
			Zotero.getString('pane.collections.untitled'));
		
		var newName = { value: untitled };
		var result = promptService.prompt(window,
			Zotero.getString('pane.collections.newCollection'),
			Zotero.getString('pane.collections.name'), newName, "", {});
		
		if (!result)
		{
			return;
		}
		
		if (!newName.value)
		{
			newName.value = untitled;
		}
		
		var collection = new Zotero.Collection;
		collection.name = newName.value;
		collection.parent = parent;
		collection.save();
	}
	
	function newSearch()
	{
		var s = new Zotero.Search();
		s.addCondition('title', 'contains', '');
		
		var untitled = Zotero.getString('pane.collections.untitled');
		untitled = Zotero.DB.getNextName('savedSearches', 'savedSearchName',
			Zotero.getString('pane.collections.untitled'));
		var io = {dataIn: {search: s, name: untitled}, dataOut: null};
		window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io);
	}
	
	
	function openAdvancedSearchWindow() {
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
					.getService(Components.interfaces.nsIWindowMediator);
		var enumerator = wm.getEnumerator('zotero:search');
		while (enumerator.hasMoreElements()) {
			var win = enumerator.getNext();
		}
		
		if (win) {
			win.focus();
			return;
		}
		
		var s = new Zotero.Search();
		s.addCondition('title', 'contains', '');
		var io = {dataIn: {search: s}, dataOut: null};
		window.openDialog('chrome://zotero/content/advancedSearch.xul', '', 'chrome,dialog=no,centerscreen', io);
	}
	
	
	function toggleTagSelector(){
		var zoteroPane = document.getElementById('zotero-pane');
		var splitter = document.getElementById('zotero-tags-splitter');
		var tagSelector = document.getElementById('zotero-tag-selector');
		
		var showing = tagSelector.getAttribute('collapsed') == 'true';
		tagSelector.setAttribute('collapsed', !showing);
		splitter.setAttribute('collapsed', !showing);
		this.updateTagSelectorSize();
		
		// If showing, set scope to items in current view
		// and focus filter textbox
		if (showing) {
			_setTagScope();
			tagSelector.focusTextbox();
		}
		// If hiding, clear selection
		else {
			tagSelector.uninit();
		}
	}
	
	
	function updateTagSelectorSize() {
		//Zotero.debug('Updating tag selector size');
		var zoteroPane = document.getElementById('zotero-pane');
		var splitter = document.getElementById('zotero-tags-splitter');
		var tagSelector = document.getElementById('zotero-tag-selector');
		
		// Nothing should be bigger than appcontent's height
		var max = document.getElementById('appcontent').boxObject.height
					- splitter.boxObject.height;
		
		// Shrink tag selector to appcontent's height
		var maxTS = max - COLLECTIONS_HEIGHT;
		if (parseInt(tagSelector.getAttribute("height")) > maxTS) {
			//Zotero.debug("Limiting tag selector height to appcontent");
			tagSelector.setAttribute('height', maxTS);
		}
		
		var height = tagSelector.boxObject.height;
		
		/*
		Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height);
		Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height'));
		Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
		Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));
		*/
		
		// Don't let the Z-pane jump back down to its previous height
		// (if shrinking or hiding the tag selector let it clear the min-height)
		if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) {
			//Zotero.debug("Setting Zotero pane height attribute to " +  zoteroPane.boxObject.height);
			zoteroPane.setAttribute('height', zoteroPane.boxObject.height);
		}
		
		if (tagSelector.getAttribute('collapsed') == 'true') {
			// 32px is the default Z pane min-height in overlay.css
			height = 32;
		}
		else {
			// tS.boxObject.height doesn't exist at startup, so get from attribute
			if (!height) {
				height = parseInt(tagSelector.getAttribute('height'));
			}
			// 121px seems to be enough room for the toolbar and collections
			// tree at minimum height
			height = height + 125;
		}
		
		//Zotero.debug('Setting Zotero pane minheight to ' + height);
		zoteroPane.setAttribute('minheight', height);
		
		if (this.isShowing() && !this.isFullScreen()) {
			zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height);
		}
		
		// Fix bug whereby resizing the Z pane downward after resizing
		// the tag selector up and then down sometimes caused the Z pane to
		// stay at a fixed size and get pushed below the bottom
		tagSelector.height++;
		tagSelector.height--;
	}
	
	
	function getTagSelection(){
		var tagSelector = document.getElementById('zotero-tag-selector');
		return tagSelector.selection ? tagSelector.selection : {};
	}
	
	
	function clearTagSelection() {
		if (Zotero.hasValues(this.getTagSelection())) {
			var tagSelector = document.getElementById('zotero-tag-selector');
			tagSelector.clearAll();
		}
	}
	
	
	/*
	 * Sets the tag filter on the items view
	 */
	function updateTagFilter(){
		this.itemsView.setFilter('tags', getTagSelection());
	}
	
	
	/*
	 * Set the tags scope to the items in the current view
	 *
	 * Passed to the items tree to trigger on changes
	 */
	function _setTagScope() {
		var itemgroup = self.collectionsView.
			_getItemAtRow(self.collectionsView.selection.currentIndex);
		var tagSelector = document.getElementById('zotero-tag-selector');
		if (!tagSelector.getAttribute('collapsed') ||
				tagSelector.getAttribute('collapsed') == 'false') {
			Zotero.debug('Updating tag selector with current tags');
			tagSelector.scope = itemgroup.getChildTags();
		}
	}
	
	
	function onCollectionSelected()
	{
		if (this.itemsView)
		{
			this.itemsView.unregister();
		}
		
		document.getElementById('zotero-tb-search').value = ""; 
		
		if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) {
			var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
			itemgroup.setSearch('');
			itemgroup.setTags(getTagSelection());
			
			try {
				Zotero.UnresponsiveScriptIndicator.disable();
				this.itemsView = new Zotero.ItemTreeView(itemgroup);
				this.itemsView.addCallback(_setTagScope);
				document.getElementById('zotero-items-tree').view = this.itemsView;
				this.itemsView.selection.clearSelection();
			}
			finally {
				Zotero.UnresponsiveScriptIndicator.enable();
			}
			
			if (itemgroup.isLibrary()) {
				Zotero.Prefs.set('lastViewedFolder', 'L');
			}
			if (itemgroup.isCollection()) {
				Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id);
			}
			else if (itemgroup.isSearch()) {
				Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id);
			}
		}
		else
		{
			document.getElementById('zotero-items-tree').view = this.itemsView = null;
		}
	}
	
	function itemSelected()
	{
		if (!Zotero.stateCheck()) {
			this.displayErrorMessage();
			return;
		}
		
		if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1)
		{
			var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
			
			if(item.ref.isNote()) {
				var noteEditor = document.getElementById('zotero-note-editor');
				if (this.itemsView.readOnly) {
					noteEditor.mode = 'view';
				}
				else {
					noteEditor.mode = 'edit';
				}
				
				// If loading new or different note, disable undo while we repopulate the text field
				// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
				// undo content from another note into the current one.
				if (!noteEditor.item || noteEditor.item.id != item.ref.id) {
					noteEditor.disableUndo();
				}
				noteEditor.parent = null;
				noteEditor.item = item.ref;
				
				noteEditor.enableUndo();
				
				document.getElementById('zotero-view-note-button').setAttribute('noteID',item.ref.id);
				if(item.ref.getSource())
				{
					document.getElementById('zotero-view-note-button').setAttribute('sourceID',item.ref.getSource());
				}
				else
				{
					document.getElementById('zotero-view-note-button').removeAttribute('sourceID');
				}
				document.getElementById('zotero-item-pane-content').selectedIndex = 2;
			}
			
			else if(item.ref.isAttachment()) {
				// DEBUG: this is annoying -- we really want to use an abstracted
				// version of createValueElement() from itemPane.js
				// (ideally in an XBL binding)
				
				// Wrap title to multiple lines if necessary
				var label = document.getElementById('zotero-attachment-label');
				while (label.hasChildNodes())
				{
					label.removeChild(label.firstChild);
				}
				var val = item.getField('title');
				
				var firstSpace = val.indexOf(" ");
				// Crop long uninterrupted text
				if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29)
				{
					label.setAttribute('crop', 'end');
					label.setAttribute('value', val);
				}
				// Create a <description> element, essentially
				else
				{
					label.removeAttribute('value');
					label.appendChild(document.createTextNode(val));
				}
				
				// For the time being, use a silly little popup
				label.className = 'zotero-clicky';
				label.onclick = function(event){
					var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
							.getService(Components.interfaces.nsIPromptService);
					
					var newTitle = { value: val };
					var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') };
					
					while (true) {
						// Don't show "Rename associated file" option for
						// linked URLs
						if (item.ref.getAttachmentLinkMode() ==
								Zotero.Attachments.LINK_MODE_LINKED_URL) {
							var result = nsIPS.prompt(
								window,
								'',
								Zotero.getString('pane.item.attachments.rename.title'),
								newTitle,
								null,
								{}
							);
							
							// If they hit cancel or left it blank
							if (!result || !newTitle.value) {
								return;
							}
							
							break;
						}
						
						var result = nsIPS.prompt(
							window,
							'',
							Zotero.getString('pane.item.attachments.rename.title'),
							newTitle,
							Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'),
							checkState
						);
						
						// If they hit cancel or left it blank
						if (!result || !newTitle.value) {
							return;
						}
						
						Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value);
						
						// Rename associated file
						if (checkState.value) {
							var renamed = item.ref.renameAttachmentFile(newTitle.value);
							if (renamed == -1) {
								var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?');
								if (confirmed) {
									item.ref.renameAttachmentFile(newTitle.value, true);
									break;
								}
								// If they said not to overwrite existing file,
								// start again
								continue;
							}
							else if (renamed == -2 || !renamed) {
								alert(Zotero.getString('pane.item.attachments.rename.error'));
								return;
							}
						}
						
						break;
					}
					
					if (newTitle.value != val) {
						item.ref.setField('title', newTitle.value);
						item.ref.save();
					}
				}
				
				
				var isImportedURL = item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_IMPORTED_URL;
				
				// Metadata for URL's
				if (item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL
					|| isImportedURL)
				{
					// "View Page"/"View Snapshot" label
					if (isImportedURL)
					{
						var str = Zotero.getString('pane.item.attachments.view.snapshot');
					}
					else
					{
						var str = Zotero.getString('pane.item.attachments.view.link');
					}
					
					document.getElementById('zotero-attachment-show').setAttribute('hidden', !isImportedURL);
					
					// URL
					document.getElementById('zotero-attachment-url').setAttribute('value', item.getField('url'));
					document.getElementById('zotero-attachment-url').setAttribute('hidden', false);
					// Access date
					document.getElementById('zotero-attachment-accessed').setAttribute('value',
						Zotero.getString('itemFields.accessDate') + ': '
						+ Zotero.Date.sqlToDate(item.getField('accessDate'), true).toLocaleString());
					document.getElementById('zotero-attachment-accessed').setAttribute('hidden', false);
				}
				// Metadata for files
				else
				{
					var str = Zotero.getString('pane.item.attachments.view.file');
					document.getElementById('zotero-attachment-show').setAttribute('hidden', false);
					document.getElementById('zotero-attachment-url').setAttribute('hidden', true);
					document.getElementById('zotero-attachment-accessed').setAttribute('hidden', true);
				}
				
				document.getElementById('zotero-attachment-view').setAttribute('label', str);
				
				// Display page count
				var pages = Zotero.Fulltext.getPages(item.ref.id);
				var pages = pages ? pages.total : null;
				var pagesRow = document.getElementById('zotero-attachment-pages');
				if (pages) {
					var str = Zotero.getString('itemFields.pages') + ': ' + pages;
					pagesRow.setAttribute('value', str);
					pagesRow.setAttribute('hidden', false);
				}
				else {
					pagesRow.setAttribute('hidden', true);
				}
				
				this.updateItemIndexedState();
				
				var noteEditor = document.getElementById('zotero-attachment-note-editor');
				noteEditor.mode = 'edit';
				noteEditor.parent = null;
				noteEditor.item = item.ref;
				
				document.getElementById('zotero-item-pane-content').selectedIndex = 3;
			}
			
			// Regular item
			else
			{
				ZoteroItemPane.viewItem(item.ref, this.itemsView.readOnly ? 'view' : false);
				document.getElementById('zotero-item-pane-content').selectedIndex = 1;
			}
		}
		else
		{
			document.getElementById('zotero-item-pane-content').selectedIndex = 0;
			
			var label = document.getElementById('zotero-view-selected-label');
			
			if (this.itemsView && this.itemsView.selection.count) {
				label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count);
			}
			else {
				label.value = Zotero.getString('pane.item.selected.zero');
			}
		}

	}
	
	
	/*
	 * Update Indexed: (Yes|No|Partial) line in attachment info pane
	 */
	function updateItemIndexedState() {
		var indexBox = document.getElementById('zotero-attachment-index-box');
		var indexStatus = document.getElementById('zotero-attachment-index-status');
		var reindexButton = document.getElementById('zotero-attachment-reindex');
		
		var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
		var status = Zotero.Fulltext.getIndexedState(item.ref.id);
		var str = 'fulltext.indexState.';
		switch (status) {
			case Zotero.Fulltext.INDEX_STATE_UNAVAILABLE:
				str += 'unavailable';
				break;
			case Zotero.Fulltext.INDEX_STATE_UNINDEXED:
				str = 'general.no';
				break;
			case Zotero.Fulltext.INDEX_STATE_PARTIAL:
				str += 'partial';
				break;
			case Zotero.Fulltext.INDEX_STATE_INDEXED:
				str = 'general.yes';
				break;
		}
		str = Zotero.getString('fulltext.indexState.indexed') + ': ' +
			Zotero.getString(str);
		indexStatus.setAttribute('value', str);
		
		// Reindex button tooltip (string stored in zotero.properties)
		var str = Zotero.getString('pane.items.menu.reindexItem');
		reindexButton.setAttribute('tooltiptext', str);
		
		if (Zotero.Fulltext.canReindex(item.ref.id)) {
			reindexButton.setAttribute('hidden', false);
		}
		else {
			reindexButton.setAttribute('hidden', true);
		}
	}
	
	
	function reindexItem() {
		var items = this.getSelectedItems();
		if (!items) {
			return;
		}
		
		for (var i=0; i<items.length; i++) {
			if (!items[i].isAttachment()) {
				continue;
			}
			var itemID = items[i].id;
			Zotero.Fulltext.indexItems(itemID, true);
		}
		this.updateItemIndexedState();
	}
	
	
	function duplicateSelectedItem() {
		var item = this.getSelectedItems()[0];
		if (item.getTags()) {
			var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
											.getService(Components.interfaces.nsIPromptService);
			ps.alert(null, "Error", "Duplication of tagged items is not available in this Zotero release.");
			return;
		}
		
		var newItem = this.getSelectedItems()[0].clone();
		var newItemID = newItem.save()
{'fraction_non_alphanumeric': 0.12947057883187282, 'fraction_numerical': 0.0022816119515775093, 'mean_word_length': 3.6712375263050707, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5427455', 'n_tokens_mistral': 25780, 'n_tokens_neox': 21275, 'n_words': 4062}