merge.xml (11984B)
1 <?xml version="1.0"?> 2 <!-- 3 ***** BEGIN LICENSE BLOCK ***** 4 5 Copyright © 2009 Center for History and New Media 6 George Mason University, Fairfax, Virginia, USA 7 http://zotero.org 8 9 This file is part of Zotero. 10 11 Zotero is free software: you can redistribute it and/or modify 12 it under the terms of the GNU Affero General Public License as published by 13 the Free Software Foundation, either version 3 of the License, or 14 (at your option) any later version. 15 16 Zotero is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU Affero General Public License for more details. 20 21 You should have received a copy of the GNU Affero General Public License 22 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 23 24 ***** END LICENSE BLOCK ***** 25 --> 26 27 <!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd"> 28 29 <bindings xmlns="http://www.mozilla.org/xbl" 30 xmlns:xbl="http://www.mozilla.org/xbl" 31 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 32 xmlns:html="http://www.w3.org/1999/xhtml"> 33 34 <binding id="merge-group"> 35 <resources> 36 <stylesheet src="chrome://zotero/skin/merge.css"/> 37 </resources> 38 39 <implementation> 40 <constructor> 41 <![CDATA[ 42 this._leftpane = this._id('leftpane'); 43 this._rightpane = this._id('rightpane'); 44 this._mergepane = this._id('mergepane'); 45 ]]> 46 </constructor> 47 48 <field name="libraryID"/> 49 50 <field name="_data"/> 51 <property name="data" onget="return this._data;"> 52 <setter> 53 <![CDATA[ 54 this._data = val; 55 this.refresh(); 56 ]]> 57 </setter> 58 </property> 59 60 <property name="merged" onget="return this._mergepane.data"/> 61 62 <field name="_type"/> 63 <property name="type" onget="return this._type;"> 64 <setter> 65 <![CDATA[ 66 switch (val) { 67 case 'item': 68 case 'attachment': 69 case 'note': 70 case 'file': 71 break; 72 73 default: 74 throw new Error(`Unsupported merge object type '${type}'`); 75 } 76 77 this._type = val; 78 var hbox = document.getAnonymousNodes(this)[0]; 79 hbox.setAttribute('mergetype', val); 80 ]]> 81 </setter> 82 </property> 83 84 <property name="leftCaption" onset="this._leftpane.caption.label = val"/> 85 <property name="rightCaption" onset="this._rightpane.caption.label = val"/> 86 <property name="mergeCaption" onset="this._mergepane.caption.label = val"/> 87 88 <field name="_leftpane"/> 89 <property name="leftpane" onget="return this._leftpane"/> 90 <field name="_rightpane"/> 91 <property name="rightpane" onget="return this._rightpane"/> 92 <field name="_mergepane"/> 93 <property name="mergepane" onget="return this._mergepane"/> 94 95 <property name="onSelectionChange"/> 96 97 <method name="refresh"> 98 <body> 99 <![CDATA[ 100 if (this._data.left.deleted && this._data.right.deleted) { 101 throw new Error("'left' and 'right' cannot both be deleted"); 102 } 103 104 // Check for note or attachment 105 if (!this.type) { 106 this.type = this._getTypeFromObject( 107 this._data.left.deleted ? this._data.right : this._data.left 108 ); 109 } 110 111 var showButton = this.type != 'item'; 112 113 this._leftpane.showButton = showButton; 114 this._rightpane.showButton = showButton; 115 this._leftpane.libraryID = this.libraryID; 116 this._rightpane.libraryID = this.libraryID; 117 this._mergepane.libraryID = this.libraryID; 118 this._leftpane.data = this._data.left; 119 this._rightpane.data = this._data.right; 120 this._mergepane.data = this._data.merge; 121 122 if (this._data.selected == 'left') { 123 this.choosePane(this._leftpane); 124 } 125 else { 126 this.choosePane(this._rightpane); 127 } 128 129 /* 130 131 Code to display only the different values -- not used 132 133 var diff = this._leftpane.ref.diff(this._rightpane.ref, true); 134 135 var fields = []; 136 var diffFields = []; 137 for (var field in diff[0].primary) { 138 fields.push(field); 139 if (diff[0].primary[field] != diff[1].primary[field]) { 140 diffFields.push(field); 141 } 142 } 143 for (var field in diff[0].fields) { 144 fields.push(field); 145 if (diff[0].fields[field] != diff[1].fields[field]) { 146 diffFields.push(field); 147 } 148 } 149 150 this._leftpane.objectbox.fieldOrder = fields; 151 this._rightpane.objectbox.fieldOrder = fields; 152 153 // Display merge pane if item types match 154 if (this._leftpane.ref.itemTypeID == this._rightpane.ref.itemTypeID) { 155 this._leftpane.objectbox.visibleFields = fields; 156 this._rightpane.objectbox.visibleFields = fields; 157 158 this._leftpane.objectbox.clickable = false; 159 this._rightpane.objectbox.clickable = false; 160 this._leftpane.objectbox.clickableFields = diffFields; 161 this._rightpane.objectbox.clickableFields = diffFields; 162 163 var mergeItem = new Zotero.Item(this._leftpane.ref.itemTypeID); 164 this._mergepane.ref = mergeItem; 165 this._mergepane.objectbox.visibleFields = fields; 166 } 167 // Otherwise only allow clicking on item types 168 else { 169 this._leftpane.objectbox.clickableFields = ['itemType']; 170 this._rightpane.objectbox.clickableFields = ['itemType']; 171 } 172 */ 173 174 175 this._mergepane.objectbox.editable = true; 176 177 178 /* 179 180 No need to refresh if not comparing fields 181 182 this._leftpane.objectbox.refresh(); 183 this._rightpane.objectbox.refresh(); 184 */ 185 ]]> 186 </body> 187 </method> 188 189 190 <method name="choosePane"> 191 <parameter name="pane"/> 192 <body> 193 <![CDATA[ 194 if (pane.getAttribute('anonid') == 'leftpane') { 195 var position = 'left'; 196 var otherPane = this._rightpane; 197 } 198 else { 199 var position = 'right'; 200 var otherPane = this._leftpane; 201 } 202 203 pane.removeAttribute("selected"); 204 otherPane.removeAttribute("selected"); 205 pane.setAttribute("selected", "true"); 206 207 if (pane.deleted) { 208 this._mergepane.deleted = true; 209 } 210 else { 211 this._mergepane.data = pane.data; 212 } 213 214 if (this.onSelectionChange) { 215 this.onSelectionChange(); 216 } 217 ]]> 218 </body> 219 </method> 220 221 222 <method name="_getTypeFromObject"> 223 <parameter name="obj"/> 224 <body> 225 <![CDATA[ 226 if (!obj.itemType) { 227 Zotero.debug(obj, 1); 228 throw new Error("obj is not item JSON"); 229 } 230 if (obj.itemType == 'attachment' || obj.itemType == 'note') { 231 return obj.itemType; 232 } 233 return 'item'; 234 ]]> 235 </body> 236 </method> 237 238 <method name="_id"> 239 <parameter name="id"/> 240 <body> 241 <![CDATA[ 242 return document.getAnonymousNodes(this)[0].getElementsByAttribute('anonid',id)[0]; 243 ]]> 244 </body> 245 </method> 246 </implementation> 247 248 <content> 249 <xul:hbox id="merge-group" flex="1"> 250 <xul:zoteromergepane anonid="leftpane" flex="1"/> 251 <xul:zoteromergepane anonid="rightpane" flex="1"/> 252 <xul:zoteromergepane anonid="mergepane" flex="1"/> 253 </xul:hbox> 254 </content> 255 </binding> 256 257 258 <binding id="merge-pane"> 259 <resources> 260 <stylesheet src="chrome://zotero/skin/bindings/merge.css"/> 261 </resources> 262 263 <implementation> 264 <constructor> 265 <![CDATA[ 266 this.parent = document.getBindingParent(this.parentNode); 267 this.isMergePane = this.getAttribute('anonid') == 'mergepane'; 268 269 if (!this.isMergePane) { 270 this.pane.onclick = function () { 271 this.parent.choosePane(this); 272 }.bind(this); 273 } 274 ]]> 275 </constructor> 276 277 <property name="type" onget="return this.parent.type" readonly="true"/> 278 <property name="caption" onget="return this._id('caption')" readonly="true"/> 279 <field name="showButton"/> 280 <property name="pane" onget="return document.getAnonymousNodes(this)[0]"/> 281 <property name="objectbox" onget="return this._id('objectbox')" readonly="true"/> 282 283 <field name="_deleted"/> 284 <property name="deleted"> 285 <setter> 286 <![CDATA[ 287 this._deleted = !!val; 288 289 var placeholder = this._id('object-placeholder'); 290 if (placeholder) { 291 placeholder.hidden = !!val; 292 } 293 else { 294 this._id('objectbox').hidden = true; 295 } 296 var deleteBox = this._id('delete-box'); 297 deleteBox.hidden = !val; 298 ]]> 299 </setter> 300 </property> 301 302 <field name="libraryID"/> 303 304 <field name="_data"/> 305 <property name="data" onget="return this._data"> 306 <setter> 307 <![CDATA[ 308 this._data = val; 309 310 var button = this._id('choose-button'); 311 button.label = Zotero.getString('sync.conflict.chooseThisVersion'); 312 if (this.showButton) { 313 button.onclick = () => this.parent.choosePane(this); 314 button.style.visibility = 'visible'; 315 } 316 else { 317 button.style.visibility = 'hidden'; 318 } 319 320 if (val.deleted) { 321 this.deleted = true; 322 return; 323 } 324 325 this.deleted = false; 326 327 // Replace XUL placeholder with XUL object box of given type 328 var elementName; 329 switch (this.type) { 330 case 'item': 331 elementName = 'zoteroitembox'; 332 break; 333 334 case 'attachment': 335 case 'file': 336 elementName = 'zoteroattachmentbox'; 337 break; 338 339 case 'note': 340 elementName = 'zoteronoteeditor'; 341 break; 342 343 default: 344 throw new Error("Object type '" + this.type + "' not supported"); 345 } 346 347 var objbox = document.createElement(elementName); 348 349 var parentRow = this._id('parent-row'); 350 if (val.parentItem) { 351 parentRow.textContent = ''; 352 353 let label = document.createElement('span'); 354 label.textContent = Zotero.getString('pane.item.parentItem'); 355 parentRow.appendChild(label); 356 357 let parentItem = Zotero.Items.getByLibraryAndKey(this.libraryID, val.parentItem); 358 let text = document.createTextNode(" " + parentItem.getDisplayTitle(true)); 359 parentRow.appendChild(text); 360 361 parentRow.hidden = false; 362 } 363 else { 364 parentRow.hidden = true; 365 } 366 367 if (this._id('object-placeholder')) { 368 var placeholder = this._id('object-placeholder'); 369 placeholder.parentNode.replaceChild(objbox, placeholder); 370 } 371 else { 372 var oldObjBox = this._id('objectbox'); 373 oldObjBox.parentNode.replaceChild(objbox, oldObjBox); 374 } 375 376 objbox.setAttribute("anonid", "objectbox"); 377 objbox.setAttribute("flex", "1"); 378 objbox.mode = this.type == 'file' ? 'filemerge' : 'merge'; 379 380 // Store JSON 381 this._data = val; 382 383 // Create a copy of the JSON that we can clean for display, since the remote object 384 // might reference things that don't exist locally 385 var displayJSON = Object.assign({}, val); 386 displayJSON.collections = []; 387 388 // Create item from JSON for metadata box 389 var item = new Zotero.Item(val.itemType); 390 item.libraryID = this.libraryID; 391 item.fromJSON(displayJSON); 392 objbox.item = item; 393 ]]> 394 </setter> 395 </property> 396 397 <field name="parent"/> 398 399 <method name="click"> 400 <body><![CDATA[ 401 this.pane.click(); 402 ]]></body> 403 </method> 404 405 <method name="_id"> 406 <parameter name="id"/> 407 <body> 408 <![CDATA[ 409 var elems = document.getAnonymousNodes(this)[0].getElementsByAttribute('anonid', id); 410 return elems.length ? elems[0] : false; 411 ]]> 412 </body> 413 </method> 414 </implementation> 415 416 <content> 417 <xul:vbox flex="1"> 418 <xul:groupbox anonid="merge-pane" flex="1"> 419 <xul:caption anonid="caption"/> 420 <html:div anonid="parent-row" hidden="true"/> 421 <xul:box anonid="object-placeholder"/> 422 <xul:hbox anonid="delete-box" hidden="true" flex="1"> 423 <xul:label value="&zotero.merge.deleted;"/> 424 </xul:hbox> 425 </xul:groupbox> 426 <xul:button anonid="choose-button"/> 427 </xul:vbox> 428 </content> 429 </binding> 430 </bindings>