zotero/chrome/content/zotero/itemPane.js
Dan Stillman f7671f2a93 Add autocomplete on some more fields in the metadata pane, including all the type and medium fields (but not across each other, since they're distinct fields)
Add asNames option to Z.ItemFields.getTypeFieldsFromBase() to return field names instead of ids
2007-02-18 07:57:02 +00:00

1676 lines
44 KiB
JavaScript

/*
***** 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 *****
*/
var ZoteroItemPane = new function()
{
var _dynamicFields;
var _creatorTypeMenu;
var _beforeRow;
var _notesList;
var _linksBox;
var _notesLabel;
var _creatorCount;
var _lastPane;
var _loaded;
var _itemBeingEdited;
var _activeScrollbox;
var _lastTabIndex;
var _tabDirection;
var _tabIndexMinCreators = 10;
var _tabIndexMaxCreators = 0;
var _tabIndexMinFields = 1000;
var _tabIndexMaxInfoFields = 0;
var _tabIndexMaxTagsFields = 0;
const _defaultFirstName =
'(' + Zotero.getString('pane.item.defaultFirstName') + ')';
const _defaultLastName =
'(' + Zotero.getString('pane.item.defaultLastName') + ')';
const _defaultFullName =
'(' + Zotero.getString('pane.item.defaultFullName') + ')';
this.onLoad = onLoad;
this.viewItem = viewItem;
this.scrollToTop = scrollToTop;
this.ensureElementIsVisible = ensureElementIsVisible;
this.loadPane = loadPane;
this.changeTypeTo = changeTypeTo;
this.onViewClick = onViewClick;
this.onOpenURLClick = onOpenURLClick;
this.addCreatorRow = addCreatorRow;
this.switchCreatorMode = switchCreatorMode;
this.toggleAbstractExpand = toggleAbstractExpand;
this.disableButton = disableButton;
this.createValueElement = createValueElement;
this.removeCreator = removeCreator;
this.showEditor = showEditor;
this.handleKeyPress = handleKeyPress;
this.handleCreatorAutoCompleteSelect = handleCreatorAutoCompleteSelect;
this.hideEditor = hideEditor;
this.getCreatorFields = getCreatorFields;
this.modifyCreator = modifyCreator;
this.removeNote = removeNote;
this.addNote = addNote;
this.removeAttachment = removeAttachment;
this.addAttachmentFromDialog = addAttachmentFromDialog;
this.addAttachmentFromPage = addAttachmentFromPage;
this.focusFirstField = focusFirstField;
function onLoad()
{
_tabs = document.getElementById('zotero-view-tabs');
// Not in item pane, so skip the introductions
if (!_tabs)
{
return false;
}
_dynamicFields = document.getElementById('zotero-editpane-dynamic-fields');
_itemTypeMenu = document.getElementById('zotero-editpane-type-menu');
_creatorTypeMenu = document.getElementById('zotero-creator-type-menu');
_notesList = document.getElementById('zotero-editpane-dynamic-notes');
_notesLabel = document.getElementById('zotero-editpane-notes-label');
_attachmentsList = document.getElementById('zotero-editpane-dynamic-attachments');
_attachmentsLabel = document.getElementById('zotero-editpane-attachments-label');
_tagsBox = document.getElementById('zotero-editpane-tags');
_relatedBox = document.getElementById('zotero-editpane-related');
var itemTypes = Zotero.ItemTypes.getTypes();
for(var i = 0; i<itemTypes.length; i++)
if(itemTypes[i]['name'] != 'note' && itemTypes[i]['name'] != 'attachment')
_itemTypeMenu.appendItem(Zotero.getString("itemTypes."+itemTypes[i]['name']),itemTypes[i]['id']);
return true;
}
/*
* Loads an item
*/
function viewItem(thisItem)
{
//Zotero.debug('Viewing item');
// Force blur() when clicking off a textbox to another item in middle
// pane, since for some reason it's not being called automatically
if (_itemBeingEdited && _itemBeingEdited!=thisItem)
{
switch (_tabs.selectedIndex)
{
// Info
case 0:
var boxes = _dynamicFields.getElementsByTagName('textbox');
// When coming from another element, scroll pane to top
scrollToTop();
break;
// Tags
case 3:
var boxes = document.getAnonymousNodes(_tagsBox)[0].getElementsByTagName('textbox');
break;
}
if (boxes && boxes.length==1)
{
boxes[0].inputField.blur();
}
}
_itemBeingEdited = thisItem;
_loaded = {};
loadPane(_tabs.selectedIndex);
}
function loadPane(index)
{
//Zotero.debug('Loading item pane ' + index);
// Clear the tab index when switching panes
if (_lastPane!=index)
{
_lastTabIndex = null;
}
_lastPane = index;
if(_loaded[index])
{
return;
}
_loaded[index] = true;
// Info pane
if(index == 0)
{
_activeScrollbox = document.getElementById('zotero-info');
// Enable/disable "View =>" button
testView: try
{
var viewButton = document.getElementById('zotero-go-to-url');
viewButton.removeAttribute('viewSnapshot');
viewButton.removeAttribute('viewURL');
viewButton.setAttribute('label',
Zotero.getString('pane.item.goToURL.online.label'));
viewButton.setAttribute('tooltiptext',
Zotero.getString('pane.item.goToURL.online.tooltip'));
var spec = false, validURI = false;
var uri = Components.classes["@mozilla.org/network/standard-url;1"].
createInstance(Components.interfaces.nsIURI);
// First try to find a snapshot matching the item's URL field
var snapID = _itemBeingEdited.getBestSnapshot();
if (snapID) {
spec = Zotero.Items.get(snapID).getLocalFileURL();
uri.spec = spec;
if (!uri.scheme || uri.scheme != 'file') {
snapID = false;
spec = false;
}
}
// If that fails, try the URL field itself
if (!spec) {
spec = _itemBeingEdited.getField('url');
uri.spec = spec;
if (!(uri.scheme && (uri.host || uri.scheme == 'file'))) {
spec = false;
}
}
if (!spec) {
break testView;
}
validURI = true;
if (snapID) {
viewButton.setAttribute('label',
Zotero.getString('pane.item.goToURL.snapshot.label'));
viewButton.setAttribute('tooltiptext',
Zotero.getString('pane.item.goToURL.snapshot.tooltip'));
viewButton.setAttribute('viewSnapshot', snapID);
}
else {
viewButton.setAttribute('viewURL', spec);
}
}
catch (e){Zotero.debug(e);}
viewButton.setAttribute('disabled', !validURI);
// Enable/disable "Locate =>" (OpenURL) button
switch (_itemBeingEdited.getType())
{
// DEBUG: handle descendents of these types as well?
case Zotero.ItemTypes.getID('book'):
case Zotero.ItemTypes.getID('bookSection'):
case Zotero.ItemTypes.getID('journalArticle'):
case Zotero.ItemTypes.getID('thesis'):
var openURL = true;
break;
default:
var openURL = false;
}
document.getElementById('zotero-openurl').setAttribute('disabled', !openURL);
// Clear and rebuild creator type menu
while(_creatorTypeMenu.hasChildNodes())
{
_creatorTypeMenu.removeChild(_creatorTypeMenu.firstChild);
}
var creatorTypes = Zotero.CreatorTypes.getTypesForItemType(_itemBeingEdited.getType());
var localized = [];
for (var i in creatorTypes)
{
localized[creatorTypes[i]['name']]
= Zotero.getString('creatorTypes.' + creatorTypes[i]['name']);
}
for (var i in localized)
{
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", localized[i]);
menuitem.setAttribute("typeid", Zotero.CreatorTypes.getID(i));
_creatorTypeMenu.appendChild(menuitem);
}
//
// Clear and rebuild metadata fields
//
while(_dynamicFields.hasChildNodes())
_dynamicFields.removeChild(_dynamicFields.firstChild);
for(var i = 0, len = _itemTypeMenu.firstChild.childNodes.length; i < len; i++)
if(_itemTypeMenu.firstChild.childNodes[i].value == _itemBeingEdited.getType())
_itemTypeMenu.selectedIndex = i;
var fieldNames = new Array("title");
var fields = Zotero.ItemFields.getItemTypeFields(_itemBeingEdited.getField("itemTypeID"));
for(var i = 0; i<fields.length; i++)
fieldNames.push(Zotero.ItemFields.getName(fields[i]));
fieldNames.push("dateAdded","dateModified");
for(var i = 0; i<fieldNames.length; i++)
{
var editable = (!_itemBeingEdited.isPrimaryField(fieldNames[i]) || _itemBeingEdited.isEditableField(fieldNames[i]));
var val = _itemBeingEdited.getField(fieldNames[i]);
// Start tabindex at 1000 after creators
var tabindex = editable ? (i>0 ? _tabIndexMinFields + i : 1) : 0;
_tabIndexMaxInfoFields = Math.max(_tabIndexMaxInfoFields, tabindex);
if (fieldNames[i]=='date'){
addDateRow(_itemBeingEdited.getField('date', true), tabindex);
continue;
}
var valueElement = createValueElement(
val, fieldNames[i], tabindex, !editable
);
var label = document.createElement("label");
label.setAttribute('fieldname', fieldNames[i]);
var prefix = '';
// Add '(...)' before 'Abstract:' for collapsed abstracts
if (fieldNames[i] == 'abstract') {
if (val && !Zotero.Prefs.get('lastAbstractExpand')) {
prefix = '(...) ';
}
}
label.setAttribute("value", prefix +
Zotero.ItemFields.getLocalizedString(_itemBeingEdited.getType(), fieldNames[i]) + ":");
if (fieldNames[i] == 'url' && val) {
label.setAttribute("isButton", true);
label.setAttribute("onclick", "ZoteroPane.loadURI(this.nextSibling.value, event)");
label.setAttribute("tooltiptext", Zotero.getString('pane.item.goToURL.online.tooltip'));
}
else if (fieldNames[i] == 'abstract') {
label.setAttribute("onclick", "if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); } else { ZoteroItemPane.toggleAbstractExpand(this); }");
}
else {
label.setAttribute("onclick", "if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); }");
}
addDynamicRow(label,valueElement);
}
//CREATORS:
_beforeRow = _dynamicFields.firstChild.nextSibling;
_creatorCount = 0;
if(_itemBeingEdited.numCreators() > 0)
{
for(var i = 0, len=_itemBeingEdited.numCreators(); i<len; i++)
{
var creator = _itemBeingEdited.getCreator(i);
addCreatorRow(creator['firstName'], creator['lastName'], creator['creatorTypeID'], creator['fieldMode']);
}
}
else
{
// Add default row
addCreatorRow('', '', false, Zotero.Prefs.get('lastCreatorFieldMode'), true, true);
}
var focusMode = 'info';
var focusBox = _dynamicFields;
}
// Notes pane
else if(index == 1)
{
while(_notesList.hasChildNodes())
_notesList.removeChild(_notesList.firstChild);
var notes = Zotero.Items.get(_itemBeingEdited.getNotes());
if(notes.length)
{
for(var i = 0; i < notes.length; i++)
{
var icon = document.createElement('image');
icon.setAttribute('src','chrome://zotero/skin/treeitem-note.png');
var label = document.createElement('label');
label.setAttribute('value',_noteToTitle(notes[i].getNote()));
label.setAttribute('flex','1'); //so that the long names will flex smaller
label.setAttribute('crop','end');
var box = document.createElement('box');
box.setAttribute('onclick',"ZoteroPane.selectItem("+notes[i].getID()+");");
box.setAttribute('class','zotero-clicky');
box.appendChild(icon);
box.appendChild(label);
var removeButton = document.createElement('label');
removeButton.setAttribute("value","-");
removeButton.setAttribute("class","zotero-clicky");
removeButton.setAttribute("onclick","ZoteroItemPane.removeNote("+notes[i].getID()+")");
var row = document.createElement('row');
row.appendChild(box);
row.appendChild(removeButton);
_notesList.appendChild(row);
}
}
_updateNoteCount();
}
// Attachments pane
else if(index == 2)
{
while(_attachmentsList.hasChildNodes())
_attachmentsList.removeChild(_attachmentsList.firstChild);
var attachments = Zotero.Items.get(_itemBeingEdited.getAttachments());
if(attachments.length)
{
for(var i = 0; i < attachments.length; i++)
{
var icon = document.createElement('image');
var linkMode = attachments[i].getAttachmentLinkMode();
var itemType = '';
if(linkMode == Zotero.Attachments.LINK_MODE_IMPORTED_FILE)
{
itemType = "-file";
}
else if(linkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE)
{
itemType = "-link";
}
else if(linkMode == Zotero.Attachments.LINK_MODE_IMPORTED_URL)
{
itemType = "-snapshot";
}
else if(linkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)
{
itemType = "-web-link";
}
icon.setAttribute('src','chrome://zotero/skin/treeitem-file'+itemType+'.png');
var label = document.createElement('label');
label.setAttribute('value',attachments[i].getField('title'));
label.setAttribute('flex','1'); //so that the long names will flex smaller
label.setAttribute('crop','end');
var box = document.createElement('box');
box.setAttribute('onclick',"ZoteroPane.selectItem('"+attachments[i].getID()+"')");
box.setAttribute('class','zotero-clicky');
box.appendChild(icon);
box.appendChild(label);
var removeButton = document.createElement('label');
removeButton.setAttribute("value","-");
removeButton.setAttribute("class","zotero-clicky");
removeButton.setAttribute("onclick","ZoteroItemPane.removeAttachment("+attachments[i].getID()+")");
var row = document.createElement('row');
row.appendChild(box);
row.appendChild(removeButton);
_attachmentsList.appendChild(row);
}
}
_updateAttachmentCount();
}
// Tags pane
else if(index == 3)
{
_activeScrollbox = document.getElementById('zotero-editpane-tags').getScrollBox();
var focusMode = 'tags';
var focusBox = _tagsBox;
_tagsBox.item = _itemBeingEdited;
}
// Related pane
else if(index == 4)
{
_relatedBox.item = _itemBeingEdited;
}
// Move to next or previous field if (shift-)tab was pressed
if (focusMode && _lastTabIndex && _tabDirection)
{
_focusNextField(focusMode, focusBox, _lastTabIndex, _tabDirection==-1);
}
}
function scrollToTop() {
if (!_activeScrollbox) {
return;
}
var sbo = _activeScrollbox.boxObject;
sbo.QueryInterface(Components.interfaces.nsIScrollBoxObject);
sbo.scrollTo(0,0);
}
function ensureElementIsVisible(elem) {
if (!_activeScrollbox) {
return;
}
var sbo = _activeScrollbox.boxObject;
sbo.QueryInterface(Components.interfaces.nsIScrollBoxObject);
sbo.ensureElementIsVisible(elem);
}
function changeTypeTo(itemTypeID, menu) {
if (itemTypeID == _itemBeingEdited.getType()) {
return;
}
var fieldsToDelete = _itemBeingEdited.getFieldsNotInType(itemTypeID, true);
// Generate list of localized field names for display in pop-up
if (fieldsToDelete) {
var fieldNames = "";
for each(var fieldID in fieldsToDelete) {
fieldNames += "\n - " +
Zotero.ItemFields.getLocalizedString(_itemBeingEdited.getType(), fieldID);
}
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
}
if (!fieldsToDelete ||
promptService.confirm(null,
Zotero.getString('pane.item.changeType.title'),
Zotero.getString('pane.item.changeType.message') + "\n" + fieldNames)) {
_itemBeingEdited.setType(itemTypeID);
_itemBeingEdited.save();
loadPane(0);
}
// Revert the menu (which changes before the pop-up)
else {
menu.value = _itemBeingEdited.getType();
}
}
function onViewClick(button, event) {
if (button.getAttribute('viewURL')) {
ZoteroPane.loadURI(button.getAttribute('viewURL'), event);
}
else if (button.getAttribute('viewSnapshot')) {
ZoteroPane.viewAttachment(button.getAttribute('viewSnapshot'), event);
}
}
function onOpenURLClick(event)
{
var url = Zotero.OpenURL.resolve(_itemBeingEdited);
if (url)
{
ZoteroPane.loadURI(url, event);
}
}
function addDynamicRow(label, value, beforeElement)
{
var row = document.createElement("row");
row.appendChild(label);
row.appendChild(value);
if(beforeElement)
_dynamicFields.insertBefore(row, _beforeRow);
else
_dynamicFields.appendChild(row);
}
function addCreatorRow(firstName, lastName, typeID, singleField, unsaved, defaultRow)
{
// Disable the "+" button on previous rows
var elems = _dynamicFields.getElementsByAttribute('value', '+');
if (elems.length){
ZoteroItemPane.disableButton(elems[elems.length-1]);
}
if (singleField)
{
if (!lastName)
{
lastName = _defaultFullName;
}
}
else
{
if (!firstName)
{
firstName = _defaultFirstName;
}
if (!lastName)
{
lastName = _defaultLastName;
}
}
// Use the first entry in the drop-down for the default type
if (!typeID)
{
typeID = _creatorTypeMenu.childNodes[0].getAttribute('typeid');
}
var label = document.createElement("toolbarbutton");
label.setAttribute("label",Zotero.getString('creatorTypes.'+Zotero.CreatorTypes.getName(typeID))+":");
label.setAttribute("typeid", typeID);
label.setAttribute("popup","zotero-creator-type-menu");
label.setAttribute("fieldname",'creator-'+_creatorCount+'-typeID');
label.className = 'zotero-clicky';
// getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect()
// may need need to be adjusted if this DOM structure changes
var hbox = document.createElement("hbox");
// Name
var firstlast = document.createElement("hbox");
firstlast.setAttribute("flex","1");
var tabindex = _tabIndexMinCreators + (_creatorCount * 2);
var lastNameLabel = firstlast.appendChild(
createValueElement(
lastName,
'creator-' + _creatorCount + '-lastName',
tabindex
)
);
// Comma
var comma = document.createElement('label');
comma.setAttribute('value', ',');
comma.className = 'comma';
firstlast.appendChild(comma);
firstlast.appendChild(
createValueElement(
firstName,
'creator-' + _creatorCount + '-firstName',
tabindex + 1
)
);
if (singleField)
{
firstlast.lastChild.setAttribute('hidden', true);
}
_tabIndexMaxCreators = Math.max(_tabIndexMaxCreators, tabindex);
hbox.appendChild(firstlast);
// Single/double field toggle
var toggleButton = document.createElement('toolbarbutton');
toggleButton.setAttribute('fieldname', 'creator-' + _creatorCount + '-singleField');
toggleButton.className = 'zotero-clicky';
hbox.appendChild(toggleButton);
// Minus (-) button
var removeButton = document.createElement('label');
removeButton.setAttribute("value","-");
// If default first row, don't let user remove it
if (defaultRow){
disableButton(removeButton);
}
else {
removeButton.setAttribute("class","zotero-clicky");
removeButton.setAttribute("onclick","ZoteroItemPane.removeCreator("+_creatorCount+", this.parentNode.parentNode)");
}
hbox.appendChild(removeButton);
// Plus (+) button
var addButton = document.createElement('label');
addButton.setAttribute("value","+");
addButton.setAttribute("class","zotero-clicky");
// If row isn't saved, don't let user add more
if (unsaved)
{
disableButton(addButton);
}
else
{
_enablePlusButton(addButton, typeID, singleField);
}
hbox.appendChild(addButton);
_creatorCount++;
addDynamicRow(label, hbox, true);
// Set single/double field toggle mode
if (singleField)
{
switchCreatorMode(hbox.parentNode, true, true);
}
else
{
switchCreatorMode(hbox.parentNode, false, true);
}
// Focus new rows
if (unsaved && !defaultRow){
lastNameLabel.click();
}
}
/**
* Add a date row with a label editor and a ymd indicator to show date parsing
*/
function addDateRow(value, tabindex)
{
var label = document.createElement("label");
label.setAttribute("value", Zotero.getString("itemFields.date") + ':');
label.setAttribute("fieldname",'date');
label.setAttribute("onclick", "this.nextSibling.firstChild.blur()");
var hbox = document.createElement("hbox");
var elem = createValueElement(Zotero.Date.multipartToStr(value), 'date', tabindex);
// y-m-d status indicator
var datebox = document.createElement('hbox');
datebox.className = 'zotero-date-field-status';
var year = document.createElement('label');
var month = document.createElement('label');
var day = document.createElement('label');
year.setAttribute('value', Zotero.getString('date.abbreviation.year'));
month.setAttribute('value', Zotero.getString('date.abbreviation.month'));
day.setAttribute('value', Zotero.getString('date.abbreviation.day'));
// Display the date parts we have and hide the others
var sqldate = Zotero.Date.multipartToSQL(value);
year.setAttribute('hidden', !Zotero.Date.sqlHasYear(sqldate));
month.setAttribute('hidden', !Zotero.Date.sqlHasMonth(sqldate));
day.setAttribute('hidden', !Zotero.Date.sqlHasDay(sqldate));
datebox.appendChild(year);
datebox.appendChild(month);
datebox.appendChild(day);
var hbox = document.createElement('hbox');
hbox.setAttribute('flex', 1);
hbox.appendChild(elem);
hbox.appendChild(datebox);
addDynamicRow(label, hbox);
}
function switchCreatorMode(row, singleField, initial)
{
// Change if button position changes
// row->hbox->label->label->toolbarbutton
var button = row.lastChild.lastChild.previousSibling.previousSibling;
var hbox = button.previousSibling;
var lastName = hbox.firstChild;
var comma = hbox.firstChild.nextSibling;
var firstName = hbox.lastChild;
// Switch to single-field mode
if (singleField)
{
button.setAttribute('image', 'chrome://zotero/skin/textfield-dual.png');
button.setAttribute('tooltiptext', 'Switch to two fields');
lastName.setAttribute('singleField', 'true');
button.setAttribute('onclick', "ZoteroItemPane.switchCreatorMode(this.parentNode.parentNode, false)");
lastName.setAttribute('flex', '1');
// Remove firstname field from tabindex
var tab = parseInt(firstName.getAttribute('ztabindex'));
firstName.setAttribute('ztabindex', -1);
if (_tabIndexMaxCreators==tab)
{
_tabIndexMaxCreators--;
}
// Hide first name field and prepend to last name field
firstName.setAttribute('hidden', true);
comma.setAttribute('hidden', true);
if (!initial){
var first = _getFieldValue(firstName);
if (first && first != _defaultFirstName)
{
var last = _getFieldValue(lastName);
_setFieldValue(lastName, first + ' ' + last);
}
}
if (_getFieldValue(lastName) == _defaultLastName)
{
_setFieldValue(lastName, _defaultFullName);
}
}
// Switch to two-field mode
else
{
button.setAttribute('image', 'chrome://zotero/skin/textfield-single.png');
button.setAttribute('tooltiptext', 'Switch to single field');
lastName.setAttribute('singleField', 'false');
button.setAttribute('onclick', "ZoteroItemPane.switchCreatorMode(this.parentNode.parentNode, true)");
lastName.setAttribute('flex', '0');
// Add firstname field to tabindex
var tab = parseInt(lastName.getAttribute('ztabindex'));
firstName.setAttribute('ztabindex', tab + 1);
if (_tabIndexMaxCreators==tab)
{
_tabIndexMaxCreators++;
}
if (!initial){
// Move all but last word to first name field and show it
var last = _getFieldValue(lastName);
if (last && last != _defaultFullName)
{
var lastNameRE = /(.*?)[ ]*([^ ]+[ ]*)$/;
var parts = lastNameRE.exec(last);
if (parts[2] && parts[2] != last)
{
_setFieldValue(lastName, parts[2]);
_setFieldValue(firstName, parts[1]);
}
}
}
if (!_getFieldValue(firstName))
{
_setFieldValue(firstName, _defaultFirstName);
}
if (_getFieldValue(lastName) == _defaultFullName)
{
_setFieldValue(lastName, _defaultLastName);
}
firstName.setAttribute('hidden', false);
comma.setAttribute('hidden', false);
}
// Save the last-used field mode
Zotero.Prefs.set('lastCreatorFieldMode', singleField);
if (!initial)
{
var [, index, field] = button.getAttribute('fieldname').split('-');
var otherFields = getCreatorFields(row); // row
modifyCreator(index, field, !!singleField, otherFields);
}
}
function toggleAbstractExpand(label) {
var cur = Zotero.Prefs.get('lastAbstractExpand');
Zotero.Prefs.set('lastAbstractExpand', !cur);
var ab = label.nextSibling;
var valueText = _itemBeingEdited.getField('abstract');
var tabindex = ab.getAttribute('ztabindex');
var elem = createValueElement(valueText, 'abstract', tabindex);
ab.parentNode.replaceChild(elem, ab);
var text = Zotero.ItemFields.getLocalizedString(_itemBeingEdited.getType(), 'abstract') + ':';
// Add '(...)' before "Abstract:" for collapsed abstracts
if (valueText && cur) {
text = '(...) ' + text;
}
label.setAttribute('value', text);
}
function disableButton(button)
{
button.setAttribute('disabled', true);
button.setAttribute('onclick', false);
}
function _enablePlusButton(button, creatorTypeID, fieldMode)
{
button.setAttribute('disabled', false);
button.setAttribute("onclick",
"ZoteroItemPane.disableButton(this); ZoteroItemPane.addCreatorRow('', '', " + (creatorTypeID ? creatorTypeID : 'false') + ", " + fieldMode + ", true);");
}
function createValueElement(valueText, fieldName, tabindex, noedit)
{
var abstractAsVbox = (fieldName == 'abstract') &&
Zotero.Prefs.get('lastAbstractExpand');
if (fieldName == 'extra' || abstractAsVbox) {
var valueElement = document.createElement("vbox");
}
else
{
var valueElement = document.createElement("label");
}
valueElement.setAttribute('fieldname',fieldName);
if (!noedit){
valueElement.setAttribute('flex', 1);
valueElement.setAttribute('ztabindex', tabindex);
valueElement.setAttribute('onclick', 'ZoteroItemPane.showEditor(this)');
valueElement.className = 'zotero-clicky';
}
switch (fieldName){
case 'tag':
_tabIndexMaxTagsFields = Math.max(_tabIndexMaxTagsFields, tabindex);
break;
// Display the SQL date as a tooltip for the date field
case 'date':
valueElement.setAttribute('tooltiptext',
Zotero.Date.multipartToSQL(_itemBeingEdited.getField('date', true)));
break;
// Convert dates from UTC
case 'dateAdded':
case 'dateModified':
case 'accessDate':
if (valueText){
var date = Zotero.Date.sqlToDate(valueText, true);
valueText = date ? date.toLocaleString() : '';
// Don't show time for access date if none
if (fieldName == 'accessDate') {
valueText = valueText.replace('00:00:00 ', '');
}
}
break;
}
if (fieldName.indexOf('firstName')!=-1){
valueElement.setAttribute('flex', '1');
}
var firstSpace;
if(typeof valueText == 'string')
firstSpace = valueText.indexOf(" ");
// To support newlines in 'extra' fields, we use multiple
// <description> elements inside a vbox
if (fieldName == 'extra' || abstractAsVbox) {
var lines = valueText.split("\n");
for (var i = 0; i < lines.length; i++) {
var descriptionNode = document.createElement("description");
var linetext = document.createTextNode(lines[i]);
descriptionNode.appendChild(linetext);
valueElement.appendChild(descriptionNode);
}
}
// 29 == arbitrary length at which to chop uninterrupted text
else if ((firstSpace == -1 && valueText.length > 29 ) || firstSpace > 29
|| (fieldName &&
(fieldName.substr(0, 7) == 'creator') || fieldName == 'abstract')) {
if (fieldName == 'abstract') {
valueText = valueText.replace(/[\t\n]/g, ' ');
}
valueElement.setAttribute('crop', 'end');
valueElement.setAttribute('value',valueText);
}
else
{
// Wrap to multiple lines
valueElement.appendChild(document.createTextNode(valueText));
}
return valueElement;
}
function removeCreator(index, labelToDelete)
{
// If unsaved row, just remove element
if (!_itemBeingEdited.hasCreatorAt(index)){
labelToDelete.parentNode.removeChild(labelToDelete);
// Enable the "+" button on the previous row
var elems = _dynamicFields.getElementsByAttribute('value', '+');
var button = elems[elems.length-1];
var creatorFields = getCreatorFields(Zotero.getAncestorByTagName(button, 'row'));
_enablePlusButton(button, creatorFields.typeID, creatorFields.singleField);
_creatorCount--;
return;
}
_itemBeingEdited.removeCreator(index);
_itemBeingEdited.save();
loadPane(0);
}
function showEditor(elem)
{
// Blur any active fields
if (_dynamicFields) {
_dynamicFields.focus();
}
//Zotero.debug('Showing editor');
var fieldName = elem.getAttribute('fieldname');
var tabindex = elem.getAttribute('ztabindex');
var [field, creatorIndex, creatorField] = fieldName.split('-');
if (field == 'creator')
{
var c = _itemBeingEdited.getCreator(creatorIndex);
var value = c ? c[creatorField] : '';
var itemID = _itemBeingEdited.getID();
}
else if (fieldName=='tag')
{
var tagID = elem.parentNode.getAttribute('id').split('-')[1];
var value = tagID ? Zotero.Tags.getName(tagID) : '';
var itemID = Zotero.getAncestorByTagName(elem, 'tagsbox').item.getID();
}
else
{
var value = _itemBeingEdited.getField(fieldName);
var itemID = _itemBeingEdited.getID();
// Access date needs to be converted from UTC
if (fieldName=='accessDate' && value!='')
{
var localDate = Zotero.Date.sqlToDate(value, true);
var value = Zotero.Date.dateToSQL(localDate);
}
}
var t = document.createElement("textbox");
t.setAttribute('value',value);
t.setAttribute('fieldname', fieldName);
t.setAttribute('ztabindex', tabindex);
t.setAttribute('flex','1');
if (creatorField=='lastName')
{
t.setAttribute('singleField', elem.getAttribute('singleField'));
}
if (fieldName == 'abstract' || fieldName == 'extra')
{
t.setAttribute('multiline', true);
t.setAttribute('rows', 8);
}
else
{
var autoCompleteFields = [
'creator',
'journalAbbreviation',
'seriesTitle',
'seriesText',
'repository',
'callNumber',
'archiveLocation',
'language',
'rights',
'tag'
];
// Add the type-specific versions of these base fields
var baseACFields = ['publisher', 'publicationTitle', 'type',
'medium', 'place'];
autoCompleteFields = autoCompleteFields.concat(baseACFields);
for each(var baseField in baseACFields) {
var add = Zotero.ItemFields.getTypeFieldsFromBase(baseField, true)
autoCompleteFields = autoCompleteFields.concat(add);
}
// Add auto-complete for certain fields
if (autoCompleteFields.indexOf(field) != -1) {
t.setAttribute('type', 'autocomplete');
t.setAttribute('autocompletesearch', 'zotero');
var suffix = itemID ? itemID : '';
if (field=='creator') {
suffix = (elem.getAttribute('singleField')=='true'
? '1' : '0') + '-' + suffix;
}
t.setAttribute('autocompletesearchparam', fieldName + '/' + suffix);
t.setAttribute('ontextentered',
'ZoteroItemPane.handleCreatorAutoCompleteSelect(this)');
}
}
var box = elem.parentNode;
box.replaceChild(t,elem);
t.select();
t.setAttribute('onblur',"ZoteroItemPane.hideEditor(this, true)");
t.setAttribute('onkeypress',"return ZoteroItemPane.handleKeyPress(event)");
_tabDirection = false;
_lastTabIndex = tabindex;
}
/*
* Save a multiple-field selection for the creator autocomplete
* (e.g. "Shakespeare, William")
*/
function handleCreatorAutoCompleteSelect(textbox)
{
var comment = Zotero.Utilities.AutoComplete.getResultComment(textbox);
if (!comment)
{
return;
}
var [creatorID, numFields] = comment.split('-');
// If result uses two fields, save both
if (numFields==2)
{
var [field, creatorIndex, creatorField] =
textbox.getAttribute('fieldname').split('-');
var creator = Zotero.Creators.get(creatorID);
var otherField = creatorField=='lastName' ? 'firstName' : 'lastName';
// Update this textbox
textbox.setAttribute('value', creator[creatorField]);
textbox.value = creator[creatorField];
// Update the other label
if (otherField=='firstName'){
var label = textbox.nextSibling.nextSibling;
}
else if (otherField=='lastName'){
var label = textbox.previousSibling.previousSibling;
}
if (label.firstChild){
label.firstChild.nodeValue = creator[otherField];
}
else {
label.value = creator[otherField];
}
var row = textbox.parentNode.parentNode.parentNode;
var otherFields = ZoteroItemPane.getCreatorFields(row);
otherFields[otherField] = creator[otherField];
ZoteroItemPane.modifyCreator(creatorIndex, creatorField,
creator[creatorField], otherFields);
}
// Otherwise let the autocomplete popup handle matters
}
function handleKeyPress(event){
var target = event.target;
var focused = document.commandDispatcher.focusedElement;
switch (event.keyCode)
{
case event.DOM_VK_RETURN:
// Use shift-enter as the save action for the larger fields
if ((target.getAttribute('fieldname') == 'abstract'
|| target.getAttribute('fieldname') == 'extra')
&& !event.shiftKey)
{
break;
}
else if (target.getAttribute('fieldname')=='tag')
{
// If last tag row, create new one
var row = target.parentNode.parentNode;
if (row == row.parentNode.lastChild)
{
_tabDirection = 1;
var lastTag = true;
}
}
focused.blur();
// Return focus to items pane
if (!lastTag) {
var tree = document.getElementById('zotero-items-tree');
if (tree) {
tree.focus();
}
}
return false;
case event.DOM_VK_ESCAPE:
// Reset field to original value
target.value = target.getAttribute('value');
focused.blur();
// Return focus to items pane
var tree = document.getElementById('zotero-items-tree');
if (tree) {
tree.focus();
}
return false;
case event.DOM_VK_TAB:
_tabDirection = event.shiftKey ? -1 : 1;
// Blur the old manually -- not sure why this is necessary,
// but it prevents an immediate blur() on the next tag
focused.blur();
return false;
}
return true;
}
function hideEditor(t, saveChanges)
{
//Zotero.debug('Hiding editor');
var textbox = Zotero.getAncestorByTagName(t, 'textbox');
if (!textbox){
Zotero.debug('Textbox not found in hideEditor');
return;
}
var fieldName = textbox.getAttribute('fieldname');
var tabindex = textbox.getAttribute('ztabindex');
var value = t.value;
var elem;
var [field, creatorIndex, creatorField] = fieldName.split('-');
// Creator fields
if (field == 'creator')
{
var row = textbox.parentNode.parentNode.parentNode;
var otherFields = getCreatorFields(row);
if (saveChanges){
modifyCreator(creatorIndex, creatorField, value, otherFields);
}
var val = _itemBeingEdited.getCreator(creatorIndex)[creatorField];
if (!val){
// Reset to '(first)'/'(last)'/'(name)'
if (creatorField=='lastName')
{
val = otherFields['singleField']
? _defaultFullName : _defaultLastName;
}
else if (creatorField=='firstName')
{
val = _defaultFirstName;
}
}
elem = createValueElement(val, fieldName, tabindex);
// Reset creator mode settings
if (otherFields['singleField'])
{
switchCreatorMode(row, true, true);
}
else
{
switchCreatorMode(row, false, true);
}
}
// Tags
else if (fieldName=='tag')
{
var tagsbox = Zotero.getAncestorByTagName(textbox, 'tagsbox');
if (!tagsbox)
{
Zotero.debug('Tagsbox not found', 1);
return;
}
var row = textbox.parentNode;
var rows = row.parentNode;
// Tag id encoded as 'tag-1234'
var id = row.getAttribute('id').split('-')[1];
if (saveChanges)
{
if (id)
{
if (value)
{
// If trying to replace with another existing tag
// (which causes a delete of the row),
// clear the tab direction so we don't advance
// when the notifier kicks in
var existing = Zotero.Tags.getID(value, 0);
if (existing && id != existing)
{
_tabDirection = false;
}
var changed = tagsbox.replace(id, value);
if (changed)
{
return;
}
}
else
{
tagsbox.remove(id);
return;
}
}
// New tag
else
{
// If this is an existing automatic tag, it's going to be
// deleted and the number of rows will stay the same,
// so we have to compensate
var existingTypes = Zotero.Tags.getTypes(value);
if (existingTypes && existingTypes.indexOf(1) != -1) {
_lastTabIndex--;
}
var id = tagsbox.add(value);
// DEBUG: why does this need to continue if added?
}
}
if (id)
{
elem = createValueElement(value, 'tag', tabindex);
}
else
{
// Just remove the row
//
// If there's an open popup, this throws NODE CANNOT BE FOUND
try {
var row = rows.removeChild(row);
}
catch (e) {}
tagsbox.fixPopup();
_tabDirection = false;
return;
}
var focusMode = 'tags';
var focusBox = tagsbox;
}
// Fields
else
{
// Access date needs to be parsed and converted to UTC
if (fieldName=='accessDate' && value!='')
{
if (Zotero.Date.isSQLDate(value) || Zotero.Date.isSQLDateTime(value)) {
var localDate = Zotero.Date.sqlToDate(value);
value = Zotero.Date.dateToSQL(localDate, true);
}
else {
var d = Zotero.Date.strToDate(value);
value = null;
if (d.year && d.month != undefined && d.day) {
d = new Date(d.year, d.month, d.day);
value = Zotero.Date.dateToSQL(d, true);
}
}
}
if(saveChanges)
modifyField(fieldName,value);
elem = createValueElement(_itemBeingEdited.getField(fieldName), fieldName, tabindex);
}
var box = textbox.parentNode;
box.replaceChild(elem,textbox);
if (_tabDirection)
{
if (!focusMode)
{
var focusMode = 'info';
var focusBox = _dynamicFields;
}
_focusNextField(focusMode, focusBox, _lastTabIndex, _tabDirection==-1);
}
}
function modifyField(field, value)
{
_itemBeingEdited.setField(field,value);
return _itemBeingEdited.save();
}
function _getFieldValue(field)
{
return field.firstChild
? field.firstChild.nodeValue : field.value;
}
function _setFieldValue(field, value)
{
if (field.firstChild)
{
field.firstChild.nodeValue = value;
}
else
{
field.value = value;
}
}
function getCreatorFields(row){
var typeID = row.getElementsByTagName('toolbarbutton')[0].getAttribute('typeid');
var label1 = row.getElementsByTagName('hbox')[0].firstChild.firstChild;
var label2 = label1.parentNode.lastChild;
return {
lastName: label1.firstChild ? label1.firstChild.nodeValue
: label1.value,
firstName: label2.firstChild ? label2.firstChild.nodeValue
: label2.value,
typeID: typeID,
singleField: label1.getAttribute('singleField') == 'true'
}
}
function modifyCreator(index, field, value, otherFields)
{
if (otherFields){
var firstName = otherFields.firstName;
var lastName = otherFields.lastName;
var typeID = otherFields.typeID;
var singleField = otherFields.singleField;
// Ignore '(first)'/'(last)' or '(name)'
if (singleField || firstName == _defaultFirstName){
firstName = '';
}
if (lastName==_defaultFullName || lastName == _defaultLastName){
lastName = '';
}
}
else {
var creator = _itemBeingEdited.getCreator(index);
var firstName = creator['firstName'];
var lastName = creator['lastName'];
var typeID = creator['creatorTypeID'];
var singleField = creator['singleField'];
}
// Don't save empty creators
if (!_itemBeingEdited.hasCreatorAt(index) && !firstName && !lastName){
return;
}
switch (field){
case 'firstName':
firstName = value;
break;
case 'lastName':
lastName = value;
break;
case 'typeID':
typeID = value;
break;
case 'singleField':
singleField = value;
break;
}
_itemBeingEdited.setCreator(index, firstName, lastName, typeID, singleField);
_itemBeingEdited.save();
}
function removeNote(id)
{
var note = Zotero.Items.get(id);
if(note)
if(confirm(Zotero.getString('pane.item.notes.delete.confirm')))
note.erase();
}
function addNote()
{
ZoteroPane.openNoteWindow(_itemBeingEdited.getID());
}
function _noteToTitle(text)
{
var MAX_LENGTH = 100;
var t = text.substring(0, MAX_LENGTH);
var ln = t.indexOf("\n");
if (ln>-1 && ln<MAX_LENGTH)
{
t = t.substring(0, ln);
}
if(t == "")
{
return Zotero.getString('pane.item.notes.untitled');
}
else
{
return t;
}
}
function _updateNoteCount()
{
var c = _notesList.childNodes.length;
var str = 'pane.item.notes.count.';
switch (c){
case 0:
str += 'zero';
break;
case 1:
str += 'singular';
break;
default:
str += 'plural';
break;
}
_notesLabel.value = Zotero.getString(str, [c]);
}
function _updateAttachmentCount()
{
var c = _attachmentsList.childNodes.length;
var str = 'pane.item.attachments.count.';
switch (c){
case 0:
str += 'zero';
break;
case 1:
str += 'singular';
break;
default:
str += 'plural';
break;
}
_attachmentsLabel.value = Zotero.getString(str, [c]);
}
function removeAttachment(id)
{
var attachment = Zotero.Items.get(id);
if(attachment)
if(confirm(Zotero.getString('pane.item.attachments.delete.confirm')))
attachment.erase();
}
function addAttachmentFromDialog(link)
{
ZoteroPane.addAttachmentFromDialog(link, _itemBeingEdited.getID());
}
function addAttachmentFromPage(link)
{
ZoteroPane.addAttachmentFromPage(link, _itemBeingEdited.getID());
}
function focusFirstField(mode) {
switch (mode) {
case 'info':
_focusNextField('info', _dynamicFields, 0, false);
break;
}
}
/*
* Advance the field focus forward or backward
*
* Note: We're basically replicating the built-in tabindex functionality,
* which doesn't work well with the weird label/textbox stuff we're doing.
* (The textbox being tabbed away from is deleted before the blur()
* completes, so it doesn't know where it's supposed to go next.)
*/
function _focusNextField(mode, box, tabindex, back){
tabindex = parseInt(tabindex);
if (back)
{
if (mode=='info')
{
switch (tabindex)
{
case 1:
//Zotero.debug('At beginning');
document.getElementById('zotero-editpane-type-menu').focus();
return false;
case _tabIndexMinCreators:
var nextIndex = 1;
break;
case _tabIndexMinFields:
var nextIndex = _tabIndexMaxCreators;
break;
default:
var nextIndex = tabindex - 1;
}
}
else if (mode=='tags')
{
switch (tabindex)
{
case 1:
return false;
default:
var nextIndex = tabindex - 1;
}
}
}
else
{
if (mode=='info')
{
switch (tabindex)
{
case 1:
var nextIndex = _tabIndexMinCreators;
break;
case _tabIndexMaxCreators:
var nextIndex = _tabIndexMinFields;
break;
case _tabIndexMaxInfoFields:
//Zotero.debug('At end');
return false;
default:
var nextIndex = tabindex + 1;
}
}
else if (mode=='tags')
{
switch (tabindex)
{
case _tabIndexMaxTagsFields:
// In tags box, keep going to create new row
var nextIndex = tabindex + 1;
break;
default:
var nextIndex = tabindex + 1;
}
}
}
Zotero.debug('Looking for tabindex ' + nextIndex, 4);
switch (mode)
{
case 'info':
var next = box.getElementsByAttribute('ztabindex', nextIndex);
if (!next[0])
{
//Zotero.debug("Next field not found");
return _focusNextField(mode, box, nextIndex, back);
}
break;
// Tags pane
case 'tags':
var next = document.getAnonymousNodes(box)[0].
getElementsByAttribute('ztabindex', nextIndex);
if (!next[0]){
next[0] = box.addDynamicRow();
}
break;
}
next[0].click();
ensureElementIsVisible(next[0]);
return true;
}
}
addEventListener("load", function(e) { ZoteroItemPane.onLoad(e); }, false);