2514 lines
77 KiB
XML
2514 lines
77 KiB
XML
<?xml version="1.0"?>
|
|
<!--
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright © 2009 Center for History and New Media
|
|
George Mason University, Fairfax, Virginia, USA
|
|
http://zotero.org
|
|
|
|
This file is part of Zotero.
|
|
|
|
Zotero is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Zotero is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
-->
|
|
|
|
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
|
|
<!-- <!DOCTYPE bindings SYSTEM "chrome://zotero/locale/itembox.dtd"> -->
|
|
|
|
<bindings xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
<binding id="item-box">
|
|
<resources>
|
|
<stylesheet src="chrome://zotero/skin/bindings/itembox.css"/>
|
|
<stylesheet src="chrome://zotero-platform/content/itembox.css"/>
|
|
</resources>
|
|
|
|
<implementation>
|
|
<!--
|
|
Public properties
|
|
-->
|
|
<field name="clickable">false</field>
|
|
<field name="editable">false</field>
|
|
<field name="saveOnEdit">false</field>
|
|
<field name="showTypeMenu">false</field>
|
|
<field name="hideEmptyFields">false</field>
|
|
<field name="clickByRow">false</field> <!-- Click entire row rather than just field value -->
|
|
<field name="clickByItem">false</field>
|
|
|
|
<field name="clickHandler"/>
|
|
<field name="blurHandler"/>
|
|
<field name="eventHandlers">[]</field>
|
|
|
|
<field name="_initialVisibleCreators">10</field>
|
|
<field name="_displayAllCreators"/>
|
|
|
|
<!-- Modes are predefined settings groups for particular tasks -->
|
|
<field name="_mode">"view"</field>
|
|
<property name="mode" onget="return this._mode;">
|
|
<setter>
|
|
<![CDATA[
|
|
this.clickable = false;
|
|
this.editable = false;
|
|
this.saveOnEdit = false;
|
|
this.showTypeMenu = false;
|
|
this.hideEmptyFields = false;
|
|
this.clickByRow = false;
|
|
this.clickByItem = false;
|
|
|
|
switch (val) {
|
|
case 'view':
|
|
break;
|
|
|
|
case 'edit':
|
|
this.clickable = true;
|
|
this.editable = true;
|
|
this.saveOnEdit = true
|
|
this.showTypeMenu = true;
|
|
this.clickHandler = this.showEditor;
|
|
this.blurHandler = this.hideEditor;
|
|
break;
|
|
|
|
case 'merge':
|
|
this.clickByItem = true;
|
|
break;
|
|
|
|
case 'mergeedit':
|
|
this.clickable = true;
|
|
this.editable = true;
|
|
this.saveOnEdit = false;
|
|
this.showTypeMenu = true;
|
|
this.clickHandler = this.showEditor;
|
|
this.blurHandler = this.hideEditor;
|
|
break;
|
|
|
|
case 'fieldmerge':
|
|
this.hideEmptyFields = true;
|
|
this._fieldAlternatives = {};
|
|
break;
|
|
|
|
default:
|
|
throw ("Invalid mode '" + val + "' in itembox.xml");
|
|
}
|
|
|
|
this._mode = val;
|
|
document.getAnonymousNodes(this)[0].setAttribute('mode', val);
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<field name="_item"/>
|
|
<property name="item" onget="return this._item;">
|
|
<setter>
|
|
<![CDATA[
|
|
if (!(val instanceof Zotero.Item)) {
|
|
throw ("<zoteroitembox>.item must be a Zotero.Item");
|
|
}
|
|
|
|
// When changing items, reset truncation of creator list
|
|
if (!this._item || val.id != this._item.id) {
|
|
this._displayAllCreators = false;
|
|
}
|
|
|
|
this._item = val;
|
|
this.refresh();
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!-- .ref is an alias for .item -->
|
|
<property name="ref"
|
|
onget="return this._item;"
|
|
onset="this.item = val; this.refresh();">
|
|
</property>
|
|
|
|
|
|
<!--
|
|
An array of field names that should be shown
|
|
even if they're empty and hideEmptyFields is set
|
|
-->
|
|
<field name="_visibleFields">[]</field>
|
|
<property name="visibleFields">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Array') {
|
|
throw ('visibleFields must be an array in <itembox>.visibleFields');
|
|
}
|
|
|
|
this._visibleFields = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!--
|
|
An array of field names that should be hidden
|
|
-->
|
|
<field name="_hiddenFields">[]</field>
|
|
<property name="hiddenFields">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Array') {
|
|
throw ('hiddenFields must be an array in <itembox>.visibleFields');
|
|
}
|
|
|
|
this._hiddenFields = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!--
|
|
An array of field names that should be clickable
|
|
even if this.clickable is false
|
|
-->
|
|
<field name="_clickableFields">[]</field>
|
|
<property name="clickableFields">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Array') {
|
|
throw ('clickableFields must be an array in <itembox>.clickableFields');
|
|
}
|
|
|
|
this._clickableFields = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!--
|
|
An array of field names that should be editable
|
|
even if this.editable is false
|
|
-->
|
|
<field name="_editableFields">[]</field>
|
|
<property name="editableFields">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Array') {
|
|
throw ('editableFields must be an array in <itembox>.editableFields');
|
|
}
|
|
|
|
this._editableFields = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!--
|
|
An object of alternative values for keyed fields
|
|
|
|
-->
|
|
<field name="_fieldAlternatives">{}</field>
|
|
<property name="fieldAlternatives">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Object') {
|
|
throw ('fieldAlternatives must be an Object in <itembox>.fieldAlternatives');
|
|
}
|
|
|
|
if (this.mode != 'fieldmerge') {
|
|
throw ('fieldAlternatives is valid only in fieldmerge mode in <itembox>.fieldAlternatives');
|
|
}
|
|
|
|
this._fieldAlternatives = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!--
|
|
An array of field names in the order they should appear
|
|
in the list; empty spaces can be created with null
|
|
-->
|
|
<field name="_fieldOrder">[]</field>
|
|
<property name="fieldOrder">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val.constructor.name != 'Array') {
|
|
throw ('fieldOrder must be an array in <itembox>.fieldOrder');
|
|
}
|
|
|
|
this._fieldOrder = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="itemTypeMenu" onget="return this._id('item-type-menu')"/>
|
|
|
|
<!-- Private properties -->
|
|
<property name="_dynamicFields" onget="return this._id('dynamic-fields')"/>
|
|
<property name="_creatorTypeMenu" onget="return this._id('creator-type-menu')"/>
|
|
|
|
<field name="_selectField"/>
|
|
<field name="_beforeRow"/>
|
|
<field name="_addCreatorRow"/>
|
|
<field name="_creatorCount"/>
|
|
|
|
<field name="_lastTabIndex"/>
|
|
<field name="_tabDirection"/>
|
|
<field name="_tabIndexMinCreators" readonly="true">10</field>
|
|
<field name="_tabIndexMaxCreators">0</field>
|
|
<field name="_tabIndexMinFields" readonly="true">1000</field>
|
|
<field name="_tabIndexMaxFields">0</field>
|
|
|
|
<property name="_defaultFirstName"
|
|
onget="return '(' + Zotero.getString('pane.item.defaultFirstName') + ')'"/>
|
|
<property name="_defaultLastName"
|
|
onget="return '(' + Zotero.getString('pane.item.defaultLastName') + ')'"/>
|
|
<property name="_defaultFullName"
|
|
onget="return '(' + Zotero.getString('pane.item.defaultFullName') + ')'"/>
|
|
<method name="refresh">
|
|
<body>
|
|
<![CDATA[
|
|
Zotero.debug('Refreshing item box');
|
|
|
|
if (!this.item) {
|
|
Zotero.debug('No item to refresh', 2);
|
|
return;
|
|
}
|
|
|
|
if (this.clickByItem) {
|
|
var itemBox = document.getAnonymousNodes(this)[0];
|
|
itemBox.setAttribute('onclick',
|
|
'document.getBindingParent(this).clickHandler(this)');
|
|
}
|
|
|
|
// Item type menu
|
|
if (this.showTypeMenu) {
|
|
// Build item type menu if it hasn't been built yet
|
|
if (this.itemTypeMenu.itemCount == 0) {
|
|
this.buildItemTypeMenu();
|
|
}
|
|
else {
|
|
this.updateItemTypeMenuSelection();
|
|
}
|
|
|
|
this.itemTypeMenu.parentNode.hidden = false;
|
|
}
|
|
else {
|
|
this.itemTypeMenu.parentNode.hidden = true;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear and rebuild metadata fields
|
|
//
|
|
while (this._dynamicFields.childNodes.length > 1) {
|
|
this._dynamicFields.removeChild(this._dynamicFields.lastChild);
|
|
}
|
|
|
|
var fieldNames = [];
|
|
|
|
// Manual field order
|
|
if (this._fieldOrder.length) {
|
|
for each(var field in this._fieldOrder) {
|
|
fieldNames.push(field);
|
|
}
|
|
}
|
|
// Get field order from database
|
|
else {
|
|
if (!this.showTypeMenu) {
|
|
fieldNames.push("itemType");
|
|
}
|
|
|
|
var fields = Zotero.ItemFields.getItemTypeFields(this.item.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 fieldName = fieldNames[i];
|
|
var val = '';
|
|
|
|
if (fieldName) {
|
|
var fieldID = Zotero.ItemFields.getID(fieldName);
|
|
if (fieldID && !Zotero.ItemFields.isValidForType(fieldID, this.item.itemTypeID)) {
|
|
fieldName = null;
|
|
}
|
|
}
|
|
|
|
if (fieldName) {
|
|
if (this._hiddenFields.indexOf(fieldName) != -1) {
|
|
continue;
|
|
}
|
|
|
|
// createValueElement() adds the itemTypeID as an attribute
|
|
// and converts it to a localized string for display
|
|
if (fieldName == 'itemType') {
|
|
val = this.item.getField('itemTypeID');
|
|
}
|
|
else {
|
|
val = this.item.getField(fieldName);
|
|
}
|
|
|
|
if (!val && this.hideEmptyFields
|
|
&& this._visibleFields.indexOf(fieldName) == -1
|
|
&& (this.mode != 'fieldmerge' || typeof this._fieldAlternatives[fieldName] == 'undefined')) {
|
|
continue;
|
|
}
|
|
|
|
var fieldIsClickable = this._fieldIsClickable(fieldName);
|
|
|
|
// Start tabindex at 1001 after creators
|
|
var tabindex = fieldIsClickable
|
|
? (i>0 ? this._tabIndexMinFields + i : 1) : 0;
|
|
this._tabIndexMaxFields = Math.max(this._tabIndexMaxFields, tabindex);
|
|
|
|
if (fieldIsClickable
|
|
&& !Zotero.Items.isPrimaryField(fieldName)
|
|
&& (Zotero.ItemFields.isFieldOfBase(Zotero.ItemFields.getID(fieldName), 'date')
|
|
// TEMP - filingDate
|
|
|| fieldName == 'filingDate')
|
|
// TEMP - NSF
|
|
&& fieldName != 'dateSent') {
|
|
this.addDateRow(fieldNames[i], this.item.getField(fieldName, true), tabindex);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
var valueElement = this.createValueElement(
|
|
val, fieldName, tabindex
|
|
);
|
|
|
|
var label = document.createElement("label");
|
|
label.setAttribute('fieldname', fieldName);
|
|
|
|
var prefix = '';
|
|
// Add '(...)' before 'Abstract:' for collapsed abstracts
|
|
if (fieldName == 'abstractNote') {
|
|
if (val && !Zotero.Prefs.get('lastAbstractExpand')) {
|
|
prefix = '(...) ';
|
|
}
|
|
}
|
|
|
|
if (fieldName) {
|
|
label.setAttribute("value", prefix +
|
|
Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, fieldName) + ":");
|
|
}
|
|
|
|
// TEMP - NSF (homepage)
|
|
if ((fieldName == 'url' || fieldName == 'homepage') && val) {
|
|
label.setAttribute("isButton", true);
|
|
// TODO: make getFieldValue non-private and use below instead
|
|
label.setAttribute("onclick", "ZoteroPane_Local.loadURI(this.nextSibling.firstChild ? this.nextSibling.firstChild.nodeValue : this.nextSibling.value, event)");
|
|
label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip'));
|
|
}
|
|
else if (fieldName == 'DOI' && val && typeof val == 'string') {
|
|
// Pull out DOI, in case there's a prefix
|
|
var doi = Zotero.Utilities.cleanDOI(val);
|
|
if (doi) {
|
|
doi = "http://dx.doi.org/" + encodeURIComponent(doi);
|
|
label.setAttribute("isButton", true);
|
|
label.setAttribute("onclick", "ZoteroPane_Local.loadURI('" + doi + "', event)");
|
|
label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip'));
|
|
valueElement.setAttribute('contextmenu', 'zotero-doi-menu');
|
|
|
|
var openURLMenuItem = document.getElementById('zotero-doi-menu-view-online');
|
|
openURLMenuItem.setAttribute("oncommand", "ZoteroPane_Local.loadURI('" + doi + "', event)");
|
|
|
|
var copyMenuItem = document.getElementById('zotero-doi-menu-copy');
|
|
copyMenuItem.setAttribute("oncommand", "Zotero.Utilities.Internal.copyTextToClipboard('" + doi + "')");
|
|
}
|
|
}
|
|
else if (fieldName == 'abstractNote') {
|
|
label.setAttribute("onclick",
|
|
"if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); } "
|
|
+ "else { document.getBindingParent(this).toggleAbstractExpand(this); }");
|
|
}
|
|
else {
|
|
label.setAttribute("onclick",
|
|
"if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); }");
|
|
}
|
|
|
|
var row = this.addDynamicRow(label, valueElement);
|
|
|
|
if (fieldName && this._selectField == fieldName) {
|
|
this.showEditor(valueElement);
|
|
}
|
|
|
|
// In field merge mode, add a button to switch field versions
|
|
else if (this.mode == 'fieldmerge' && typeof this._fieldAlternatives[fieldName] != 'undefined') {
|
|
var button = document.createElement("toolbarbutton");
|
|
button.className = 'zotero-field-version-button';
|
|
button.setAttribute('image', 'chrome://zotero/skin/treesource-duplicates.png');
|
|
button.setAttribute('type', 'menu');
|
|
|
|
var popup = button.appendChild(document.createElement("menupopup"));
|
|
|
|
for each(var v in this._fieldAlternatives[fieldName]) {
|
|
var menuitem = document.createElement("menuitem");
|
|
var sv = Zotero.Utilities.ellipsize(v, 60);
|
|
menuitem.setAttribute('label', sv);
|
|
if (v != sv) {
|
|
menuitem.setAttribute('tooltiptext', v);
|
|
}
|
|
menuitem.setAttribute('fieldName', fieldName);
|
|
menuitem.setAttribute('originalValue', v);
|
|
menuitem.setAttribute(
|
|
'oncommand',
|
|
"var binding = document.getBindingParent(this); "
|
|
+ "var item = binding.item; "
|
|
+ "item.setField(this.getAttribute('fieldName'), this.getAttribute('originalValue')); "
|
|
+ "var row = Zotero.getAncestorByTagName(this, 'row'); "
|
|
+ "binding.refresh();"
|
|
);
|
|
popup.appendChild(menuitem);
|
|
}
|
|
|
|
row.appendChild(button);
|
|
}
|
|
}
|
|
this._selectField = false;
|
|
|
|
//
|
|
// Creators
|
|
//
|
|
|
|
// Creator type menu
|
|
if (this.editable) {
|
|
while (this._creatorTypeMenu.hasChildNodes()) {
|
|
this._creatorTypeMenu.removeChild(this._creatorTypeMenu.firstChild);
|
|
}
|
|
|
|
var creatorTypes = Zotero.CreatorTypes.getTypesForItemType(this.item.itemTypeID);
|
|
var localized = {};
|
|
for (var i=0; i<creatorTypes.length; i++) {
|
|
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));
|
|
this._creatorTypeMenu.appendChild(menuitem);
|
|
}
|
|
|
|
var moveSep = document.createElement("menuseparator");
|
|
var moveUp = document.createElement("menuitem");
|
|
var moveDown = document.createElement("menuitem");
|
|
moveSep.id = "zotero-creator-move-sep";
|
|
moveUp.id = "zotero-creator-move-up";
|
|
moveDown.id = "zotero-creator-move-down";
|
|
moveUp.className = "zotero-creator-move";
|
|
moveDown.className = "zotero-creator-move";
|
|
moveUp.setAttribute("label", Zotero.getString('pane.item.creator.moveUp'));
|
|
moveDown.setAttribute("label", Zotero.getString('pane.item.creator.moveDown'));
|
|
this._creatorTypeMenu.appendChild(moveSep);
|
|
this._creatorTypeMenu.appendChild(moveUp);
|
|
this._creatorTypeMenu.appendChild(moveDown);
|
|
}
|
|
|
|
// Creator rows
|
|
|
|
// Place, in order of preference, after title, after type,
|
|
// or at beginning
|
|
var titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this.item.itemTypeID, 'title');
|
|
var field = this._dynamicFields.getElementsByAttribute('fieldname', Zotero.ItemFields.getName(titleFieldID)).item(0);
|
|
if (!field) {
|
|
var field = this._dynamicFields.getElementsByAttribute('fieldname', 'itemType').item(0);
|
|
}
|
|
if (field) {
|
|
this._beforeRow = field.parentNode.nextSibling;
|
|
}
|
|
else {
|
|
this._beforeRow = this._dynamicFields.firstChild;
|
|
}
|
|
|
|
this._creatorCount = 0;
|
|
var num = this.item.numCreators();
|
|
if (num > 0) {
|
|
// Limit number of creators display
|
|
var max = Math.min(num, this._initialVisibleCreators);
|
|
// If fewer than five more, just display
|
|
if (num < max + 5 || this._displayAllCreators) {
|
|
max = num;
|
|
}
|
|
for (var i = 0; i < max; i++) {
|
|
this.addCreatorRow(this.item.getCreator(i).ref,
|
|
this.item.getCreator(i).creatorTypeID);
|
|
|
|
// Display "+" button on all but last row
|
|
if (i == max - 2) {
|
|
this.disableCreatorAddButtons();
|
|
}
|
|
}
|
|
|
|
// Additional creators not displayed
|
|
if (num > max) {
|
|
this.addMoreCreatorsRow(num - max);
|
|
|
|
this.disableCreatorAddButtons();
|
|
}
|
|
else {
|
|
// If we didn't start with creators truncated,
|
|
// don't truncate for as long as we're viewing
|
|
// this item, so that added creators aren't
|
|
// immediately hidden
|
|
this._displayAllCreators = true;
|
|
|
|
if (this._addCreatorRow) {
|
|
this.addCreatorRow(false, this.item.getCreator(max-1).creatorTypeID, true);
|
|
this._addCreatorRow = false;
|
|
this.disableCreatorAddButtons();
|
|
}
|
|
}
|
|
}
|
|
else if (this.editable && Zotero.CreatorTypes.itemTypeHasCreators(this.item.itemTypeID)) {
|
|
// Add default row
|
|
this.addCreatorRow(false, false, true, true);
|
|
this.disableCreatorAddButtons();
|
|
}
|
|
|
|
// Move to next or previous field if (shift-)tab was pressed
|
|
if (this._lastTabIndex && this._tabDirection)
|
|
{
|
|
this._focusNextField(this._dynamicFields, this._lastTabIndex, this._tabDirection == -1);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="buildItemTypeMenu">
|
|
<body>
|
|
<![CDATA[
|
|
if (!this.item) {
|
|
return;
|
|
}
|
|
|
|
this.itemTypeMenu.removeAllItems();
|
|
|
|
var t = Zotero.ItemTypes.getTypes();
|
|
|
|
// Sort by localized name
|
|
var itemTypes = [];
|
|
for (var i=0; i<t.length; i++) {
|
|
itemTypes.push({
|
|
id: t[i].id,
|
|
name: t[i].name,
|
|
localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
|
|
});
|
|
}
|
|
var collation = Zotero.getLocaleCollation();
|
|
itemTypes.sort(function(a, b) {
|
|
return collation.compareString(1, a.localized, b.localized);
|
|
});
|
|
|
|
for (var i=0; i<itemTypes.length; i++) {
|
|
var name = itemTypes[i].name;
|
|
if (name != 'attachment' && name != 'note') {
|
|
this.itemTypeMenu.appendItem(itemTypes[i].localized, itemTypes[i].id);
|
|
}
|
|
}
|
|
|
|
this.updateItemTypeMenuSelection();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="updateItemTypeMenuSelection">
|
|
<body>
|
|
<![CDATA[
|
|
var listitems = this.itemTypeMenu.firstChild.childNodes;
|
|
for (var i=0, len=listitems.length; i < len; i++) {
|
|
if (listitems[i].getAttribute('value') == this.item.itemTypeID) {
|
|
this.itemTypeMenu.selectedIndex = i;
|
|
}
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="addDynamicRow">
|
|
<parameter name="label"/>
|
|
<parameter name="value"/>
|
|
<parameter name="beforeElement"/>
|
|
<body>
|
|
<![CDATA[
|
|
var row = document.createElement("row");
|
|
|
|
// Add click event to row
|
|
if (this._rowIsClickable(value.getAttribute('fieldname'))) {
|
|
row.className = 'zotero-clicky';
|
|
row.addEventListener('click', function (event) {
|
|
document.getBindingParent(this).clickHandler(this);
|
|
}, false);
|
|
}
|
|
|
|
row.appendChild(label);
|
|
row.appendChild(value);
|
|
if (beforeElement) {
|
|
this._dynamicFields.insertBefore(row, this._beforeRow);
|
|
}
|
|
else {
|
|
this._dynamicFields.appendChild(row);
|
|
}
|
|
|
|
return row;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="addCreatorRow">
|
|
<parameter name="creator"/>
|
|
<parameter name="creatorTypeID"/>
|
|
<parameter name="unsaved"/>
|
|
<parameter name="defaultRow"/>
|
|
<body>
|
|
<![CDATA[
|
|
// getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect()
|
|
// may need need to be adjusted if this DOM structure changes
|
|
|
|
if (!creator) {
|
|
creator = {
|
|
firstName: '',
|
|
lastName: '',
|
|
fieldMode: Zotero.Prefs.get('lastCreatorFieldMode')
|
|
};
|
|
}
|
|
|
|
if (creator.fieldMode == 1) {
|
|
var firstName = '';
|
|
var lastName = creator.lastName ? creator.lastName : this._defaultFullName;
|
|
}
|
|
else {
|
|
var firstName = creator.firstName ? creator.firstName : this._defaultFirstName;
|
|
var lastName = creator.lastName ? creator.lastName : this._defaultLastName;
|
|
}
|
|
|
|
// Use the first entry in the drop-down for the default type if none specified
|
|
var typeID = creatorTypeID ?
|
|
creatorTypeID : this._creatorTypeMenu.childNodes[0].getAttribute('typeid');
|
|
|
|
var typeBox = document.createElement("hbox");
|
|
typeBox.setAttribute("typeid", typeID);
|
|
typeBox.setAttribute("popup", "creator-type-menu");
|
|
typeBox.setAttribute("fieldname", 'creator-' + this._creatorCount + '-typeID');
|
|
if (this.editable) {
|
|
typeBox.className = 'creator-type-label zotero-clicky';
|
|
var img = document.createElement('image');
|
|
typeBox.appendChild(img);
|
|
}
|
|
else {
|
|
typeBox.className = 'creator-type-label';
|
|
}
|
|
|
|
var label = document.createElement("label");
|
|
label.setAttribute('value',
|
|
Zotero.getString('creatorTypes.'+Zotero.CreatorTypes.getName(typeID)) + ":")
|
|
typeBox.appendChild(label);
|
|
|
|
var hbox = document.createElement("hbox");
|
|
hbox.className = 'creator-type-value';
|
|
|
|
// Name
|
|
var firstlast = document.createElement("hbox");
|
|
firstlast.className = 'creator-name-box';
|
|
firstlast.setAttribute("flex","1");
|
|
var tabindex = this._tabIndexMinCreators + (this._creatorCount * 2);
|
|
var fieldName = 'creator-' + this._creatorCount + '-lastName';
|
|
var lastNameLabel = firstlast.appendChild(
|
|
this.createValueElement(
|
|
lastName,
|
|
fieldName,
|
|
tabindex
|
|
)
|
|
);
|
|
|
|
// Comma
|
|
var comma = document.createElement('label');
|
|
comma.setAttribute('value', ',');
|
|
comma.className = 'comma';
|
|
firstlast.appendChild(comma);
|
|
|
|
var fieldName = 'creator-' + this._creatorCount + '-firstName';
|
|
firstlast.appendChild(
|
|
this.createValueElement(
|
|
firstName,
|
|
fieldName,
|
|
tabindex + 1
|
|
)
|
|
);
|
|
if (creator.fieldMode) {
|
|
firstlast.lastChild.setAttribute('hidden', true);
|
|
}
|
|
|
|
if (this.editable) {
|
|
firstlast.setAttribute('contextmenu', 'zotero-creator-transform-menu');
|
|
}
|
|
|
|
this._tabIndexMaxCreators = Math.max(this._tabIndexMaxCreators, tabindex);
|
|
|
|
hbox.appendChild(firstlast);
|
|
|
|
// Single/double field toggle
|
|
var toggleButton = document.createElement('label');
|
|
toggleButton.setAttribute('fieldname',
|
|
'creator-' + this._creatorCount + '-fieldMode');
|
|
toggleButton.className = 'zotero-field-toggle zotero-clicky';
|
|
hbox.appendChild(toggleButton);
|
|
|
|
// Minus (-) button
|
|
var removeButton = document.createElement('label');
|
|
removeButton.setAttribute("value","-");
|
|
removeButton.setAttribute("class","zotero-clicky zotero-clicky-minus");
|
|
// If default first row, don't let user remove it
|
|
if (defaultRow) {
|
|
this.disableButton(removeButton);
|
|
}
|
|
else {
|
|
removeButton.setAttribute("onclick",
|
|
"document.getBindingParent(this).removeCreator("
|
|
+ this._creatorCount
|
|
+ ", this.parentNode.parentNode)");
|
|
}
|
|
hbox.appendChild(removeButton);
|
|
|
|
// Plus (+) button
|
|
var addButton = document.createElement('label');
|
|
addButton.setAttribute("value","+");
|
|
addButton.setAttribute("class", "zotero-clicky zotero-clicky-plus");
|
|
// If row isn't saved, don't let user add more
|
|
if (unsaved) {
|
|
this.disableButton(addButton);
|
|
}
|
|
else {
|
|
this._enablePlusButton(addButton, typeID, creator.fieldMode);
|
|
}
|
|
hbox.appendChild(addButton);
|
|
|
|
this._creatorCount++;
|
|
|
|
if (!this.editable) {
|
|
toggleButton.hidden = true;
|
|
removeButton.hidden = true;
|
|
addButton.hidden = true;
|
|
}
|
|
|
|
this.addDynamicRow(typeBox, hbox, true);
|
|
|
|
// Set single/double field toggle mode
|
|
if (creator.fieldMode) {
|
|
this.switchCreatorMode(hbox.parentNode, 1, true);
|
|
}
|
|
else {
|
|
this.switchCreatorMode(hbox.parentNode, 0, true);
|
|
}
|
|
|
|
// Focus new rows
|
|
if (unsaved && !defaultRow){
|
|
lastNameLabel.click();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="addMoreCreatorsRow">
|
|
<parameter name="num"/>
|
|
<body>
|
|
<![CDATA[
|
|
var box = document.createElement('box');
|
|
|
|
var label = document.createElement('label');
|
|
label.id = 'more-creators-label';
|
|
label.setAttribute('value', Zotero.getString('general.numMore', num));
|
|
label.setAttribute('onclick',
|
|
"var binding = document.getBindingParent(this); "
|
|
+ "binding._displayAllCreators = true; "
|
|
+ "binding.refresh()"
|
|
);
|
|
|
|
this.addDynamicRow(box, label, true);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="addDateRow">
|
|
<parameter name="field"/>
|
|
<parameter name="value"/>
|
|
<parameter name="tabindex"/>
|
|
<body>
|
|
<![CDATA[
|
|
var label = document.createElement("label");
|
|
label.setAttribute("value", Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, field) + ':');
|
|
label.setAttribute("fieldname", field);
|
|
label.setAttribute("onclick", "this.nextSibling.firstChild.blur()");
|
|
|
|
var elem = this.createValueElement(
|
|
Zotero.Date.multipartToStr(value),
|
|
field,
|
|
tabindex
|
|
);
|
|
elem.setAttribute('flex', 1);
|
|
|
|
// y-m-d status indicator
|
|
var ymd = document.createElement('label');
|
|
ymd.id = 'zotero-date-field-status';
|
|
ymd.setAttribute(
|
|
'value',
|
|
Zotero.Date.strToDate(Zotero.Date.multipartToStr(value))
|
|
.order.split('').join(' ')
|
|
);
|
|
|
|
var hbox = document.createElement('hbox');
|
|
hbox.setAttribute('flex', 1);
|
|
hbox.className = "date-box";
|
|
hbox.appendChild(elem);
|
|
hbox.appendChild(ymd);
|
|
|
|
this.addDynamicRow(label, hbox);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="switchCreatorMode">
|
|
<parameter name="row"/>
|
|
<parameter name="fieldMode"/>
|
|
<parameter name="initial"/>
|
|
<parameter name="updatePref"/>
|
|
<body>
|
|
<![CDATA[
|
|
// 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 (fieldMode == 1) {
|
|
button.style.backgroundImage = 'url("chrome://zotero/skin/textfield-dual.png")';
|
|
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two'));
|
|
lastName.setAttribute('fieldMode', '1');
|
|
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 0, false, true)");
|
|
lastName.setAttribute('flex', '1');
|
|
delete lastName.style.width;
|
|
delete lastName.style.maxWidth;
|
|
|
|
// Remove firstname field from tabindex
|
|
var tab = parseInt(firstName.getAttribute('ztabindex'));
|
|
firstName.setAttribute('ztabindex', -1);
|
|
if (this._tabIndexMaxCreators == tab) {
|
|
this._tabIndexMaxCreators--;
|
|
}
|
|
|
|
// Hide first name field and prepend to last name field
|
|
firstName.setAttribute('hidden', true);
|
|
comma.setAttribute('hidden', true);
|
|
|
|
if (!initial) {
|
|
var first = this._getFieldValue(firstName);
|
|
if (first && first != this._defaultFirstName) {
|
|
var last = this._getFieldValue(lastName);
|
|
this._setFieldValue(lastName, first + ' ' + last);
|
|
}
|
|
}
|
|
|
|
if (this._getFieldValue(lastName) == this._defaultLastName) {
|
|
this._setFieldValue(lastName, this._defaultFullName);
|
|
}
|
|
}
|
|
// Switch to two-field mode
|
|
else {
|
|
button.style.backgroundImage = 'url("chrome://zotero/skin/textfield-single.png")';
|
|
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one'));
|
|
lastName.setAttribute('fieldMode', '0');
|
|
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 1, false, true)");
|
|
lastName.setAttribute('flex', '0');
|
|
|
|
// appropriately truncate lastName
|
|
|
|
// get item box width
|
|
var computedStyle = window.getComputedStyle(this, null);
|
|
var boxWidth = computedStyle.getPropertyValue('width');
|
|
// get field label width
|
|
var computedStyle = window.getComputedStyle(row.firstChild, null);
|
|
var leftHboxWidth = computedStyle.getPropertyValue('width');
|
|
// get last name width
|
|
computedStyle = window.getComputedStyle(lastName, null);
|
|
var lastNameWidth = computedStyle.getPropertyValue('width');
|
|
if(boxWidth.substr(-2) === 'px'
|
|
&& leftHboxWidth.substr(-2) === 'px'
|
|
&& lastNameWidth.substr(-2) === "px") {
|
|
// compute a maximum width
|
|
boxWidth = parseInt(boxWidth);
|
|
leftHboxWidth = parseInt(leftHboxWidth);
|
|
lastNameWidth = parseInt(lastNameWidth);
|
|
var maxWidth = boxWidth-leftHboxWidth-140;
|
|
if(lastNameWidth > maxWidth) {
|
|
lastName.style.width = maxWidth+"px";
|
|
lastName.style.maxWidth = maxWidth+"px";
|
|
} else {
|
|
delete lastName.style.width;
|
|
delete lastName.style.maxWidth;
|
|
}
|
|
}
|
|
|
|
// Add firstname field to tabindex
|
|
var tab = parseInt(lastName.getAttribute('ztabindex'));
|
|
firstName.setAttribute('ztabindex', tab + 1);
|
|
if (this._tabIndexMaxCreators == tab)
|
|
{
|
|
this._tabIndexMaxCreators++;
|
|
}
|
|
|
|
if (!initial) {
|
|
// Move all but last word to first name field and show it
|
|
var last = this._getFieldValue(lastName);
|
|
if (last && last != this._defaultFullName) {
|
|
var lastNameRE = /(.*?)[ ]*([^ ]+[ ]*)$/;
|
|
var parts = lastNameRE.exec(last);
|
|
if (parts[2] && parts[2] != last)
|
|
{
|
|
this._setFieldValue(lastName, parts[2]);
|
|
this._setFieldValue(firstName, parts[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!this._getFieldValue(firstName)) {
|
|
this._setFieldValue(firstName, this._defaultFirstName);
|
|
}
|
|
|
|
if (this._getFieldValue(lastName) == this._defaultFullName) {
|
|
this._setFieldValue(lastName, this._defaultLastName);
|
|
}
|
|
|
|
firstName.setAttribute('hidden', false);
|
|
comma.setAttribute('hidden', false);
|
|
}
|
|
|
|
// Save the last-used field mode
|
|
if (updatePref) {
|
|
Zotero.debug("Switching lastCreatorFieldMode to " + fieldMode);
|
|
Zotero.Prefs.set('lastCreatorFieldMode', fieldMode);
|
|
}
|
|
|
|
if (!initial)
|
|
{
|
|
var index = button.getAttribute('fieldname').split('-')[1];
|
|
var fields = this.getCreatorFields(row);
|
|
fields.fieldMode = fieldMode;
|
|
this.modifyCreator(index, fields);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="scrollToTop">
|
|
<body>
|
|
<![CDATA[
|
|
// DEBUG: Valid nsIScrollBoxObject but methods return errors
|
|
return;
|
|
|
|
var sbo = document.getAnonymousNodes(this)[0].boxObject;
|
|
sbo.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
|
sbo.scrollTo(0,0);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="ensureElementIsVisible">
|
|
<parameter name="elem"/>
|
|
<body>
|
|
<![CDATA[
|
|
var sbo = document.getAnonymousNodes(this)[0].boxObject;
|
|
if (Zotero.platformMajorVersion < 36) {
|
|
sbo.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
|
}
|
|
sbo.ensureElementIsVisible(elem);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="changeTypeTo">
|
|
<parameter name="itemTypeID"/>
|
|
<parameter name="menu"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (itemTypeID == this.item.itemTypeID) {
|
|
return true;
|
|
}
|
|
|
|
var fieldsToDelete = this.item.getFieldsNotInType(itemTypeID, true);
|
|
|
|
// Special cases handled below
|
|
var bookTypeID = Zotero.ItemTypes.getID('book');
|
|
var bookSectionTypeID = Zotero.ItemTypes.getID('bookSection');
|
|
|
|
// Add warning for shortTitle when moving from book to bookSection
|
|
// when title will be transferred
|
|
if (this.item.itemTypeID == bookTypeID && itemTypeID == bookSectionTypeID) {
|
|
var titleFieldID = Zotero.ItemFields.getID('title');
|
|
var shortTitleFieldID = Zotero.ItemFields.getID('shortTitle');
|
|
if (this.item.getField(titleFieldID) && this.item.getField(shortTitleFieldID)) {
|
|
if (!fieldsToDelete) {
|
|
fieldsToDelete = [];
|
|
}
|
|
fieldsToDelete.push(shortTitleFieldID);
|
|
}
|
|
}
|
|
|
|
// Generate list of localized field names for display in pop-up
|
|
if (fieldsToDelete) {
|
|
// Ignore warning for bookTitle when going from bookSection to book
|
|
// if there's not also a title, since the book title is transferred
|
|
// to title automatically in Zotero.Item.setType()
|
|
if (this.item.itemTypeID == bookSectionTypeID && itemTypeID == bookTypeID) {
|
|
var titleFieldID = Zotero.ItemFields.getID('title');
|
|
var bookTitleFieldID = Zotero.ItemFields.getID('bookTitle');
|
|
var shortTitleFieldID = Zotero.ItemFields.getID('shortTitle');
|
|
if (this.item.getField(bookTitleFieldID) && !this.item.getField(titleFieldID)) {
|
|
var index = fieldsToDelete.indexOf(bookTitleFieldID);
|
|
fieldsToDelete.splice(index, 1);
|
|
// But warn for short title, which will be removed
|
|
if (this.item.getField(shortTitleFieldID)) {
|
|
fieldsToDelete.push(shortTitleFieldID);
|
|
}
|
|
}
|
|
}
|
|
|
|
var fieldNames = "";
|
|
for (var i=0; i<fieldsToDelete.length; i++) {
|
|
fieldNames += "\n - " +
|
|
Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, fieldsToDelete[i]);
|
|
}
|
|
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
}
|
|
|
|
if (!fieldsToDelete || fieldsToDelete.length == 0 ||
|
|
promptService.confirm(null,
|
|
Zotero.getString('pane.item.changeType.title'),
|
|
Zotero.getString('pane.item.changeType.text') + "\n" + fieldNames)) {
|
|
this.item.setType(itemTypeID);
|
|
|
|
if (this.saveOnEdit) {
|
|
this.item.save();
|
|
}
|
|
else {
|
|
this.refresh();
|
|
}
|
|
|
|
if (this.eventHandlers['itemtypechange'] && this.eventHandlers['itemtypechange'].length) {
|
|
var self = this;
|
|
this.eventHandlers['itemtypechange'].forEach(function (f) f.bind(self)());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Revert the menu (which changes before the pop-up)
|
|
if (menu) {
|
|
menu.value = this.item.itemTypeID;
|
|
}
|
|
|
|
return false;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="toggleAbstractExpand">
|
|
<parameter name="label"/>
|
|
<body>
|
|
<![CDATA[
|
|
var cur = Zotero.Prefs.get('lastAbstractExpand');
|
|
Zotero.Prefs.set('lastAbstractExpand', !cur);
|
|
|
|
var ab = label.nextSibling;
|
|
var valueText = this.item.getField('abstractNote');
|
|
var tabindex = ab.getAttribute('ztabindex');
|
|
var elem = this.createValueElement(
|
|
valueText,
|
|
'abstractNote',
|
|
tabindex
|
|
);
|
|
ab.parentNode.replaceChild(elem, ab);
|
|
|
|
var text = Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, 'abstractNote') + ':';
|
|
// Add '(...)' before "Abstract:" for collapsed abstracts
|
|
if (valueText && cur) {
|
|
text = '(...) ' + text;
|
|
}
|
|
label.setAttribute('value', text);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="disableButton">
|
|
<parameter name="button"/>
|
|
<body>
|
|
<![CDATA[
|
|
button.setAttribute('disabled', true);
|
|
button.setAttribute('onclick', false);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_enablePlusButton">
|
|
<parameter name="button"/>
|
|
<parameter name="creatorTypeID"/>
|
|
<parameter name="fieldMode"/>
|
|
<body>
|
|
<![CDATA[
|
|
button.setAttribute('disabled', false);
|
|
button.setAttribute("onclick",
|
|
"var parent = document.getBindingParent(this); "
|
|
+ "parent.disableButton(this); "
|
|
+ "var creator = new Zotero.Creator; "
|
|
+ "creator.fieldMode = " + (fieldMode ? fieldMode : 0) + "; "
|
|
+ "parent.addCreatorRow("
|
|
+ "creator, "
|
|
+ (creatorTypeID ? creatorTypeID : 'false') + ", true"
|
|
+ ");"
|
|
);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="disableCreatorAddButtons">
|
|
<body>
|
|
<![CDATA[
|
|
// Disable the "+" button on all creator rows
|
|
var elems = this._dynamicFields.getElementsByAttribute('value', '+');
|
|
for (var i = 0, len = elems.length; i < len; i++) {
|
|
this.disableButton(elems[i]);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="createValueElement">
|
|
<parameter name="valueText"/>
|
|
<parameter name="fieldName"/>
|
|
<parameter name="tabindex"/>
|
|
<body>
|
|
<![CDATA[
|
|
valueText = valueText + '';
|
|
|
|
if (fieldName) {
|
|
var fieldID = Zotero.ItemFields.getID(fieldName);
|
|
}
|
|
|
|
// If an abstract, check last expand state
|
|
var abstractAsVbox = fieldName == 'abstractNote' && Zotero.Prefs.get('lastAbstractExpand');
|
|
|
|
// Use a vbox for multiline fields (but Abstract only if it's expanded)
|
|
var useVbox = (fieldName != 'abstractNote' || abstractAsVbox)
|
|
&& Zotero.ItemFields.isMultiline(fieldName);
|
|
|
|
if (useVbox) {
|
|
var valueElement = document.createElement("vbox");
|
|
}
|
|
else {
|
|
var valueElement = document.createElement("label");
|
|
}
|
|
|
|
valueElement.setAttribute('fieldname', fieldName);
|
|
valueElement.setAttribute('flex', 1);
|
|
|
|
if (this._fieldIsClickable(fieldName)) {
|
|
valueElement.setAttribute('ztabindex', tabindex);
|
|
valueElement.addEventListener('click', function (event) {
|
|
/* Skip right-click on Windows */
|
|
if (event.button) {
|
|
return;
|
|
}
|
|
document.getBindingParent(this).clickHandler(this);
|
|
}, false);
|
|
valueElement.className = 'zotero-clicky';
|
|
}
|
|
|
|
switch (fieldName) {
|
|
case 'itemType':
|
|
valueElement.setAttribute('itemTypeID', valueText);
|
|
valueText = Zotero.ItemTypes.getLocalizedString(valueText);
|
|
break;
|
|
|
|
// Convert dates from UTC
|
|
case 'dateAdded':
|
|
case 'dateModified':
|
|
case 'accessDate':
|
|
|
|
// TEMP - NSF
|
|
case 'dateSent':
|
|
case 'dateDue':
|
|
case 'accepted':
|
|
if (valueText) {
|
|
var date = Zotero.Date.sqlToDate(valueText, true);
|
|
if (date) {
|
|
// If no time, interpret as local, not UTC
|
|
if (Zotero.Date.isSQLDate(valueText)) {
|
|
date = Zotero.Date.sqlToDate(valueText);
|
|
valueText = date.toLocaleDateString();
|
|
}
|
|
else {
|
|
valueText = date.toLocaleString();
|
|
}
|
|
}
|
|
else {
|
|
valueText = '';
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fieldID) {
|
|
// Display the SQL date as a tooltip for date fields
|
|
// TEMP - filingDate
|
|
if (Zotero.ItemFields.isFieldOfBase(fieldID, 'date') || fieldName == 'filingDate') {
|
|
valueElement.setAttribute('tooltiptext',
|
|
Zotero.Date.multipartToSQL(this.item.getField(fieldName, true)));
|
|
}
|
|
|
|
// Display a context menu for certain fields
|
|
if (this.editable && (fieldName == 'seriesTitle' || fieldName == 'shortTitle' ||
|
|
Zotero.ItemFields.isFieldOfBase(fieldID, 'title') ||
|
|
Zotero.ItemFields.isFieldOfBase(fieldID, 'publicationTitle'))) {
|
|
valueElement.setAttribute('contextmenu', 'zotero-field-transform-menu');
|
|
}
|
|
}
|
|
|
|
|
|
if (fieldName && fieldName.indexOf('firstName') != -1) {
|
|
valueElement.setAttribute('flex', '1');
|
|
}
|
|
|
|
var firstSpace = valueText.indexOf(" ");
|
|
|
|
// To support newlines in Abstract and Extra fields, use multiple
|
|
// <description> elements inside a vbox
|
|
if (useVbox) {
|
|
var lines = valueText.split("\n");
|
|
for (var i = 0; i < lines.length; i++) {
|
|
var descriptionNode = document.createElement("description");
|
|
// Add non-breaking space to empty lines to prevent them from collapsing.
|
|
// (Just using CSS min-height results in overflow in some cases.)
|
|
if (lines[i] === "") {
|
|
lines[i] = "\u00a0";
|
|
}
|
|
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 == 'abstractNote')) {
|
|
if (fieldName == 'abstractNote') {
|
|
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;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="removeCreator">
|
|
<parameter name="index"/>
|
|
<parameter name="labelToDelete"/>
|
|
<body>
|
|
<![CDATA[
|
|
// If unsaved row, just remove element
|
|
if (!this.item.hasCreatorAt(index)) {
|
|
labelToDelete.parentNode.removeChild(labelToDelete);
|
|
|
|
// Enable the "+" button on the previous row
|
|
var elems = this._dynamicFields.getElementsByAttribute('value', '+');
|
|
var button = elems[elems.length-1];
|
|
var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(button, 'row'));
|
|
this._enablePlusButton(button, creatorFields.creatorTypeID, creatorFields.fieldMode);
|
|
|
|
this._creatorCount--;
|
|
return;
|
|
}
|
|
this.item.removeCreator(index);
|
|
this.item.save();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="showEditor">
|
|
<parameter name="elem"/>
|
|
<body>
|
|
<![CDATA[
|
|
// Blur any active fields
|
|
if (this._dynamicFields) {
|
|
this._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 = this.item.getCreator(creatorIndex);
|
|
var value = c ? c.ref[creatorField] : '';
|
|
var itemID = this.item.id;
|
|
}
|
|
else {
|
|
var value = this.item.getField(fieldName);
|
|
var itemID = this.item.id;
|
|
|
|
// Access date needs to be converted from UTC
|
|
if (value != '') {
|
|
switch (fieldName) {
|
|
case 'accessDate':
|
|
|
|
// TEMP - NSF
|
|
case 'dateSent':
|
|
case 'dateDue':
|
|
case 'accepted':
|
|
// If no time, interpret as local, not UTC
|
|
if (Zotero.Date.isSQLDate(value)) {
|
|
var localDate = Zotero.Date.sqlToDate(value);
|
|
}
|
|
else {
|
|
var localDate = Zotero.Date.sqlToDate(value, true);
|
|
}
|
|
var value = Zotero.Date.dateToSQL(localDate);
|
|
|
|
// Don't show time in editor
|
|
value = value.replace(' 00:00:00', '');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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('fieldMode', elem.getAttribute('fieldMode'));
|
|
t.setAttribute('newlines','pasteintact');
|
|
}
|
|
|
|
if (Zotero.ItemFields.isMultiline(fieldName) || Zotero.ItemFields.isLong(fieldName)) {
|
|
t.setAttribute('multiline', true);
|
|
t.setAttribute('rows', 8);
|
|
}
|
|
else {
|
|
// Add auto-complete for certain fields
|
|
if (Zotero.ItemFields.isAutocompleteField(fieldName)
|
|
|| fieldName == 'creator') {
|
|
t.setAttribute('type', 'autocomplete');
|
|
t.setAttribute('autocompletesearch', 'zotero');
|
|
|
|
let params = {
|
|
fieldName: fieldName,
|
|
libraryID: this.item.libraryID
|
|
};
|
|
if (field == 'creator') {
|
|
params.fieldMode = parseInt(elem.getAttribute('fieldMode'));
|
|
|
|
// Include itemID and creatorTypeID so the autocomplete can
|
|
// avoid showing results for creators already set on the item
|
|
let row = Zotero.getAncestorByTagName(elem, 'row');
|
|
let creatorTypeID = parseInt(
|
|
row.getElementsByClassName('creator-type-label')[0]
|
|
.getAttribute('typeid')
|
|
);
|
|
if (itemID) {
|
|
params.itemID = itemID;
|
|
params.creatorTypeID = creatorTypeID;
|
|
}
|
|
|
|
t.setAttribute('ontextentered',
|
|
'document.getBindingParent(this).handleCreatorAutoCompleteSelect(this)');
|
|
};
|
|
t.setAttribute(
|
|
'autocompletesearchparam', JSON.stringify(params)
|
|
);
|
|
t.setAttribute('completeselectedindex', true);
|
|
}
|
|
}
|
|
var box = elem.parentNode;
|
|
box.replaceChild(t, elem);
|
|
|
|
// Prevent error when clicking between a changed field
|
|
// and another -- there's probably a better way
|
|
if (!t.select) {
|
|
return;
|
|
}
|
|
|
|
t.select();
|
|
|
|
// Leave text field open when window loses focus
|
|
var ignoreBlur = function () {
|
|
this.ignoreBlur = true;
|
|
}.bind(this);
|
|
var unignoreBlur = function () {
|
|
this.ignoreBlur = false;
|
|
}.bind(this);
|
|
addEventListener("deactivate", ignoreBlur);
|
|
addEventListener("activate", unignoreBlur);
|
|
|
|
t.addEventListener('blur', function () {
|
|
var self = document.getBindingParent(this);
|
|
if (self.ignoreBlur) return;
|
|
|
|
removeEventListener("deactivate", ignoreBlur);
|
|
removeEventListener("activate", unignoreBlur);
|
|
self.blurHandler(this);
|
|
});
|
|
t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)");
|
|
|
|
this._tabDirection = false;
|
|
this._lastTabIndex = tabindex;
|
|
|
|
return t;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<!--
|
|
Save a multiple-field selection for the creator autocomplete
|
|
(e.g. "Shakespeare, William")
|
|
-->
|
|
<method name="handleCreatorAutoCompleteSelect">
|
|
<parameter name="textbox"/>
|
|
<body>
|
|
<![CDATA[
|
|
var comment = false;
|
|
var controller = textbox.controller;
|
|
if (!controller.matchCount) return;
|
|
|
|
for (var i=0; i<controller.matchCount; i++)
|
|
{
|
|
if (controller.getValueAt(i) == textbox.value)
|
|
{
|
|
comment = controller.getCommentAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
var [creatorID, numFields] = comment.split('-');
|
|
|
|
// If result uses two fields, save both
|
|
if (numFields==2)
|
|
{
|
|
// Manually clear autocomplete controller's reference to
|
|
// textbox to prevent error next time around
|
|
textbox.mController.input = null;
|
|
|
|
var [field, creatorIndex, creatorField] =
|
|
textbox.getAttribute('fieldname').split('-');
|
|
|
|
// Stay focused
|
|
this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex')) - 1;
|
|
this._tabDirection = 1;
|
|
|
|
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;
|
|
}
|
|
|
|
//this._setFieldValue(label, creator[otherField]);
|
|
if (label.firstChild){
|
|
label.firstChild.nodeValue = creator[otherField];
|
|
}
|
|
else {
|
|
label.value = creator[otherField];
|
|
}
|
|
|
|
var row = Zotero.getAncestorByTagName(textbox, 'row');
|
|
|
|
var fields = this.getCreatorFields(row);
|
|
fields[creatorField] = creator[creatorField];
|
|
fields[otherField] = creator[otherField];
|
|
this.modifyCreator(creatorIndex, fields);
|
|
}
|
|
|
|
// Otherwise let the autocomplete popup handle matters
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="handleKeyPress">
|
|
<parameter name="event"/>
|
|
<body>
|
|
<![CDATA[
|
|
var target = event.target;
|
|
var focused = document.commandDispatcher.focusedElement;
|
|
|
|
switch (event.keyCode)
|
|
{
|
|
case event.DOM_VK_RETURN:
|
|
var fieldname = target.getAttribute('fieldname');
|
|
// Use shift-enter as the save action for the larger fields
|
|
if (Zotero.ItemFields.isMultiline(fieldname) && !event.shiftKey) {
|
|
break;
|
|
}
|
|
|
|
|
|
// Prevent blur on containing textbox
|
|
// DEBUG: what happens if this isn't present?
|
|
event.preventDefault();
|
|
|
|
// Shift-enter adds new creator row
|
|
if (fieldname.indexOf('creator-') == 0 && event.shiftKey) {
|
|
// Value hasn't changed
|
|
if (target.getAttribute('value') == target.value) {
|
|
Zotero.debug("Value hasn't changed");
|
|
// If + button is disabled, just focus next creator row
|
|
if (Zotero.getAncestorByTagName(target, 'row').lastChild.lastChild.disabled) {
|
|
this._focusNextField(this._dynamicFields, this._lastTabIndex, false);
|
|
}
|
|
else {
|
|
var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(target, 'row'));
|
|
this.addCreatorRow(false, creatorFields.creatorTypeID, true);
|
|
}
|
|
}
|
|
// Value has changed
|
|
else {
|
|
this._tabDirection = 1;
|
|
this._addCreatorRow = true;
|
|
focused.blur();
|
|
}
|
|
return false;
|
|
}
|
|
focused.blur();
|
|
|
|
// Return focus to items pane
|
|
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:
|
|
this._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;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="itemTypeMenuTab">
|
|
<parameter name="event"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!event.shiftKey) {
|
|
this.focusFirstField();
|
|
event.preventDefault();
|
|
}
|
|
// Shift-tab
|
|
else {
|
|
this._tabDirection = false;
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="hideEditor">
|
|
<parameter name="textbox"/>
|
|
<body>
|
|
<![CDATA[
|
|
try {
|
|
Zotero.debug('Hiding editor');
|
|
/*
|
|
var textbox = Zotero.getAncestorByTagName(t, 'textbox');
|
|
if (!textbox){
|
|
Zotero.debug('Textbox not found in hideEditor');
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// TODO: get rid of this?
|
|
var saveChanges = this.saveOnEdit;
|
|
|
|
// Prevent autocomplete breakage in Firefox 3
|
|
if (textbox.mController) {
|
|
textbox.mController.input = null;
|
|
}
|
|
|
|
var fieldName = textbox.getAttribute('fieldname');
|
|
var tabindex = textbox.getAttribute('ztabindex');
|
|
|
|
//var value = t.value;
|
|
var value = textbox.value;
|
|
|
|
var elem;
|
|
var [field, creatorIndex, creatorField] = fieldName.split('-');
|
|
|
|
// Creator fields
|
|
if (field == 'creator') {
|
|
var row = Zotero.getAncestorByTagName(textbox, 'row');
|
|
|
|
var otherFields = this.getCreatorFields(row);
|
|
otherFields[creatorField] = value;
|
|
var lastName = otherFields.lastName.trim();
|
|
|
|
//Handle \n\r and \n delimited entries
|
|
var rawNameArray = lastName.split(/\r\n?|\n/);
|
|
if (rawNameArray.length > 1) {
|
|
//Save tab direction and add creator flags since they are reset in the
|
|
//process of adding multiple authors
|
|
var tabDirectionBuffer = this._tabDirection;
|
|
var addCreatorRowBuffer = this._addCreatorRow;
|
|
var tabIndexBuffer = this._lastTabIndex;
|
|
this._tabDirection = false;
|
|
this._addCreatorRow = false;
|
|
|
|
//Filter out bad names
|
|
var nameArray = [tempName for each(tempName in rawNameArray) if(tempName)];
|
|
|
|
//If not adding names at the end of the creator list, make new creator
|
|
//entries and then shift down existing creators.
|
|
var initNumCreators = this.item.numCreators();
|
|
var creatorsToShift = initNumCreators - creatorIndex;
|
|
if (creatorsToShift > 0) {
|
|
//Add extra creators
|
|
for (var i=0;i<nameArray.length;i++) {
|
|
this.modifyCreator(i+initNumCreators,otherFields);
|
|
}
|
|
|
|
//Shift existing creators
|
|
for (var i=initNumCreators-1; i>=creatorIndex; i--) {
|
|
var shiftedCreator = this.item.getCreator(i);
|
|
this.item.setCreator(nameArray.length+i,shiftedCreator.ref,shiftedCreator.creatorTypeID);
|
|
}
|
|
}
|
|
|
|
//Add the creators in lastNameArray one at a time
|
|
for each(var tempName in nameArray) {
|
|
// Check for tab to determine creator name format
|
|
otherFields.fieldMode = (tempName.indexOf('\t') == -1) ? 1 : 0;
|
|
if (otherFields.fieldMode == 0) {
|
|
otherFields.lastName=tempName.split('\t')[0];
|
|
otherFields.firstName=tempName.split('\t')[1];
|
|
}
|
|
else {
|
|
otherFields.lastName=tempName;
|
|
otherFields.firstName='';
|
|
}
|
|
this.modifyCreator(creatorIndex,otherFields);
|
|
creatorIndex++;
|
|
}
|
|
this._tabDirection = tabDirectionBuffer;
|
|
this._addCreatorRow = (creatorsToShift==0) ? addCreatorRowBuffer : false;
|
|
if (this._tabDirection == 1) {
|
|
this._lastTabIndex = parseInt(tabIndexBuffer,10) + 2*(nameArray.length-1);
|
|
if (otherFields.fieldMode == 0) {
|
|
this._lastTabIndex++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this.modifyCreator(creatorIndex, otherFields);
|
|
}
|
|
|
|
var val = this.item.getCreator(creatorIndex);
|
|
val = val ? val.ref[creatorField] : null;
|
|
|
|
if (!val) {
|
|
// Reset to '(first)'/'(last)'/'(name)'
|
|
if (creatorField == 'lastName') {
|
|
val = otherFields.fieldMode
|
|
? this._defaultFullName : this._defaultLastName;
|
|
}
|
|
else if (creatorField == 'firstName') {
|
|
val = this._defaultFirstName;
|
|
}
|
|
}
|
|
|
|
elem = this.createValueElement(
|
|
val,
|
|
fieldName,
|
|
tabindex
|
|
);
|
|
}
|
|
|
|
// Fields
|
|
else {
|
|
// Access date needs to be parsed and converted to UTC
|
|
if (value != '') {
|
|
switch (fieldName) {
|
|
case 'accessDate':
|
|
// If just date, don't convert to UTC
|
|
if (Zotero.Date.isSQLDate(value)) {
|
|
var localDate = Zotero.Date.sqlToDate(value);
|
|
value = Zotero.Date.dateToSQL(localDate).replace(' 00:00:00', '');
|
|
}
|
|
else if (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).replace(' 00:00:00', '');
|
|
}
|
|
}
|
|
break;
|
|
|
|
// TEMP - NSF
|
|
case 'dateSent':
|
|
case 'dateDue':
|
|
case 'accepted':
|
|
if (Zotero.Date.isSQLDate(value)) {
|
|
var localDate = Zotero.Date.sqlToDate(value);
|
|
value = Zotero.Date.dateToSQL(localDate).replace(' 00:00:00', '');
|
|
}
|
|
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).replace(' 00:00:00', '');
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// TODO: generalize to all date rows/fields
|
|
if (Zotero.ItemFields.isFieldOfBase(fieldName, 'date')) {
|
|
// Parse 'yesterday'/'today'/'tomorrow' and convert to dates,
|
|
// since it doesn't make sense for those to be actual metadata values
|
|
var lc = value.toLowerCase();
|
|
if (lc == 'yesterday' || lc == Zotero.getString('date.yesterday')) {
|
|
value = Zotero.Date.dateToSQL(new Date(new Date().getTime() - 86400000)).substr(0, 10);
|
|
}
|
|
else if (lc == 'today' || lc == Zotero.getString('date.today')) {
|
|
value = Zotero.Date.dateToSQL(new Date()).substr(0, 10);
|
|
}
|
|
else if (lc == 'tomorrow' || lc == Zotero.getString('date.tomorrow')) {
|
|
value = Zotero.Date.dateToSQL(new Date(new Date().getTime() + 86400000)).substr(0, 10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this._modifyField(fieldName, value, this.saveOnEdit);
|
|
|
|
elem = this.createValueElement(
|
|
this.item.getField(fieldName),
|
|
fieldName,
|
|
tabindex
|
|
);
|
|
}
|
|
|
|
var box = textbox.parentNode;
|
|
box.replaceChild(elem,textbox);
|
|
|
|
if(field === 'creator') {
|
|
// Reset creator mode settings here so that flex attribute gets reset
|
|
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
|
|
|
|
if(Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
|
|
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
|
|
document.getElementById("zotero-author-guidance").show(creatorTypeLabels[creatorTypeLabels.length-1]);
|
|
}
|
|
}
|
|
|
|
if (this._tabDirection) {
|
|
var focusBox = this._dynamicFields;
|
|
this._focusNextField(focusBox, this._lastTabIndex, this._tabDirection == -1);
|
|
}
|
|
}
|
|
// Thrown errors don't seem to show up within XBL without explicit logging
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
Components.utils.reportError(e);
|
|
throw (e);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_rowIsClickable">
|
|
<parameter name="fieldName"/>
|
|
<body>
|
|
<![CDATA[
|
|
return this.clickByRow &&
|
|
(this.clickable ||
|
|
this._clickableFields.indexOf(fieldName) != -1);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_fieldIsClickable">
|
|
<parameter name="fieldName"/>
|
|
<body>
|
|
<![CDATA[
|
|
return !this.clickByRow &&
|
|
((this.clickable && !Zotero.Items.isPrimaryField(fieldName))
|
|
|| this._clickableFields.indexOf(fieldName) != -1);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_modifyField">
|
|
<parameter name="field"/>
|
|
<parameter name="value"/>
|
|
<parameter name="save"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.item.setField(field,value);
|
|
if (save) {
|
|
this.item.save();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_getFieldValue">
|
|
<parameter name="label"/>
|
|
<body>
|
|
<![CDATA[
|
|
return label.firstChild
|
|
? label.firstChild.nodeValue : label.value;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_setFieldValue">
|
|
<parameter name="label"/>
|
|
<parameter name="value"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (label.firstChild) {
|
|
label.firstChild.nodeValue = value;
|
|
}
|
|
else {
|
|
label.value = value;
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<!-- TODO: work with textboxes too -->
|
|
<method name="textTransform">
|
|
<parameter name="label"/>
|
|
<parameter name="mode"/>
|
|
<body>
|
|
<![CDATA[
|
|
var val = this._getFieldValue(label);
|
|
switch (mode) {
|
|
case 'title':
|
|
var newVal = Zotero.Utilities.capitalizeTitle(val.toLowerCase(), true);
|
|
break;
|
|
case 'sentence':
|
|
// capitalize the first letter, including after beginning punctuation
|
|
// capitalize after ?, ! and remove space(s) before those as well as colon analogous to capitalizeTitle function
|
|
// also deal with initial punctuation here - open quotes and Spanish beginning punctuation marks
|
|
newVal = val.toLowerCase().replace(/\s*:/, ":");
|
|
newVal = newVal.replace(/(([\?!]\s*|^)([\'\"¡¿“‘„«\s]+)?[^\s])/g, function (x) {
|
|
return x.replace(/\s+/m, " ").toUpperCase();});
|
|
break;
|
|
default:
|
|
throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()");
|
|
}
|
|
this._setFieldValue(label, newVal);
|
|
this._modifyField(label.getAttribute('fieldname'), newVal, this.saveOnEdit);
|
|
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="getCreatorFields">
|
|
<parameter name="row"/>
|
|
<body>
|
|
<![CDATA[
|
|
var typeID = row.getElementsByClassName('creator-type-label')[0].getAttribute('typeid');
|
|
var label1 = row.getElementsByClassName('creator-name-box')[0].firstChild;
|
|
var label2 = label1.parentNode.lastChild;
|
|
|
|
var fields = {
|
|
lastName: label1.firstChild ? label1.firstChild.nodeValue
|
|
: label1.value,
|
|
firstName: label2.firstChild ? label2.firstChild.nodeValue
|
|
: label2.value,
|
|
fieldMode: label1.getAttribute('fieldMode')
|
|
? parseInt(label1.getAttribute('fieldMode')) : 0,
|
|
creatorTypeID: parseInt(typeID),
|
|
};
|
|
|
|
// Ignore '(first)'
|
|
if (fields.fieldMode == 1 || fields.firstName == this._defaultFirstName) {
|
|
fields.firstName = '';
|
|
}
|
|
// Ignore '(last)' or '(name)'
|
|
if (fields.lastName == this._defaultFullName
|
|
|| fields.lastName == this._defaultLastName) {
|
|
fields.lastName = '';
|
|
}
|
|
|
|
return fields;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="modifyCreator">
|
|
<parameter name="index"/>
|
|
<parameter name="fields"/>
|
|
<parameter name="changeGlobally"/>
|
|
<body>
|
|
<![CDATA[
|
|
try {
|
|
|
|
var libraryID = this.item.libraryID;
|
|
var firstName = fields.firstName;
|
|
var lastName = fields.lastName;
|
|
//var shortName = fields.shortName;
|
|
var fieldMode = fields.fieldMode;
|
|
var creatorTypeID = fields.creatorTypeID;
|
|
|
|
var oldCreator = this.item.getCreator(index);
|
|
|
|
// Don't save empty creators
|
|
if (!firstName && !lastName){
|
|
if (!oldCreator) {
|
|
return;
|
|
}
|
|
this.item.removeCreator(index);
|
|
this.item.save();
|
|
return;
|
|
}
|
|
|
|
Zotero.DB.beginTransaction();
|
|
|
|
var newCreator = new Zotero.Creator;
|
|
newCreator.libraryID = libraryID;
|
|
newCreator.setFields(fields);
|
|
|
|
var newLinkedCreators = [];
|
|
var creatorDataID = Zotero.Creators.getDataID(fields);
|
|
if (creatorDataID) {
|
|
newLinkedCreators = Zotero.Creators.getCreatorsWithData(creatorDataID, libraryID);
|
|
}
|
|
|
|
if (oldCreator) {
|
|
if (oldCreator.ref.equals(newCreator) || (oldCreator.ref.libraryID != newCreator.libraryID)) {
|
|
if (oldCreator.creatorTypeID == creatorTypeID) {
|
|
Zotero.debug("Creator " + oldCreator.ref.id + " hasn't changed");
|
|
}
|
|
// Just change creatorTypeID
|
|
else {
|
|
this.item.setCreator(index, oldCreator.ref, creatorTypeID);
|
|
if (this.saveOnEdit) {
|
|
this.item.save();
|
|
}
|
|
}
|
|
Zotero.DB.commitTransaction();
|
|
return;
|
|
}
|
|
|
|
oldCreator = oldCreator.ref;
|
|
}
|
|
|
|
var creator;
|
|
var creatorID;
|
|
|
|
if (oldCreator) {
|
|
var numLinkedItems = oldCreator.countLinkedItems();
|
|
// Creator is linked only to the current item
|
|
if (numLinkedItems == 1) {
|
|
if (newLinkedCreators.length) {
|
|
// Use the first creator found with this data
|
|
// TODO: support choosing among options
|
|
creatorID = newLinkedCreators[0];
|
|
creator = Zotero.Creators.get(creatorID);
|
|
}
|
|
else {
|
|
oldCreator.setFields(fields);
|
|
//creatorID = oldCreator.save();
|
|
creator = oldCreator;
|
|
}
|
|
}
|
|
// Creator is linked to multiple items with changeGlobally off
|
|
else if (!changeGlobally) {
|
|
if (newLinkedCreators.length) {
|
|
// Use the first creator found with this data
|
|
// TODO: support choosing among options
|
|
creatorID = newLinkedCreators[0];
|
|
creator = Zotero.Creators.get(creatorID);
|
|
}
|
|
else {
|
|
//creatorID = newCreator.save();
|
|
creator = newCreator;
|
|
}
|
|
}
|
|
// Creator is linked to multiple items with changeGlobally on
|
|
else {
|
|
throw ('changeGlobally unimplemented');
|
|
if (newLinkedCreators.length) {
|
|
// Use the first creator found with this data
|
|
// TODO: support choosing among options
|
|
creatorID = newLinkedCreators[0];
|
|
|
|
// TODO: switch all linked items to this creator
|
|
}
|
|
else {
|
|
creatorID = newCreator.save();
|
|
|
|
// TODO: switch all linked items to new creatorID
|
|
}
|
|
}
|
|
}
|
|
// No existing creator
|
|
else {
|
|
if (newLinkedCreators.length) {
|
|
creatorID = newLinkedCreators[0];
|
|
creator = Zotero.Creators.get(creatorID);
|
|
}
|
|
else {
|
|
//creatorID = newCreator.save();
|
|
creator = newCreator;
|
|
}
|
|
}
|
|
|
|
this.item.setCreator(index, creator, creatorTypeID);
|
|
if (this.saveOnEdit) {
|
|
try {
|
|
this.item.save();
|
|
}
|
|
catch (e) {
|
|
// DEBUG: Errors aren't being logged in Fx3.1b4pre without this
|
|
Zotero.debug(e);
|
|
Components.utils.reportError(e);
|
|
throw (e);
|
|
}
|
|
}
|
|
|
|
Zotero.DB.commitTransaction();
|
|
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
Components.utils.reportError(e);
|
|
throw (e);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="swapNames">
|
|
<body><![CDATA[
|
|
var row = Zotero.getAncestorByTagName(document.popupNode, 'row');
|
|
var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0];
|
|
var creatorIndex = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
|
|
var fields = this.getCreatorFields(row);
|
|
var lastName = fields.lastName;
|
|
var firstName = fields.firstName;
|
|
fields.lastName = firstName;
|
|
fields.firstName = lastName;
|
|
this.modifyCreator(creatorIndex, fields);
|
|
this.item.save();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="moveCreator">
|
|
<parameter name="index"/>
|
|
<parameter name="moveUp"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (index == 0 && moveUp) {
|
|
Zotero.debug("Can't move up creator 0");
|
|
return;
|
|
}
|
|
else if (index + 1 == this.item.numCreators() && !moveUp) {
|
|
Zotero.debug("Can't move down last creator");
|
|
return;
|
|
}
|
|
|
|
var newIndex = moveUp ? index - 1 : index + 1;
|
|
var creator = this.item.getCreator(index);
|
|
var swapCreator = this.item.getCreator(newIndex);
|
|
this.item.setCreator(newIndex, creator.ref, creator.creatorTypeID);
|
|
this.item.setCreator(index, swapCreator.ref, swapCreator.creatorTypeID);
|
|
if (this.saveOnEdit) {
|
|
this.item.save();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_updateAutoCompleteParams">
|
|
<parameter name="row"/>
|
|
<parameter name="changedParams"/>
|
|
<body>
|
|
<![CDATA[
|
|
var textboxes = row.getElementsByTagName('textbox');
|
|
if (textboxes.length) {
|
|
var t = textboxes[0];
|
|
var params = JSON.parse(t.getAttribute('autocompletesearchparam'));
|
|
for (var param in changedParams) {
|
|
params[param] = changedParams[param];
|
|
}
|
|
t.setAttribute('autocompletesearchparam', JSON.stringify(params));
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!--
|
|
/*
|
|
function modifyCreatorByID(index, creatorID, creatorTypeID) {
|
|
throw ('Unimplemented');
|
|
var oldCreator = _itemBeingEdited.getCreator(index);
|
|
if (creator) {
|
|
oldCreator = creator.ref;
|
|
var oldCreatorID = oldCreator.creatorID;
|
|
}
|
|
|
|
Zotero.debug("Old creatorID is " + oldCreatorID);
|
|
|
|
_itemBeingEdited.setCreator(index, firstName, lastName, typeID, fieldMode);
|
|
_itemBeingEdited.save();
|
|
}
|
|
*/
|
|
-->
|
|
|
|
|
|
<method name="focusFirstField">
|
|
<body>
|
|
<![CDATA[
|
|
this._focusNextField(this._dynamicFields, 0, false);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<!--
|
|
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.)
|
|
-->
|
|
<method name="_focusNextField">
|
|
<parameter name="box"/>
|
|
<parameter name="tabindex"/>
|
|
<parameter name="back"/>
|
|
<body>
|
|
<![CDATA[
|
|
tabindex = parseInt(tabindex);
|
|
|
|
if (back)
|
|
{
|
|
switch (tabindex)
|
|
{
|
|
case 1:
|
|
//Zotero.debug('At beginning');
|
|
document.getElementById('item-type-menu').focus();
|
|
return false;
|
|
|
|
case this._tabIndexMinCreators:
|
|
var nextIndex = 1; // Title field
|
|
break;
|
|
|
|
case this._tabIndexMinFields:
|
|
// No creators
|
|
if (this._tabIndexMaxCreators == 0) {
|
|
var nextIndex = 1; // Title field
|
|
}
|
|
else {
|
|
var nextIndex = this._tabIndexMaxCreators;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
var nextIndex = tabindex - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (tabindex)
|
|
{
|
|
case 1:
|
|
var nextIndex = this._tabIndexMinCreators;
|
|
break;
|
|
|
|
case this._tabIndexMaxCreators:
|
|
var nextIndex = this._tabIndexMinFields;
|
|
break;
|
|
|
|
case this._tabIndexMaxFields:
|
|
//Zotero.debug('At end');
|
|
return false;
|
|
|
|
default:
|
|
var nextIndex = tabindex + 1;
|
|
}
|
|
}
|
|
|
|
Zotero.debug('Looking for tabindex ' + nextIndex, 4);
|
|
|
|
var next = box.getElementsByAttribute('ztabindex', nextIndex);
|
|
if (!next[0])
|
|
{
|
|
//Zotero.debug("Next field not found");
|
|
return this._focusNextField(box, nextIndex, back);
|
|
}
|
|
|
|
next[0].click();
|
|
|
|
// DEBUG: next[0] is always equal to the target element,
|
|
// but for some reason it's necessary to scroll to the next
|
|
// element when moving forward for the target element to
|
|
// be fully in view
|
|
if (!back && next[0].parentNode.nextSibling) {
|
|
var visElem = next[0].parentNode.nextSibling;
|
|
}
|
|
else {
|
|
var visElem = next[0];
|
|
}
|
|
this.ensureElementIsVisible(visElem);
|
|
|
|
return true;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="blurOpenField">
|
|
<body>
|
|
<![CDATA[
|
|
var textboxes = document.getAnonymousNodes(this)[0].getElementsByTagName('textbox');
|
|
if (textboxes && textboxes.length) {
|
|
textboxes[0].inputField.blur();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<!--
|
|
Available handlers:
|
|
|
|
- 'itemtypechange'
|
|
|
|
Note: 'this' in the function will be bound to the item box.
|
|
-->
|
|
<method name="addHandler">
|
|
<parameter name="eventName"/>
|
|
<parameter name="func"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!this.eventHandlers[eventName]) {
|
|
this.eventHandlers[eventName] = [];
|
|
}
|
|
this.eventHandlers[eventName].push(func);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="removeHandler">
|
|
<parameter name="eventName"/>
|
|
<parameter name="func"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!this.eventHandlers[eventName]) {
|
|
return;
|
|
}
|
|
var pos = this.eventHandlers[eventName].indexOf(func);
|
|
if (pos != -1) {
|
|
this.eventHandlers[eventName].splice(pos, 1);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
|
|
<method name="_id">
|
|
<parameter name="id"/>
|
|
<body>
|
|
<![CDATA[
|
|
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0];
|
|
]]>
|
|
</body>
|
|
</method>
|
|
</implementation>
|
|
|
|
<content>
|
|
<scrollbox id="item-box" flex="1" orient="vertical"
|
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
<popupset>
|
|
<menupopup id="creator-type-menu" position="after_start"
|
|
onpopupshowing="var typeBox = document.popupNode.localName == 'hbox' ? document.popupNode : document.popupNode.parentNode;
|
|
var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
|
|
|
|
var item = document.getBindingParent(this).item;
|
|
var exists = item.hasCreatorAt(index);
|
|
var moreCreators = item.numCreators() > index + 1;
|
|
|
|
var hideMoveUp = !exists || index == 0;
|
|
var hideMoveDown = !exists || !moreCreators;
|
|
var hideMoveSep = hideMoveUp && hideMoveDown;
|
|
|
|
document.getElementById('zotero-creator-move-sep').setAttribute('hidden', hideMoveSep);
|
|
document.getElementById('zotero-creator-move-up').setAttribute('hidden', hideMoveUp);
|
|
document.getElementById('zotero-creator-move-down').setAttribute('hidden', hideMoveDown);"
|
|
oncommand="var typeBox = document.popupNode.localName == 'hbox' ? document.popupNode : document.popupNode.parentNode;
|
|
var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
|
|
|
|
var itemBox = document.getBindingParent(this);
|
|
|
|
if (event.explicitOriginalTarget.className == 'zotero-creator-move') {
|
|
var up = event.explicitOriginalTarget.id == 'zotero-creator-move-up';
|
|
itemBox.moveCreator(index, up);
|
|
return;
|
|
}
|
|
|
|
var typeID = event.explicitOriginalTarget.getAttribute('typeid');
|
|
var row = typeBox.parentNode;
|
|
var fields = itemBox.getCreatorFields(row);
|
|
fields.creatorTypeID = typeID;
|
|
typeBox.getElementsByTagName('label')[0].setAttribute(
|
|
'value',
|
|
Zotero.getString(
|
|
'creatorTypes.' + Zotero.CreatorTypes.getName(typeID)
|
|
) + ':'
|
|
);
|
|
typeBox.setAttribute('typeid', typeID);
|
|
|
|
/* If a creator textbox is already open, we need to
|
|
change its autocomplete parameters so that it
|
|
completes on a creator with a different creator type */
|
|
var changedParams = {
|
|
creatorTypeID: typeID
|
|
};
|
|
itemBox._updateAutoCompleteParams(row, changedParams);
|
|
|
|
itemBox.modifyCreator(index, fields);"/>
|
|
<menupopup id="zotero-field-transform-menu">
|
|
<menu label="&zotero.item.textTransform;">
|
|
<menupopup>
|
|
<menuitem label="&zotero.item.textTransform.titlecase;" class="menuitem-non-iconic"
|
|
oncommand="document.getBindingParent(this).textTransform(document.popupNode, 'title')"/>
|
|
<menuitem label="&zotero.item.textTransform.sentencecase;" class="menuitem-non-iconic"
|
|
oncommand="document.getBindingParent(this).textTransform(document.popupNode, 'sentence')"/>
|
|
</menupopup>
|
|
</menu>
|
|
</menupopup>
|
|
<menupopup id="zotero-creator-transform-menu"
|
|
onpopupshowing="var row = Zotero.getAncestorByTagName(document.popupNode, 'row');
|
|
var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0];
|
|
var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
|
|
var item = document.getBindingParent(this).item;
|
|
var exists = item.hasCreatorAt(index);
|
|
if (exists) {
|
|
var fieldMode = item.getCreator(index).ref.fieldMode;
|
|
}
|
|
var hideTransforms = !exists || !!fieldMode;
|
|
return !hideTransforms;">
|
|
<menuitem label="&zotero.item.creatorTransform.nameSwap;"
|
|
oncommand="document.getBindingParent(this).swapNames();"/>
|
|
</menupopup>
|
|
<menupopup id="zotero-doi-menu">
|
|
<menuitem id="zotero-doi-menu-view-online" label="&zotero.item.viewOnline;"/>
|
|
<menuitem id="zotero-doi-menu-copy" label="&zotero.item.copyAsURL;"/>
|
|
</menupopup>
|
|
<zoteroguidancepanel id="zotero-author-guidance" about="authorMenu" position="after_end" x="-25"/>
|
|
</popupset>
|
|
<grid flex="1">
|
|
<columns>
|
|
<column/>
|
|
<column flex="1"/>
|
|
</columns>
|
|
<rows id="dynamic-fields" flex="1">
|
|
<row class="zotero-item-first-row">
|
|
<label value="&zotero.items.itemType;:"/>
|
|
<menulist class="zotero-clicky" id="item-type-menu" oncommand="document.getBindingParent(this).changeTypeTo(this.value, this)" flex="1"
|
|
onfocus="document.getBindingParent(this).ensureElementIsVisible(this)"
|
|
onkeypress="if (event.keyCode == event.DOM_VK_TAB) { document.getBindingParent(this).itemTypeMenuTab(event); }">
|
|
<menupopup/>
|
|
</menulist>
|
|
</row>
|
|
</rows>
|
|
</grid>
|
|
</scrollbox>
|
|
</content>
|
|
</binding>
|
|
</bindings>
|