Extract field transform menu and use for title header
And extract edit context menu building code into a separate function in `editMenuOverlay.js` so we can build menus on top of it.
This commit is contained in:
parent
17f758d0cf
commit
6bc97a6af6
4 changed files with 102 additions and 79 deletions
|
@ -92,6 +92,31 @@ window.addEventListener(
|
|||
{ once: true }
|
||||
);
|
||||
|
||||
function goBuildEditContextMenu() {
|
||||
let popup = document.getElementById("textbox-contextmenu");
|
||||
if (!popup) {
|
||||
MozXULElement.insertFTLIfNeeded("toolkit/global/textActions.ftl");
|
||||
document.documentElement.appendChild(
|
||||
MozXULElement.parseXULToFragment(`
|
||||
<popupset>
|
||||
<menupopup id="textbox-contextmenu" class="textbox-contextmenu">
|
||||
<menuitem data-l10n-id="text-action-undo" command="cmd_undo"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-redo" command="cmd_redo"></menuitem>
|
||||
<menuseparator></menuseparator>
|
||||
<menuitem data-l10n-id="text-action-cut" command="cmd_cut"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-copy" command="cmd_copy"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-paste" command="cmd_paste"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-delete" command="cmd_delete"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-select-all" command="cmd_selectAll"></menuitem>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
`)
|
||||
);
|
||||
popup = document.documentElement.lastElementChild.firstElementChild;
|
||||
}
|
||||
return popup;
|
||||
}
|
||||
|
||||
// Support context menus on html textareas in the parent process:
|
||||
window.addEventListener("contextmenu", e => {
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
@ -107,28 +132,7 @@ window.addEventListener("contextmenu", e => {
|
|||
return;
|
||||
}
|
||||
|
||||
let popup = document.getElementById("textbox-contextmenu");
|
||||
if (!popup) {
|
||||
MozXULElement.insertFTLIfNeeded("toolkit/global/textActions.ftl");
|
||||
document.documentElement.appendChild(
|
||||
MozXULElement.parseXULToFragment(`
|
||||
<popupset>
|
||||
<menupopup id="textbox-contextmenu" class="textbox-contextmenu">
|
||||
<menuitem data-l10n-id="text-action-undo" command="cmd_undo"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-redo" command="cmd_redo"></menuitem>
|
||||
<menuseparator></menuseparator>
|
||||
<menuitem data-l10n-id="text-action-cut" command="cmd_cut"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-copy" command="cmd_copy"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-paste" command="cmd_paste"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-delete" command="cmd_delete"></menuitem>
|
||||
<menuitem data-l10n-id="text-action-select-all" command="cmd_selectAll"></menuitem>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
`)
|
||||
);
|
||||
popup = document.documentElement.lastElementChild.firstElementChild;
|
||||
}
|
||||
|
||||
let popup = goBuildEditContextMenu();
|
||||
goUpdateGlobalEditMenuItems(true);
|
||||
popup.openPopupAtScreen(e.screenX, e.screenY, true, e);
|
||||
// Don't show any other context menu at the same time. There can be a
|
||||
|
|
|
@ -60,12 +60,6 @@
|
|||
<div id="item-box" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<popupset xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<menupopup id="creator-type-menu" position="after_start"/>
|
||||
<menupopup id="zotero-field-transform-menu">
|
||||
<menuitem id="creator-transform-title-case" label="&zotero.item.textTransform.titlecase;"
|
||||
class="menuitem-non-iconic"/>
|
||||
<menuitem id="creator-transform-sentence-case" label="&zotero.item.textTransform.sentencecase;"
|
||||
class="menuitem-non-iconic"/>
|
||||
</menupopup>
|
||||
<menupopup id="zotero-creator-transform-menu">
|
||||
<menuitem id="creator-transform-swap-names" label="&zotero.item.creatorTransform.nameSwap;"/>
|
||||
<menuitem id="creator-transform-capitalize" label="&zotero.item.creatorTransform.fixCase;"/>
|
||||
|
@ -174,16 +168,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
this._id('zotero-field-transform-menu').addEventListener('popupshowing', () => {
|
||||
this._id('creator-transform-title-case').disabled = !this.canTextTransformField(document.popupNode, 'title');
|
||||
this._id('creator-transform-sentence-case').disabled = !this.canTextTransformField(document.popupNode, 'sentence');
|
||||
});
|
||||
|
||||
this._id('creator-transform-title-case').addEventListener('command',
|
||||
() => this.textTransformField(document.popupNode, 'title'));
|
||||
this._id('creator-transform-sentence-case').addEventListener('command',
|
||||
() => this.textTransformField(document.popupNode, 'sentence'));
|
||||
|
||||
this._id('zotero-creator-transform-menu').addEventListener('popupshowing', (event) => {
|
||||
var row = document.popupNode.closest('tr');
|
||||
var typeBox = row.querySelector('.creator-type-label');
|
||||
|
@ -1446,14 +1430,17 @@
|
|||
if (this.editable && (fieldName == 'seriesTitle' || fieldName == 'shortTitle'
|
||||
|| Zotero.ItemFields.isFieldOfBase(fieldID, 'title')
|
||||
|| Zotero.ItemFields.isFieldOfBase(fieldID, 'publicationTitle'))) {
|
||||
valueElement.setAttribute('context', 'zotero-field-transform-menu');
|
||||
valueElement.oncontextmenu = (event) => {
|
||||
document.popupNode = valueElement;
|
||||
this._id('zotero-field-transform-menu').openPopupAtScreen(
|
||||
event.screenX + 1,
|
||||
event.screenY + 1,
|
||||
true
|
||||
);
|
||||
let oldValue = this._getFieldValue(valueElement);
|
||||
let menupopup = ZoteroItemPane.buildFieldTransformMenu({
|
||||
value: oldValue,
|
||||
onTransform: (newValue) => {
|
||||
this._setFieldTransformedValue(valueElement, newValue);
|
||||
}
|
||||
});
|
||||
this.querySelector('popupset').append(menupopup);
|
||||
menupopup.addEventListener('popuphidden', () => menupopup.remove());
|
||||
menupopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2132,39 +2119,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
textTransformString(val, mode) {
|
||||
switch (mode) {
|
||||
case 'title':
|
||||
return Zotero.Utilities.capitalizeTitle(val.toLowerCase(), true);
|
||||
case 'sentence':
|
||||
return Zotero.Utilities.sentenceCase(val);
|
||||
default:
|
||||
throw new Error("Invalid transform mode '" + mode + "' in ItemBox.textTransformString()");
|
||||
}
|
||||
}
|
||||
|
||||
canTextTransformField(label, mode) {
|
||||
let val = this._getFieldValue(label);
|
||||
return this.textTransformString(val, mode) != val;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: work with textboxes too
|
||||
*/
|
||||
async textTransformField(label, mode) {
|
||||
var val = this._getFieldValue(label);
|
||||
var newVal = this.textTransformString(val, mode);
|
||||
this._setFieldValue(label, newVal);
|
||||
async _setFieldTransformedValue(label, newValue) {
|
||||
this._setFieldValue(label, newValue);
|
||||
var fieldName = label.getAttribute('fieldname');
|
||||
this._modifyField(fieldName, newVal);
|
||||
|
||||
// If this is a title field, convert the Short Title too
|
||||
var isTitle = Zotero.ItemFields.getBaseIDFromTypeAndField(
|
||||
this.item.itemTypeID, fieldName) == Zotero.ItemFields.getID('title');
|
||||
var shortTitleVal = this.item.getField('shortTitle');
|
||||
if (isTitle && newVal.toLowerCase().startsWith(shortTitleVal.toLowerCase())) {
|
||||
this._modifyField('shortTitle', newVal.substr(0, shortTitleVal.length));
|
||||
}
|
||||
this._modifyField(fieldName, newValue);
|
||||
|
||||
if (this.saveOnEdit) {
|
||||
// If a field is open, blur it, which will trigger a save and cause
|
||||
|
@ -2232,7 +2190,8 @@
|
|||
fields.firstName = lastName;
|
||||
this.modifyCreator(creatorIndex, fields);
|
||||
if (this.saveOnEdit) {
|
||||
// See note in transformText()
|
||||
// If a field is open, blur it, which will trigger a save and cause
|
||||
// the saveTx() to be a no-op
|
||||
await this.blurOpenField();
|
||||
await this.item.saveTx();
|
||||
}
|
||||
|
@ -2256,7 +2215,8 @@
|
|||
fields.lastName = fields.lastName && Zotero.Utilities.capitalizeName(fields.lastName);
|
||||
this.modifyCreator(creatorIndex, fields);
|
||||
if (this.saveOnEdit) {
|
||||
// See note in transformText()
|
||||
// If a field is open, blur it, which will trigger a save and cause
|
||||
// the saveTx() to be a no-op
|
||||
await this.blurOpenField();
|
||||
await this.item.saveTx();
|
||||
}
|
||||
|
|
|
@ -79,6 +79,22 @@
|
|||
|
||||
this.titleField.addEventListener('change', () => this.save());
|
||||
this.titleField.ariaLabel = Zotero.getString('itemFields.title');
|
||||
this.titleField.addEventListener('contextmenu', (event) => {
|
||||
if (!this._item) return;
|
||||
|
||||
event.preventDefault();
|
||||
let oldValue = this.titleField.value;
|
||||
let menupopup = ZoteroItemPane.buildFieldTransformMenu({
|
||||
value: oldValue,
|
||||
onTransform: (newValue) => {
|
||||
this._setTransformedValue(oldValue, newValue);
|
||||
},
|
||||
includeEditMenuOptions: true
|
||||
});
|
||||
this.ownerDocument.querySelector('popupset').append(menupopup);
|
||||
menupopup.addEventListener('popuphidden', () => menupopup.remove());
|
||||
menupopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true);
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
@ -93,6 +109,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
async _setTransformedValue(oldValue, newValue) {
|
||||
await this.blurOpenField();
|
||||
this._item.setField(this._titleFieldID, newValue);
|
||||
let shortTitleVal = this._item.getField('shortTitle');
|
||||
if (newValue.toLowerCase().startsWith(shortTitleVal.toLowerCase())) {
|
||||
this._item.setField('shortTitle', newValue.substr(0, shortTitleVal.length));
|
||||
}
|
||||
await this._item.saveTx();
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (this.item) {
|
||||
this.item.setField(this._titleFieldID, this.titleField.value);
|
||||
|
|
|
@ -250,6 +250,39 @@ var ZoteroItemPane = new function() {
|
|||
this.getSidenavSelectedPane = function () {
|
||||
return _sidenav.selectedPane;
|
||||
};
|
||||
|
||||
|
||||
this.buildFieldTransformMenu = function ({ value, onTransform, includeEditMenuOptions = false }) {
|
||||
let valueTitleCased = Zotero.Utilities.capitalizeTitle(value.toLowerCase(), true);
|
||||
let valueSentenceCased = Zotero.Utilities.sentenceCase(value);
|
||||
|
||||
let menupopup = document.createXULElement('menupopup');
|
||||
|
||||
if (includeEditMenuOptions) {
|
||||
goBuildEditContextMenu();
|
||||
let menuitems = [...document.getElementById('textbox-contextmenu').children];
|
||||
menupopup.append(...menuitems.map(menuitem => menuitem.cloneNode(true)));
|
||||
menupopup.append(document.createXULElement('menuseparator'));
|
||||
}
|
||||
|
||||
let titleCase = document.createXULElement('menuitem');
|
||||
titleCase.setAttribute('label', Zotero.getString('zotero.item.textTransform.titlecase'));
|
||||
titleCase.addEventListener('command', () => {
|
||||
onTransform(valueTitleCased);
|
||||
});
|
||||
titleCase.disabled = valueTitleCased == value;
|
||||
menupopup.append(titleCase);
|
||||
|
||||
let sentenceCase = document.createXULElement('menuitem');
|
||||
sentenceCase.setAttribute('label', Zotero.getString('zotero.item.textTransform.sentencecase'));
|
||||
sentenceCase.addEventListener('command', () => {
|
||||
onTransform(valueSentenceCased);
|
||||
});
|
||||
sentenceCase.disabled = valueSentenceCased == value;
|
||||
menupopup.append(sentenceCase);
|
||||
|
||||
return menupopup;
|
||||
};
|
||||
};
|
||||
|
||||
addEventListener("load", function(e) { ZoteroItemPane.onLoad(e); }, false);
|
||||
|
|
Loading…
Reference in a new issue