mirror of
https://github.com/zotero/zotero.git
synced 2026-08-28 05:25:31 +00:00
Move Advanced Search and saved search editing to the main window (#5658)
--------- Co-authored-by: Dan Stillman <dstillman@zotero.org>
This commit is contained in:
parent
d2f1c56250
commit
00527332c4
31 changed files with 1016 additions and 718 deletions
|
|
@ -1,194 +0,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 *****
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import CollectionViewItemTree from 'zotero/collectionViewItemTree';
|
|
||||||
import { COLUMNS } from 'zotero/itemTreeColumns';
|
|
||||||
|
|
||||||
|
|
||||||
var ZoteroAdvancedSearch = new function () {
|
|
||||||
this.onLoad = onLoad;
|
|
||||||
this.search = search;
|
|
||||||
this.clear = clear;
|
|
||||||
this.onItemActivate = onItemActivate;
|
|
||||||
|
|
||||||
this.itemsView = false;
|
|
||||||
this._loadedDeferred = Zotero.Promise.defer();
|
|
||||||
|
|
||||||
var _searchBox;
|
|
||||||
var _libraryID;
|
|
||||||
var _searchCounter = 0;
|
|
||||||
|
|
||||||
async function onLoad() {
|
|
||||||
_searchBox = document.getElementById('zotero-search-box');
|
|
||||||
|
|
||||||
// Set font size from pref
|
|
||||||
var sbc = document.getElementById('zotero-search-box-container');
|
|
||||||
Zotero.UIProperties.registerRoot(sbc);
|
|
||||||
|
|
||||||
_searchBox.onLibraryChange = this.onLibraryChange;
|
|
||||||
var io = window.arguments[0];
|
|
||||||
|
|
||||||
io.dataIn.search.loadPrimaryData()
|
|
||||||
.then(function () {
|
|
||||||
_searchBox.search = io.dataIn.search;
|
|
||||||
});
|
|
||||||
|
|
||||||
var elem = document.getElementById('zotero-items-tree');
|
|
||||||
const columns = COLUMNS.map((column) => {
|
|
||||||
column = Object.assign({}, column);
|
|
||||||
column.hidden = !['title', 'firstCreator', 'year', 'hasAttachment'].includes(column.dataKey);
|
|
||||||
return column;
|
|
||||||
});
|
|
||||||
this.itemsView = await CollectionViewItemTree.init(elem, {
|
|
||||||
id: "advanced-search",
|
|
||||||
dragAndDrop: true,
|
|
||||||
columnPicker: true,
|
|
||||||
onActivate: this.onItemActivate.bind(this),
|
|
||||||
columns,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.itemsView.changeCollectionTreeRow({
|
|
||||||
id: 'advanced-search-' + _searchCounter++,
|
|
||||||
ref: _searchBox.search,
|
|
||||||
visibilityGroup: 'default',
|
|
||||||
isSearchMode: () => true,
|
|
||||||
getItems: async () => [],
|
|
||||||
isLibrary: () => false,
|
|
||||||
isCollection: () => false,
|
|
||||||
isSearch: () => true,
|
|
||||||
isPublications: () => false,
|
|
||||||
isDuplicates: () => false,
|
|
||||||
isFeed: () => false,
|
|
||||||
isFeeds: () => false,
|
|
||||||
isFeedsOrFeed: () => false,
|
|
||||||
isRecentlyRead: () => false,
|
|
||||||
isSortable: () => true,
|
|
||||||
isShare: () => false,
|
|
||||||
isTrash: () => false,
|
|
||||||
isSearch: () => true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus the first field in the window
|
|
||||||
Services.focus.moveFocus(window, null, Services.focus.MOVEFOCUS_FORWARD, 0);
|
|
||||||
this._loadedDeferred.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onUnload = function () {
|
|
||||||
this.itemsView.unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
function search() {
|
|
||||||
_searchBox.updateSearch();
|
|
||||||
_searchBox.active = true;
|
|
||||||
|
|
||||||
return this.itemsView.changeCollectionTreeRow({
|
|
||||||
id: 'advanced-search-' + _searchCounter++,
|
|
||||||
ref: _searchBox.search,
|
|
||||||
visibilityGroup: 'default',
|
|
||||||
isSearchMode: () => true,
|
|
||||||
isSearch: () => true,
|
|
||||||
getItems: async function () {
|
|
||||||
await Zotero.Libraries.get(_libraryID).waitForDataLoad('item');
|
|
||||||
|
|
||||||
var search = _searchBox.search.clone();
|
|
||||||
search.libraryID = _libraryID;
|
|
||||||
var ids = await search.search();
|
|
||||||
return Zotero.Items.get(ids);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
this.itemsView.changeCollectionTreeRow(null);
|
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
|
||||||
// Don't clear the selected library
|
|
||||||
s.libraryID = _searchBox.search.libraryID;
|
|
||||||
s.addCondition('title', 'contains', '');
|
|
||||||
_searchBox.search = s;
|
|
||||||
_searchBox.active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.save = async function () {
|
|
||||||
_searchBox.updateSearch();
|
|
||||||
|
|
||||||
var promptService = Services.prompt;
|
|
||||||
|
|
||||||
var libraryID = _searchBox.search.libraryID;
|
|
||||||
|
|
||||||
var searches = await Zotero.Searches.getAll(libraryID);
|
|
||||||
var prefix = Zotero.getString('pane.collections.untitled');
|
|
||||||
var name = Zotero.Utilities.Internal.getNextName(
|
|
||||||
prefix,
|
|
||||||
searches.map(s => s.name).filter(n => n.startsWith(prefix))
|
|
||||||
);
|
|
||||||
|
|
||||||
name = { value: name };
|
|
||||||
var result = promptService.prompt(window,
|
|
||||||
Zotero.getString('pane.collections.newSavedSeach'),
|
|
||||||
Zotero.getString('pane.collections.savedSearchName'), name, "", {});
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!name.value) {
|
|
||||||
name.value = 'untitled';
|
|
||||||
}
|
|
||||||
|
|
||||||
var s = _searchBox.search.clone();
|
|
||||||
s.name = name.value;
|
|
||||||
await s.saveTx();
|
|
||||||
|
|
||||||
window.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this.onLibraryChange = function (libraryID) {
|
|
||||||
_libraryID = libraryID;
|
|
||||||
var library = Zotero.Libraries.get(libraryID);
|
|
||||||
var isEditable = library.editable && library.libraryType != 'publications';
|
|
||||||
document.getElementById('zotero-search-save').disabled = !isEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onItemActivate(event, items)
|
|
||||||
{
|
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
|
|
||||||
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
|
||||||
|
|
||||||
if (!lastWin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastWin.ZoteroPane.selectItems(items.map(item => item.id));
|
|
||||||
lastWin.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css"?>
|
|
||||||
|
|
||||||
<!DOCTYPE window [
|
|
||||||
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd">
|
|
||||||
%zoteroDTD;
|
|
||||||
<!ENTITY % searchboxDTD SYSTEM "chrome://zotero/locale/searchbox.dtd">
|
|
||||||
%searchboxDTD;
|
|
||||||
]>
|
|
||||||
|
|
||||||
<window
|
|
||||||
id="zotero-advanced-search-dialog"
|
|
||||||
title="&zotero.toolbar.advancedSearch;"
|
|
||||||
orient="vertical"
|
|
||||||
persist="screenX screenY width height"
|
|
||||||
onload="ZoteroAdvancedSearch.onLoad()"
|
|
||||||
onunload="ZoteroAdvancedSearch.onUnload();"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
||||||
windowtype="zotero:search"
|
|
||||||
style="display: flex;">
|
|
||||||
|
|
||||||
<script src="include.js"/>
|
|
||||||
<script>
|
|
||||||
</script>
|
|
||||||
<script src="customElements.js"/>
|
|
||||||
<script src="advancedSearch.js"/>
|
|
||||||
|
|
||||||
<popupset>
|
|
||||||
<panel is="autocomplete-richlistbox-popup"
|
|
||||||
id="search-autocomplete-popup"
|
|
||||||
type="autocomplete-richlistbox"
|
|
||||||
noautofocus="true"/>
|
|
||||||
</popupset>
|
|
||||||
|
|
||||||
<vbox id="zotero-search-box-container" flex="1">
|
|
||||||
<vbox id="zotero-search-box-controls">
|
|
||||||
<zoterosearch id="zotero-search-box" oncommand="if (this.active) { ZoteroAdvancedSearch.search(); }" flex="1"/>
|
|
||||||
|
|
||||||
<hbox id="zotero-search-buttons">
|
|
||||||
<button label="&zotero.search.search;" default="true" oncommand="ZoteroAdvancedSearch.search()"/>
|
|
||||||
<button label="&zotero.search.clear;" oncommand="ZoteroAdvancedSearch.clear()"/>
|
|
||||||
<button id="zotero-search-save" label="&zotero.search.saveSearch;" oncommand="ZoteroAdvancedSearch.save()"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
<hbox class="virtualized-table-container" flex="1">
|
|
||||||
<html:div id="zotero-items-tree"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
|
|
||||||
<keyset>
|
|
||||||
<key id="key_close" key="W" modifiers="accel" oncommand="window.close()"/>
|
|
||||||
</keyset>
|
|
||||||
</window>
|
|
||||||
|
|
@ -152,6 +152,9 @@ class CollectionViewItemTreeRowProvider extends ItemTreeRowProvider {
|
||||||
case 'citation-search':
|
case 'citation-search':
|
||||||
changed = this.collectionTreeRow.setSearch(data, 'fields');
|
changed = this.collectionTreeRow.setSearch(data, 'fields');
|
||||||
break;
|
break;
|
||||||
|
case 'advanced-search':
|
||||||
|
changed = this.collectionTreeRow.setAdvancedSearch(data);
|
||||||
|
break;
|
||||||
case 'tags':
|
case 'tags':
|
||||||
changed = this.collectionTreeRow.setTags(data);
|
changed = this.collectionTreeRow.setTags(data);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemTreeMe
|
||||||
{
|
{
|
||||||
// https://searchfox.org/mozilla-central/rev/8e885f04a0a4ff6d64ea59741c10d9b8e45d9ff8/toolkit/content/customElements.js#826-832
|
// https://searchfox.org/mozilla-central/rev/8e885f04a0a4ff6d64ea59741c10d9b8e45d9ff8/toolkit/content/customElements.js#826-832
|
||||||
for (let [tag, script] of [
|
for (let [tag, script] of [
|
||||||
|
['advanced-search-deck', 'chrome://zotero/content/elements/advancedSearchDeck.js'],
|
||||||
|
['advanced-search-pane', 'chrome://zotero/content/elements/advancedSearchPane.js'],
|
||||||
['attachment-box', 'chrome://zotero/content/elements/attachmentBox.js'],
|
['attachment-box', 'chrome://zotero/content/elements/attachmentBox.js'],
|
||||||
['attachment-preview', 'chrome://zotero/content/elements/attachmentPreview.js'],
|
['attachment-preview', 'chrome://zotero/content/elements/attachmentPreview.js'],
|
||||||
['attachment-preview-box', 'chrome://zotero/content/elements/attachmentPreviewBox.js'],
|
['attachment-preview-box', 'chrome://zotero/content/elements/attachmentPreviewBox.js'],
|
||||||
|
|
|
||||||
83
chrome/content/zotero/elements/advancedSearchDeck.js
Normal file
83
chrome/content/zotero/elements/advancedSearchDeck.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2025 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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
{
|
||||||
|
class AdvancedSearchDeck extends XULElementBase {
|
||||||
|
content = MozXULElement.parseXULToFragment(`
|
||||||
|
<deck>
|
||||||
|
<advanced-search-pane id="zotero-temporary-advanced-search-pane" type="temporary"/>
|
||||||
|
<advanced-search-pane id="zotero-saved-advanced-search-pane" type="saved"/>
|
||||||
|
</deck>
|
||||||
|
`);
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.hidden = true;
|
||||||
|
this._state = 'closed';
|
||||||
|
|
||||||
|
this.deck = this.firstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
get state() {
|
||||||
|
return this._state;
|
||||||
|
}
|
||||||
|
|
||||||
|
set state(state) {
|
||||||
|
switch (state) {
|
||||||
|
case 'open':
|
||||||
|
this.hidden = false;
|
||||||
|
break;
|
||||||
|
case 'collapsed':
|
||||||
|
case 'closed':
|
||||||
|
this.hidden = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Invalid state: ' + state);
|
||||||
|
}
|
||||||
|
this._state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedSearchType() {
|
||||||
|
return this.deck.selectedIndex === 0 ? 'temporary' : 'saved';
|
||||||
|
}
|
||||||
|
|
||||||
|
set selectedSearchType(selectedSearchType) {
|
||||||
|
switch (selectedSearchType) {
|
||||||
|
case 'temporary':
|
||||||
|
this.deck.selectedIndex = 0;
|
||||||
|
break;
|
||||||
|
case 'saved':
|
||||||
|
this.deck.selectedIndex = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get pane() {
|
||||||
|
return this.deck.selectedPanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define("advanced-search-deck", AdvancedSearchDeck);
|
||||||
|
}
|
||||||
209
chrome/content/zotero/elements/advancedSearchPane.js
Normal file
209
chrome/content/zotero/elements/advancedSearchPane.js
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2025 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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
{
|
||||||
|
class AdvancedSearchPane extends XULElementBase {
|
||||||
|
content = MozXULElement.parseXULToFragment(`
|
||||||
|
<hbox class="saved-search-name-row">
|
||||||
|
<label control="saved-search-name" data-l10n-id="new-collection-name"/>
|
||||||
|
<html:input type="text" id="saved-search-name"/>
|
||||||
|
</hbox>
|
||||||
|
<zoterosearch/>
|
||||||
|
<hbox class="advanced-search-buttons">
|
||||||
|
<button class="cancel-button" data-l10n-id="cancel-button"/>
|
||||||
|
<button class="search-button" data-l10n-id="search-button" default="true"/>
|
||||||
|
<button class="clear-button" data-l10n-id="clear-button"/>
|
||||||
|
<button class="save-button" data-l10n-id="save-search-button"/>
|
||||||
|
</hbox>
|
||||||
|
`);
|
||||||
|
|
||||||
|
_active = false;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._nameField = this.querySelector('#saved-search-name');
|
||||||
|
this._searchElem = this.querySelector('zoterosearch');
|
||||||
|
this._cancelButton = this.querySelector('.cancel-button');
|
||||||
|
this._searchButton = this.querySelector('.search-button');
|
||||||
|
this._clearButton = this.querySelector('.clear-button');
|
||||||
|
this._saveButton = this.querySelector('.save-button');
|
||||||
|
|
||||||
|
this._searchElem.addEventListener('input', () => this._searchElem.updateSearch());
|
||||||
|
this._searchElem.addEventListener('command', () => this._searchElem.updateSearch());
|
||||||
|
|
||||||
|
this._cancelButton.addEventListener('command', () => this.cancel());
|
||||||
|
this._searchButton.addEventListener('command', () => this.submit());
|
||||||
|
this._clearButton.addEventListener('command', () => this.clear());
|
||||||
|
this._saveButton.addEventListener('command', () => this.save());
|
||||||
|
|
||||||
|
if (!['temporary', 'saved'].includes(this.type)) {
|
||||||
|
throw new Error(`Invalid type: ${this.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'saved') {
|
||||||
|
this._saveButton.setAttribute('default', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEventListener('keydown', this._handleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleKeyDown = async (event) => {
|
||||||
|
this._searchElem.updateSearch();
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (this.type === 'temporary') {
|
||||||
|
await this.submit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await this.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {'temporary' | 'saved'}
|
||||||
|
*/
|
||||||
|
get type() {
|
||||||
|
return this.getAttribute('type');
|
||||||
|
}
|
||||||
|
|
||||||
|
get search() {
|
||||||
|
return this._search;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the search has been submitted and should filter the items list
|
||||||
|
*/
|
||||||
|
get active() {
|
||||||
|
return this._active;
|
||||||
|
}
|
||||||
|
|
||||||
|
set search(search) {
|
||||||
|
this._active = false;
|
||||||
|
if (this.type === 'saved') {
|
||||||
|
if (!search?.id) {
|
||||||
|
throw new Error('Cannot edit unsaved search');
|
||||||
|
}
|
||||||
|
this._searchID = search.id;
|
||||||
|
this._search = search.clone();
|
||||||
|
this._nameField.value = search.name;
|
||||||
|
}
|
||||||
|
else if (search) {
|
||||||
|
this._search = search.clone();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._search = new Zotero.Search();
|
||||||
|
this._search.addCondition('title', 'contains', '');
|
||||||
|
}
|
||||||
|
this._searchElem.search = this._search;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ensureSearch() {
|
||||||
|
if (!this._search && this.type === 'temporary') {
|
||||||
|
this.search = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this._ensureSearch();
|
||||||
|
let libraryID = ZoteroPane.getSelectedLibraryID();
|
||||||
|
// Keep the previous library when the selected row doesn't have one (e.g., Feeds)
|
||||||
|
if (libraryID) {
|
||||||
|
this._search.libraryID = libraryID;
|
||||||
|
}
|
||||||
|
this._searchElem.search = this._search;
|
||||||
|
// There would be no way to scope a saved search to anything but the library root,
|
||||||
|
// so disable the option to save.
|
||||||
|
// Somewhat unfortunate - revisit when/if we support nested condition sets in the UI.
|
||||||
|
this._saveButton.disabled = this.type === 'temporary' && !ZoteroPane.getCollectionTreeRow().isLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancel() {
|
||||||
|
await ZoteroPane.setSavedSearchEditorState('closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
if (this.type === 'saved') {
|
||||||
|
throw new Error('submit() is unsupported for saved search');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._searchElem.updateSearch();
|
||||||
|
this._active = true;
|
||||||
|
await ZoteroPane.itemsView.setFilter('advanced-search', this._search);
|
||||||
|
ZoteroPane.itemsView.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
if (this.type === 'saved') {
|
||||||
|
throw new Error('clear() is unsupported for saved search');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.search = null;
|
||||||
|
await ZoteroPane.itemsView.setFilter('advanced-search', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
this._searchElem.updateSearch();
|
||||||
|
|
||||||
|
if (this.type === 'saved') {
|
||||||
|
let search = Zotero.Searches.get(this._searchID);
|
||||||
|
if (!search) {
|
||||||
|
throw new Error('Missing search');
|
||||||
|
}
|
||||||
|
search.fromJSON(this._search.toJSON());
|
||||||
|
search.name = this._nameField.value;
|
||||||
|
await search.saveTx();
|
||||||
|
await ZoteroPane.setSavedSearchEditorState('closed');
|
||||||
|
Zotero_Tabs.rename('zotero-pane', search.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let collectionTreeRow = ZoteroPane.getCollectionTreeRow();
|
||||||
|
if (!collectionTreeRow.isLibrary()) {
|
||||||
|
throw new Error('Can only save in library root');
|
||||||
|
}
|
||||||
|
this._ensureSearch();
|
||||||
|
|
||||||
|
let libraryID = collectionTreeRow.ref.libraryID;
|
||||||
|
let searches = await Zotero.Searches.getAll(libraryID);
|
||||||
|
let prefix = Zotero.getString('pane.collections.untitled');
|
||||||
|
let name = Zotero.Utilities.Internal.getNextName(
|
||||||
|
prefix,
|
||||||
|
searches.map(s => s.name).filter(n => n.startsWith(prefix))
|
||||||
|
);
|
||||||
|
|
||||||
|
let search = this._search.clone(libraryID);
|
||||||
|
search.name = name;
|
||||||
|
await search.saveTx();
|
||||||
|
|
||||||
|
await ZoteroPane.setAdvancedSearchState('closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(options) {
|
||||||
|
this._searchElem.querySelector('#conditionsmenu').focus(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define("advanced-search-pane", AdvancedSearchPane);
|
||||||
|
}
|
||||||
|
|
@ -33,8 +33,15 @@
|
||||||
this.searchTextbox = null;
|
this.searchTextbox = null;
|
||||||
MozXULElement.insertFTLIfNeeded("zotero.ftl");
|
MozXULElement.insertFTLIfNeeded("zotero.ftl");
|
||||||
this.content = MozXULElement.parseXULToFragment(`
|
this.content = MozXULElement.parseXULToFragment(`
|
||||||
<hbox id="search-wrapper" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
<deck id="search-deck">
|
||||||
</hbox>
|
<hbox id="search-wrapper">
|
||||||
|
</hbox>
|
||||||
|
<hbox id="advanced-search-indicator">
|
||||||
|
<label id="advanced-search-label"/>
|
||||||
|
<toolbarbutton class="zotero-clicky advanced-collapse-button" tabindex="0"/>
|
||||||
|
<toolbarbutton class="zotero-clicky advanced-close-button" tabindex="0"/>
|
||||||
|
</hbox>
|
||||||
|
</deck>
|
||||||
`, ['chrome://zotero/locale/zotero.dtd']);
|
`, ['chrome://zotero/locale/zotero.dtd']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,6 +102,30 @@
|
||||||
wrapper.appendChild(dropmarkerHost);
|
wrapper.appendChild(dropmarkerHost);
|
||||||
wrapper.appendChild(searchBox);
|
wrapper.appendChild(searchBox);
|
||||||
|
|
||||||
|
// Add Advanced Search button at the end of the field in main window
|
||||||
|
if (document.documentElement.getAttribute('windowtype') === 'navigator:browser') {
|
||||||
|
let advancedButton = document.createXULElement('toolbarbutton');
|
||||||
|
advancedButton.id = 'zotero-tb-search-advanced-button';
|
||||||
|
advancedButton.tabIndex = 0;
|
||||||
|
document.l10n.setAttributes(advancedButton, 'quicksearch-advanced-search-button');
|
||||||
|
advancedButton.addEventListener('command', (event) => {
|
||||||
|
// Don't trigger a quick search via the oncommand handler
|
||||||
|
event.stopPropagation();
|
||||||
|
ZoteroPane.toggleAdvancedSearchState('open');
|
||||||
|
});
|
||||||
|
wrapper.appendChild(advancedButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deck = this.firstElementChild;
|
||||||
|
|
||||||
|
this.querySelector('.advanced-collapse-button').addEventListener('command', (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
ZoteroPane.toggleAdvancedSearchState('collapsed');
|
||||||
|
});
|
||||||
|
this.querySelector('.advanced-close-button').addEventListener('command', (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
ZoteroPane.toggleAdvancedSearchState('closed');
|
||||||
|
});
|
||||||
|
|
||||||
// If Alt-Up/Down, show popup
|
// If Alt-Up/Down, show popup
|
||||||
this.addEventListener('keypress', (event) => {
|
this.addEventListener('keypress', (event) => {
|
||||||
|
|
@ -132,21 +163,14 @@
|
||||||
popup.append(item);
|
popup.append(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Advanced Search menu item in main window
|
|
||||||
if (document.documentElement.getAttribute('windowtype') === 'navigator:browser') {
|
|
||||||
let separator = document.createXULElement('menuseparator');
|
|
||||||
popup.append(separator);
|
|
||||||
let advancedSearchOption = document.createXULElement('menuitem');
|
|
||||||
advancedSearchOption.label = Zotero.getString("zotero.toolbar.advancedSearch");
|
|
||||||
advancedSearchOption.addEventListener("command", () => {
|
|
||||||
ZoteroPane.openAdvancedSearchWindow();
|
|
||||||
});
|
|
||||||
popup.append(advancedSearchOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._searchModePopup = popup;
|
return this._searchModePopup = popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCollectionSelected() {
|
||||||
|
this.searchTextbox.value = '';
|
||||||
|
this.updateMode();
|
||||||
|
}
|
||||||
|
|
||||||
updateMode() {
|
updateMode() {
|
||||||
let mode = Zotero.Prefs.get("search.quicksearch-mode");
|
let mode = Zotero.Prefs.get("search.quicksearch-mode");
|
||||||
|
|
||||||
|
|
@ -158,6 +182,29 @@
|
||||||
this.searchModePopup.querySelector(`menuitem[value="${mode}"]`)
|
this.searchModePopup.querySelector(`menuitem[value="${mode}"]`)
|
||||||
.setAttribute('checked', 'true');
|
.setAttribute('checked', 'true');
|
||||||
document.l10n.setAttributes(this.searchTextbox.inputField, "quicksearch-input", { placeholder: this._searchModes[mode] });
|
document.l10n.setAttributes(this.searchTextbox.inputField, "quicksearch-input", { placeholder: this._searchModes[mode] });
|
||||||
|
|
||||||
|
let advancedSearchDeck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
if (advancedSearchDeck) {
|
||||||
|
let state = advancedSearchDeck.state;
|
||||||
|
let selectedSearchType = advancedSearchDeck.selectedSearchType;
|
||||||
|
|
||||||
|
document.l10n.setAttributes(
|
||||||
|
this.querySelector('#advanced-search-label'),
|
||||||
|
selectedSearchType === 'temporary' ? 'advanced-search' : 'edit-saved-search',
|
||||||
|
);
|
||||||
|
this.deck.selectedIndex = state === 'closed' ? 0 : 1;
|
||||||
|
this.querySelector('#advanced-search-indicator').dataset.collapsed = state === 'collapsed';
|
||||||
|
this.querySelector('.advanced-collapse-button').hidden = selectedSearchType === 'saved';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(options) {
|
||||||
|
if (this.deck.selectedIndex === 0) {
|
||||||
|
this._searchModePopup.flattenedTreeParentNode.focus(options);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Services.focus.moveFocus(window, this.deck.selectedPanel, Services.focus.MOVEFOCUS_FORWARD, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_id(id) {
|
_id(id) {
|
||||||
|
|
|
||||||
|
|
@ -37,35 +37,24 @@
|
||||||
|
|
||||||
class ZoteroSearch extends SearchElementBase {
|
class ZoteroSearch extends SearchElementBase {
|
||||||
content = MozXULElement.parseXULToFragment(`
|
content = MozXULElement.parseXULToFragment(`
|
||||||
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
<groupbox>
|
||||||
id="search-box" flex="1" onkeypress="this.closest('zoterosearch').handleKeyPress(event)">
|
<caption align="center">
|
||||||
<hbox align="center">
|
<label id="joinModeMenu-label" value="&zotero.search.joinMode.prefix;"/>
|
||||||
<label id="libraryMenu-label" value="&zotero.search.searchInLibrary;" control="libraryMenu"/>
|
<menulist id="joinModeMenu" aria-labelledby="joinModeMenu-label" oncommand="this.closest('zoterosearch').updateJoinMode();" native="true">
|
||||||
<menulist id="libraryMenu" aria-labelledby="libraryMenu-label" oncommand="this.closest('zoterosearch').updateLibrary();" native="true">
|
<menupopup>
|
||||||
<menupopup/>
|
<menuitem label="&zotero.search.joinMode.any;" value="any"/>
|
||||||
|
<menuitem label="&zotero.search.joinMode.all;" value="all" selected="true"/>
|
||||||
|
</menupopup>
|
||||||
</menulist>
|
</menulist>
|
||||||
</hbox>
|
<label value="&zotero.search.joinMode.suffix;"/>
|
||||||
<groupbox>
|
</caption>
|
||||||
<caption align="center">
|
<vbox id="conditions"/>
|
||||||
<label id="joinModeMenu-label" value="&zotero.search.joinMode.prefix;"/>
|
</groupbox>
|
||||||
<menulist id="joinModeMenu" aria-labelledby="joinModeMenu-label" oncommand="this.closest('zoterosearch').updateJoinMode();" native="true">
|
<hbox id="search-option-checkboxes">
|
||||||
<menupopup>
|
<checkbox id="recursiveCheckbox" label="&zotero.search.recursive.label;" oncommand="this.closest('zoterosearch').updateCheckbox('recursive');" native="true"/>
|
||||||
<menuitem label="&zotero.search.joinMode.any;" value="any"/>
|
<checkbox id="noChildrenCheckbox" label="&zotero.search.noChildren;" oncommand="this.closest('zoterosearch').updateCheckbox('noChildren');" native="true"/>
|
||||||
<menuitem label="&zotero.search.joinMode.all;" value="all" selected="true"/>
|
<checkbox id="includeParentsAndChildrenCheckbox" label="&zotero.search.includeParentsAndChildren;" oncommand="this.closest('zoterosearch').updateCheckbox('includeParentsAndChildren');" native="true"/>
|
||||||
</menupopup>
|
</hbox>
|
||||||
</menulist>
|
|
||||||
<label value="&zotero.search.joinMode.suffix;"/>
|
|
||||||
</caption>
|
|
||||||
<vbox id="conditions"/>
|
|
||||||
</groupbox>
|
|
||||||
<hbox>
|
|
||||||
<checkbox id="recursiveCheckbox" label="&zotero.search.recursive.label;" oncommand="this.closest('zoterosearch').updateCheckbox('recursive');" native="true"/>
|
|
||||||
<checkbox id="noChildrenCheckbox" label="&zotero.search.noChildren;" oncommand="this.closest('zoterosearch').updateCheckbox('noChildren');" native="true"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox>
|
|
||||||
<checkbox id="includeParentsAndChildrenCheckbox" label="&zotero.search.includeParentsAndChildren;" oncommand="this.closest('zoterosearch').updateCheckbox('includeParentsAndChildren');" native="true"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
`, ['chrome://zotero/locale/zotero.dtd', 'chrome://zotero/locale/searchbox.dtd']);
|
`, ['chrome://zotero/locale/zotero.dtd', 'chrome://zotero/locale/searchbox.dtd']);
|
||||||
|
|
||||||
get search() {
|
get search() {
|
||||||
|
|
@ -75,19 +64,13 @@
|
||||||
set search(val) {
|
set search(val) {
|
||||||
this.searchRef = val;
|
this.searchRef = val;
|
||||||
|
|
||||||
var libraryMenu = this.querySelector('#libraryMenu');
|
|
||||||
var libraries = Zotero.Libraries.getAll();
|
|
||||||
Zotero.Utilities.Internal.buildLibraryMenu(
|
|
||||||
libraryMenu, libraries, this.searchRef.libraryID
|
|
||||||
);
|
|
||||||
if (this.searchRef.id) {
|
|
||||||
libraryMenu.disabled = true;
|
|
||||||
}
|
|
||||||
this.updateLibrary();
|
|
||||||
|
|
||||||
this.querySelector('#joinModeMenu').removeAttribute('condition');
|
this.querySelector('#joinModeMenu').removeAttribute('condition');
|
||||||
this.querySelector('#joinModeMenu').value = 'all';
|
this.querySelector('#joinModeMenu').value = 'all';
|
||||||
|
|
||||||
|
this.querySelector('#recursiveCheckbox').checked = false;
|
||||||
|
this.querySelector('#noChildrenCheckbox').checked = false;
|
||||||
|
this.querySelector('#includeParentsAndChildrenCheckbox').checked = false;
|
||||||
|
|
||||||
var conditionsBox = this.querySelector('#conditions');
|
var conditionsBox = this.querySelector('#conditions');
|
||||||
while (conditionsBox.hasChildNodes()) {
|
while (conditionsBox.hasChildNodes()) {
|
||||||
conditionsBox.removeChild(conditionsBox.firstChild);
|
conditionsBox.removeChild(conditionsBox.firstChild);
|
||||||
|
|
@ -119,6 +102,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.addEventListener('keypress', event => this.handleKeyPress(event));
|
||||||
|
}
|
||||||
|
|
||||||
addCondition(ref) {
|
addCondition(ref) {
|
||||||
var conditionsBox = this.querySelector('#conditions');
|
var conditionsBox = this.querySelector('#conditions');
|
||||||
var condition = document.createXULElement('zoterosearchcondition');
|
var condition = document.createXULElement('zoterosearchcondition');
|
||||||
|
|
@ -167,20 +154,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLibrary() {
|
|
||||||
var menu = this.querySelector('#libraryMenu');
|
|
||||||
var libraryID = parseInt(menu.selectedItem.value);
|
|
||||||
|
|
||||||
if (this.onLibraryChange) {
|
|
||||||
this.onLibraryChange(libraryID);
|
|
||||||
}
|
|
||||||
if (!this.searchRef.id) {
|
|
||||||
this.searchRef.libraryID = libraryID;
|
|
||||||
}
|
|
||||||
|
|
||||||
[...this.querySelector('#conditions').childNodes].forEach(x => x.onLibraryChange());
|
|
||||||
}
|
|
||||||
|
|
||||||
updateJoinMode() {
|
updateJoinMode() {
|
||||||
var menu = this.querySelector('#joinModeMenu');
|
var menu = this.querySelector('#joinModeMenu');
|
||||||
if (menu.hasAttribute('condition')) this.search.updateCondition(menu.getAttribute('condition'), 'joinMode', menu.value, null);
|
if (menu.hasAttribute('condition')) this.search.updateCondition(menu.getAttribute('condition'), 'joinMode', menu.value, null);
|
||||||
|
|
@ -586,6 +559,11 @@
|
||||||
|
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
valueMenu.value = this.value;
|
valueMenu.value = this.value;
|
||||||
|
// If the value isn't in the menu (e.g., a collection from another
|
||||||
|
// library after a library change), fall back to the first item
|
||||||
|
if (!valueMenu.selectedItem) {
|
||||||
|
valueMenu.selectedIndex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -747,8 +725,6 @@
|
||||||
|
|
||||||
onRemoveClicked() {
|
onRemoveClicked() {
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
window.resizeBy(0, -1 * this.getBoundingClientRect().height);
|
|
||||||
window.dispatchEvent(new CustomEvent('resize'));
|
|
||||||
this.parent.removeCondition(this.conditionID);
|
this.parent.removeCondition(this.conditionID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -764,7 +740,6 @@
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
this.parent.addCondition(ref);
|
this.parent.addCondition(ref);
|
||||||
window.resizeBy(0, this.getBoundingClientRect().height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,15 +214,15 @@ var LibraryTree = class LibraryTree extends React.Component {
|
||||||
this.tree && this.tree.scrollToRow(index);
|
this.tree && this.tree.scrollToRow(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateHeight = () => {
|
updateHeight = () => {
|
||||||
this.forceUpdate(() => {
|
this.forceUpdate(() => {
|
||||||
if (this.tree) {
|
if (this.tree) {
|
||||||
this.tree.rerender();
|
this.tree.rerender();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
updateHeight = Zotero.Utilities.debounce(this._updateHeight, 200);
|
updateHeightDebounced = Zotero.Utilities.debounce(this.updateHeight, 200);
|
||||||
|
|
||||||
updateFontSize() {
|
updateFontSize() {
|
||||||
this.tree.updateFontSize();
|
this.tree.updateFontSize();
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,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 *****
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var itemsView;
|
|
||||||
var collectionsView;
|
|
||||||
var io;
|
|
||||||
|
|
||||||
function doLoad()
|
|
||||||
{
|
|
||||||
// Set font size from pref
|
|
||||||
var sbc = document.getElementById('zotero-search-box-container');
|
|
||||||
Zotero.UIProperties.registerRoot(sbc);
|
|
||||||
|
|
||||||
io = window.arguments[0];
|
|
||||||
|
|
||||||
var searchBox = document.getElementById('search-box');
|
|
||||||
searchBox.groups = io.dataIn.groups;
|
|
||||||
searchBox.search = io.dataIn.search;
|
|
||||||
let searchName = document.getElementById('search-name');
|
|
||||||
searchName.value = io.dataIn.name;
|
|
||||||
searchName.select();
|
|
||||||
|
|
||||||
document.addEventListener('dialogaccept', doAccept);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doUnload()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function doAccept()
|
|
||||||
{
|
|
||||||
document.getElementById('search-box').search.name = document.getElementById('search-name').value;
|
|
||||||
try {
|
|
||||||
let searchBox = document.getElementById('search-box');
|
|
||||||
searchBox.updateSearch();
|
|
||||||
io.dataOut = {
|
|
||||||
json: searchBox.search.toJSON()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.debug(e, 1);
|
|
||||||
Components.utils.reportError(e);
|
|
||||||
throw (e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?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/overlay.css" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css" type="text/css"?>
|
|
||||||
|
|
||||||
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/searchbox.dtd">
|
|
||||||
|
|
||||||
<window
|
|
||||||
title="&zotero.search.search;"
|
|
||||||
orient="vertical"
|
|
||||||
onload="doLoad();"
|
|
||||||
onunload="doUnload();"
|
|
||||||
drawintitlebar-platforms="mac"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
|
||||||
<dialog
|
|
||||||
id="zotero-search-dialog"
|
|
||||||
buttons="cancel,accept">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
|
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/titlebar.js", this);
|
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
|
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/searchDialog.js", this);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<popupset>
|
|
||||||
<panel is="autocomplete-richlistbox-popup"
|
|
||||||
id="search-autocomplete-popup"
|
|
||||||
type="autocomplete-richlistbox"
|
|
||||||
noautofocus="true"/>
|
|
||||||
</popupset>
|
|
||||||
|
|
||||||
<vbox id="zotero-search-box-container" flex="1">
|
|
||||||
<hbox align="center">
|
|
||||||
<label control="search-name" value="&zotero.search.name;"/>
|
|
||||||
<html:input id="search-name" type="text" width="275" maxlength="80"/>
|
|
||||||
</hbox>
|
|
||||||
<zoterosearch id="search-box" flex="1"/>
|
|
||||||
</vbox>
|
|
||||||
</dialog>
|
|
||||||
</window>
|
|
||||||
|
|
@ -464,7 +464,8 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = async function () {
|
||||||
}
|
}
|
||||||
s2.setScope(s, includeScopeChildren);
|
s2.setScope(s, includeScopeChildren);
|
||||||
|
|
||||||
if (this.searchText) {
|
// Add Quick Search unless advanced search is enabled
|
||||||
|
if (this.searchText && !this.advancedSearch) {
|
||||||
let cond = 'quicksearch-'
|
let cond = 'quicksearch-'
|
||||||
+ (this.searchMode || Zotero.Prefs.get('search.quicksearch-mode'));
|
+ (this.searchMode || Zotero.Prefs.get('search.quicksearch-mode'));
|
||||||
s2.addCondition(cond, 'contains', this.searchText);
|
s2.addCondition(cond, 'contains', this.searchText);
|
||||||
|
|
@ -476,8 +477,37 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = async function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cachedSearch = s2;
|
let s3;
|
||||||
return s2;
|
if (this.advancedSearch) {
|
||||||
|
if (this.advancedSearch.libraryID === null) {
|
||||||
|
// A library-less search (Feeds pseudo-library) can't be clone()d
|
||||||
|
s3 = new Zotero.Search();
|
||||||
|
s3.fromJSON(this.advancedSearch.toJSON());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s3 = this.advancedSearch.clone();
|
||||||
|
}
|
||||||
|
// Show matches in the trash too. includeDeleted has to be set on the
|
||||||
|
// scope searches as well, since a search's scope defines the superset
|
||||||
|
// of possible results. Special condition - unaffected by joinMode.
|
||||||
|
if (!this.isTrash()) {
|
||||||
|
if (s == this.ref) {
|
||||||
|
// Don't modify a saved search object
|
||||||
|
s = s.clone(this.ref.libraryID);
|
||||||
|
s2.setScope(s, includeScopeChildren);
|
||||||
|
}
|
||||||
|
s.addCondition('includeDeleted', 'true');
|
||||||
|
s2.addCondition('includeDeleted', 'true');
|
||||||
|
}
|
||||||
|
s3.addCondition('includeDeleted', 'true');
|
||||||
|
s3.setScope(s2, includeScopeChildren);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s3 = s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cachedSearch = s3;
|
||||||
|
return s3;
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.CollectionTreeRow.prototype.getChildTags = function () {
|
Zotero.CollectionTreeRow.prototype.getChildTags = function () {
|
||||||
|
|
@ -537,6 +567,23 @@ Zotero.CollectionTreeRow.prototype.setSearch = function (searchText, mode = null
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.CollectionTreeRow.prototype.setAdvancedSearch = function (advancedSearch) {
|
||||||
|
this.clearCache();
|
||||||
|
if (!advancedSearch) {
|
||||||
|
this.advancedSearch = undefined;
|
||||||
|
}
|
||||||
|
else if (this.ref.libraryID === undefined) {
|
||||||
|
// Feeds pseudo-library -- leave the library unset so that the search
|
||||||
|
// spans all feed libraries
|
||||||
|
this.advancedSearch = new Zotero.Search();
|
||||||
|
this.advancedSearch.fromJSON(advancedSearch.toJSON());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.advancedSearch = advancedSearch.clone(this.ref.libraryID);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
Zotero.CollectionTreeRow.prototype.setTags = function (tags) {
|
Zotero.CollectionTreeRow.prototype.setTags = function (tags) {
|
||||||
let oldTags = this.tags instanceof Set ? this.tags : new Set(this.tags || []);
|
let oldTags = this.tags instanceof Set ? this.tags : new Set(this.tags || []);
|
||||||
let newTags = tags instanceof Set ? new Set(tags) : new Set(tags || []);
|
let newTags = tags instanceof Set ? new Set(tags) : new Set(tags || []);
|
||||||
|
|
@ -570,8 +617,8 @@ Zotero.CollectionTreeRow.prototype.isSearchMode = function () {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quicksearch
|
// Search filters
|
||||||
if (this.searchText != '') {
|
if (this.advancedSearch || this.searchText != '') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -597,8 +597,9 @@ Zotero.Search.prototype.search = async function (asTempTable) {
|
||||||
|
|
||||||
// Run a subsearch to define the superset of possible results
|
// Run a subsearch to define the superset of possible results
|
||||||
if (this._scope) {
|
if (this._scope) {
|
||||||
// If subsearch has post-search filter, run and insert ids into temp table
|
// If subsearch has post-search filter or a recursive scope,
|
||||||
if (this._scope.hasPostSearchFilter()) {
|
// run and insert ids into temp table
|
||||||
|
if (this._scope.hasPostSearchFilter() || this._scope._scope) {
|
||||||
var ids = await this._scope.search();
|
var ids = await this._scope.search();
|
||||||
if (!ids) {
|
if (!ids) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ var ZoteroPane = new function () {
|
||||||
'zotero-tb-add': {
|
'zotero-tb-add': {
|
||||||
ArrowNext: () => document.getElementById("zotero-tb-lookup"),
|
ArrowNext: () => document.getElementById("zotero-tb-lookup"),
|
||||||
ArrowPrevious: () => null,
|
ArrowPrevious: () => null,
|
||||||
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
|
Tab: () => document.getElementById("zotero-tb-search").focus(),
|
||||||
ShiftTab: () => {
|
ShiftTab: () => {
|
||||||
if (collectionsPane.getAttribute("collapsed")) {
|
if (collectionsPane.getAttribute("collapsed")) {
|
||||||
return document.getElementById('zotero-tb-sync');
|
return document.getElementById('zotero-tb-sync');
|
||||||
|
|
@ -378,7 +378,7 @@ var ZoteroPane = new function () {
|
||||||
'zotero-tb-lookup': {
|
'zotero-tb-lookup': {
|
||||||
ArrowNext: () => document.getElementById("zotero-tb-attachment-add"),
|
ArrowNext: () => document.getElementById("zotero-tb-attachment-add"),
|
||||||
ArrowPrevious: () => document.getElementById("zotero-tb-add"),
|
ArrowPrevious: () => document.getElementById("zotero-tb-add"),
|
||||||
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
|
Tab: () => document.getElementById("zotero-tb-search").focus(),
|
||||||
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click(),
|
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click(),
|
||||||
Enter: () => Zotero_Lookup.showPanel(event.target),
|
Enter: () => Zotero_Lookup.showPanel(event.target),
|
||||||
' ': () => Zotero_Lookup.showPanel(event.target)
|
' ': () => Zotero_Lookup.showPanel(event.target)
|
||||||
|
|
@ -386,13 +386,13 @@ var ZoteroPane = new function () {
|
||||||
'zotero-tb-attachment-add': {
|
'zotero-tb-attachment-add': {
|
||||||
ArrowNext: () => document.getElementById("zotero-tb-note-add"),
|
ArrowNext: () => document.getElementById("zotero-tb-note-add"),
|
||||||
ArrowPrevious: () => document.getElementById("zotero-tb-lookup"),
|
ArrowPrevious: () => document.getElementById("zotero-tb-lookup"),
|
||||||
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
|
Tab: () => document.getElementById("zotero-tb-search").focus(),
|
||||||
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
|
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
|
||||||
},
|
},
|
||||||
'zotero-tb-note-add': {
|
'zotero-tb-note-add': {
|
||||||
ArrowNext: () => null,
|
ArrowNext: () => null,
|
||||||
ArrowPrevious: () => document.getElementById("zotero-tb-attachment-add"),
|
ArrowPrevious: () => document.getElementById("zotero-tb-attachment-add"),
|
||||||
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
|
Tab: () => document.getElementById("zotero-tb-search").focus(),
|
||||||
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
|
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
|
||||||
},
|
},
|
||||||
'zotero-tb-search-dropmarker': {
|
'zotero-tb-search-dropmarker': {
|
||||||
|
|
@ -403,9 +403,7 @@ var ZoteroPane = new function () {
|
||||||
},
|
},
|
||||||
'zotero-tb-search-textbox': {
|
'zotero-tb-search-textbox': {
|
||||||
Tab: () => document.getElementById("zotero-tb-toggle-item-pane-stacked"),
|
Tab: () => document.getElementById("zotero-tb-toggle-item-pane-stacked"),
|
||||||
ShiftTab: () => {
|
ShiftTab: () => document.getElementById("zotero-tb-search").focus()
|
||||||
document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'zotero-tb-toggle-item-pane-stacked': {
|
'zotero-tb-toggle-item-pane-stacked': {
|
||||||
Tab: () => itemTree.querySelector(".virtualized-table"),
|
Tab: () => itemTree.querySelector(".virtualized-table"),
|
||||||
|
|
@ -1554,60 +1552,10 @@ var ZoteroPane = new function () {
|
||||||
this.loadURI(Zotero.Groups.addGroupURL);
|
this.loadURI(Zotero.Groups.addGroupURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.newSearch = async function () {
|
|
||||||
if (Zotero.DB.inTransaction()) {
|
|
||||||
await Zotero.DB.waitForTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
var libraryID = this.getSelectedLibraryID();
|
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
|
||||||
s.libraryID = libraryID;
|
|
||||||
s.addCondition('title', 'contains', '');
|
|
||||||
|
|
||||||
var searches = await Zotero.Searches.getAll(libraryID)
|
|
||||||
var prefix = Zotero.getString('pane.collections.untitled');
|
|
||||||
var name = Zotero.Utilities.Internal.getNextName(
|
|
||||||
prefix,
|
|
||||||
searches.map(s => s.name).filter(n => n.startsWith(prefix))
|
|
||||||
);
|
|
||||||
|
|
||||||
var io = { dataIn: { search: s, name }, dataOut: null };
|
|
||||||
window.openDialog('chrome://zotero/content/searchDialog.xhtml','','chrome,modal,centerscreen',io);
|
|
||||||
if (!io.dataOut) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
s.fromJSON(io.dataOut.json);
|
|
||||||
await s.saveTx();
|
|
||||||
return s.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setVirtual = function (libraryID, type, show, select) {
|
this.setVirtual = function (libraryID, type, show, select) {
|
||||||
return this.collectionsView.toggleVirtualCollection(libraryID, type, show, select);
|
return this.collectionsView.toggleVirtualCollection(libraryID, type, show, select);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.openAdvancedSearchWindow = function () {
|
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
var enumerator = wm.getEnumerator('zotero:search');
|
|
||||||
while (enumerator.hasMoreElements()) {
|
|
||||||
var win = enumerator.getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (win) {
|
|
||||||
win.focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
|
||||||
s.libraryID = this.getSelectedLibraryID();
|
|
||||||
s.addCondition('title', 'contains', '');
|
|
||||||
|
|
||||||
var io = {dataIn: {search: s}, dataOut: null};
|
|
||||||
window.openDialog('chrome://zotero/content/advancedSearch.xhtml', '', 'chrome,dialog=no,centerscreen', io);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.initItemsTree = async function () {
|
this.initItemsTree = async function () {
|
||||||
try {
|
try {
|
||||||
const CollectionViewItemTree = require('zotero/collectionViewItemTree');
|
const CollectionViewItemTree = require('zotero/collectionViewItemTree');
|
||||||
|
|
@ -1688,7 +1636,7 @@ var ZoteroPane = new function () {
|
||||||
this.tagSelector.handleResize();
|
this.tagSelector.handleResize();
|
||||||
}
|
}
|
||||||
if (this.collectionsView) {
|
if (this.collectionsView) {
|
||||||
this.collectionsView.updateHeight();
|
this.collectionsView.updateHeightDebounced();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
|
@ -1756,7 +1704,6 @@ var ZoteroPane = new function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.onCollectionSelected = Zotero.serial(async function () {
|
this.onCollectionSelected = Zotero.serial(async function () {
|
||||||
var collectionTreeRow = this.getCollectionTreeRow();
|
var collectionTreeRow = this.getCollectionTreeRow();
|
||||||
if (!collectionTreeRow) {
|
if (!collectionTreeRow) {
|
||||||
|
|
@ -1772,13 +1719,43 @@ var ZoteroPane = new function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let advancedSearchDeck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
if (this.itemsView.collectionTreeRow?.isSearch()
|
||||||
|
&& advancedSearchDeck.state === 'open'
|
||||||
|
&& advancedSearchDeck.selectedSearchType === 'saved') {
|
||||||
|
let result = Services.prompt.confirmEx(window,
|
||||||
|
Zotero.getString('saved-search-close-confirmation-title'),
|
||||||
|
Zotero.getString('saved-search-close-confirmation-body'),
|
||||||
|
Ci.nsIPromptService.BUTTON_POS_0_DEFAULT
|
||||||
|
| Ci.nsIPrompt.BUTTON_TITLE_SAVE * Ci.nsIPrompt.BUTTON_POS_0
|
||||||
|
| Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1
|
||||||
|
| Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE * Ci.nsIPrompt.BUTTON_POS_2,
|
||||||
|
null, null, null,
|
||||||
|
null, {});
|
||||||
|
switch (result) {
|
||||||
|
case 0:
|
||||||
|
await advancedSearchDeck.pane.save();
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
this.collectionsView.selection.selectEventsSuppressed = true;
|
||||||
|
try {
|
||||||
|
await this.collectionsView.selectByID(this.itemsView.collectionTreeRow.id);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.collectionsView.selection.selectEventsSuppressed = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
await advancedSearchDeck.pane.cancel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rename tab
|
// Rename tab
|
||||||
Zotero_Tabs.rename('zotero-pane', collectionTreeRow.getName());
|
Zotero_Tabs.rename('zotero-pane', collectionTreeRow.getName());
|
||||||
|
|
||||||
let type = Zotero.Libraries.get(collectionTreeRow.ref.libraryID).libraryType;
|
|
||||||
|
|
||||||
// Clear quick search and tag selector when switching views
|
// Clear quick search and tag selector when switching views
|
||||||
document.getElementById('zotero-tb-search-textbox').value = "";
|
document.getElementById('zotero-tb-search').onCollectionSelected();
|
||||||
if (ZoteroPane.tagSelector) {
|
if (ZoteroPane.tagSelector) {
|
||||||
ZoteroPane.tagSelector.clearTagSelection();
|
ZoteroPane.tagSelector.clearTagSelection();
|
||||||
}
|
}
|
||||||
|
|
@ -1788,6 +1765,7 @@ var ZoteroPane = new function () {
|
||||||
collectionTreeRow.setTags(ZoteroPane.tagSelector.getTagSelection());
|
collectionTreeRow.setTags(ZoteroPane.tagSelector.getTagSelection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._refreshAdvancedSearchPane(collectionTreeRow);
|
||||||
this._updateEnabledActionsForRow(collectionTreeRow);
|
this._updateEnabledActionsForRow(collectionTreeRow);
|
||||||
|
|
||||||
// If item data not yet loaded for library, load it now.
|
// If item data not yet loaded for library, load it now.
|
||||||
|
|
@ -1817,6 +1795,125 @@ var ZoteroPane = new function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Zotero.CollectionTreeRow} [collectionTreeRow] - During collection selection,
|
||||||
|
* the newly selected row, which isn't in the items view yet
|
||||||
|
*/
|
||||||
|
this._refreshAdvancedSearchPane = function (collectionTreeRow) {
|
||||||
|
let deck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
|
||||||
|
deck.pane.refresh();
|
||||||
|
|
||||||
|
let search = deck.state === 'closed' || deck.selectedSearchType !== 'temporary' || !deck.pane.active
|
||||||
|
? null
|
||||||
|
: deck.pane.search;
|
||||||
|
if (collectionTreeRow) {
|
||||||
|
collectionTreeRow.setAdvancedSearch(search);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// Apply via the items view, whose row can be a different object from the
|
||||||
|
// collection tree's current row
|
||||||
|
return this.itemsView.setFilter('advanced-search', search);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {'open' | 'collapsed' | 'closed'} state
|
||||||
|
*/
|
||||||
|
this.setAdvancedSearchState = async function (state) {
|
||||||
|
let deck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
let oldState = deck.state;
|
||||||
|
deck.selectedSearchType = 'temporary';
|
||||||
|
deck.state = state;
|
||||||
|
|
||||||
|
let advancedSearchPane = deck.pane;
|
||||||
|
|
||||||
|
document.getElementById('zotero-tb-search').updateMode();
|
||||||
|
let refreshPromise;
|
||||||
|
if (state === 'open' && oldState === 'collapsed'
|
||||||
|
|| state === 'collapsed' && oldState === 'open') {
|
||||||
|
// State change only causes visual refresh - update the tree height
|
||||||
|
this.itemsView.updateHeight();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// State change changes displayed items - refresh the tree
|
||||||
|
refreshPromise = this._refreshAdvancedSearchPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the pane state synchronously, so that a state change initiated
|
||||||
|
// while the refresh below is pending doesn't see a stale search
|
||||||
|
if (state === 'closed') {
|
||||||
|
advancedSearchPane.search = null;
|
||||||
|
}
|
||||||
|
else if (state === 'open') {
|
||||||
|
Zotero_Tabs.select('zotero-pane');
|
||||||
|
advancedSearchPane.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {'open' | 'collapsed' | 'closed'} state
|
||||||
|
*/
|
||||||
|
this.toggleAdvancedSearchState = async function (state) {
|
||||||
|
let deck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
if (state === deck.state && deck.selectedSearchType !== 'saved') {
|
||||||
|
// If we're trying to open the pane, and it's already open but not focused,
|
||||||
|
// focus it
|
||||||
|
if (state === 'open' && !deck.pane.matches(':focus-within')) {
|
||||||
|
deck.pane.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip the state
|
||||||
|
switch (state) {
|
||||||
|
case 'open':
|
||||||
|
state = 'closed';
|
||||||
|
break;
|
||||||
|
case 'collapsed':
|
||||||
|
case 'closed':
|
||||||
|
state = 'open';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.setAdvancedSearchState(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {'open' | 'closed'} state
|
||||||
|
*/
|
||||||
|
this.setSavedSearchEditorState = async function (state) {
|
||||||
|
let collectionTreeRow = this.getCollectionTreeRow();
|
||||||
|
if (state === 'open' && !collectionTreeRow.isSearch()) {
|
||||||
|
throw new Error('Cannot show saved search editor outside search row');
|
||||||
|
}
|
||||||
|
|
||||||
|
let deck = document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
deck.selectedSearchType = 'saved';
|
||||||
|
deck.state = state;
|
||||||
|
|
||||||
|
if (state === 'open') {
|
||||||
|
deck.pane.search = collectionTreeRow.ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('zotero-tb-search').updateMode();
|
||||||
|
let refreshPromise = this._refreshAdvancedSearchPane();
|
||||||
|
if (state === 'open') {
|
||||||
|
deck.pane.focus();
|
||||||
|
}
|
||||||
|
await refreshPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.openAdvancedSearchWindow = function () {
|
||||||
|
Zotero.debug(`ZoteroPane.openAdvancedSearchWindow() is deprecated -- use ZoteroPane.toggleAdvancedSearchState() instead`);
|
||||||
|
this.toggleAdvancedSearchState('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable toolbar icons, menu options, and commands as necessary
|
* Enable or disable toolbar icons, menu options, and commands as necessary
|
||||||
*/
|
*/
|
||||||
|
|
@ -1828,7 +1925,6 @@ var ZoteroPane = new function () {
|
||||||
"menu_noteAdd",
|
"menu_noteAdd",
|
||||||
|
|
||||||
"cmd_zotero_newCollection",
|
"cmd_zotero_newCollection",
|
||||||
"cmd_zotero_newSavedSearch",
|
|
||||||
"cmd_zotero_import",
|
"cmd_zotero_import",
|
||||||
"cmd_zotero_importFromClipboard",
|
"cmd_zotero_importFromClipboard",
|
||||||
|
|
||||||
|
|
@ -2580,28 +2676,7 @@ var ZoteroPane = new function () {
|
||||||
this.collectionsView.startEditing(row);
|
this.collectionsView.startEditing(row);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let s = row.ref.clone();
|
this.setSavedSearchEditorState('open');
|
||||||
let groups = [];
|
|
||||||
// Promises don't work in the modal dialog, so get the group name here, if
|
|
||||||
// applicable, and pass it in. We only need the group that this search belongs
|
|
||||||
// to, if any, since the library drop-down is disabled for saved searches.
|
|
||||||
if (Zotero.Libraries.get(s.libraryID).libraryType == 'group') {
|
|
||||||
groups.push(Zotero.Groups.getByLibraryID(s.libraryID));
|
|
||||||
}
|
|
||||||
var io = {
|
|
||||||
dataIn: {
|
|
||||||
search: s,
|
|
||||||
name: row.getName(),
|
|
||||||
groups: groups
|
|
||||||
},
|
|
||||||
dataOut: null
|
|
||||||
};
|
|
||||||
window.openDialog('chrome://zotero/content/searchDialog.xhtml','','chrome,modal,centerscreen',io);
|
|
||||||
if (io.dataOut) {
|
|
||||||
row.ref.fromJSON(io.dataOut.json);
|
|
||||||
await row.ref.saveTx();
|
|
||||||
Zotero_Tabs.rename("zotero-pane", row.ref.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -3274,10 +3349,6 @@ var ZoteroPane = new function () {
|
||||||
id: "newCollection",
|
id: "newCollection",
|
||||||
command: "cmd_zotero_newCollection"
|
command: "cmd_zotero_newCollection"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "newSavedSearch",
|
|
||||||
command: "cmd_zotero_newSavedSearch"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "newSubcollection",
|
id: "newSubcollection",
|
||||||
oncommand: () => {
|
oncommand: () => {
|
||||||
|
|
@ -3564,8 +3635,7 @@ var ZoteroPane = new function () {
|
||||||
show.push(
|
show.push(
|
||||||
'sync',
|
'sync',
|
||||||
'sep1',
|
'sep1',
|
||||||
'newCollection',
|
'newCollection'
|
||||||
'newSavedSearch'
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Only show "Show Duplicates", "Show Unfiled Items", and "Show Retracted" if rows are hidden
|
// Only show "Show Duplicates", "Show Unfiled Items", and "Show Retracted" if rows are hidden
|
||||||
|
|
@ -6784,7 +6854,7 @@ var ZoteroPane = new function () {
|
||||||
if (ZoteroPane.itemsView) {
|
if (ZoteroPane.itemsView) {
|
||||||
// Need to immediately rerender the items here without any debouncing
|
// Need to immediately rerender the items here without any debouncing
|
||||||
// since tree height will have changed
|
// since tree height will have changed
|
||||||
ZoteroPane.itemsView._updateHeight();
|
ZoteroPane.itemsView.updateHeight();
|
||||||
}
|
}
|
||||||
ZoteroContextPane.update();
|
ZoteroContextPane.update();
|
||||||
Zotero_Tabs.updateSidebarLayout();
|
Zotero_Tabs.updateSidebarLayout();
|
||||||
|
|
@ -6970,7 +7040,7 @@ var ZoteroPane = new function () {
|
||||||
var collectionsPaneWidth = collectionsPane.getBoundingClientRect().width;
|
var collectionsPaneWidth = collectionsPane.getBoundingClientRect().width;
|
||||||
tagSelector.style.maxWidth = collectionsPaneWidth + 'px';
|
tagSelector.style.maxWidth = collectionsPaneWidth + 'px';
|
||||||
if (ZoteroPane.itemsView) {
|
if (ZoteroPane.itemsView) {
|
||||||
ZoteroPane.itemsView.updateHeight();
|
ZoteroPane.itemsView.updateHeightDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleTagSelectorResize();
|
this.handleTagSelectorResize();
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
<command id="cmd_zotero_import" oncommand="Zotero_File_Interface.showImportWizard();"/>
|
<command id="cmd_zotero_import" oncommand="Zotero_File_Interface.showImportWizard();"/>
|
||||||
<command id="cmd_zotero_importFromClipboard" oncommand="Zotero_File_Interface.importFromClipboard();"/>
|
<command id="cmd_zotero_importFromClipboard" oncommand="Zotero_File_Interface.importFromClipboard();"/>
|
||||||
<command id="cmd_zotero_exportLibrary" oncommand="Zotero_File_Interface.exportFile();"/>
|
<command id="cmd_zotero_exportLibrary" oncommand="Zotero_File_Interface.exportFile();"/>
|
||||||
<command id="cmd_zotero_advancedSearch" oncommand="ZoteroPane_Local.openAdvancedSearchWindow();"/>
|
<command id="cmd_zotero_advancedSearch" oncommand="ZoteroPane.toggleAdvancedSearchState('open');"/>
|
||||||
<command id="cmd_zotero_copyCitation"
|
<command id="cmd_zotero_copyCitation"
|
||||||
oncommand="ZoteroPane_Local.copySelectedItemsToClipboard(true);"
|
oncommand="ZoteroPane_Local.copySelectedItemsToClipboard(true);"
|
||||||
disabled="true"/>
|
disabled="true"/>
|
||||||
|
|
@ -116,7 +116,6 @@
|
||||||
<command id="cmd_zotero_rtfScan" oncommand="window.openDialog('chrome://zotero/content/rtfScan.xhtml', 'rtfScan', 'chrome,centerscreen')"/>
|
<command id="cmd_zotero_rtfScan" oncommand="window.openDialog('chrome://zotero/content/rtfScan.xhtml', 'rtfScan', 'chrome,centerscreen')"/>
|
||||||
<command id="cmd_zotero_newCollection" oncommand="ZoteroPane_Local.newCollection(ZoteroPane_Local.getSelectedCollection()?.key)"/>
|
<command id="cmd_zotero_newCollection" oncommand="ZoteroPane_Local.newCollection(ZoteroPane_Local.getSelectedCollection()?.key)"/>
|
||||||
<command id="cmd_zotero_newFeed_fromURL" oncommand="ZoteroPane_Local.newFeedFromURL()"/>
|
<command id="cmd_zotero_newFeed_fromURL" oncommand="ZoteroPane_Local.newFeedFromURL()"/>
|
||||||
<command id="cmd_zotero_newSavedSearch" oncommand="ZoteroPane_Local.newSearch()"/>
|
|
||||||
<command id="cmd_zotero_addByIdentifier" oncommand="Zotero_Lookup.showPanel()"/>
|
<command id="cmd_zotero_addByIdentifier" oncommand="Zotero_Lookup.showPanel()"/>
|
||||||
<command id="cmd_zotero_newStandaloneFileAttachment" oncommand="ZoteroPane.addAttachmentFromDialog()"/>
|
<command id="cmd_zotero_newStandaloneFileAttachment" oncommand="ZoteroPane.addAttachmentFromDialog()"/>
|
||||||
<command id="cmd_zotero_newStandaloneLinkedFileAttachment" oncommand="ZoteroPane.addAttachmentFromDialog(true)"/>
|
<command id="cmd_zotero_newStandaloneLinkedFileAttachment" oncommand="ZoteroPane.addAttachmentFromDialog(true)"/>
|
||||||
|
|
@ -428,7 +427,7 @@
|
||||||
key="key_find" accesskey="&findCmd.accesskey;"
|
key="key_find" accesskey="&findCmd.accesskey;"
|
||||||
oncommand="ZoteroStandalone.currentReader.toggleFindPopup({ open: true });"/>
|
oncommand="ZoteroStandalone.currentReader.toggleFindPopup({ open: true });"/>
|
||||||
<menuitem id="menu_advancedSearch"
|
<menuitem id="menu_advancedSearch"
|
||||||
label="&zotero.toolbar.advancedSearch;"
|
data-l10n-id="menuitem-advanced-search"
|
||||||
key="key_advancedSearch"
|
key="key_advancedSearch"
|
||||||
command="cmd_zotero_advancedSearch"/>
|
command="cmd_zotero_advancedSearch"/>
|
||||||
<menuseparator hidden="true" id="textfieldDirection-separator"/>
|
<menuseparator hidden="true" id="textfieldDirection-separator"/>
|
||||||
|
|
@ -936,7 +935,6 @@
|
||||||
<menuitem class="zotero-menuitem-sync"/>
|
<menuitem class="zotero-menuitem-sync"/>
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem class="zotero-menuitem-new-collection" data-l10n-id="menu-new-collection"/>
|
<menuitem class="zotero-menuitem-new-collection" data-l10n-id="menu-new-collection"/>
|
||||||
<menuitem class="zotero-menuitem-new-saved-search" label="&zotero.toolbar.newSavedSearch.label;"/>
|
|
||||||
<menuitem class="zotero-menuitem-new-collection" label="&zotero.toolbar.newSubcollection.label;"/>
|
<menuitem class="zotero-menuitem-new-collection" label="&zotero.toolbar.newSubcollection.label;"/>
|
||||||
<menuitem class="zotero-menuitem-refresh-feed"/>
|
<menuitem class="zotero-menuitem-refresh-feed"/>
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
|
|
@ -1371,6 +1369,7 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
</toolbar>
|
</toolbar>
|
||||||
|
<advanced-search-deck id="zotero-advanced-search-pane-deck"/>
|
||||||
<hbox id="zotero-items-pane" class="virtualized-table-container" flex="1" clickthrough="never">
|
<hbox id="zotero-items-pane" class="virtualized-table-container" flex="1" clickthrough="never">
|
||||||
<html:div id="zotero-items-tree"></html:div>
|
<html:div id="zotero-items-tree"></html:div>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<!ENTITY zotero.search.name "Name:">
|
<!ENTITY zotero.search.name "Name:">
|
||||||
|
|
||||||
<!ENTITY zotero.search.searchInLibrary "Search in library:">
|
|
||||||
|
|
||||||
<!ENTITY zotero.search.joinMode.prefix "Match">
|
<!ENTITY zotero.search.joinMode.prefix "Match">
|
||||||
<!ENTITY zotero.search.joinMode.any "any">
|
<!ENTITY zotero.search.joinMode.any "any">
|
||||||
<!ENTITY zotero.search.joinMode.all "all">
|
<!ENTITY zotero.search.joinMode.all "all">
|
||||||
|
|
@ -19,7 +17,3 @@
|
||||||
<!ENTITY zotero.search.date.units.days "days">
|
<!ENTITY zotero.search.date.units.days "days">
|
||||||
<!ENTITY zotero.search.date.units.months "months">
|
<!ENTITY zotero.search.date.units.months "months">
|
||||||
<!ENTITY zotero.search.date.units.years "years">
|
<!ENTITY zotero.search.date.units.years "years">
|
||||||
|
|
||||||
<!ENTITY zotero.search.search "Search">
|
|
||||||
<!ENTITY zotero.search.clear "Clear">
|
|
||||||
<!ENTITY zotero.search.saveSearch "Save Search">
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,6 @@
|
||||||
<!ENTITY zotero.toolbar.removeItem.label "Remove Item…">
|
<!ENTITY zotero.toolbar.removeItem.label "Remove Item…">
|
||||||
<!ENTITY zotero.toolbar.newGroup "New Group…">
|
<!ENTITY zotero.toolbar.newGroup "New Group…">
|
||||||
<!ENTITY zotero.toolbar.newSubcollection.label "New Subcollection…">
|
<!ENTITY zotero.toolbar.newSubcollection.label "New Subcollection…">
|
||||||
<!ENTITY zotero.toolbar.newSavedSearch.label "New Saved Search…">
|
|
||||||
<!ENTITY zotero.toolbar.emptyTrash.label "Empty Trash">
|
<!ENTITY zotero.toolbar.emptyTrash.label "Empty Trash">
|
||||||
<!ENTITY zotero.toolbar.tagSelector.label "Show/Hide Tag Selector">
|
<!ENTITY zotero.toolbar.tagSelector.label "Show/Hide Tag Selector">
|
||||||
<!ENTITY zotero.toolbar.actions.label "Actions">
|
<!ENTITY zotero.toolbar.actions.label "Actions">
|
||||||
|
|
@ -93,7 +92,6 @@
|
||||||
<!ENTITY zotero.toolbar.preferences.label "Preferences…">
|
<!ENTITY zotero.toolbar.preferences.label "Preferences…">
|
||||||
<!ENTITY zotero.toolbar.supportAndDocumentation "Support and Documentation">
|
<!ENTITY zotero.toolbar.supportAndDocumentation "Support and Documentation">
|
||||||
<!ENTITY zotero.toolbar.about.label "About Zotero">
|
<!ENTITY zotero.toolbar.about.label "About Zotero">
|
||||||
<!ENTITY zotero.toolbar.advancedSearch "Advanced Search">
|
|
||||||
<!ENTITY zotero.toolbar.openURL.label "Locate">
|
<!ENTITY zotero.toolbar.openURL.label "Locate">
|
||||||
<!ENTITY zotero.toolbar.openURL.tooltip "Find through your local library">
|
<!ENTITY zotero.toolbar.openURL.tooltip "Find through your local library">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,15 @@ general-view-troubleshooting-instructions = View Troubleshooting Instructions
|
||||||
general-go-back = Go Back
|
general-go-back = Go Back
|
||||||
general-accept = Accept
|
general-accept = Accept
|
||||||
general-cancel = Cancel
|
general-cancel = Cancel
|
||||||
|
cancel-button =
|
||||||
|
.label = { general-cancel }
|
||||||
general-show-in-library = Show in Library
|
general-show-in-library = Show in Library
|
||||||
general-restartApp = Restart { -app-name }
|
general-restartApp = Restart { -app-name }
|
||||||
general-restartInTroubleshootingMode = Restart in Troubleshooting Mode
|
general-restartInTroubleshootingMode = Restart in Troubleshooting Mode
|
||||||
general-save = Save
|
general-save = Save
|
||||||
general-clear = Clear
|
general-clear = Clear
|
||||||
|
clear-button =
|
||||||
|
.label = { general-clear }
|
||||||
general-update = Update
|
general-update = Update
|
||||||
general-back = Back
|
general-back = Back
|
||||||
general-edit = Edit
|
general-edit = Edit
|
||||||
|
|
@ -193,8 +197,9 @@ item-menu-remove-from-recently-read =
|
||||||
.label = Remove from { recently-read }…
|
.label = Remove from { recently-read }…
|
||||||
collections-menu-rename-collection =
|
collections-menu-rename-collection =
|
||||||
.label = Rename Collection
|
.label = Rename Collection
|
||||||
|
edit-saved-search = Edit Saved Search
|
||||||
collections-menu-edit-saved-search =
|
collections-menu-edit-saved-search =
|
||||||
.label = Edit Saved Search
|
.label = { edit-saved-search }
|
||||||
collections-menu-move-collection =
|
collections-menu-move-collection =
|
||||||
.label = Move To
|
.label = Move To
|
||||||
collections-menu-copy-collection =
|
collections-menu-copy-collection =
|
||||||
|
|
@ -725,6 +730,13 @@ quicksearch-input =
|
||||||
.placeholder = { $placeholder }
|
.placeholder = { $placeholder }
|
||||||
.aria-description = { $placeholder }
|
.aria-description = { $placeholder }
|
||||||
|
|
||||||
|
advanced-search = Advanced Search
|
||||||
|
menuitem-advanced-search =
|
||||||
|
.label = { advanced-search }
|
||||||
|
quicksearch-advanced-search-button =
|
||||||
|
.tooltiptext = { advanced-search }
|
||||||
|
.aria-label = { advanced-search }
|
||||||
|
|
||||||
item-pane-header-view-as =
|
item-pane-header-view-as =
|
||||||
.label = View As
|
.label = View As
|
||||||
item-pane-header-none =
|
item-pane-header-none =
|
||||||
|
|
@ -926,3 +938,11 @@ os-keystore-migrate-failed =
|
||||||
[windows] { -app-name } couldn’t encrypt your stored credentials. Your credentials remain stored unencrypted on disk. Restart { -app-name } and try again.
|
[windows] { -app-name } couldn’t encrypt your stored credentials. Your credentials remain stored unencrypted on disk. Restart { -app-name } and try again.
|
||||||
*[other] { -app-name } couldn’t access your { -os-name } keyring to encrypt your stored credentials. Your credentials remain stored unencrypted on disk. Make sure a keyring service is running and restart { -app-name }.
|
*[other] { -app-name } couldn’t access your { -os-name } keyring to encrypt your stored credentials. Your credentials remain stored unencrypted on disk. Make sure a keyring service is running and restart { -app-name }.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
search-button =
|
||||||
|
.label = Search
|
||||||
|
save-search-button =
|
||||||
|
.label = Save Search
|
||||||
|
|
||||||
|
saved-search-close-confirmation-title = Editing Saved Search
|
||||||
|
saved-search-close-confirmation-body = Do you want to save changes you made to this saved search?
|
||||||
|
|
|
||||||
3
chrome/skin/default/zotero/20/universal/maximize.svg
Normal file
3
chrome/skin/default/zotero/20/universal/maximize.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.75 7H18V2H13V3.25L15.8661 3.25L11 8.11612L11.8839 9L16.75 4.13389V7ZM4.13389 16.75L9 11.8839L8.11612 11L3.25 15.8661V13H2V17.375V18H2.625H7V16.75H4.13389Z" fill="context-fill"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 334 B |
3
chrome/skin/default/zotero/20/universal/minimize.svg
Normal file
3
chrome/skin/default/zotero/20/universal/minimize.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1339 7.75L18 2.88388L17.1161 2L12.25 6.86612V4H11V8.375V9H11.625H16V7.75H13.1339ZM7.75 13.1339L7.75 16H9L9 11H4L4 12.25H6.86612L2 17.1161L2.88388 18L7.75 13.1339Z" fill="context-fill"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 342 B |
|
|
@ -21,7 +21,6 @@
|
||||||
// Components
|
// Components
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
@import "components/advancedSearch";
|
|
||||||
@import "components/annotation";
|
@import "components/annotation";
|
||||||
@import "components/autosuggest";
|
@import "components/autosuggest";
|
||||||
@import "components/banners";
|
@import "components/banners";
|
||||||
|
|
@ -79,6 +78,7 @@
|
||||||
// Elements
|
// Elements
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
@import "elements/advancedSearchPane";
|
||||||
@import "elements/attachmentBox";
|
@import "elements/attachmentBox";
|
||||||
@import "elements/attachmentPreview";
|
@import "elements/attachmentPreview";
|
||||||
@import "elements/attachmentPreviewBox";
|
@import "elements/attachmentPreviewBox";
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#zotero-advanced-search-dialog {
|
|
||||||
@include macOS-normalize-controls;
|
|
||||||
|
|
||||||
min-height: 500px;
|
|
||||||
min-width: 700px;
|
|
||||||
|
|
||||||
#zotero-search-box-container {
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zotero-search-box-controls {
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#recursiveCheckbox, #noChildrenCheckbox, #includeParentsAndChildrenCheckbox {
|
|
||||||
margin: 3px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zotero-search-buttons {
|
|
||||||
margin: 3px 0;
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin-inline-start: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -27,7 +27,6 @@ $menu-icons: (
|
||||||
library-lookup: "library-lookup",
|
library-lookup: "library-lookup",
|
||||||
new-feed: "feed",
|
new-feed: "feed",
|
||||||
note: "note",
|
note: "note",
|
||||||
new-saved-search: "saved-search",
|
|
||||||
show-duplicates: "duplicate",
|
show-duplicates: "duplicate",
|
||||||
show-unfiled: "unfiled",
|
show-unfiled: "unfiled",
|
||||||
show-retracted: "retracted",
|
show-retracted: "retracted",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
$toolbar-padding-inline: 8px;
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
height: $height-toolbar !important; /* Hard-code this to fix toolbar icon compression on Linux */
|
height: $height-toolbar !important; /* Hard-code this to fix toolbar icon compression on Linux */
|
||||||
min-height: $height-toolbar; /* Needed to prevent squashing by stretched tag selector */
|
min-height: $height-toolbar; /* Needed to prevent squashing by stretched tag selector */
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0px 8px 0px 8px;
|
padding: 0 $toolbar-padding-inline;
|
||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
54
scss/elements/_advancedSearchPane.scss
Normal file
54
scss/elements/_advancedSearchPane.scss
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
advanced-search-deck {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> advanced-search-pane:not(.deck-selected) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
advanced-search-pane {
|
||||||
|
@include inactive-opacity;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
// Inline padding is further down the tree so focus rings work with overflow: auto
|
||||||
|
padding-block: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--material-toolbar);
|
||||||
|
|
||||||
|
.saved-search-name-row {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-search-buttons {
|
||||||
|
padding-inline: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[type="temporary"] {
|
||||||
|
.saved-search-name-row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-search-buttons {
|
||||||
|
.cancel-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[type="saved"] {
|
||||||
|
.advanced-search-buttons {
|
||||||
|
.search-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,10 @@ quick-search-textbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zotero-tb-search {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
#zotero-tb-search-dropmarker {
|
#zotero-tb-search-dropmarker {
|
||||||
position: relative;
|
position: relative;
|
||||||
// 6px padding + 16px icon width + 2px padding + 8px dropmarker width + 4px padding
|
// 6px padding + 16px icon width + 2px padding + 8px dropmarker width + 4px padding
|
||||||
|
|
@ -70,6 +74,51 @@ quick-search-textbox {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
// Keep input text and the clear icon out from under the Advanced Search button
|
||||||
|
&:has(~ #zotero-tb-search-advanced-button) {
|
||||||
|
padding-inline-end: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the specificity of the #zotero-items-toolbar toolbarbutton rules
|
||||||
|
#zotero-tb-search #zotero-tb-search-advanced-button {
|
||||||
|
position: relative;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-inline-start: -30px;
|
||||||
|
margin-inline-end: 2px;
|
||||||
|
padding: 0;
|
||||||
|
z-index: 2;
|
||||||
|
@include svgicon-menu("filter", "universal", "16");
|
||||||
|
color: var(--fill-secondary);
|
||||||
|
|
||||||
|
@include focus-ring;
|
||||||
|
|
||||||
|
// Show the hover effect on a chip around the icon, inset within the field,
|
||||||
|
// rather than on the whole button
|
||||||
|
&:hover, &:active {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbarbutton-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
// The glyph is weighted slightly to the right within the SVG, so shift it
|
||||||
|
// a pixel left to optically center it (physical sides -- doesn't flip in RTL)
|
||||||
|
padding: 5px 6px 5px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .toolbarbutton-icon {
|
||||||
|
background-color: var(--fill-quinary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active .toolbarbutton-icon {
|
||||||
|
background-color: var(--fill-quarternary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-search-spinner {
|
#zotero-tb-search-spinner {
|
||||||
|
|
@ -79,6 +128,59 @@ quick-search-textbox {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-wrapper {
|
#search-deck {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#search-wrapper {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#advanced-search-indicator {
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
// Cover the inline-end toolbar padding and the existing bottom border
|
||||||
|
margin-inline-end: -$toolbar-padding-inline;
|
||||||
|
padding-inline: $toolbar-padding-inline;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
|
||||||
|
background: var(--material-toolbar);
|
||||||
|
border-inline-start: var(--material-border);
|
||||||
|
|
||||||
|
// Prevent text from shifting when border shows after panel is collapsed
|
||||||
|
border-block: var(--material-border-transparent);
|
||||||
|
|
||||||
|
// This is styled like it's part of the Advanced Search panel, which can't drag the window,
|
||||||
|
// so match that behavior
|
||||||
|
-moz-window-dragging: no-drag;
|
||||||
|
|
||||||
|
& > label {
|
||||||
|
flex: 1;
|
||||||
|
padding-inline: 4px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zotero-clicky {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-collapse-button {
|
||||||
|
@include svgicon-menu("minimize", "universal", "20");
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-close-button {
|
||||||
|
@include svgicon-menu("x", "universal", "20");
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-collapsed="true"] {
|
||||||
|
border-bottom: var(--material-border);
|
||||||
|
|
||||||
|
.advanced-collapse-button {
|
||||||
|
@include svgicon-menu("maximize", "universal", "20");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,31 @@
|
||||||
zoterosearch {
|
zoterosearch {
|
||||||
#search-box > hbox, #search-box > groupbox {
|
flex-direction: column;
|
||||||
margin: 0 6px;
|
padding-inline: 8px;
|
||||||
}
|
gap: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 33vh;
|
||||||
|
@include macOS-normalize-controls;
|
||||||
|
|
||||||
groupbox {
|
groupbox {
|
||||||
margin-top: 0;
|
gap: 8px;
|
||||||
padding-top: 0;
|
|
||||||
|
|
||||||
& > #conditions {
|
& > #conditions {
|
||||||
-moz-appearance: groupbox;
|
min-width: fit-content; // Grow with content, don't overflow
|
||||||
padding: 6px;
|
padding: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--fill-senary);
|
||||||
|
border: var(--material-border-quinary);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-option-checkboxes {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
checkbox {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,10 +39,6 @@ zoterosearch {
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkbox {
|
|
||||||
margin-right: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
describe("Advanced Search", function () {
|
describe("Advanced Search", function () {
|
||||||
var win, zp;
|
var win, zp, deck;
|
||||||
|
|
||||||
before(function* () {
|
before(function* () {
|
||||||
yield resetDB({
|
yield resetDB({
|
||||||
|
|
@ -10,56 +10,170 @@ describe("Advanced Search", function () {
|
||||||
});
|
});
|
||||||
win = yield loadZoteroPane();
|
win = yield loadZoteroPane();
|
||||||
zp = win.ZoteroPane;
|
zp = win.ZoteroPane;
|
||||||
|
deck = win.document.getElementById('zotero-advanced-search-pane-deck');
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function () {
|
after(function () {
|
||||||
win.close();
|
win.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should perform a search", function* () {
|
it("should perform a search", async function () {
|
||||||
var item = yield createDataObject('item', { setTitle: true });
|
var item = await createDataObject('item', { setTitle: true });
|
||||||
|
var otherItem = await createDataObject('item', { setTitle: true });
|
||||||
|
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
var pane = deck.pane;
|
||||||
|
|
||||||
|
// Opening the pane shouldn't filter the items list
|
||||||
|
await zp.itemsView.waitForLoad();
|
||||||
|
assert.equal(zp.itemsView.rowCount, 2);
|
||||||
|
|
||||||
var promise = waitForWindow('chrome://zotero/content/advancedSearch.xhtml');
|
|
||||||
zp.openAdvancedSearchWindow();
|
|
||||||
var searchWin = yield promise;
|
|
||||||
yield searchWin.ZoteroAdvancedSearch._loadedDeferred.promise;
|
|
||||||
// Add condition
|
// Add condition
|
||||||
var searchBox = searchWin.document.getElementById('zotero-search-box');
|
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
s.addCondition('title', 'is', item.getField('title'))
|
s.libraryID = item.libraryID;
|
||||||
searchBox.search = s;
|
s.addCondition('title', 'is', item.getField('title'));
|
||||||
|
pane.search = s;
|
||||||
|
|
||||||
// Run search and wait for results
|
// Run search and wait for results
|
||||||
var o = searchWin.ZoteroAdvancedSearch;
|
var iv = zp.itemsView;
|
||||||
var iv = o.itemsView;
|
await pane.submit();
|
||||||
yield iv.waitForLoad();
|
await iv.waitForLoad();
|
||||||
yield o.search();
|
|
||||||
yield iv.waitForLoad();
|
|
||||||
|
|
||||||
// Check results
|
// Check results
|
||||||
assert.equal(iv.rowCount, 1);
|
assert.equal(iv.rowCount, 1);
|
||||||
var index = iv.getRowIndexByID(item.id);
|
var index = iv.getRowIndexByID(item.id);
|
||||||
assert.isNumber(index);
|
assert.isNumber(index);
|
||||||
|
|
||||||
searchWin.close();
|
// Closing should restore the unfiltered view
|
||||||
|
await zp.setAdvancedSearchState('closed');
|
||||||
|
assert.equal(iv.rowCount, 2);
|
||||||
|
|
||||||
yield item.eraseTx();
|
await item.eraseTx();
|
||||||
|
await otherItem.eraseTx();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show results in trash", async function () {
|
||||||
|
var item = await createDataObject('item', { setTitle: true });
|
||||||
|
item.deleted = true;
|
||||||
|
await item.saveTx();
|
||||||
|
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
var pane = deck.pane;
|
||||||
|
|
||||||
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = item.libraryID;
|
||||||
|
s.addCondition('title', 'is', item.getField('title'));
|
||||||
|
pane.search = s;
|
||||||
|
|
||||||
|
var iv = zp.itemsView;
|
||||||
|
await pane.submit();
|
||||||
|
await iv.waitForLoad();
|
||||||
|
|
||||||
|
assert.isNumber(iv.getRowIndexByID(item.id));
|
||||||
|
|
||||||
|
await zp.setAdvancedSearchState('closed');
|
||||||
|
|
||||||
|
await item.eraseTx();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't reapply previous search when reopened while closing", async function () {
|
||||||
|
var item = await createDataObject('item', { setTitle: true });
|
||||||
|
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = item.libraryID;
|
||||||
|
s.addCondition('title', 'is', 'nomatch');
|
||||||
|
deck.pane.search = s;
|
||||||
|
await deck.pane.submit();
|
||||||
|
await zp.itemsView.waitForLoad();
|
||||||
|
assert.equal(zp.itemsView.rowCount, 0);
|
||||||
|
|
||||||
|
// Close and immediately reopen, without waiting, as with UI clicks
|
||||||
|
zp.toggleAdvancedSearchState('closed');
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
await zp.itemsView.waitForLoad();
|
||||||
|
|
||||||
|
// The previous search shouldn't have been reapplied
|
||||||
|
assert.isNumber(zp.itemsView.getRowIndexByID(item.id));
|
||||||
|
|
||||||
|
await zp.setAdvancedSearchState('closed');
|
||||||
|
await item.eraseTx();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should scope results to the selected saved search", async function () {
|
||||||
|
var inBoth = await createDataObject('item', { title: "foo bar" });
|
||||||
|
var inSavedOnly = await createDataObject('item', { title: "foo baz" });
|
||||||
|
var inAdvancedOnly = await createDataObject('item', { title: "bar qux" });
|
||||||
|
|
||||||
|
var saved = new Zotero.Search();
|
||||||
|
saved.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
|
saved.name = "Scope Test";
|
||||||
|
saved.addCondition('title', 'contains', 'foo');
|
||||||
|
await saved.saveTx();
|
||||||
|
await select(win, saved);
|
||||||
|
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = saved.libraryID;
|
||||||
|
s.addCondition('title', 'contains', 'bar');
|
||||||
|
deck.pane.search = s;
|
||||||
|
|
||||||
|
var iv = zp.itemsView;
|
||||||
|
await deck.pane.submit();
|
||||||
|
await iv.waitForLoad();
|
||||||
|
|
||||||
|
// Only the item matching both the saved search and the advanced search
|
||||||
|
assert.equal(iv.rowCount, 1);
|
||||||
|
assert.isNumber(iv.getRowIndexByID(inBoth.id));
|
||||||
|
|
||||||
|
// The saved search itself shouldn't have been modified
|
||||||
|
assert.lengthOf(Object.keys(saved.getConditions()), 1);
|
||||||
|
|
||||||
|
await zp.setAdvancedSearchState('closed');
|
||||||
|
|
||||||
|
await Zotero.Items.erase([inBoth.id, inSavedOnly.id, inAdvancedOnly.id]);
|
||||||
|
await saved.eraseTx();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should search across feeds in Feeds view", async function () {
|
||||||
|
let feed = await createFeed();
|
||||||
|
let feedItem = await createDataObject('feedItem', { libraryID: feed.libraryID, setTitle: true }, { skipSelect: true });
|
||||||
|
let otherFeedItem = await createDataObject('feedItem', { libraryID: feed.libraryID, setTitle: true }, { skipSelect: true });
|
||||||
|
|
||||||
|
await zp.collectionsView.selectFeeds();
|
||||||
|
await waitForItemsLoad(win);
|
||||||
|
|
||||||
|
await zp.toggleAdvancedSearchState('open');
|
||||||
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
|
s.addCondition('title', 'is', feedItem.getField('title'));
|
||||||
|
deck.pane.search = s;
|
||||||
|
|
||||||
|
var iv = zp.itemsView;
|
||||||
|
await deck.pane.submit();
|
||||||
|
await iv.waitForLoad();
|
||||||
|
|
||||||
|
assert.equal(iv.rowCount, 1);
|
||||||
|
assert.isNumber(iv.getRowIndexByID(feedItem.id));
|
||||||
|
|
||||||
|
await zp.setAdvancedSearchState('closed');
|
||||||
|
await selectLibrary(win);
|
||||||
|
|
||||||
|
await feed.eraseTx();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Conditions", function () {
|
describe("Conditions", function () {
|
||||||
var searchWin, searchBox, conditions;
|
var pane, searchBox, conditions;
|
||||||
|
|
||||||
before(function* () {
|
before(async function () {
|
||||||
var promise = waitForWindow('chrome://zotero/content/advancedSearch.xhtml');
|
await zp.toggleAdvancedSearchState('open');
|
||||||
zp.openAdvancedSearchWindow();
|
pane = deck.pane;
|
||||||
searchWin = yield promise;
|
searchBox = pane.querySelector('zoterosearch');
|
||||||
searchBox = searchWin.document.getElementById('zotero-search-box');
|
|
||||||
conditions = searchBox.querySelector('#conditions');
|
conditions = searchBox.querySelector('#conditions');
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function () {
|
after(async function () {
|
||||||
searchWin.close();
|
await zp.setAdvancedSearchState('closed');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Collection", function () {
|
describe("Collection", function () {
|
||||||
|
|
@ -73,8 +187,9 @@ describe("Advanced Search", function () {
|
||||||
|
|
||||||
// Add condition
|
// Add condition
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
s.addCondition('title', 'is', '');
|
s.addCondition('title', 'is', '');
|
||||||
searchBox.search = s;
|
pane.search = s;
|
||||||
|
|
||||||
var searchCondition = conditions.firstChild;
|
var searchCondition = conditions.firstChild;
|
||||||
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
||||||
|
|
@ -113,8 +228,9 @@ describe("Advanced Search", function () {
|
||||||
var search = await createDataObject('search', { name: "A" });
|
var search = await createDataObject('search', { name: "A" });
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
s.addCondition('savedSearch', 'is', search.key);
|
s.addCondition('savedSearch', 'is', search.key);
|
||||||
searchBox.search = s;
|
pane.search = s;
|
||||||
|
|
||||||
var searchCondition = conditions.firstChild;
|
var searchCondition = conditions.firstChild;
|
||||||
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
||||||
|
|
@ -132,8 +248,9 @@ describe("Advanced Search", function () {
|
||||||
var search = await createDataObject('search', { name: "B" });
|
var search = await createDataObject('search', { name: "B" });
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
s.addCondition('title', 'is', '');
|
s.addCondition('title', 'is', '');
|
||||||
searchBox.search = s;
|
pane.search = s;
|
||||||
|
|
||||||
var searchCondition = conditions.firstChild;
|
var searchCondition = conditions.firstChild;
|
||||||
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
||||||
|
|
@ -174,8 +291,9 @@ describe("Advanced Search", function () {
|
||||||
var search2 = await createDataObject('search', { name: "D", libraryID: groupLibraryID });
|
var search2 = await createDataObject('search', { name: "D", libraryID: groupLibraryID });
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
|
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||||
s.addCondition('title', 'is', '');
|
s.addCondition('title', 'is', '');
|
||||||
searchBox.search = s;
|
pane.search = s;
|
||||||
|
|
||||||
var searchCondition = conditions.firstChild;
|
var searchCondition = conditions.firstChild;
|
||||||
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
var conditionsMenu = searchCondition.querySelector('#conditionsmenu');
|
||||||
|
|
@ -198,18 +316,13 @@ describe("Advanced Search", function () {
|
||||||
}
|
}
|
||||||
assert.equal(valueMenu.value, "S" + search1.key);
|
assert.equal(valueMenu.value, "S" + search1.key);
|
||||||
|
|
||||||
var libraryMenu = searchWin.document.getElementById('libraryMenu');
|
// Switch to the group library in the collection tree, which changes
|
||||||
for (let i = 0; i < libraryMenu.itemCount; i++) {
|
// the search library and re-renders the conditions
|
||||||
let menuitem = libraryMenu.getItemAtIndex(i);
|
await selectLibrary(win, groupLibraryID);
|
||||||
// Switch to group library
|
|
||||||
if (menuitem.value == groupLibraryID) {
|
|
||||||
menuitem.click();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var values = [];
|
var values = [];
|
||||||
valueMenu = searchCondition.querySelector('#valuemenu')
|
searchCondition = conditions.firstChild;
|
||||||
|
valueMenu = searchCondition.querySelector('#valuemenu');
|
||||||
assert.equal(valueMenu.value, "C" + collection2.key);
|
assert.equal(valueMenu.value, "C" + collection2.key);
|
||||||
for (let i = 0; i < valueMenu.itemCount; i++) {
|
for (let i = 0; i < valueMenu.itemCount; i++) {
|
||||||
let menuitem = valueMenu.getItemAtIndex(i);
|
let menuitem = valueMenu.getItemAtIndex(i);
|
||||||
|
|
@ -220,6 +333,8 @@ describe("Advanced Search", function () {
|
||||||
assert.include(values, "C" + collection2.key);
|
assert.include(values, "C" + collection2.key);
|
||||||
assert.include(values, "S" + search2.key);
|
assert.include(values, "S" + search2.key);
|
||||||
|
|
||||||
|
await selectLibrary(win);
|
||||||
|
|
||||||
await Zotero.Collections.erase([collection1.id, collection2.id]);
|
await Zotero.Collections.erase([collection1.id, collection2.id]);
|
||||||
await Zotero.Searches.erase([search1.id, search2.id]);
|
await Zotero.Searches.erase([search1.id, search2.id]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2784,47 +2784,6 @@ describe("CollectionViewItemTree", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Advanced Search', function () {
|
|
||||||
describe('#notify', function () {
|
|
||||||
it('should resolve the returned promise when an item is selected', async function() {
|
|
||||||
var item = await createDataObject('item', { setTitle: true });
|
|
||||||
var promise = waitForWindow('chrome://zotero/content/advancedSearch.xhtml');
|
|
||||||
zp.openAdvancedSearchWindow();
|
|
||||||
var searchWin = await promise;
|
|
||||||
await searchWin.ZoteroAdvancedSearch._loadedDeferred.promise;
|
|
||||||
// Add condition
|
|
||||||
var searchBox = searchWin.document.getElementById('zotero-search-box');
|
|
||||||
|
|
||||||
var s = new Zotero.Search();
|
|
||||||
s.addCondition('title', 'is', item.getField('title'))
|
|
||||||
searchBox.search = s;
|
|
||||||
|
|
||||||
// Run search and wait for results
|
|
||||||
var o = searchWin.ZoteroAdvancedSearch;
|
|
||||||
var iv = o.itemsView;
|
|
||||||
await iv.waitForLoad();
|
|
||||||
await o.search();
|
|
||||||
await iv.waitForLoad();
|
|
||||||
|
|
||||||
// Check results
|
|
||||||
assert.equal(iv.rowCount, 1);
|
|
||||||
|
|
||||||
// Make sure an item is selected (otherwise notify resolves fine)
|
|
||||||
await iv.selectItem(item.id);
|
|
||||||
assert.equal(iv.selection.count, 1);
|
|
||||||
|
|
||||||
let notifySpy = sinon.spy(iv, 'notify');
|
|
||||||
await createDataObject('item');
|
|
||||||
assert.isTrue(notifySpy.calledOnce);
|
|
||||||
await notifySpy.returnValues[0];
|
|
||||||
notifySpy.restore();
|
|
||||||
|
|
||||||
searchWin.close();
|
|
||||||
await item.eraseTx();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Search error handling", function () {
|
describe("Search error handling", function () {
|
||||||
var rowProvider;
|
var rowProvider;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,9 @@ describe("Zotero.Search", function () {
|
||||||
matches = await s.search();
|
matches = await s.search();
|
||||||
// Result should be the same
|
// Result should be the same
|
||||||
assert.sameMembers(matches, [itemOne.id, itemTwo.id]);
|
assert.sameMembers(matches, [itemOne.id, itemTwo.id]);
|
||||||
|
|
||||||
|
await itemOne.eraseTx();
|
||||||
|
await itemTwo.eraseTx();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,30 +117,22 @@ describe("ZoteroPane", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#newSearch()", function () {
|
describe("Advanced Search", function () {
|
||||||
it("should create a saved search", async function () {
|
it("should create a saved search", async function () {
|
||||||
var promise = waitForDialog(
|
await selectLibrary(win);
|
||||||
// TODO: Test changing a condition
|
await zp.toggleAdvancedSearchState('open');
|
||||||
function (dialog) {},
|
var deck = doc.getElementById('zotero-advanced-search-pane-deck');
|
||||||
'accept',
|
|
||||||
'chrome://zotero/content/searchDialog.xhtml'
|
|
||||||
);
|
|
||||||
var id = await zp.newSearch();
|
|
||||||
await promise;
|
|
||||||
var search = Zotero.Searches.get(id);
|
|
||||||
assert.ok(search);
|
|
||||||
assert.isTrue(search.name.startsWith(Zotero.getString('pane.collections.untitled')));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle clicking Cancel in the search window", async function () {
|
var searchIDs = (await Zotero.Searches.getAll(userLibraryID)).map(s => s.id);
|
||||||
var promise = waitForDialog(
|
await deck.pane.save();
|
||||||
function (dialog) {},
|
|
||||||
'cancel',
|
var newSearches = (await Zotero.Searches.getAll(userLibraryID))
|
||||||
'chrome://zotero/content/searchDialog.xhtml'
|
.filter(s => !searchIDs.includes(s.id));
|
||||||
);
|
assert.lengthOf(newSearches, 1);
|
||||||
var id = await zp.newSearch();
|
assert.isTrue(newSearches[0].name.startsWith(Zotero.getString('pane.collections.untitled')));
|
||||||
await promise;
|
assert.equal(deck.state, 'closed');
|
||||||
assert.isFalse(id);
|
|
||||||
|
await newSearches[0].eraseTx();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1007,19 +999,26 @@ describe("ZoteroPane", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#editSelectedCollection()", function () {
|
describe("#editSelectedCollection()", function () {
|
||||||
|
async function editSearchAddCondition(search) {
|
||||||
|
await select(win, search);
|
||||||
|
await zp.editSelectedCollection();
|
||||||
|
|
||||||
|
var deck = doc.getElementById('zotero-advanced-search-pane-deck');
|
||||||
|
assert.equal(deck.state, 'open');
|
||||||
|
assert.equal(deck.selectedSearchType, 'saved');
|
||||||
|
|
||||||
|
var pane = deck.pane;
|
||||||
|
var searchBox = pane.querySelector('zoterosearch');
|
||||||
|
var c = searchBox.search.getCondition(
|
||||||
|
searchBox.search.addCondition("title", "contains", "foo")
|
||||||
|
);
|
||||||
|
searchBox.addCondition(c);
|
||||||
|
await pane.save();
|
||||||
|
}
|
||||||
|
|
||||||
it("should edit a saved search", async function () {
|
it("should edit a saved search", async function () {
|
||||||
var search = await createDataObject('search');
|
var search = await createDataObject('search');
|
||||||
await select(win, search);
|
await editSearchAddCondition(search);
|
||||||
var promise = waitForWindow('chrome://zotero/content/searchDialog.xhtml', function (win) {
|
|
||||||
let searchBox = win.document.getElementById('search-box');
|
|
||||||
var c = searchBox.search.getCondition(
|
|
||||||
searchBox.search.addCondition("title", "contains", "foo")
|
|
||||||
);
|
|
||||||
searchBox.addCondition(c);
|
|
||||||
win.document.querySelector('dialog').acceptDialog();
|
|
||||||
});
|
|
||||||
await zp.editSelectedCollection();
|
|
||||||
await promise;
|
|
||||||
var conditions = search.getConditions();
|
var conditions = search.getConditions();
|
||||||
assert.lengthOf(Object.keys(conditions), 3);
|
assert.lengthOf(Object.keys(conditions), 3);
|
||||||
});
|
});
|
||||||
|
|
@ -1027,17 +1026,7 @@ describe("ZoteroPane", function () {
|
||||||
it("should edit a saved search in a group", async function () {
|
it("should edit a saved search in a group", async function () {
|
||||||
var group = await getGroup();
|
var group = await getGroup();
|
||||||
var search = await createDataObject('search', { libraryID: group.libraryID });
|
var search = await createDataObject('search', { libraryID: group.libraryID });
|
||||||
await select(win, search);
|
await editSearchAddCondition(search);
|
||||||
var promise = waitForWindow('chrome://zotero/content/searchDialog.xhtml', function (win) {
|
|
||||||
let searchBox = win.document.getElementById('search-box');
|
|
||||||
var c = searchBox.search.getCondition(
|
|
||||||
searchBox.search.addCondition("title", "contains", "foo")
|
|
||||||
);
|
|
||||||
searchBox.addCondition(c);
|
|
||||||
win.document.querySelector('dialog').acceptDialog();
|
|
||||||
});
|
|
||||||
await zp.editSelectedCollection();
|
|
||||||
await promise;
|
|
||||||
var conditions = search.getConditions();
|
var conditions = search.getConditions();
|
||||||
assert.lengthOf(Object.keys(conditions), 3);
|
assert.lengthOf(Object.keys(conditions), 3);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue