www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

itemPane.js (11988B)


      1 /*
      2     ***** BEGIN LICENSE BLOCK *****
      3     
      4     Copyright © 2009 Center for History and New Media
      5                      George Mason University, Fairfax, Virginia, USA
      6                      http://zotero.org
      7     
      8     This file is part of Zotero.
      9     
     10     Zotero is free software: you can redistribute it and/or modify
     11     it under the terms of the GNU Affero General Public License as published by
     12     the Free Software Foundation, either version 3 of the License, or
     13     (at your option) any later version.
     14     
     15     Zotero is distributed in the hope that it will be useful,
     16     but WITHOUT ANY WARRANTY; without even the implied warranty of
     17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18     GNU Affero General Public License for more details.
     19     
     20     You should have received a copy of the GNU Affero General Public License
     21     along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
     22     
     23     ***** END LICENSE BLOCK *****
     24 */
     25 
     26 var ZoteroItemPane = new function() {
     27 	var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox;
     28 	var _selectedNoteID;
     29 	var _translationTarget;
     30 	var _noteIDs;
     31 	
     32 	this.onLoad = function () {
     33 		if (!Zotero) {
     34 			return;
     35 		}
     36 		
     37 		// Not in item pane, so skip the introductions
     38 		//
     39 		// DEBUG: remove?
     40 		if (!document.getElementById('zotero-view-tabbox')) {
     41 			return;
     42 		}
     43 		
     44 		_itemBox = document.getElementById('zotero-editpane-item-box');
     45 		_notesLabel = document.getElementById('zotero-editpane-notes-label');
     46 		_notesButton = document.getElementById('zotero-editpane-notes-add');
     47 		_notesList = document.getElementById('zotero-editpane-dynamic-notes');
     48 		_tagsBox = document.getElementById('zotero-editpane-tags');
     49 		_relatedBox = document.getElementById('zotero-editpane-related');
     50 		
     51 		this._unregisterID = Zotero.Notifier.registerObserver(this, ['item'], 'itemPane');
     52 	}
     53 	
     54 	
     55 	this.onUnload = function () {
     56 		Zotero.Notifier.unregisterObserver(this._unregisterID);
     57 	},
     58 	
     59 	
     60 	/*
     61 	 * Load a top-level item
     62 	 */
     63 	this.viewItem = Zotero.Promise.coroutine(function* (item, mode, index) {
     64 		if (!index) {
     65 			index = 0;
     66 		}
     67 		
     68 		Zotero.debug('Viewing item in pane ' + index);
     69 		
     70 		switch (index) {
     71 			case 0:
     72 				var box = _itemBox;
     73 				break;
     74 			
     75 			case 2:
     76 				var box = _tagsBox;
     77 				break;
     78 			
     79 			case 3:
     80 				var box = _relatedBox;
     81 				break;
     82 		}
     83 		
     84 		// Force blur() when clicking off a textbox to another item in middle
     85 		// pane, since for some reason it's not being called automatically
     86 		if (_lastItem && _lastItem != item) {
     87 			switch (index) {
     88 				case 0:
     89 				case 2:
     90 					yield box.blurOpenField();
     91 					// DEBUG: Currently broken
     92 					//box.scrollToTop();
     93 					break;
     94 			}
     95 		}
     96 		
     97 		_lastItem = item;
     98 		
     99 		var viewBox = document.getElementById('zotero-view-item');
    100 		viewBox.classList.remove('no-tabs');
    101 		
    102 		if (index == 0) {
    103 			document.getElementById('zotero-editpane-tabs').setAttribute('hidden', item.isFeedItem);
    104 			
    105 			if (item.isFeedItem) {
    106 				viewBox.classList.add('no-tabs');
    107 				
    108 				let lastTranslationTarget = Zotero.Prefs.get('feeds.lastTranslationTarget');
    109 				if (lastTranslationTarget) {
    110 					let id = parseInt(lastTranslationTarget.substr(1));
    111 					if (lastTranslationTarget[0] == "L") {
    112 						_translationTarget = Zotero.Libraries.get(id);
    113 					}
    114 					else if (lastTranslationTarget[0] == "C") {
    115 						_translationTarget = Zotero.Collections.get(id);
    116 					}
    117 				}
    118 				if (!_translationTarget) {
    119 					_translationTarget = Zotero.Libraries.userLibrary;
    120 				}	
    121 				this.setTranslateButton();
    122 			}
    123 		}
    124 		else if (index == 1) {
    125 			var editable = ZoteroPane_Local.canEdit();
    126 			_notesButton.hidden = !editable;
    127 			
    128 			while(_notesList.hasChildNodes()) {
    129 				_notesList.removeChild(_notesList.firstChild);
    130 			}
    131 			
    132 			_noteIDs = new Set();
    133 			let notes = yield Zotero.Items.getAsync(item.getNotes());
    134 			if (notes.length) {
    135 				for (var i = 0; i < notes.length; i++) {
    136 					let note = notes[i];
    137 					let id = notes[i].id;
    138 					
    139 					var icon = document.createElement('image');
    140 					icon.className = "zotero-box-icon";
    141 					icon.setAttribute('src', `chrome://zotero/skin/treeitem-note${Zotero.hiDPISuffix}.png`);
    142 					
    143 					var label = document.createElement('label');
    144 					label.className = "zotero-box-label";
    145 					var title = note.getNoteTitle();
    146 					title = title ? title : Zotero.getString('pane.item.notes.untitled');
    147 					label.setAttribute('value', title);
    148 					label.setAttribute('flex','1');	//so that the long names will flex smaller
    149 					label.setAttribute('crop','end');
    150 					
    151 					var box = document.createElement('box');
    152 					box.setAttribute('class','zotero-clicky');
    153 					box.addEventListener('click', function () { ZoteroPane_Local.selectItem(id); });
    154 					box.appendChild(icon);
    155 					box.appendChild(label);
    156 					
    157 					if (editable) {
    158 						var removeButton = document.createElement('label');
    159 						removeButton.setAttribute("value","-");
    160 						removeButton.setAttribute("class","zotero-clicky zotero-clicky-minus");
    161 						removeButton.addEventListener('click', function () { ZoteroItemPane.removeNote(id); });
    162 					}
    163 					
    164 					var row = document.createElement('row');
    165 					row.appendChild(box);
    166 					if (editable) {
    167 						row.appendChild(removeButton);
    168 					}
    169 					
    170 					_notesList.appendChild(row);
    171 					_noteIDs.add(id);
    172 				}
    173 			}
    174 			
    175 			_updateNoteCount();
    176 			return;
    177 		}
    178 		
    179 		if (mode) {
    180 			box.mode = mode;
    181 			
    182 			if (box.mode == 'view') {
    183 				box.hideEmptyFields = true;
    184 			}
    185 		}
    186 		else {
    187 			box.mode = 'edit';
    188 		}
    189 		
    190 		box.item = item;
    191 	});
    192 	
    193 	
    194 	this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
    195 		var viewBox = document.getElementById('zotero-view-item');
    196 		// If notes pane is selected, refresh it if any of the notes change or are deleted
    197 		if (viewBox.selectedIndex == 1 && (action == 'modify' || action == 'delete')) {
    198 			let refresh = false;
    199 			if (ids.some(id => _noteIDs.has(id))) {
    200 				refresh = true;
    201 			}
    202 			if (refresh) {
    203 				yield this.viewItem(_lastItem, null, 1);
    204 			}
    205 		}
    206 	});
    207 	
    208 	
    209 	this.blurOpenField = Zotero.Promise.coroutine(function* () {
    210 		var tabBox = document.getElementById('zotero-view-tabbox');
    211 		switch (tabBox.selectedIndex) {
    212 		case 0:
    213 			var box = _itemBox;
    214 			break;
    215 			
    216 		case 2:
    217 			var box = _tagsBox;
    218 			break;
    219 		}
    220 		if (box) {
    221 			yield box.blurOpenField();
    222 		}
    223 	});
    224 	
    225 	
    226 	this.onNoteSelected = function (item, editable) {
    227 		_selectedNoteID = item.id;
    228 		
    229 		// If an external note window is open for this item, don't show the editor
    230 		if (ZoteroPane.findNoteWindow(item.id)) {
    231 			this.showNoteWindowMessage();
    232 			return;
    233 		}
    234 		
    235 		var noteEditor = document.getElementById('zotero-note-editor');
    236 		
    237 		// If loading new or different note, disable undo while we repopulate the text field
    238 		// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
    239 		// undo content from another note into the current one.
    240 		var clearUndo = noteEditor.item ? noteEditor.item.id != item.id : false;
    241 		
    242 		noteEditor.mode = editable ? 'edit' : 'view';
    243 		noteEditor.parent = null;
    244 		noteEditor.item = item;
    245 		
    246 		if (clearUndo) {
    247 			noteEditor.clearUndo();
    248 		}
    249 		
    250 		document.getElementById('zotero-view-note-button').hidden = !editable;
    251 		document.getElementById('zotero-item-pane-content').selectedIndex = 2;
    252 	};
    253 	
    254 	
    255 	this.showNoteWindowMessage = function () {
    256 		ZoteroPane.setItemPaneMessage(Zotero.getString('pane.item.notes.editingInWindow'));
    257 	};
    258 	
    259 	
    260 	/**
    261 	 * Select the parent item and open the note editor
    262 	 */
    263 	this.openNoteWindow = async function () {
    264 		var selectedNote = Zotero.Items.get(_selectedNoteID);
    265 		
    266 		// We don't want to show the note in two places, since it causes unnecessary UI updates
    267 		// and can result in weird bugs where note content gets lost.
    268 		//
    269 		// If this is a child note, select the parent
    270 		if (selectedNote.parentID) {
    271 			await ZoteroPane.selectItem(selectedNote.parentID);
    272 		}
    273 		// Otherwise, hide note and replace with a message that we're editing externally
    274 		else {
    275 			this.showNoteWindowMessage();
    276 		}
    277 		ZoteroPane.openNoteWindow(selectedNote.id);
    278 	};
    279 	
    280 	
    281 	this.addNote = function (popup) {
    282 		ZoteroPane_Local.newNote(popup, _lastItem.key);
    283 	}
    284 	
    285 	
    286 	this.removeNote = function (id) {
    287 		var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
    288 								.getService(Components.interfaces.nsIPromptService);
    289 		if (ps.confirm(null, '', Zotero.getString('pane.item.notes.delete.confirm'))) {
    290 			Zotero.Items.trashTx(id);
    291 		}
    292 	}
    293 	
    294 	
    295 	this.translateSelectedItems = Zotero.Promise.coroutine(function* () {
    296 		var collectionID = _translationTarget.objectType == 'collection' ? _translationTarget.id : undefined;
    297 		var items = ZoteroPane_Local.itemsView.getSelectedItems();
    298 		for (let item of items) {
    299 			yield item.translate(_translationTarget.libraryID, collectionID);
    300 		}
    301 	});
    302 	
    303 	
    304 	this.buildTranslateSelectContextMenu = function (event) {
    305 		var menu = document.getElementById('zotero-item-addTo-menu');
    306 		// Don't trigger rebuilding on nested popupmenu open/close
    307 		if (event.target != menu) {
    308 			return;
    309 		}
    310 		// Clear previous items
    311 		while (menu.firstChild) {
    312 			menu.removeChild(menu.firstChild);
    313 		}
    314 		
    315 		let target = Zotero.Prefs.get('feeds.lastTranslationTarget');
    316 		if (!target) {
    317 			target = "L" + Zotero.Libraries.userLibraryID;
    318 		}
    319 		
    320 		var libraries = Zotero.Libraries.getAll();
    321 		for (let library of libraries) {
    322 			if (!library.editable || library.libraryType == 'publications') {
    323 				continue;
    324 			}
    325 			Zotero.Utilities.Internal.createMenuForTarget(
    326 				library,
    327 				menu,
    328 				target,
    329 				function(event, libraryOrCollection) {
    330 					if (event.target.tagName == 'menu') {
    331 						Zotero.Promise.coroutine(function* () {
    332 							// Simulate menuitem flash on OS X
    333 							if (Zotero.isMac) {
    334 								event.target.setAttribute('_moz-menuactive', false);
    335 								yield Zotero.Promise.delay(50);
    336 								event.target.setAttribute('_moz-menuactive', true);
    337 								yield Zotero.Promise.delay(50);
    338 								event.target.setAttribute('_moz-menuactive', false);
    339 								yield Zotero.Promise.delay(50);
    340 								event.target.setAttribute('_moz-menuactive', true);
    341 							}
    342 							menu.hidePopup();
    343 							
    344 							ZoteroItemPane.setTranslationTarget(libraryOrCollection);
    345 							event.stopPropagation();
    346 						})();
    347 					}
    348 					else {
    349 						ZoteroItemPane.setTranslationTarget(libraryOrCollection);
    350 						event.stopPropagation();
    351 					}
    352 				}
    353 			);
    354 		}
    355 	};
    356 	
    357 	
    358 	this.setTranslateButton = function() {
    359 		var label = Zotero.getString('pane.item.addTo', _translationTarget.name);
    360 		var elem = document.getElementById('zotero-feed-item-addTo-button');
    361 		elem.setAttribute('label', label);
    362 
    363 		var key = Zotero.Keys.getKeyForCommand('saveToZotero');
    364 		
    365 		var tooltip = label 
    366 			+ (Zotero.rtl ? ' \u202B' : ' ') + '(' 
    367 			+ (Zotero.isMac ? '⇧⌘' : Zotero.getString('general.keys.ctrlShift'))
    368 			+ key + ')';
    369 		elem.setAttribute('tooltiptext', tooltip);
    370 		elem.setAttribute('image', _translationTarget.treeViewImage);
    371 	};
    372 	
    373 
    374 	this.setTranslationTarget = function(translationTarget) {
    375 		_translationTarget = translationTarget;
    376 		Zotero.Prefs.set('feeds.lastTranslationTarget', translationTarget.treeViewID);
    377 		ZoteroItemPane.setTranslateButton();
    378 	};
    379 	
    380 	
    381 	this.setReadLabel = function (isRead) {
    382 		var elem = document.getElementById('zotero-feed-item-toggleRead-button');
    383 		var label = Zotero.getString('pane.item.' + (isRead ? 'markAsUnread' : 'markAsRead'));
    384 		elem.setAttribute('label', label);
    385 
    386 		var key = Zotero.Keys.getKeyForCommand('toggleRead');
    387 		var tooltip = label + (Zotero.rtl ? ' \u202B' : ' ') + '(' + key + ')'
    388 		elem.setAttribute('tooltiptext', tooltip);
    389 	};
    390 	
    391 	
    392 	function _updateNoteCount() {
    393 		var c = _notesList.childNodes.length;
    394 		
    395 		var str = 'pane.item.notes.count.';
    396 		switch (c){
    397 		case 0:
    398 			str += 'zero';
    399 			break;
    400 		case 1:
    401 			str += 'singular';
    402 			break;
    403 		default:
    404 			str += 'plural';
    405 			break;
    406 		}
    407 		
    408 		_notesLabel.value = Zotero.getString(str, [c]);
    409 	}
    410 }   
    411 
    412 addEventListener("load", function(e) { ZoteroItemPane.onLoad(e); }, false);
    413 addEventListener("unload", function(e) { ZoteroItemPane.onUnload(e); }, false);