zoterosearch.xml (31565B)
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 window [ 28 <!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd"> 29 %zoteroDTD; 30 <!ENTITY % searchboxDTD SYSTEM "chrome://zotero/locale/searchbox.dtd"> 31 %searchboxDTD; 32 ]> 33 34 <bindings xmlns="http://www.mozilla.org/xbl" 35 xmlns:xbl="http://www.mozilla.org/xbl" 36 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 37 38 <binding id="search-box"> 39 <resources> 40 <stylesheet src="chrome://zotero/skin/bindings/search.css"/> 41 </resources> 42 43 <implementation> 44 <property name="groups"/> 45 46 <field name="searchRef"/> 47 <property name="search" onget="return this.searchRef;"> 48 <setter> 49 <![CDATA[ 50 this.searchRef = val; 51 52 var libraryMenu = this.id('libraryMenu'); 53 var libraries = Zotero.Libraries.getAll(); 54 Zotero.Utilities.Internal.buildLibraryMenu( 55 libraryMenu, libraries, this.searchRef.libraryID 56 ); 57 if (this.searchRef.id) { 58 libraryMenu.disabled = true; 59 } 60 this.updateLibrary(); 61 62 63 var conditionsBox = this.id('conditions'); 64 while(conditionsBox.hasChildNodes()) 65 conditionsBox.removeChild(conditionsBox.firstChild); 66 67 var conditions = this.search.getConditions(); 68 for (let id in conditions) { 69 let condition = conditions[id]; 70 // Checkboxes 71 switch (condition.condition) { 72 case 'recursive': 73 case 'noChildren': 74 case 'includeParentsAndChildren': 75 let checkbox = condition.condition + 'Checkbox'; 76 this.id(checkbox).setAttribute('condition', id); 77 this.id(checkbox).checked = condition.operator == 'true'; 78 continue; 79 } 80 81 if(condition.condition == 'joinMode') { 82 this.id('joinModeMenu').setAttribute('condition', id); 83 this.id('joinModeMenu').value = condition.operator; 84 } 85 else { 86 this.addCondition(condition); 87 } 88 } 89 ]]> 90 </setter> 91 </property> 92 93 <method name="addCondition"> 94 <parameter name="ref"/> 95 <body> 96 <![CDATA[ 97 var conditionsBox = this.id('conditions'); 98 var condition = document.createElement('zoterosearchcondition'); 99 condition.setAttribute('flex','1'); 100 101 conditionsBox.appendChild(condition); 102 103 // Default to an empty 'title' condition 104 if (!ref) { 105 ref = this.search.getCondition(this.search.addCondition("title","contains","")) 106 } 107 108 condition.initWithParentAndCondition(this, ref); 109 110 if (conditionsBox.childNodes.length == 2){ 111 conditionsBox.childNodes[0].enableRemoveButton(); 112 } 113 else if (conditionsBox.childNodes.length == 1){ 114 conditionsBox.childNodes[0].disableRemoveButton(); 115 } 116 ]]> 117 </body> 118 </method> 119 120 <method name="removeCondition"> 121 <parameter name="id"/> 122 <body> 123 <![CDATA[ 124 var conditionsBox = this.id('conditions'); 125 126 this.search.removeCondition(id); 127 128 for (var i = 0, len=conditionsBox.childNodes.length; i < len; i++){ 129 if (conditionsBox.childNodes[i].conditionID == id){ 130 conditionsBox.removeChild(conditionsBox.childNodes[i]); 131 break; 132 } 133 } 134 135 if (conditionsBox.childNodes.length == 1){ 136 conditionsBox.childNodes[0].disableRemoveButton(); 137 } 138 ]]> 139 </body> 140 </method> 141 142 <method name="updateLibrary"> 143 <body><![CDATA[ 144 var menu = this.id('libraryMenu'); 145 var libraryID = parseInt(menu.selectedItem.value); 146 147 if (this.onLibraryChange) { 148 this.onLibraryChange(libraryID); 149 } 150 if (!this.searchRef.id) { 151 this.searchRef.libraryID = libraryID; 152 } 153 154 [...this.id('conditions').childNodes].forEach(x => x.onLibraryChange()); 155 ]]></body> 156 </method> 157 158 <method name="updateJoinMode"> 159 <body> 160 <![CDATA[ 161 var menu = this.id('joinModeMenu'); 162 if(menu.hasAttribute('condition')) 163 this.search.updateCondition(menu.getAttribute('condition'),'joinMode',menu.value,null); 164 else 165 menu.setAttribute('condition', this.search.addCondition('joinMode',menu.value,null)); 166 ]]> 167 </body> 168 </method> 169 170 <method name="updateCheckbox"> 171 <parameter name="condition"/> 172 <body> 173 <![CDATA[ 174 var checkbox = this.id(condition + 'Checkbox'); 175 var value = checkbox.checked ? 'true' : 'false'; 176 if(checkbox.hasAttribute('condition')) 177 { 178 this.search.updateCondition(checkbox.getAttribute('condition'), 179 condition, value, null); 180 } 181 else 182 { 183 checkbox.setAttribute('condition', 184 this.search.addCondition(condition, value, null)); 185 } 186 ]]> 187 </body> 188 </method> 189 190 <!-- Calls updateSearch() on all search conditions --> 191 <method name="updateSearch"> 192 <body> 193 <![CDATA[ 194 var conditionsBox = this.id('conditions'); 195 if (conditionsBox.hasChildNodes()) { 196 for(var i = 0, len=conditionsBox.childNodes.length; i < len; i++) { 197 conditionsBox.childNodes[i].updateSearch(); 198 } 199 } 200 ]]> 201 </body> 202 </method> 203 204 <method name="handleKeyPress"> 205 <parameter name="event"/> 206 <body> 207 <![CDATA[ 208 switch (event.keyCode) { 209 case event.DOM_VK_RETURN: 210 this.active = true; 211 212 if (event.shiftKey) { 213 this.addCondition(); 214 } 215 else { 216 this.doCommand(); 217 } 218 break; 219 } 220 ]]> 221 </body> 222 </method> 223 224 <method name="id"> 225 <parameter name="id"/> 226 <body> 227 <![CDATA[ 228 return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0]; 229 ]]> 230 </body> 231 </method> 232 </implementation> 233 234 <content> 235 <vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 236 id="search-box" flex="1" onkeypress="document.getBindingParent(this).handleKeyPress(event)"> 237 <hbox align="center"> 238 <label value="&zotero.search.searchInLibrary;" control="libraryMenu"/> 239 <menulist id="libraryMenu" oncommand="document.getBindingParent(this).updateLibrary();"> 240 <menupopup/> 241 </menulist> 242 </hbox> 243 <groupbox xbl:inherits="flex"> 244 <caption align="center"> 245 <label value="&zotero.search.joinMode.prefix;"/> 246 <menulist id="joinModeMenu" oncommand="document.getBindingParent(this).updateJoinMode();"> 247 <menupopup> 248 <menuitem label="&zotero.search.joinMode.any;" value="any"/> 249 <menuitem label="&zotero.search.joinMode.all;" value="all" selected="true"/> 250 </menupopup> 251 </menulist> 252 <label value="&zotero.search.joinMode.suffix;"/> 253 </caption> 254 <vbox id="conditions"/> 255 </groupbox> 256 <hbox> 257 <checkbox id="recursiveCheckbox" label="&zotero.search.recursive.label;" oncommand="document.getBindingParent(this).updateCheckbox('recursive');"/> 258 <checkbox id="noChildrenCheckbox" label="&zotero.search.noChildren;" oncommand="document.getBindingParent(this).updateCheckbox('noChildren');"/> 259 </hbox> 260 <hbox> 261 <checkbox id="includeParentsAndChildrenCheckbox" label="&zotero.search.includeParentsAndChildren;" oncommand="document.getBindingParent(this).updateCheckbox('includeParentsAndChildren');"/> 262 </hbox> 263 </vbox> 264 </content> 265 </binding> 266 267 268 269 <binding id="search-condition"> 270 <resources> 271 <stylesheet src="chrome://zotero/skin/bindings/search.css"/> 272 </resources> 273 274 <implementation> 275 <field name="conditionID"/> 276 <field name="selectedCondition"/> 277 <field name="mode"/> 278 <field name="selectedOperator"/> 279 <field name="value"/> 280 <field name="parent"/> 281 <field name="dontupdate"/> 282 <constructor> 283 <![CDATA[ 284 var operators = [ 285 'is', 286 'isNot', 287 'beginsWith', 288 'contains', 289 'doesNotContain', 290 'isLessThan', 291 'isGreaterThan', 292 'isBefore', 293 'isAfter', 294 'isInTheLast' 295 ]; 296 var operatorsList = this.id('operatorsmenu'); 297 298 // Build operator menu 299 for (let operator of operators) { 300 operatorsList.appendItem( 301 Zotero.getString('searchOperator.' + operator), 302 operator 303 ); 304 } 305 306 // Build conditions menu 307 var conditionsMenu = this.id('conditionsmenu'); 308 var moreConditionsMenu = this.id('more-conditions-menu'); 309 var conditions = Zotero.SearchConditions.getStandardConditions(); 310 311 var lastInsertPos = 0; 312 313 for (let condition of conditions) { 314 if (this.isPrimaryCondition(condition.name)) { 315 var menuitem = conditionsMenu.insertItemAt( 316 lastInsertPos++, condition.localized, condition.name 317 ); 318 } 319 else { 320 var menuitem = moreConditionsMenu.appendItem( 321 condition.localized, condition.name 322 ); 323 } 324 325 var baseFields = null; 326 try { 327 baseFields = Zotero.ItemFields.getTypeFieldsFromBase(condition.name); 328 } 329 catch (e) {} 330 331 // Add tooltip, building it if it doesn't exist 332 if (baseFields) { 333 if (!this.id(condition.name + '-tooltip')) { 334 var fieldName = null; 335 try { 336 fieldName = Zotero.ItemFields.getLocalizedString(null, condition.name); 337 } 338 catch (e) {} 339 340 if (fieldName) { 341 var localized = [fieldName]; 342 } 343 else { 344 var localized = []; 345 } 346 347 for (let baseField of baseFields) { 348 var str = Zotero.SearchConditions.getLocalizedName( 349 Zotero.ItemFields.getName(baseField) 350 ); 351 352 if (localized.indexOf(str) == -1) { 353 localized.push(str); 354 } 355 } 356 localized.sort(); 357 358 var tt = document.createElement('tooltip'); 359 tt.setAttribute('id', condition.name + '-tooltip'); 360 tt.setAttribute('orient', 'vertical'); 361 tt.setAttribute('noautohide', true); 362 363 var grid = document.createElement('grid'); 364 365 var columns = document.createElement('columns'); 366 var col1 = document.createElement('column'); 367 var col2 = document.createElement('column'); 368 columns.appendChild(col1); 369 columns.appendChild(col2); 370 371 var rows = document.createElement('rows'); 372 373 var fieldRow = document.createElement('row'); 374 var label = document.createElement('label'); 375 label.setAttribute('value', Zotero.getString('searchConditions.tooltip.fields')); 376 fieldRow.appendChild(label); 377 var vbox = document.createElement('vbox'); 378 for (let str of localized) { 379 var label = document.createElement('label') 380 label.setAttribute('value', str); 381 vbox.appendChild(label); 382 } 383 fieldRow.appendChild(vbox); 384 385 rows.appendChild(fieldRow); 386 387 grid.appendChild(rows); 388 tt.appendChild(grid); 389 390 this.id('condition-tooltips').appendChild(tt); 391 } 392 393 menuitem.setAttribute('tooltip', condition.name + '-tooltip'); 394 } 395 } 396 conditionsMenu.selectedIndex = 0; 397 ]]> 398 </constructor> 399 400 <method name="isPrimaryCondition"> 401 <parameter name="condition"/> 402 <body><![CDATA[ 403 switch (condition) { 404 case 'collection': 405 case 'creator': 406 case 'title': 407 case 'date': 408 case 'dateAdded': 409 case 'dateModified': 410 case 'itemType': 411 case 'fileTypeID': 412 case 'publicationTitle': 413 case 'tag': 414 case 'note': 415 case 'childNote': 416 case 'fulltextContent': 417 return true; 418 } 419 420 return false; 421 ]]></body> 422 </method> 423 424 <method name="onConditionSelected"> 425 <parameter name="conditionName"/> 426 <parameter name="reload"/> 427 <body><![CDATA[ 428 var conditionsMenu = this.id('conditionsmenu'); 429 var operatorsList = this.id('operatorsmenu'); 430 431 // Skip if no condition or correct condition already selected 432 if (!conditionName || (conditionName == this.selectedCondition && !reload)) { 433 return; 434 } 435 436 this.selectedCondition = conditionName; 437 this.selectedOperator = operatorsList.value; 438 439 var condition = Zotero.SearchConditions.get(conditionName); 440 var operators = condition.operators; 441 442 conditionsMenu.value = conditionName; 443 444 // Parent state isn't set automatically for submenu selections 445 if (!this.isPrimaryCondition(conditionName)) { 446 conditionsMenu.selectedIndex = -1; 447 conditionsMenu.setAttribute( 448 'label', 449 Zotero.SearchConditions.getLocalizedName(conditionName) 450 ); 451 } 452 453 this.updateSubmenuCheckboxes(conditionsMenu); 454 455 // Display appropriate operators for condition 456 var selectThis; 457 for(var i = 0, len = operatorsList.firstChild.childNodes.length; i < len; i++) 458 { 459 var val = operatorsList.firstChild.childNodes[i].getAttribute('value'); 460 var hidden = !operators[val]; 461 operatorsList.firstChild.childNodes[i].setAttribute('hidden', hidden); 462 if (!hidden && (selectThis == null || this.selectedOperator == val)) 463 { 464 selectThis = i; 465 } 466 } 467 operatorsList.selectedIndex = selectThis; 468 469 // Generate drop-down menu instead of textbox for certain conditions 470 switch (conditionName) { 471 case 'collection': 472 var rows = []; 473 474 var libraryID = this.parent.search.libraryID; 475 476 // Add collections 477 let cols = Zotero.Collections.getByLibrary(libraryID, true); 478 for (let col of cols) { 479 // Indent subcollections 480 var indent = ''; 481 if (col.level) { 482 for (let j = 1; j < col.level; j++) { 483 indent += ' '; 484 } 485 indent += '- '; 486 } 487 rows.push({ 488 name: indent + col.name, 489 value: 'C' + col.key, 490 image: Zotero.Collection.prototype.treeViewImage 491 }); 492 } 493 494 // Add saved searches 495 let searches = Zotero.Searches.getByLibrary(libraryID); 496 for (let search of searches) { 497 if (search.id != this.parent.search.id) { 498 rows.push({ 499 name: search.name, 500 value: 'S' + search.key, 501 image: Zotero.Search.prototype.treeViewImage 502 }); 503 } 504 } 505 this.createValueMenu(rows); 506 break; 507 508 case 'itemType': 509 var rows = Zotero.ItemTypes.getTypes().map(type => ({ 510 name: Zotero.ItemTypes.getLocalizedString(type.id), 511 value: type.name 512 })); 513 514 // Sort by localized name 515 var collation = Zotero.getLocaleCollation(); 516 rows.sort((a, b) => collation.compareString(1, a.name, b.name)); 517 518 this.createValueMenu(rows); 519 break; 520 521 case 'fileTypeID': 522 var rows = Zotero.FileTypes.getTypes().map(type => ({ 523 name: Zotero.getString('fileTypes.' + type.name), 524 value: type.id 525 })); 526 527 // Sort by localized name 528 var collation = Zotero.getLocaleCollation(); 529 rows.sort((a, b) => collation.compareString(1, a.name, b.name)); 530 531 this.createValueMenu(rows); 532 break; 533 534 default: 535 if (operatorsList.value=='isInTheLast') 536 { 537 this.id('value-date-age').value = this.value; 538 } 539 540 // Textbox 541 else { 542 // If switching from menu to textbox, clear value 543 if (this.id('valuefield').hidden){ 544 this.id('valuefield').value = ''; 545 } 546 // If switching between textbox conditions, get loaded value for new one 547 else { 548 this.id('valuefield').value = this.value; 549 } 550 551 // Update field drop-down if applicable 552 this.id('valuefield').update(conditionName, this.mode); 553 } 554 } 555 556 this.onOperatorSelected(); 557 ]]></body> 558 </method> 559 <method name="onOperatorSelected"> 560 <body> 561 <![CDATA[ 562 var operatorsList = this.id('operatorsmenu'); 563 564 // Drop-down menu 565 if (this.selectedCondition == 'collection' 566 || this.selectedCondition == 'itemType' 567 || this.selectedCondition == 'fileTypeID') { 568 this.id('valuefield').hidden = true; 569 this.id('valuemenu').hidden = false; 570 this.id('value-date-age').hidden = true; 571 } 572 573 // Textbox + units dropdown for isInTheLast operator 574 else if (operatorsList.value=='isInTheLast') 575 { 576 // If switching from text field, clear value 577 if (this.id('value-date-age').hidden){ 578 this.value = ''; 579 } 580 this.id('valuefield').hidden = true; 581 this.id('valuemenu').hidden = true; 582 this.id('value-date-age').hidden = false; 583 } 584 585 // Textbox 586 else 587 { 588 // If switching from date age, clear value 589 if (this.id('valuefield').hidden){ 590 this.value = ''; 591 } 592 this.id('valuefield').hidden = false; 593 this.id('valuemenu').hidden = true; 594 this.id('value-date-age').hidden = true; 595 } 596 ]]> 597 </body> 598 </method> 599 <method name="createValueMenu"> 600 <parameter name="rows"/> 601 <body> 602 <![CDATA[ 603 while (this.id('valuemenu').hasChildNodes()){ 604 this.id('valuemenu').removeChild(this.id('valuemenu').firstChild); 605 } 606 607 for (let row of rows) { 608 let menuitem = this.id('valuemenu').appendItem(row.name, row.value); 609 if (row.image) { 610 menuitem.className = 'menuitem-iconic'; 611 menuitem.setAttribute('image', row.image); 612 } 613 } 614 this.id('valuemenu').selectedIndex = 0; 615 616 if (this.value) 617 { 618 this.id('valuemenu').value = this.value; 619 } 620 ]]> 621 </body> 622 </method> 623 <method name="initWithParentAndCondition"> 624 <parameter name="parent"/> 625 <parameter name="condition"/> 626 <body><![CDATA[ 627 this.parent = parent; 628 this.conditionID = condition['id']; 629 var menu = this.id('conditionsmenu'); 630 631 if(this.parent.search) 632 { 633 this.dontupdate = true; //so that the search doesn't get updated while we are creating controls. 634 var prefix = ''; 635 636 // Handle special conditions 637 switch (condition.condition) { 638 case 'savedSearch': 639 prefix = 'S'; 640 break; 641 642 case 'collection': 643 prefix = 'C'; 644 break; 645 } 646 647 // Map certain conditions to other menu items 648 let uiCondition = condition.condition; 649 switch (condition.condition) { 650 case 'savedSearch': 651 uiCondition = 'collection'; 652 break; 653 } 654 655 menu.setAttribute('value', uiCondition); 656 657 // Convert datetimes from UTC to localtime 658 if ((condition['condition']=='accessDate' || 659 condition['condition']=='dateAdded' || 660 condition['condition']=='dateModified') && 661 Zotero.Date.isSQLDateTime(condition['value'])){ 662 663 condition['value'] = 664 Zotero.Date.dateToSQL(Zotero.Date.sqlToDate(condition['value'], true)); 665 } 666 667 this.mode = condition['mode']; 668 this.id('operatorsmenu').value = condition['operator']; 669 this.value = prefix + 670 (condition.value ? condition.value : ''); 671 672 this.dontupdate = false; 673 } 674 675 this.onConditionSelected(menu.value); 676 677 this.id('conditionsmenu').focus(); 678 ]]></body> 679 </method> 680 <!-- Gets the value from the UI and updates the associated condition on the Zotero.Search object --> 681 <method name="updateSearch"> 682 <body> 683 <![CDATA[ 684 if(this.parent && this.parent.search && !this.dontupdate) 685 { 686 var condition = this.selectedCondition; 687 var operator = this.id('operatorsmenu').value; 688 689 // Regular text field 690 if (!this.id('valuefield').hidden) 691 { 692 var value = this.id('valuefield').value; 693 694 // Convert datetimes to UTC before saving 695 switch (condition) { 696 case 'accessDate': 697 case 'dateAdded': 698 case 'dateModified': 699 if (Zotero.Date.isSQLDateTime(value)) { 700 var value = Zotero.Date.dateToSQL(Zotero.Date.sqlToDate(value), true); 701 } 702 } 703 704 // Append mode to condition 705 if (this.id('valuefield').mode){ 706 condition += '/' + this.id('valuefield').mode; 707 } 708 } 709 710 // isInTheLast operator 711 else if (!this.id('value-date-age').hidden) 712 { 713 var value = this.id('value-date-age').value; 714 } 715 716 // Handle special C1234 and S5678 form for 717 // collections and searches 718 else if (condition == 'collection') { 719 var letter = this.id('valuemenu').value.substr(0,1); 720 if (letter=='C') 721 { 722 condition = 'collection'; 723 } 724 else if (letter=='S') 725 { 726 condition = 'savedSearch'; 727 } 728 var value = this.id('valuemenu').value.substr(1); 729 } 730 731 // Regular drop-down menu 732 else 733 { 734 var value = this.id('valuemenu').value; 735 } 736 this.parent.search.updateCondition(this.conditionID, condition, operator, value); 737 } 738 ]]> 739 </body> 740 </method> 741 742 <method name="updateSubmenuCheckboxes"> 743 <parameter name="menu"/> 744 <body><![CDATA[ 745 for (let i = 0; i < menu.itemCount; i++) { 746 let item = menu.getItemAtIndex(i); 747 if (item.localName == 'menuitem') { 748 if (item.getAttribute('value') == this.selectedCondition) { 749 item.setAttribute('checked', true); 750 } 751 else { 752 item.removeAttribute('checked'); 753 } 754 } 755 else { 756 this.updateSubmenuCheckboxes(item); 757 } 758 } 759 ]]></body> 760 </method> 761 762 <method name="revealSelectedCondition"> 763 <parameter name="menu"/> 764 <body><![CDATA[ 765 if (!this.selectedCondition || this.isPrimaryCondition(this.selectedCondition)) { 766 return; 767 } 768 769 if (!menu) { 770 menu = this.id('conditionsmenu'); 771 } 772 for (let i = 0; i < menu.itemCount; i++) { 773 let item = menu.getItemAtIndex(i); 774 if (item.localName == 'menuitem') { 775 if (item.getAttribute('value') == this.selectedCondition) { 776 menu.open = true; 777 return true; 778 } 779 } 780 else { 781 var opened = this.revealSelectedCondition(item); 782 if (opened) { 783 return true; 784 } 785 } 786 } 787 788 return false; 789 ]]></body></method> 790 791 <method name="onLibraryChange"> 792 <body><![CDATA[ 793 switch (this.selectedCondition) { 794 case 'collection': 795 this.onConditionSelected(this.selectedCondition, true); 796 break; 797 } 798 ]]></body> 799 </method> 800 801 <method name="onRemoveClicked"> 802 <body> 803 <![CDATA[ 804 if (this.parent){ 805 this.parent.removeCondition(this.conditionID); 806 window.sizeToContent() 807 } 808 ]]> 809 </body> 810 </method> 811 <method name="onAddClicked"> 812 <body> 813 <![CDATA[ 814 if (this.parent){ 815 this.parent.addCondition(); 816 window.sizeToContent(); 817 } 818 ]]> 819 </body> 820 </method> 821 <method name="disableRemoveButton"> 822 <body> 823 <![CDATA[ 824 var button = this.id("remove"); 825 button.setAttribute('disabled', true); 826 button.removeAttribute('onclick'); 827 ]]> 828 </body> 829 </method> 830 <method name="enableRemoveButton"> 831 <body> 832 <![CDATA[ 833 var button = this.id("remove"); 834 button.setAttribute('disabled', false); 835 button.setAttribute('onclick', "document.getBindingParent(this).onRemoveClicked(event)"); 836 ]]> 837 </body> 838 </method> 839 <method name="id"> 840 <parameter name="id"/> 841 <body> 842 <![CDATA[ 843 var elems = document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id); 844 return elems[0] ? elems[0] : false; 845 ]]> 846 </body> 847 </method> 848 </implementation> 849 850 <content> 851 <xul:hbox id="search-condition" xbl:inherits="flex"> 852 <xul:popupset id="condition-tooltips"/> 853 854 <xul:menulist id="conditionsmenu" oncommand="document.getBindingParent(this).onConditionSelected(event.target.value); event.stopPropagation()"> 855 <xul:menupopup onpopupshown="document.getBindingParent(this).revealSelectedCondition()"> 856 <xul:menu id="more-conditions-menu" label="&zotero.general.more;"> 857 <xul:menupopup/> 858 </xul:menu> 859 </xul:menupopup> 860 </xul:menulist> 861 <xul:menulist id="operatorsmenu" oncommand="document.getBindingParent(this).onOperatorSelected(); event.stopPropagation()"> 862 <xul:menupopup/> 863 </xul:menulist> 864 <xul:zoterosearchtextbox id="valuefield" flex="1"/> 865 <xul:menulist id="valuemenu" flex="1" hidden="true"> 866 <xul:menupopup/> 867 </xul:menulist> 868 <xul:zoterosearchagefield id="value-date-age" hidden="true" flex="1"/> 869 <xul:label id="remove" class="zotero-clicky zotero-clicky-minus" value="-" onclick="document.getBindingParent(this).onRemoveClicked(event)"/> 870 <xul:label id="add" class="zotero-clicky zotero-clicky-plus" value="+" onclick="document.getBindingParent(this).onAddClicked(event)"/> 871 </xul:hbox> 872 </content> 873 </binding> 874 875 876 877 <binding id="search-textbox"> 878 <resources> 879 <stylesheet src="chrome://zotero/skin/bindings/search.css"/> 880 </resources> 881 882 <implementation> 883 <property name="value" 884 onget="return document.getAnonymousNodes(this)[0].value" 885 onset="document.getAnonymousNodes(this)[0].setAttribute('value', val); return val"/> 886 <property name="mode"> 887 <getter> 888 <![CDATA[ 889 if (this.getAttribute('hasOptions')!='true'){ 890 return false; 891 } 892 893 var button = this.id('textbox-button'); 894 var menu = this.id(button.popup); 895 896 var selectedIndex = -1; 897 for (var i=0; i<menu.childNodes.length; i++){ 898 if (menu.childNodes[i].getAttribute('checked')=='true'){ 899 selectedIndex = i; 900 break; 901 } 902 } 903 switch (button.popup){ 904 case 'textbox-fulltext-menu': 905 switch (selectedIndex){ 906 case 0: 907 return false; 908 909 case 1: 910 return 'phraseBinary'; 911 912 case 2: 913 return 'regexp'; 914 915 case 3: 916 return 'regexpCS'; 917 } 918 break; 919 } 920 921 throw('Invalid search textbox popup'); 922 ]]> 923 </getter> 924 </property> 925 <method name="update"> 926 <parameter name="condition"/> 927 <parameter name="mode"/> 928 <body> 929 <![CDATA[ 930 var button = this.id('textbox-button'); 931 932 switch (condition){ 933 case 'fulltextContent': 934 button.popup = 'textbox-fulltext-menu'; 935 button.setAttribute('popup', 'textbox-fulltext-menu'); 936 var menu = this.id(button.popup); 937 this.setAttribute('hasOptions', true); 938 939 var selectedIndex = 0; 940 if (mode){ 941 switch (mode){ 942 case 'phrase': 943 selectedIndex = 0; 944 break; 945 946 case 'phraseBinary': 947 selectedIndex = 1; 948 break; 949 950 case 'regexp': 951 selectedIndex = 2; 952 break; 953 954 case 'regexpCS': 955 selectedIndex = 3; 956 break; 957 } 958 } 959 menu.childNodes[selectedIndex].setAttribute('checked', true); 960 break; 961 962 default: 963 this.setAttribute('hasOptions', false); 964 965 // Set textbox to autocomplete mode 966 switch (condition) 967 { 968 // Skip autocomplete for these fields 969 case 'date': 970 case 'note': 971 case 'extra': 972 break; 973 974 default: 975 976 var textbox = document.getAnonymousNodes(this)[0]; 977 textbox.setAttribute('type', 'autocomplete'); 978 textbox.setAttribute('autocompletesearch', 'zotero'); 979 textbox.setAttribute('timeout', '250'); 980 981 var autocompleteParams = { 982 fieldName: condition 983 }; 984 if (condition == 'creator') { 985 autocompleteParams.fieldMode = 2; 986 } 987 textbox.setAttribute( 988 'autocompletesearchparam', 989 JSON.stringify(autocompleteParams) 990 ); 991 } 992 } 993 994 if (!autocompleteParams) { 995 var textbox = document.getAnonymousNodes(this)[0]; 996 textbox.removeAttribute('type'); 997 } 998 ]]> 999 </body> 1000 </method> 1001 <method name="id"> 1002 <parameter name="id"/> 1003 <body> 1004 <![CDATA[ 1005 return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0]; 1006 ]]> 1007 </body> 1008 </method> 1009 </implementation> 1010 1011 <content> 1012 <xul:textbox id="search-textbox" xbl:inherits="hasOptions,flex"> 1013 <xul:popupset> 1014 <xul:menupopup id="textbox-fulltext-menu"> 1015 <xul:menuitem type="radio" label="&zotero.search.textModes.phrase;"/> 1016 <xul:menuitem type="radio" label="&zotero.search.textModes.phraseBinary;"/> 1017 <xul:menuitem type="radio" label="&zotero.search.textModes.regexp;"/> 1018 <xul:menuitem type="radio" label="&zotero.search.textModes.regexpCS;"/> 1019 </xul:menupopup> 1020 </xul:popupset> 1021 1022 <xul:toolbarbutton id="textbox-button" type="menu"/> 1023 </xul:textbox> 1024 </content> 1025 </binding> 1026 1027 1028 1029 <binding id="search-in-the-last"> 1030 <resources> 1031 <stylesheet src="chrome://zotero/skin/bindings/search.css"/> 1032 </resources> 1033 1034 <implementation> 1035 <property name="value"> 1036 <getter> 1037 <![CDATA[ 1038 var menulist = document.getAnonymousNodes(this)[0].firstChild.nextSibling; 1039 1040 return document.getAnonymousNodes(this)[0].firstChild.value + ' ' + 1041 menulist.firstChild.childNodes[menulist.selectedIndex].getAttribute('value') 1042 ]]> 1043 </getter> 1044 <setter> 1045 <![CDATA[ 1046 var [num, units] = val.split(' '); 1047 document.getAnonymousNodes(this)[0].firstChild.setAttribute('value', num); 1048 1049 var menulist = document.getAnonymousNodes(this)[0].firstChild.nextSibling; 1050 var menupopup = menulist.firstChild; 1051 1052 var selectThis = 0; 1053 for (var i=0; i<menupopup.childNodes.length; i++){ 1054 if (menupopup.childNodes[i].value == units) 1055 { 1056 selectThis = i; 1057 break; 1058 } 1059 } 1060 menulist.selectedIndex = selectThis; 1061 1062 return val; 1063 ]]> 1064 </setter> 1065 </property> 1066 <method name="id"> 1067 <parameter name="id"/> 1068 <body> 1069 <![CDATA[ 1070 return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0]; 1071 ]]> 1072 </body> 1073 </method> 1074 </implementation> 1075 1076 <content> 1077 <xul:hbox id="search-in-the-last" flex="1"> 1078 <xul:textbox flex="1"/> 1079 <xul:menulist> 1080 <xul:menupopup flex="1"> 1081 <xul:menuitem label="&zotero.search.date.units.days;" value="days" selected="true"/> 1082 <xul:menuitem label="&zotero.search.date.units.months;" value="months"/> 1083 <xul:menuitem label="&zotero.search.date.units.years;" value="years"/> 1084 </xul:menupopup> 1085 </xul:menulist> 1086 </xul:hbox> 1087 </content> 1088 </binding> 1089 </bindings>