![Dan Stillman](/assets/img/avatar_default.png)
And fixed a bug that caused the text of some search conditions to not display when editing a previously saved search Also added autocomplete to more fields in metadata pane: - creator - publisher - place - publicationTitle - journalAbbreviation - seriesTitle - seriesText It should also do the type and medium fields, but they need to be separated out first--e.g. artworkType, interviewMedium--since they're describing different data
1237 lines
31 KiB
JavaScript
1237 lines
31 KiB
JavaScript
/*
|
|
Zotero
|
|
Copyright (C) 2006 Center for History and New Media, George Mason University, Fairfax, VA
|
|
http://chnm.gmu.edu/
|
|
*/
|
|
|
|
var ScholarItemPane = new function()
|
|
{
|
|
var _dynamicFields;
|
|
var _creatorTypeMenu;
|
|
var _beforeRow;
|
|
var _notesList;
|
|
var _linksBox;
|
|
var _notesLabel;
|
|
|
|
var _creatorCount;
|
|
|
|
var _lastPane;
|
|
var _loaded;
|
|
|
|
var _itemBeingEdited;
|
|
|
|
var _lastTabIndex;
|
|
var _tabDirection;
|
|
var _tabIndexMinCreators = 10;
|
|
var _tabIndexMaxCreators = 0;
|
|
var _tabIndexMinFields = 1000;
|
|
var _tabIndexMaxInfoFields = 0;
|
|
var _tabIndexMaxTagsFields = 0;
|
|
|
|
const _defaultFirstName =
|
|
'(' + Scholar.getString('pane.item.defaultFirstName') + ')';
|
|
const _defaultLastName =
|
|
'(' + Scholar.getString('pane.item.defaultLastName') + ')';
|
|
const _defaultFullName =
|
|
'(' + Scholar.getString('pane.item.defaultFullName') + ')';
|
|
|
|
this.onLoad = onLoad;
|
|
this.viewItem = viewItem;
|
|
this.loadPane = loadPane;
|
|
this.changeTypeTo = changeTypeTo;
|
|
this.onGoToURLClick = onGoToURLClick;
|
|
this.onOpenURLClick = onOpenURLClick;
|
|
this.addCreatorRow = addCreatorRow;
|
|
this.switchCreatorMode = switchCreatorMode;
|
|
this.disableButton = disableButton;
|
|
this.createValueElement = createValueElement;
|
|
this.removeCreator = removeCreator;
|
|
this.showEditor = showEditor;
|
|
this.handleKeyPress = handleKeyPress;
|
|
this.hideEditor = hideEditor;
|
|
this.getCreatorFields = getCreatorFields;
|
|
this.modifyCreator = modifyCreator;
|
|
this.removeNote = removeNote;
|
|
this.addNote = addNote;
|
|
this.removeAttachment = removeAttachment;
|
|
this.addAttachmentFromDialog = addAttachmentFromDialog;
|
|
this.addAttachmentFromPage = addAttachmentFromPage;
|
|
|
|
|
|
function onLoad()
|
|
{
|
|
_tabs = document.getElementById('scholar-view-tabs');
|
|
|
|
// Not in item pane, so skip the introductions
|
|
if (!_tabs)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_dynamicFields = document.getElementById('editpane-dynamic-fields');
|
|
_itemTypeMenu = document.getElementById('editpane-type-menu');
|
|
_creatorTypeMenu = document.getElementById('creatorTypeMenu');
|
|
_notesList = document.getElementById('editpane-dynamic-notes');
|
|
_notesLabel = document.getElementById('editpane-notes-label');
|
|
_attachmentsList = document.getElementById('editpane-dynamic-attachments');
|
|
_attachmentsLabel = document.getElementById('editpane-attachments-label');
|
|
_tagsBox = document.getElementById('editpane-tags');
|
|
_relatedBox = document.getElementById('editpane-related');
|
|
|
|
var creatorTypes = Scholar.CreatorTypes.getTypes();
|
|
for(var i = 0; i < creatorTypes.length; i++)
|
|
{
|
|
var menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute("label",Scholar.getString('creatorTypes.'+creatorTypes[i]['name']));
|
|
menuitem.setAttribute("typeid",creatorTypes[i]['id']);
|
|
if(creatorTypes[i]['id'] == 0)
|
|
menuitem.setAttribute("selected",true);
|
|
_creatorTypeMenu.appendChild(menuitem);
|
|
}
|
|
|
|
var itemTypes = Scholar.ItemTypes.getTypes();
|
|
for(var i = 0; i<itemTypes.length; i++)
|
|
if(itemTypes[i]['name'] != 'note' && itemTypes[i]['name'] != 'attachment')
|
|
_itemTypeMenu.appendItem(Scholar.getString("itemTypes."+itemTypes[i]['name']),itemTypes[i]['id']);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Loads an item
|
|
*/
|
|
function viewItem(thisItem)
|
|
{
|
|
//Scholar.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');
|
|
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)
|
|
{
|
|
//Scholar.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)
|
|
{
|
|
// Enable/disable "View =>" button
|
|
testView: try
|
|
{
|
|
var validURI = false;
|
|
var spec = _itemBeingEdited.getField('url');
|
|
if (!spec)
|
|
{
|
|
break testView;
|
|
}
|
|
var uri = Components.classes["@mozilla.org/network/io-service;1"]
|
|
.getService(Components.interfaces.nsIIOService)
|
|
.newURI(spec, null, null);
|
|
validURI = uri.scheme && uri.host;
|
|
}
|
|
catch (e){}
|
|
document.getElementById('tb-go-to-url').setAttribute('disabled', !validURI);
|
|
|
|
// Enable/disable "Locate =>" (OpenURL) button
|
|
switch (_itemBeingEdited.getType())
|
|
{
|
|
// DEBUG: handle descendents of these types as well?
|
|
case Scholar.ItemTypes.getID('book'):
|
|
case Scholar.ItemTypes.getID('bookSection'):
|
|
case Scholar.ItemTypes.getID('journalArticle'):
|
|
case Scholar.ItemTypes.getID('thesis'):
|
|
var openURL = true;
|
|
break;
|
|
|
|
default:
|
|
var openURL = false;
|
|
}
|
|
document.getElementById('tb-openurl').setAttribute('disabled', !openURL);
|
|
|
|
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 = Scholar.ItemFields.getItemTypeFields(_itemBeingEdited.getField("itemTypeID"));
|
|
for(var i = 0; i<fields.length; i++)
|
|
fieldNames.push(Scholar.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]);
|
|
|
|
// Convert dates from UTC
|
|
if (fieldNames[i]=='dateAdded' || fieldNames[i]=='dateModified'
|
|
|| fieldNames[i]=='accessDate'){
|
|
var date = Scholar.Date.sqlToDate(val, true);
|
|
val = date ? date.toLocaleString() : '';
|
|
}
|
|
|
|
// Start tabindex at 1000 after creators
|
|
var tabindex = editable ? (i>0 ? _tabIndexMinFields + i : 1) : 0;
|
|
|
|
var valueElement = createValueElement(
|
|
val, editable ? fieldNames[i] : null, tabindex
|
|
);
|
|
|
|
_tabIndexMaxInfoFields = Math.max(_tabIndexMaxInfoFields, tabindex);
|
|
|
|
var label = document.createElement("label");
|
|
label.setAttribute("value",Scholar.getString("itemFields."+fieldNames[i])+":");
|
|
label.setAttribute("onclick","this.nextSibling.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['isInstitution']);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add default row
|
|
addCreatorRow('', '', 1, false, true, true);
|
|
}
|
|
|
|
var focusMode = 'info';
|
|
var focusBox = _dynamicFields;
|
|
}
|
|
|
|
// Notes pane
|
|
else if(index == 1)
|
|
{
|
|
while(_notesList.hasChildNodes())
|
|
_notesList.removeChild(_notesList.firstChild);
|
|
|
|
var notes = Scholar.Items.get(_itemBeingEdited.getNotes());
|
|
if(notes.length)
|
|
{
|
|
for(var i = 0; i < notes.length; i++)
|
|
{
|
|
var icon = document.createElement('image');
|
|
icon.setAttribute('src','chrome://scholar/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',"ScholarPane.selectItem("+notes[i].getID()+");");
|
|
box.setAttribute('class','clicky');
|
|
box.appendChild(icon);
|
|
box.appendChild(label);
|
|
|
|
var removeButton = document.createElement('label');
|
|
removeButton.setAttribute("value","-");
|
|
removeButton.setAttribute("class","clicky");
|
|
removeButton.setAttribute("onclick","ScholarItemPane.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 = Scholar.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();
|
|
if(linkMode == Scholar.Attachments.LINK_MODE_IMPORTED_FILE)
|
|
{
|
|
itemType = "-file";
|
|
}
|
|
else if(linkMode == Scholar.Attachments.LINK_MODE_LINKED_FILE)
|
|
{
|
|
itemType = "-link";
|
|
}
|
|
else if(linkMode == Scholar.Attachments.LINK_MODE_IMPORTED_URL)
|
|
{
|
|
itemType = "-snapshot";
|
|
}
|
|
else if(linkMode == Scholar.Attachments.LINK_MODE_LINKED_URL)
|
|
{
|
|
itemType = "-web-link";
|
|
}
|
|
icon.setAttribute('src','chrome://scholar/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',"ScholarPane.selectItem('"+attachments[i].getID()+"')");
|
|
box.setAttribute('class','clicky');
|
|
box.appendChild(icon);
|
|
box.appendChild(label);
|
|
|
|
var removeButton = document.createElement('label');
|
|
removeButton.setAttribute("value","-");
|
|
removeButton.setAttribute("class","clicky");
|
|
removeButton.setAttribute("onclick","ScholarItemPane.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)
|
|
{
|
|
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 changeTypeTo(id)
|
|
{
|
|
if(id != _itemBeingEdited.getType() && confirm(Scholar.getString('pane.item.changeType')))
|
|
{
|
|
_itemBeingEdited.setType(id);
|
|
_itemBeingEdited.save();
|
|
loadPane(0);
|
|
}
|
|
}
|
|
|
|
function onGoToURLClick()
|
|
{
|
|
window.loadURI(_itemBeingEdited.getField('url'));
|
|
}
|
|
|
|
function onOpenURLClick()
|
|
{
|
|
var url = Scholar.OpenURL.resolve(_itemBeingEdited);
|
|
if (url)
|
|
{
|
|
window.loadURI(Scholar.OpenURL.resolve(_itemBeingEdited));
|
|
}
|
|
}
|
|
|
|
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){
|
|
ScholarItemPane.disableButton(elems[elems.length-1]);
|
|
}
|
|
|
|
if (singleField)
|
|
{
|
|
if (!lastName)
|
|
{
|
|
lastName = _defaultFullName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!firstName)
|
|
{
|
|
firstName = _defaultFirstName;
|
|
}
|
|
if (!lastName)
|
|
{
|
|
lastName = _defaultLastName;
|
|
}
|
|
}
|
|
|
|
var label = document.createElement("label");
|
|
label.setAttribute("value",Scholar.getString('creatorTypes.'+Scholar.CreatorTypes.getName(typeID))+":");
|
|
label.setAttribute("popup","creatorTypeMenu");
|
|
label.setAttribute("fieldname",'creator-'+_creatorCount+'-typeID');
|
|
label.className = 'clicky';
|
|
|
|
// getCreatorFields() and switchCreatorMode() 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);
|
|
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 = '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","clicky");
|
|
removeButton.setAttribute("onclick","ScholarItemPane.removeCreator("+_creatorCount+", this.parentNode.parentNode)");
|
|
}
|
|
hbox.appendChild(removeButton);
|
|
|
|
// Plus (+) button
|
|
var addButton = document.createElement('label');
|
|
addButton.setAttribute("value","+");
|
|
// If row isn't saved, don't let user add more
|
|
if (unsaved)
|
|
{
|
|
disableButton(addButton);
|
|
}
|
|
else
|
|
{
|
|
_enablePlusButton(addButton);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
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://scholar/skin/textfield-dual.png');
|
|
button.setAttribute('tooltiptext', 'Switch to two fields');
|
|
lastName.setAttribute('singleField', 'true');
|
|
button.setAttribute('onclick', "ScholarItemPane.switchCreatorMode(this.parentNode.parentNode, false)");
|
|
|
|
// Remove firstname field from tabindex
|
|
var tab = parseInt(firstName.getAttribute('tabindex'));
|
|
firstName.setAttribute('tabindex', -1);
|
|
if (_tabIndexMaxCreators==tab)
|
|
{
|
|
_tabIndexMaxCreators--;
|
|
}
|
|
|
|
// Hide first name field and prepend to last name field
|
|
firstName.setAttribute('hidden', true);
|
|
comma.setAttribute('hidden', true);
|
|
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://scholar/skin/textfield-single.png');
|
|
button.setAttribute('tooltiptext', 'Switch to single field');
|
|
lastName.setAttribute('singleField', 'false');
|
|
button.setAttribute('onclick', "ScholarItemPane.switchCreatorMode(this.parentNode.parentNode, true)");
|
|
|
|
// Add firstname field to tabindex
|
|
var tab = parseInt(lastName.getAttribute('tabindex'));
|
|
firstName.setAttribute('tabindex', tab + 1);
|
|
if (_tabIndexMaxCreators==tab)
|
|
{
|
|
_tabIndexMaxCreators++;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (!initial)
|
|
{
|
|
var [, index, field] = button.getAttribute('fieldname').split('-');
|
|
|
|
var otherFields = getCreatorFields(row); // row
|
|
modifyCreator(index, field, !!singleField, otherFields);
|
|
}
|
|
}
|
|
|
|
function disableButton(button)
|
|
{
|
|
button.setAttribute('disabled', true);
|
|
button.setAttribute('class', 'unclicky');
|
|
button.setAttribute('onclick', false);
|
|
}
|
|
|
|
function _enablePlusButton(button)
|
|
{
|
|
button.setAttribute('disabled', false);
|
|
button.setAttribute("class","clicky");
|
|
button.setAttribute("onclick","ScholarItemPane.disableButton(this); ScholarItemPane.addCreatorRow('','',1,false,true);");
|
|
}
|
|
|
|
function createValueElement(valueText, fieldName, tabindex)
|
|
{
|
|
if (fieldName=='extra')
|
|
{
|
|
var valueElement = document.createElement("vbox");
|
|
}
|
|
else
|
|
{
|
|
var valueElement = document.createElement("label");
|
|
}
|
|
|
|
if(fieldName)
|
|
{
|
|
valueElement.setAttribute('fieldname',fieldName);
|
|
valueElement.setAttribute('tabindex', tabindex);
|
|
valueElement.setAttribute('onclick', 'ScholarItemPane.showEditor(this)');
|
|
valueElement.className = 'clicky';
|
|
|
|
if (fieldName=='tag')
|
|
{
|
|
_tabIndexMaxTagsFields = Math.max(_tabIndexMaxTagsFields, tabindex);
|
|
}
|
|
}
|
|
|
|
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')
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
else if ((firstSpace == -1 && valueText.length > 29 ) || firstSpace > 29)
|
|
{
|
|
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', '+');
|
|
_enablePlusButton(elems[elems.length-1]);
|
|
|
|
_creatorCount--;
|
|
return;
|
|
}
|
|
_itemBeingEdited.removeCreator(index);
|
|
_itemBeingEdited.save();
|
|
loadPane(0);
|
|
}
|
|
|
|
function showEditor(elem)
|
|
{
|
|
//Scholar.debug('Showing editor');
|
|
|
|
var fieldName = elem.getAttribute('fieldname');
|
|
var tabindex = elem.getAttribute('tabindex');
|
|
|
|
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 ? Scholar.Tags.getName(tagID) : '';
|
|
var itemID = Scholar.getAncestorByTagName(elem, 'tagsbox').item.getID();
|
|
}
|
|
else
|
|
{
|
|
var value = _itemBeingEdited.getField(fieldName);
|
|
var itemID = _itemBeingEdited.getID();
|
|
}
|
|
|
|
var t = document.createElement("textbox");
|
|
t.setAttribute('value',value);
|
|
t.setAttribute('fieldname', fieldName);
|
|
t.setAttribute('tabindex', tabindex);
|
|
t.setAttribute('flex','1');
|
|
|
|
if (creatorField=='lastName')
|
|
{
|
|
t.setAttribute('singleField', elem.getAttribute('singleField'));
|
|
}
|
|
|
|
if (fieldName=='extra')
|
|
{
|
|
t.setAttribute('multiline', true);
|
|
t.setAttribute('rows', 8);
|
|
}
|
|
else
|
|
{
|
|
// Add auto-complete for certain fields
|
|
switch (field)
|
|
{
|
|
case 'creator':
|
|
case 'publisher':
|
|
case 'place':
|
|
case 'publicationTitle':
|
|
case 'journalAbbreviation':
|
|
case 'seriesTitle':
|
|
case 'seriesText':
|
|
// DEBUG: should have type and medium, but they need to be
|
|
// broken out first into multiple fields (artworkType,
|
|
// interviewMedium, etc.)
|
|
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);
|
|
break;
|
|
}
|
|
}
|
|
var box = elem.parentNode;
|
|
box.replaceChild(t,elem);
|
|
|
|
t.select();
|
|
|
|
t.setAttribute('onblur',"ScholarItemPane.hideEditor(this, true)");
|
|
t.setAttribute('onkeypress',"return ScholarItemPane.handleKeyPress(event)");
|
|
|
|
_tabDirection = false;
|
|
_lastTabIndex = tabindex;
|
|
}
|
|
|
|
function handleKeyPress(event){
|
|
var target = document.commandDispatcher.focusedElement;
|
|
switch (event.keyCode)
|
|
{
|
|
case event.DOM_VK_RETURN:
|
|
// Use shift-enter as the save action for the 'extra' field
|
|
if (target.parentNode.parentNode.getAttribute('fieldname')=='extra'
|
|
&& !event.shiftKey)
|
|
{
|
|
break;
|
|
}
|
|
else if (target.parentNode.parentNode.
|
|
parentNode.getAttribute('fieldname')=='tag')
|
|
{
|
|
// If last tag row, create new one
|
|
var row = target.parentNode.parentNode.parentNode.parentNode;
|
|
if (row == row.parentNode.lastChild)
|
|
{
|
|
_tabDirection = 1;
|
|
}
|
|
}
|
|
target.blur();
|
|
return false;
|
|
|
|
case event.DOM_VK_ESCAPE:
|
|
target.blur();
|
|
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
|
|
target.blur();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function hideEditor(t, saveChanges)
|
|
{
|
|
//Scholar.debug('Hiding editor');
|
|
var textbox = Scholar.getAncestorByTagName(t, 'textbox');
|
|
if (!textbox){
|
|
Scholar.debug('Textbox not found in hideEditor');
|
|
return;
|
|
}
|
|
var fieldName = textbox.getAttribute('fieldname');
|
|
var tabindex = textbox.getAttribute('tabindex');
|
|
|
|
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 = Scholar.getAncestorByTagName(textbox, 'tagsbox');
|
|
if (!tagsbox)
|
|
{
|
|
Scholar.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 = Scholar.Tags.getID(value);
|
|
if (existing && id != existing)
|
|
{
|
|
_tabDirection = false;
|
|
}
|
|
var changed = tagsbox.replace(id, value);
|
|
if (changed)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tagsbox.remove(id);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var id = tagsbox.add(value);
|
|
}
|
|
}
|
|
|
|
if (id)
|
|
{
|
|
elem = createValueElement(value, 'tag', tabindex);
|
|
}
|
|
else
|
|
{
|
|
// Just remove the row
|
|
var row = rows.removeChild(row);
|
|
tagsbox.fixPopup();
|
|
_tabDirection = false;
|
|
return;
|
|
}
|
|
|
|
var focusMode = 'tags';
|
|
var focusBox = tagsbox;
|
|
}
|
|
|
|
// Fields
|
|
else
|
|
{
|
|
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);
|
|
_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 type = row.getElementsByTagName('label')[0].getAttribute('value');
|
|
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: Scholar.CreatorTypes.getID(type.substr(0, type.length-1).toLowerCase()),
|
|
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['isInstitution'];
|
|
}
|
|
|
|
// 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 = Scholar.Items.get(id);
|
|
if(note)
|
|
if(confirm(Scholar.getString('pane.item.notes.delete.confirm')))
|
|
note.erase();
|
|
}
|
|
|
|
function addNote()
|
|
{
|
|
ScholarPane.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 Scholar.getString('pane.item.notes.untitled');
|
|
}
|
|
else
|
|
{
|
|
return t;
|
|
}
|
|
}
|
|
|
|
function _updateNoteCount()
|
|
{
|
|
var c = _notesList.childNodes.length;
|
|
|
|
_notesLabel.value = Scholar.getString('pane.item.notes.count.'+(c != 1 ? "plural" : "singular")).replace('%1',c) + ":";
|
|
}
|
|
|
|
function _updateAttachmentCount()
|
|
{
|
|
var c = _attachmentsList.childNodes.length;
|
|
|
|
_attachmentsLabel.value = Scholar.getString('pane.item.attachments.count.'+(c != 1 ? "plural" : "singular")).replace('%1',c) + ":";
|
|
}
|
|
|
|
function removeAttachment(id)
|
|
{
|
|
var attachment = Scholar.Items.get(id);
|
|
if(attachment)
|
|
if(confirm(Scholar.getString('pane.item.attachments.delete.confirm')))
|
|
attachment.erase();
|
|
}
|
|
|
|
function addAttachmentFromDialog(link)
|
|
{
|
|
ScholarPane.addAttachmentFromDialog(link, _itemBeingEdited.getID());
|
|
}
|
|
|
|
function addAttachmentFromPage(link)
|
|
{
|
|
ScholarPane.addAttachmentFromPage(link, _itemBeingEdited.getID());
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.)
|
|
*
|
|
* Use of the 'tabindex' attribute is arbitrary.
|
|
*/
|
|
function _focusNextField(mode, box, tabindex, back){
|
|
tabindex = parseInt(tabindex);
|
|
if (back)
|
|
{
|
|
if (mode=='info')
|
|
{
|
|
switch (tabindex)
|
|
{
|
|
case 1:
|
|
//Scholar.debug('At beginning');
|
|
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:
|
|
//Scholar.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
Scholar.debug('Looking for tabindex ' + nextIndex, 4);
|
|
switch (mode)
|
|
{
|
|
case 'info':
|
|
var next = box.getElementsByAttribute('tabindex', nextIndex);
|
|
if (!next[0])
|
|
{
|
|
//Scholar.debug("Next field not found");
|
|
return _focusNextField(mode, box, nextIndex, back);
|
|
}
|
|
break;
|
|
|
|
// Tags pane
|
|
case 'tags':
|
|
var next = document.getAnonymousNodes(box)[0].
|
|
getElementsByAttribute('tabindex', nextIndex);
|
|
if (!next[0]){
|
|
next[0] = box.addDynamicRow();
|
|
}
|
|
break;
|
|
}
|
|
|
|
next[0].click();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
addEventListener("load", function(e) { ScholarItemPane.onLoad(e); }, false);
|