fx-compat: Update conflict resolution merge window (#3090)

Tests passing, but not yet styled properly
This commit is contained in:
Dan Stillman 2023-04-29 03:13:53 -04:00
parent dbefdaac2c
commit 4a77bc4be0
13 changed files with 626 additions and 656 deletions

View file

@ -1,452 +0,0 @@
<?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">
<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"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="merge-group">
<resources>
<stylesheet src="chrome://zotero/skin/merge.css"/>
</resources>
<implementation>
<constructor>
<![CDATA[
this._leftpane = this._id('leftpane');
this._rightpane = this._id('rightpane');
this._mergepane = this._id('mergepane');
]]>
</constructor>
<field name="libraryID"/>
<field name="_data"/>
<property name="data" onget="return this._data;">
<setter>
<![CDATA[
this._data = val;
this.refresh();
]]>
</setter>
</property>
<property name="merged" onget="return this._mergepane.data"/>
<field name="_type"/>
<property name="type" onget="return this._type;">
<setter>
<![CDATA[
switch (val) {
case 'item':
case 'attachment':
case 'note':
case 'annotation':
case 'file':
break;
default:
throw new Error(`Unsupported merge object type '${type}'`);
}
this._type = val;
var hbox = document.getAnonymousNodes(this)[0];
hbox.setAttribute('mergetype', val);
]]>
</setter>
</property>
<property name="leftCaption" onset="this._leftpane.caption.label = val"/>
<property name="rightCaption" onset="this._rightpane.caption.label = val"/>
<property name="mergeCaption" onset="this._mergepane.caption.label = val"/>
<field name="_leftpane"/>
<property name="leftpane" onget="return this._leftpane"/>
<field name="_rightpane"/>
<property name="rightpane" onget="return this._rightpane"/>
<field name="_mergepane"/>
<property name="mergepane" onget="return this._mergepane"/>
<property name="onSelectionChange"/>
<method name="refresh">
<body>
<![CDATA[
if (this._data.left.deleted && this._data.right.deleted) {
throw new Error("'left' and 'right' cannot both be deleted");
}
// Check for note or attachment
this.type = this._getTypeFromObject(
this._data.left.deleted ? this._data.right : this._data.left
);
var showButton = this.type != 'item';
this._leftpane.showButton = showButton;
this._rightpane.showButton = showButton;
this._leftpane.libraryID = this.libraryID;
this._rightpane.libraryID = this.libraryID;
this._mergepane.libraryID = this.libraryID;
this._leftpane.data = this._data.left;
this._rightpane.data = this._data.right;
this._mergepane.data = this._data.merge;
if (this._data.selected == 'left') {
this.choosePane(this._leftpane);
}
else {
this.choosePane(this._rightpane);
}
/*
Code to display only the different values -- not used
var diff = this._leftpane.ref.diff(this._rightpane.ref, true);
var fields = [];
var diffFields = [];
for (var field in diff[0].primary) {
fields.push(field);
if (diff[0].primary[field] != diff[1].primary[field]) {
diffFields.push(field);
}
}
for (var field in diff[0].fields) {
fields.push(field);
if (diff[0].fields[field] != diff[1].fields[field]) {
diffFields.push(field);
}
}
this._leftpane.objectbox.fieldOrder = fields;
this._rightpane.objectbox.fieldOrder = fields;
// Display merge pane if item types match
if (this._leftpane.ref.itemTypeID == this._rightpane.ref.itemTypeID) {
this._leftpane.objectbox.visibleFields = fields;
this._rightpane.objectbox.visibleFields = fields;
this._leftpane.objectbox.clickable = false;
this._rightpane.objectbox.clickable = false;
this._leftpane.objectbox.clickableFields = diffFields;
this._rightpane.objectbox.clickableFields = diffFields;
var mergeItem = new Zotero.Item(this._leftpane.ref.itemTypeID);
this._mergepane.ref = mergeItem;
this._mergepane.objectbox.visibleFields = fields;
}
// Otherwise only allow clicking on item types
else {
this._leftpane.objectbox.clickableFields = ['itemType'];
this._rightpane.objectbox.clickableFields = ['itemType'];
}
*/
this._mergepane.objectbox.editable = true;
/*
No need to refresh if not comparing fields
this._leftpane.objectbox.refresh();
this._rightpane.objectbox.refresh();
*/
]]>
</body>
</method>
<method name="choosePane">
<parameter name="pane"/>
<body>
<![CDATA[
if (pane.getAttribute('anonid') == 'leftpane') {
var position = 'left';
var otherPane = this._rightpane;
}
else {
var position = 'right';
var otherPane = this._leftpane;
}
pane.removeAttribute("selected");
otherPane.removeAttribute("selected");
pane.setAttribute("selected", "true");
if (pane.deleted) {
this._mergepane.deleted = true;
}
else {
this._mergepane.data = pane.data;
}
if (this.onSelectionChange) {
this.onSelectionChange();
}
]]>
</body>
</method>
<method name="_getTypeFromObject">
<parameter name="obj"/>
<body>
<![CDATA[
if (!obj.itemType) {
Zotero.debug(obj, 1);
throw new Error("obj is not item JSON");
}
switch (obj.itemType) {
case 'attachment':
case 'note':
case 'annotation':
return obj.itemType;
}
return 'item';
]]>
</body>
</method>
<method name="_id">
<parameter name="id"/>
<body>
<![CDATA[
return document.getAnonymousNodes(this)[0].getElementsByAttribute('anonid',id)[0];
]]>
</body>
</method>
</implementation>
<content>
<xul:hbox id="merge-group" flex="1">
<xul:zoteromergepane anonid="leftpane" flex="1"/>
<xul:zoteromergepane anonid="rightpane" flex="1"/>
<xul:zoteromergepane anonid="mergepane" flex="1"/>
</xul:hbox>
</content>
</binding>
<binding id="merge-pane">
<resources>
<stylesheet src="chrome://zotero/skin/bindings/merge.css"/>
</resources>
<implementation>
<constructor>
<![CDATA[
this.parent = document.getBindingParent(this.parentNode);
this.isMergePane = this.getAttribute('anonid') == 'mergepane';
if (!this.isMergePane) {
this.pane.onclick = function () {
this.parent.choosePane(this);
}.bind(this);
}
]]>
</constructor>
<property name="type" onget="return this.parent.type" readonly="true"/>
<property name="caption" onget="return this._id('caption')" readonly="true"/>
<field name="showButton"/>
<property name="pane" onget="return document.getAnonymousNodes(this)[0]"/>
<property name="objectbox" onget="return this._id('objectbox')" readonly="true"/>
<field name="_deleted"/>
<property name="deleted">
<setter>
<![CDATA[
this._deleted = !!val;
var placeholder = this._id('object-placeholder');
if (placeholder) {
placeholder.hidden = !!val;
}
else {
this._id('objectbox').hidden = true;
}
var deleteBox = this._id('delete-box');
deleteBox.hidden = !val;
]]>
</setter>
</property>
<field name="libraryID"/>
<field name="_data"/>
<property name="data" onget="return this._data">
<setter>
<![CDATA[
this._data = val;
var button = this._id('choose-button');
button.label = Zotero.getString('sync.conflict.chooseThisVersion');
if (this.showButton) {
button.onclick = () => this.parent.choosePane(this);
button.style.visibility = 'visible';
}
else {
button.style.visibility = 'hidden';
}
if (val.deleted) {
this.deleted = true;
return;
}
this.deleted = false;
// Replace XUL placeholder with XUL object box of given type
var elementName;
switch (this.type) {
case 'item':
elementName = 'zoteroitembox';
break;
case 'attachment':
case 'file':
elementName = 'zoteroattachmentbox';
break;
case 'note':
var type = Zotero.Libraries.get(this.libraryID).libraryType;
elementName = 'note-editor';
break;
case 'annotation':
elementName = 'div';
break;
default:
throw new Error("Object type '" + this.type + "' not supported");
}
if (elementName == 'div') {
let HTML_NS = 'http://www.w3.org/1999/xhtml';
var objbox = document.createElementNS(HTML_NS, elementName);
}
else {
var objbox = document.createElement(elementName);
}
var parentRow = this._id('parent-row');
if (val.parentItem) {
parentRow.textContent = '';
let label = document.createElement('span');
label.textContent = Zotero.getString('pane.item.parentItem');
parentRow.appendChild(label);
let parentItem = Zotero.Items.getByLibraryAndKey(this.libraryID, val.parentItem);
let text = document.createTextNode(" " + parentItem.getDisplayTitle(true));
parentRow.appendChild(text);
parentRow.hidden = false;
}
else {
parentRow.hidden = true;
}
if (this._id('object-placeholder')) {
var placeholder = this._id('object-placeholder');
placeholder.parentNode.replaceChild(objbox, placeholder);
}
else {
var oldObjBox = this._id('objectbox');
oldObjBox.parentNode.replaceChild(objbox, oldObjBox);
}
objbox.setAttribute("anonid", "objectbox");
objbox.setAttribute("flex", "1");
objbox.mode = this.type == 'file' ? 'filemerge' : 'merge';
// Store JSON
this._data = val;
// Create a copy of the JSON that we can clean for display, since the remote object
// might reference things that don't exist locally
var displayJSON = Object.assign({}, val);
displayJSON.collections = [];
// Create item from JSON for metadata box
var item = new Zotero.Item(val.itemType);
item.libraryID = this.libraryID;
item.fromJSON(displayJSON);
if (item.isAnnotation()) {
Zotero.Annotations.toJSON(item)
.then((data) => {
Zotero.AnnotationBox.render(objbox, { data });
});
}
else {
objbox.item = item;
}
]]>
</setter>
</property>
<field name="parent"/>
<method name="click">
<body><![CDATA[
this.pane.click();
]]></body>
</method>
<method name="_id">
<parameter name="id"/>
<body>
<![CDATA[
var elems = document.getAnonymousNodes(this)[0].getElementsByAttribute('anonid', id);
return elems.length ? elems[0] : false;
]]>
</body>
</method>
</implementation>
<content>
<xul:vbox flex="1">
<xul:groupbox anonid="merge-pane" flex="1">
<xul:caption anonid="caption"/>
<html:div anonid="parent-row" hidden="true"/>
<xul:box anonid="object-placeholder"/>
<xul:hbox anonid="delete-box" hidden="true" flex="1">
<xul:label value="&zotero.merge.deleted;"/>
</xul:hbox>
</xul:groupbox>
<xul:button anonid="choose-button"/>
</xul:vbox>
</content>
</binding>
</bindings>

View file

@ -1901,7 +1901,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
}
*/
window.openDialog('chrome://zotero/content/merge.xul', '', 'chrome,modal,centerscreen', io);
window.openDialog('chrome://zotero/content/merge.xhtml', '', 'chrome,modal,centerscreen', io);
await Zotero.DB.executeTransaction(async function () {
// DEBUG: This probably needs to be updated if this starts being used

View file

@ -32,6 +32,7 @@ Services.scriptloader.loadSubScript("chrome://zotero/content/elements/base.js",
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/attachmentBox.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/guidancePanel.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemBox.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/mergeGroup.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/menulistItemTypes.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/noteEditor.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/notesBox.js', this);

View file

@ -0,0 +1,439 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2023 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.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 *****
*/
{
class MergeGroup extends XULElement {
constructor() {
super();
this.libraryID = null;
this._data = null;
this._type = null;
this.content = MozXULElement.parseXULToFragment(`
<div id="merge-group" xmlns="http://www.w3.org/1999/xhtml">
<merge-pane id="left-pane" flex="1" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
<merge-pane id="right-pane" flex="1" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
<merge-pane id="merge-pane" flex="1" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
</div>
`, ['chrome://zotero/locale/zotero.dtd']);
}
get stylesheets() {
return [
'chrome://zotero/skin/merge.css'
];
}
connectedCallback() {
this.append(document.importNode(this.content, true));
this._leftPane = this._id('left-pane');
this._rightPane = this._id('right-pane');
this._mergePane = this._id('merge-pane');
}
get data() {
return this._data;
}
set data(val) {
this._data = val;
this.refresh();
}
get merged() {
return this._mergePane.data;
}
get type() {
return this._type;
}
set type(val) {
switch (val) {
case 'item':
case 'attachment':
case 'note':
case 'annotation':
case 'file':
break;
default:
throw new Error(`Unsupported merge object type '${type}'`);
}
this._type = val;
this._id('merge-group').setAttribute('mergetype', val);
}
set leftCaption(val) {
this._leftPane.caption.label = val;
}
set rightCaption(val) {
this._rightPane.caption.label = val;
}
set mergeCaption(val) {
this._mergePane.caption.label = val;
}
get leftPane() {
return this._leftPane;
}
get rightPane() {
return this._rightPane;
}
get mergePane() {
return this._mergePane;
}
refresh() {
if (this._data.left.deleted && this._data.right.deleted) {
throw new Error("'left' and 'right' cannot both be deleted");
}
// Check for note or attachment
this.type = this._getTypeFromObject(
this._data.left.deleted ? this._data.right : this._data.left
);
var showButton = this.type != 'item';
this._leftPane.showButton = showButton;
this._rightPane.showButton = showButton;
this._leftPane.libraryID = this.libraryID;
this._rightPane.libraryID = this.libraryID;
this._mergePane.libraryID = this.libraryID;
this._leftPane.data = this._data.left;
this._rightPane.data = this._data.right;
this._mergePane.data = this._data.merge;
if (this._data.selected == 'left') {
this.choosePane(this._leftPane);
}
else {
this.choosePane(this._rightPane);
}
/*
Code to display only the different values -- not used
var diff = this._leftPane.ref.diff(this._rightPane.ref, true);
var fields = [];
var diffFields = [];
for (var field in diff[0].primary) {
fields.push(field);
if (diff[0].primary[field] != diff[1].primary[field]) {
diffFields.push(field);
}
}
for (var field in diff[0].fields) {
fields.push(field);
if (diff[0].fields[field] != diff[1].fields[field]) {
diffFields.push(field);
}
}
this._leftPane.objectBox.fieldOrder = fields;
this._rightPane.objectBox.fieldOrder = fields;
// Display merge pane if item types match
if (this._leftPane.ref.itemTypeID == this._rightPane.ref.itemTypeID) {
this._leftPane.objectBox.visibleFields = fields;
this._rightPane.objectBox.visibleFields = fields;
this._leftPane.objectBox.clickable = false;
this._rightPane.objectBox.clickable = false;
this._leftPane.objectBox.clickableFields = diffFields;
this._rightPane.objectBox.clickableFields = diffFields;
var mergeItem = new Zotero.Item(this._leftPane.ref.itemTypeID);
this._mergePane.ref = mergeItem;
this._mergePane.objectBox.visibleFields = fields;
}
// Otherwise only allow clicking on item types
else {
this._leftPane.objectBox.clickableFields = ['itemType'];
this._rightPane.objectBox.clickableFields = ['itemType'];
}
*/
this._mergePane.objectBox.editable = true;
/*
No need to refresh if not comparing fields
this._leftPane.objectBox.refresh();
this._rightPane.objectBox.refresh();
*/
}
choosePane(pane) {
Zotero.debug(new Error().stack);
if (pane.id == 'left-pane') {
var position = 'left';
var otherPane = this._rightPane;
}
else {
var position = 'right';
var otherPane = this._leftPane;
}
pane.removeAttribute("selected");
otherPane.removeAttribute("selected");
pane.setAttribute("selected", "true");
this._mergePane.data = pane.data;
if (this.onSelectionChange) {
this.onSelectionChange();
}
}
_getTypeFromObject(obj) {
if (!obj.itemType) {
Zotero.debug(obj, 1);
throw new Error("obj is not item JSON");
}
switch (obj.itemType) {
case 'attachment':
case 'note':
case 'annotation':
return obj.itemType;
}
return 'item';
}
_id(id) {
return this.querySelector(`#${id}`);
}
}
customElements.define("merge-group", MergeGroup);
}
{
class MergePane extends XULElement {
constructor() {
super();
this._data = null;
this._deleted = false;
this.content = MozXULElement.parseXULToFragment(`
<vbox flex="1">
<groupbox class="merge-pane" flex="1">
<caption class="caption"/>
<html:div class="parent-row" hidden="true"/>
<box class="object-placeholder"/>
<hbox class="delete-box" hidden="true" flex="1">
<label value="&zotero.merge.deleted;"/>
</hbox>
</groupbox>
<button class="choose-button"/>
</vbox>
`, ['chrome://zotero/locale/zotero.dtd']);
}
get stylesheets() {
return ['chrome://zotero/skin/bindings/merge.css'];
}
connectedCallback() {
this.append(document.importNode(this.content, true));
this.parent = document.querySelector('merge-group');
this.isMergePane = this.id == 'merge-pane';
if (!this.isMergePane) {
this.pane.onclick = function () {
this.parent.choosePane(this);
}.bind(this);
}
}
get type() {
return this.parent.type;
}
get pane() {
return this._class('merge-pane');
}
get caption() {
return this._class('caption');
}
get parentRow() {
return this._class('parent-row');
}
get objectBox() {
return this._class('object-box');
}
get deleted() {
return this._deleted;
}
set deleted(val) {
this._deleted = val;
var placeholder = this._class('object-placeholder');
if (placeholder) {
placeholder.hidden = !!val;
}
else {
this._class('object-box').hidden = true;
}
var deleteBox = this._class('delete-box');
deleteBox.hidden = !val;
}
get data() {
return this._data;
}
set data(val) {
this._data = val;
var button = this._class('choose-button');
button.label = Zotero.getString('sync.conflict.chooseThisVersion');
if (this.showButton) {
button.onclick = () => this.parent.choosePane(this);
button.style.visibility = 'visible';
}
else {
button.style.visibility = 'hidden';
}
if (val.deleted) {
this.deleted = true;
return;
}
this.deleted = false;
// Replace XUL placeholder with XUL object box of given type
var elementName;
switch (this.type) {
case 'item':
elementName = 'item-box';
break;
case 'attachment':
case 'file':
elementName = 'attachment-box';
break;
case 'note':
var type = Zotero.Libraries.get(this.libraryID).libraryType;
elementName = 'note-editor';
break;
case 'annotation':
elementName = 'div';
break;
default:
throw new Error("Object type '" + this.type + "' not supported");
}
if (elementName == 'div') {
var objbox = document.createElement(elementName);
}
else {
var objbox = document.createXULElement(elementName);
}
var parentRow = this._class('parent-row');
if (val.parentItem) {
parentRow.textContent = '';
let label = document.createElement('span');
label.textContent = Zotero.getString('pane.item.parentItem');
parentRow.appendChild(label);
let parentItem = Zotero.Items.getByLibraryAndKey(this.libraryID, val.parentItem);
let text = document.createTextNode(" " + parentItem.getDisplayTitle(true));
parentRow.appendChild(text);
parentRow.hidden = false;
}
else {
parentRow.hidden = true;
}
if (this._class('object-placeholder')) {
var placeholder = this._class('object-placeholder');
placeholder.parentNode.replaceChild(objbox, placeholder);
}
else {
let oldObjBox = this.objectBox;
oldObjBox.parentNode.replaceChild(objbox, oldObjBox);
}
objbox.className = "object-box";
objbox.setAttribute("flex", "1");
objbox.mode = this.type == 'file' ? 'filemerge' : 'merge';
// Store JSON
this._data = val;
// Create a copy of the JSON that we can clean for display, since the remote object
// might reference things that don't exist locally
var displayJSON = Object.assign({}, val);
displayJSON.collections = [];
// Create item from JSON for metadata box
var item = new Zotero.Item(val.itemType);
item.libraryID = this.libraryID;
item.fromJSON(displayJSON);
if (item.isAnnotation()) {
Zotero.Annotations.toJSON(item)
.then((data) => {
Zotero.AnnotationBox.render(objbox, { data });
});
}
else {
objbox.item = item;
}
}
click() {
this.pane.click();
}
_class(className) {
return this.querySelector(`.${className}`);
}
}
customElements.define("merge-pane", MergePane);
}

View file

@ -1,9 +1,9 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
Copyright © 2023 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.zotero.org
This file is part of Zotero.
@ -35,13 +35,18 @@ var Zotero_Merge_Window = new function () {
var _pos = -1;
this.init = function () {
_wizard = document.getElementsByTagName('wizard')[0];
_wizardPage = document.getElementsByTagName('wizardpage')[0];
_mergeGroup = document.getElementsByTagName('zoteromergegroup')[0];
_wizard = document.getElementById('merge-window');
_wizardPage = document.querySelector('wizardpage');
_mergeGroup = document.querySelector('merge-group');
_wizard.setAttribute('width', Math.min(980, screen.width - 20));
_wizard.setAttribute('height', Math.min(718, screen.height - 30));
_wizard.addEventListener('wizardback', this.onBack.bind(this));
_wizard.addEventListener('wizardnext', this.onNext.bind(this));
_wizard.addEventListener('wizardcancel', this.onCancel.bind(this));
_wizard.addEventListener('wizardfinish', this.onFinish.bind(this));
// Set font size from pref
Zotero.setFontSize(_wizardPage);
@ -91,28 +96,11 @@ var Zotero_Merge_Window = new function () {
_updateGroup();
var nextButton = _wizard.getButton("next");
if (Zotero.isMac) {
nextButton.setAttribute("hidden", "false");
_wizard.getButton("finish").setAttribute("hidden", "true");
}
else {
var buttons = document.getAnonymousElementByAttribute(_wizard, "anonid", "Buttons");
var deck = document.getAnonymousElementByAttribute(buttons, "anonid", "WizardButtonDeck");
deck.selectedIndex = 1;
}
_setInstructionsString(nextButton.label);
this.updateNextFinish();
}
this.onNext = function () {
// At end or resolving all
if (_pos + 1 == _conflicts.length || _resolveAllCheckbox.checked) {
return true;
}
// First page
if (_pos == -1) {
_wizard.canRewind = false;
@ -134,13 +122,7 @@ var Zotero_Merge_Window = new function () {
}
_updateResolveAllCheckbox();
if (_isLastConflict()) {
_showFinishButton();
}
else {
_showNextButton();
}
this.updateNextFinish();
return false;
}
@ -149,7 +131,7 @@ var Zotero_Merge_Window = new function () {
this.onFinish = function () {
// If using one side for all remaining, update merge object
if (!_isLastConflict() && _resolveAllCheckbox.checked) {
let side = _mergeGroup.rightpane.getAttribute("selected") == "true" ? 'right' : 'left'
let side = _mergeGroup.rightPane.getAttribute("selected") == "true" ? 'right' : 'left'
for (let i = _pos; i < _conflicts.length; i++) {
_merged[i] = {
data: _getMergeDataWithSide(i, side),
@ -180,11 +162,14 @@ var Zotero_Merge_Window = new function () {
}
this.onResolveAllChange = function (resolveAll) {
if (resolveAll || _isLastConflict()) {
_showFinishButton();
this.updateNextFinish = function () {
if (_isLastConflict() || _resolveAllCheckbox.checked) {
// The Mozilla wizard CE checks 'next' to determine if it's on the last page
_wizardPage.next = '';
_showFinishButton()
}
else {
_wizardPage.next = 'page-id';
_showNextButton();
}
}
@ -212,7 +197,7 @@ var Zotero_Merge_Window = new function () {
function _getCurrentMergeInfo() {
return {
data: _mergeGroup.merged,
selected: _mergeGroup.leftpane.getAttribute("selected") == "true" ? "left" : "right"
selected: _mergeGroup.leftPane.getAttribute("selected") == "true" ? "left" : "right"
};
}
@ -278,6 +263,7 @@ var Zotero_Merge_Window = new function () {
throw new Error("Side not provided");
}
// If either side is deleted, nothing to merge
if (_conflicts[pos].left.deleted || _conflicts[pos].right.deleted) {
return _conflicts[pos][side];
}
@ -293,7 +279,7 @@ var Zotero_Merge_Window = new function () {
function _updateResolveAllCheckbox() {
if (_mergeGroup.rightpane.getAttribute("selected") == 'true') {
if (_mergeGroup.rightPane.getAttribute("selected") == 'true') {
var label = 'resolveAllRemote';
}
else {
@ -316,8 +302,7 @@ var Zotero_Merge_Window = new function () {
_wizard.getButton("finish").setAttribute("hidden", "true");
}
else {
var buttons = document.getAnonymousElementByAttribute(_wizard, "anonid", "Buttons");
var deck = document.getAnonymousElementByAttribute(buttons, "anonid", "WizardButtonDeck");
let deck = document.querySelector(".wizard-next-deck");
deck.selectedIndex = 1;
}
@ -335,8 +320,7 @@ var Zotero_Merge_Window = new function () {
// Windows uses a deck to switch between the Next and Finish buttons
// TODO: check Linux
else {
var buttons = document.getAnonymousElementByAttribute(_wizard, "anonid", "Buttons");
var deck = document.getAnonymousElementByAttribute(buttons, "anonid", "WizardButtonDeck");
let deck = document.querySelector(".wizard-next-deck");
deck.selectedIndex = 0;
}

View file

@ -0,0 +1,66 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2023 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.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 *****
-->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/merge.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
onload="Zotero_Merge_Window.init()"
>
<wizard id="merge-window" title="">
<script>
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/components/annotation.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/merge.js", this);
</script>
<wizardpage label="&zotero.merge.title;" pageid="page-id">
<description id="zotero-merge-instructions"/>
<merge-group flex="1"/>
<separator class="thin"/>
<hbox align="center">
<separator orient="vertical" flex="1"/>
<hbox>
<checkbox id="resolve-all"
oncommand="Zotero_Merge_Window.updateNextFinish()"/>
</hbox>
<separator orient="vertical"/>
<hbox id="zotero-step-count">
<label id="zotero-merge-num-objects"/>
<label value="&zotero.merge.of;"/>
<label id="zotero-merge-total-objects"/>
</hbox>
</hbox>
</wizardpage>
</wizard>
</window>

View file

@ -1,67 +0,0 @@
<?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 *****
-->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/merge.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<wizard
id="zotero-merge-window"
orient="vertical"
title=""
onwizardfinish="return Zotero_Merge_Window.onFinish()"
onwizardcancel="return Zotero_Merge_Window.onCancel()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="include.js"/>
<script src="merge.js"/>
<script src="components/annotation.js"/>
<wizardpage onpageshow="Zotero_Merge_Window.init()"
onpagerewound="Zotero_Merge_Window.onBack(); return false"
onpageadvanced="return Zotero_Merge_Window.onNext()"
label="&zotero.merge.title;">
<description id="zotero-merge-instructions"/>
<zoteromergegroup flex="1"/>
<separator class="thin"/>
<hbox align="center">
<separator orient="vertical" flex="1"/>
<hbox>
<checkbox id="resolve-all"
oncommand="Zotero_Merge_Window.onResolveAllChange(this.checked)"/>
</hbox>
<separator orient="vertical"/>
<hbox id="zotero-step-count">
<label id="zotero-merge-num-objects"/>
<label value="&zotero.merge.of;"/>
<label id="zotero-merge-total-objects"/>
</hbox>
</hbox>
</wizardpage>
</wizard>

View file

@ -1043,10 +1043,9 @@ Zotero.Sync.Storage.Local = {
}
};
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var wm = Services.wm;
var lastWin = wm.getMostRecentWindow("navigator:browser");
lastWin.openDialog('chrome://zotero/content/merge.xul', '', 'chrome,modal,centerscreen', io);
lastWin.openDialog('chrome://zotero/content/merge.xhtml', '', 'chrome,modal,centerscreen', io);
if (!io.dataOut) {
return false;

View file

@ -1439,7 +1439,7 @@ Zotero.Sync.Data.Local = {
conflicts
}
};
var url = 'chrome://zotero/content/merge.xul';
var url = 'chrome://zotero/content/merge.xhtml';
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var lastWin = wm.getMostRecentWindow("navigator:browser");

View file

@ -1,4 +1,4 @@
/* merge.xul */
/* merge.xhtml */
wizard {
padding-left: 10px;
padding-right: 10px;

View file

@ -625,14 +625,14 @@ describe("Zotero.Sync.Storage.Local", function () {
item3.attachmentSyncState = "in_conflict";
yield item3.saveTx({ skipAll: true });
var promise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var promise = waitForWindow('chrome://zotero/content/merge.xhtml', async function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (remote)
// Later remote version should be selected
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
// Check checkbox text
assert.equal(
@ -641,17 +641,17 @@ describe("Zotero.Sync.Storage.Local", function () {
);
// Select local object
mergeGroup.leftpane.click();
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
wizard.getButton('next').click();
// 2 (local)
// Later local version should be selected
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
// Select remote object
mergeGroup.rightpane.click();
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
mergeGroup.rightPane.click();
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);
@ -686,18 +686,18 @@ describe("Zotero.Sync.Storage.Local", function () {
item1.attachmentSyncState = "in_conflict";
yield item1.saveTx({ skipAll: true });
var promise = waitForWindow('chrome://zotero/content/merge.xul', async function (dialog) {
var promise = waitForWindow('chrome://zotero/content/merge.xhtml', async function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (remote)
// Identical, so remote version should be selected
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
// Select local object
mergeGroup.leftpane.click();
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);

View file

@ -2384,18 +2384,18 @@ describe("Zotero.Sync.Data.Engine", function () {
}
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (accept remote deletion)
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.rightpane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
mergeGroup.rightPane.click();
wizard.getButton('next').click();
// 2 (ignore remote deletion)
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
wizard.getButton('finish').click();
})
yield engine._startDownload();
@ -2655,9 +2655,9 @@ describe("Zotero.Sync.Data.Engine", function () {
}
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var wizard = doc.querySelector('wizard');
wizard.getButton('cancel').click();
})
var e = yield getPromiseError(engine._startDownload());
@ -2750,9 +2750,9 @@ describe("Zotero.Sync.Data.Engine", function () {
}
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var wizard = doc.querySelector('wizard');
wizard.getButton('cancel').click();
})
var e = yield getPromiseError(engine._startDownload());
@ -3609,21 +3609,21 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (remote)
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
wizard.getButton('next').click();
// 2 (local)
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
// Select local object
mergeGroup.leftpane.click();
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);
assert.isFalse(wizard.getButton('finish').hidden);
@ -3716,21 +3716,21 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (remote)
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
wizard.getButton('next').click();
// 2 (local)
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
// Select local object
mergeGroup.leftpane.click();
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);
assert.isFalse(wizard.getButton('finish').hidden);
@ -3841,28 +3841,28 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// TODO: Make this function async and verify that annotation widgets show up here
// after rendering. This may not be possible as long as this is within XBL.
// 1 (remote)
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
wizard.getButton('next').click();
// 2 (local)
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
// Select local object
mergeGroup.leftpane.click();
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
wizard.getButton('next').click();
// 2 (remote)
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);
@ -3984,15 +3984,15 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
var resolveAll = doc.getElementById('resolve-all');
// 1 (remote)
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
assert.equal(
resolveAll.label,
Zotero.getString('sync.conflict.resolveAllRemote')
@ -4000,8 +4000,8 @@ describe("Zotero.Sync.Data.Engine", function () {
wizard.getButton('next').click();
// 2 (local and Resolve All checkbox)
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
mergeGroup.leftpane.click();
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
mergeGroup.leftPane.click();
assert.equal(
resolveAll.label,
Zotero.getString('sync.conflict.resolveAllLocal')
@ -4106,15 +4106,15 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
var resolveAll = doc.getElementById('resolve-all');
// 1 (remote)
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
assert.equal(
resolveAll.label,
Zotero.getString('sync.conflict.resolveAllRemote')
@ -4122,7 +4122,7 @@ describe("Zotero.Sync.Data.Engine", function () {
wizard.getButton('next').click();
// 2 click Resolve All checkbox
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
assert.equal(
resolveAll.label,
Zotero.getString('sync.conflict.resolveAllRemote')
@ -4191,16 +4191,16 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.ok(mergeGroup.leftpane.pane.onclick);
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
assert.ok(mergeGroup.leftPane.pane.onclick);
// Select local deleted version
mergeGroup.leftpane.pane.click();
mergeGroup.leftPane.pane.click();
wizard.getButton('finish').click();
})
yield engine._downloadObjects('item', [obj.key]);
@ -4251,16 +4251,16 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.ok(mergeGroup.leftpane.pane.onclick);
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
assert.ok(mergeGroup.leftPane.pane.onclick);
// Select local deleted version
mergeGroup.leftpane.pane.click();
mergeGroup.leftPane.pane.click();
wizard.getButton('finish').click();
});
yield engine._downloadObjects('item', [obj.key]);
@ -4306,15 +4306,15 @@ describe("Zotero.Sync.Data.Engine", function () {
json: responseJSON
});
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
assert.isTrue(doc.getElementById('resolve-all').hidden);
// Remote version should be selected by default
assert.equal(mergeGroup.rightpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.rightPane.getAttribute('selected'), 'true');
wizard.getButton('finish').click();
})
yield engine._downloadObjects('item', [key]);
@ -4797,15 +4797,15 @@ describe("Zotero.Sync.Data.Engine", function () {
});
// Apply remote deletions
var crPromise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var crPromise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// Should be one conflict for each object type; select local
var numConflicts = Object.keys(objects).length;
for (let i = 0; i < numConflicts; i++) {
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
if (i < numConflicts - 1) {
wizard.getButton('next').click();

View file

@ -1233,16 +1233,16 @@ describe("Zotero.Sync.Data.Local", function() {
note.setNote("Test");
yield note.saveTx();
var promise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var promise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// Show title for middle and right panes
var parentText = Zotero.getString('pane.item.parentItem') + " Parent";
assert.equal(mergeGroup.leftpane._id('parent-row').textContent, "");
assert.equal(mergeGroup.rightpane._id('parent-row').textContent, parentText);
assert.equal(mergeGroup.mergepane._id('parent-row').textContent, parentText);
assert.equal(mergeGroup.leftPane.parentRow.textContent, "");
assert.equal(mergeGroup.rightPane.parentRow.textContent, parentText);
assert.equal(mergeGroup.mergePane.parentRow.textContent, parentText);
wizard.getButton('finish').click();
});
@ -1268,18 +1268,18 @@ describe("Zotero.Sync.Data.Local", function() {
var note = await createDataObject('item', { itemType: 'note' });
var item = await createDataObject('item');
var promise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
var promise = waitForWindow('chrome://zotero/content/merge.xhtml', function (dialog) {
var doc = dialog.document;
var wizard = doc.documentElement;
var mergeGroup = wizard.getElementsByTagName('zoteromergegroup')[0];
var wizard = doc.querySelector('wizard');
var mergeGroup = wizard.getElementsByTagName('merge-group')[0];
// 1 (accept remote deletion)
assert.equal(mergeGroup.leftpane.getAttribute('selected'), 'true');
mergeGroup.rightpane.click();
assert.equal(mergeGroup.leftPane.getAttribute('selected'), 'true');
mergeGroup.rightPane.click();
wizard.getButton('next').click();
// 2 (accept remote deletion)
mergeGroup.rightpane.click();
mergeGroup.rightPane.click();
if (Zotero.isMac) {
assert.isTrue(wizard.getButton('next').hidden);
assert.isFalse(wizard.getButton('finish').hidden);