diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml
index 0fb381911..66ad49263 100644
--- a/chrome/content/zotero/bindings/itembox.xml
+++ b/chrome/content/zotero/bindings/itembox.xml
@@ -786,7 +786,7 @@
 						firstlast.lastChild.setAttribute('hidden', true);
 					}
 					
-					if (this.editable) {
+					if (this.editable && fieldMode == 0) {
 						firstlast.setAttribute('contextmenu', 'zotero-creator-transform-menu');
 					}
 					
@@ -2021,28 +2021,32 @@
 			<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).done();
-				]]>
-				</body>
+				<body><![CDATA[
+					return Zotero.spawn(function* () {
+						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);
+						if (this.saveOnEdit) {
+							// See note in modifyCreator()
+							yield this.blurOpenField();
+						}
+						return this._modifyField(label.getAttribute('fieldname'), newVal, this.saveOnEdit);
+					}, this);
+				]]></body>
 			</method>
 			
 			
@@ -2083,7 +2087,7 @@
 				<parameter name="fields"/>
 				<parameter name="skipSave"/>
 				<body><![CDATA[
-				return Zotero.Promise.try(function () {
+				return Zotero.spawn(function* () {
 					var libraryID = this.item.libraryID;
 					var firstName = fields.firstName;
 					var lastName = fields.lastName;
@@ -2099,6 +2103,11 @@
 						}
 						this.item.removeCreator(index);
 						if (this.saveOnEdit && !skipSave) {
+							// Make sure any open field is saved, since a blur() isn't otherwise
+							// triggered clicking directly to a popup menu. (If a field is open, the
+							// saveTx() below will become a no-op.)
+							yield this.blurOpenField();
+							
 							return this.item.saveTx();
 						}
 						return;
@@ -2106,9 +2115,12 @@
 					
 					var changed = this.item.setCreator(index, fields);
 					if (changed && this.saveOnEdit && !skipSave) {
+						// See note above
+						yield this.blurOpenField();
+						
 						return this.item.saveTx();
 					}
-				}.bind(this));
+				}, this);
 				]]></body>
 			</method>
 			
@@ -2117,6 +2129,7 @@
 			  @return {Promise}
 			-->
 			<method name="swapNames">
+				<parameter name="event"/>
 				<body><![CDATA[
 				return Zotero.Promise.try(function () {
 					var row = Zotero.getAncestorByTagName(document.popupNode, 'row');
@@ -2138,9 +2151,8 @@
 			<method name="moveCreator">
 				<parameter name="index"/>
 				<parameter name="moveUp"/>
-				<body>
-				<![CDATA[
-				return Zotero.Promise.try(function () {
+				<body><![CDATA[
+				return Zotero.spawn(function* () {
 					if (index == 0 && moveUp) {
 						Zotero.debug("Can't move up creator 0");
 						return;
@@ -2156,11 +2168,13 @@
 					this.item.setCreator(newIndex, a);
 					this.item.setCreator(index, b);
 					if (this.saveOnEdit) {
+						// See note in modifyCreator()
+						yield this.blurOpenField();
+						
 						return this.item.saveTx();
 					}
-				}.bind(this));
-				]]>
-				</body>
+				}, this);
+				]]></body>
 			</method>
 			
 			
@@ -2394,7 +2408,7 @@
 							var hideTransforms = !exists || !!fieldMode;
 							return !hideTransforms;">
 						<menuitem label="&zotero.item.creatorTransform.nameSwap;"
-							oncommand="document.getBindingParent(this).swapNames();"/>
+							oncommand="document.getBindingParent(this).swapNames(event);"/>
 					</menupopup>
 					<menupopup id="zotero-doi-menu">
 						<menuitem id="zotero-doi-menu-view-online" label="&zotero.item.viewOnline;"/>
diff --git a/test/tests/itemPaneTest.js b/test/tests/itemPaneTest.js
index 58704979a..1528e8668 100644
--- a/test/tests/itemPaneTest.js
+++ b/test/tests/itemPaneTest.js
@@ -27,6 +27,89 @@ describe("Item pane", function () {
 			
 			yield Zotero.Items.erase(id);
 		})
+		
+		
+		it.skip("should swap creator names", function* () {
+			var item = new Zotero.Item('book');
+			item.setCreators([
+				{
+					firstName: "First",
+					lastName: "Last",
+					creatorType: "author"
+				}
+			]);
+			yield item.saveTx();
+			
+			var itemBox = doc.getElementById('zotero-editpane-item-box');
+			var label = doc.getAnonymousNodes(itemBox)[0].getElementsByAttribute('fieldname', 'creator-0-lastName')[0];
+			var parent = label.parentNode;
+			assert.isTrue(parent.hasAttribute('contextmenu'));
+			
+			var menupopup = doc.getAnonymousNodes(itemBox)[0]
+				.getElementsByAttribute('id', 'zotero-creator-transform-menu')[0];
+			// Fake a right-click
+			doc.popupNode = parent;
+			menupopup.openPopup(
+				parent, "after_start", 0, 0, true, false, new MouseEvent('click', { button: 2 })
+			);
+			var menuitem = menupopup.getElementsByTagName('menuitem')[0];
+			menuitem.click();
+			yield waitForItemEvent('modify');
+			
+			var creator = item.getCreators()[0];
+			assert.propertyVal(creator, 'firstName', 'Last');
+			assert.propertyVal(creator, 'lastName', 'First');
+		});
+		
+		
+		it("shouldn't show Swap Names menu for single-field mode", function* () {
+			var item = new Zotero.Item('book');
+			item.setCreators([
+				{
+					name: "Name",
+					creatorType: "author"
+				}
+			]);
+			yield item.saveTx();
+			
+			var itemBox = doc.getElementById('zotero-editpane-item-box');
+			var label = doc.getAnonymousNodes(itemBox)[0].getElementsByAttribute('fieldname', 'creator-0-lastName')[0];
+			assert.isFalse(label.parentNode.hasAttribute('contextmenu'));
+		});
+		
+		
+		// Note: This issue applies to all context menus in the item box (text transform, name swap),
+		// though the others aren't tested. This might go away with the XUL->HTML transition.
+		it.skip("should save open field after changing creator type", function* () {
+			var item = new Zotero.Item('book');
+			item.setCreators([
+				{
+					firstName: "First",
+					lastName: "Last",
+					creatorType: "author"
+				}
+			]);
+			var id = yield item.saveTx();
+			
+			var itemBox = doc.getElementById('zotero-editpane-item-box');
+			var label = doc.getAnonymousNodes(itemBox)[0].getElementsByAttribute('fieldname', 'place')[1];
+			label.click();
+			var textbox = doc.getAnonymousNodes(itemBox)[0].getElementsByAttribute('fieldname', 'place')[1];
+			textbox.value = "Place";
+			
+			var menuLabel = doc.getAnonymousNodes(itemBox)[0].getElementsByAttribute('fieldname', 'creator-0-typeID')[0];
+			menuLabel.click();
+			var menupopup = itemBox._creatorTypeMenu;
+			var menuItems = menupopup.getElementsByTagName('menuitem');
+			menuItems[1].click();
+			yield waitForItemEvent('modify');
+			
+			assert.equal(item.getField('place'), 'Place');
+			assert.equal(Zotero.CreatorTypes.getName(item.getCreators()[0].creatorTypeID), 'contributor');
+			
+			// Wait for no-op saveTx()
+			yield Zotero.Promise.delay(1);
+		});
 	})
 	
 	describe("Attachment pane", function () {