diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml index 62eeb6739..bea4490bd 100644 --- a/chrome/content/zotero/bindings/itembox.xml +++ b/chrome/content/zotero/bindings/itembox.xml @@ -1416,11 +1416,17 @@ || fieldName == 'creator') { t.setAttribute('type', 'autocomplete'); t.setAttribute('autocompletesearch', 'zotero'); - var suffix = itemID ? itemID : ''; - if (field=='creator') { - suffix = elem.getAttribute('fieldMode') + '-' + suffix; - } - t.setAttribute('autocompletesearchparam', fieldName + '/' + suffix); + let params = { + fieldName: fieldName, + libraryID: this.item.libraryID + }; + if (field == 'creator') { + params.fieldMode = parseInt(elem.getAttribute('fieldMode')); + params.itemID = itemID ? itemID : ''; + }; + t.setAttribute( + 'autocompletesearchparam', JSON.stringify(params) + ); t.setAttribute('ontextentered', 'document.getBindingParent(this).handleCreatorAutoCompleteSelect(this)'); } diff --git a/chrome/content/zotero/bindings/tagsbox.xml b/chrome/content/zotero/bindings/tagsbox.xml index 9ef7ab6ab..bd4bf15c6 100644 --- a/chrome/content/zotero/bindings/tagsbox.xml +++ b/chrome/content/zotero/bindings/tagsbox.xml @@ -437,8 +437,14 @@ else { t.setAttribute('type', 'autocomplete'); t.setAttribute('autocompletesearch', 'zotero'); - var suffix = itemID ? itemID : ''; - t.setAttribute('autocompletesearchparam', fieldName + '/' + suffix); + let params = { + fieldName: fieldName, + libraryID: this.item.libraryID + }; + params.itemID = itemID ? itemID : ''; + t.setAttribute( + 'autocompletesearchparam', JSON.stringify(params) + ); } var box = elem.parentNode; diff --git a/chrome/content/zotero/bindings/zoterosearch.xml b/chrome/content/zotero/bindings/zoterosearch.xml index f08d5effe..477b29d0b 100644 --- a/chrome/content/zotero/bindings/zoterosearch.xml +++ b/chrome/content/zotero/bindings/zoterosearch.xml @@ -813,22 +813,20 @@ textbox.setAttribute('autocompletesearch', 'zotero'); textbox.setAttribute('timeout', '250'); - if (condition=='creator') - { - // 2 searches both single- and double-field creators - var autocompleteCondition = condition + '/2' + var autocompleteParams = { + fieldName: condition + }; + if (condition == 'creator') { + autocompleteParams.fieldMode = 2; } - else - { - var autocompleteCondition = condition; - } - - textbox.setAttribute('autocompletesearchparam', autocompleteCondition); + textbox.setAttribute( + 'autocompletesearchparam', + JSON.stringify(autocompleteParams) + ); } } - if (!autocompleteCondition) - { + if (!autocompleteParams) { var textbox = document.getAnonymousNodes(this)[0]; textbox.removeAttribute('type'); } diff --git a/components/zotero-autocomplete.js b/components/zotero-autocomplete.js index f1b11eaba..2e793879d 100644 --- a/components/zotero-autocomplete.js +++ b/components/zotero-autocomplete.js @@ -44,7 +44,7 @@ function ZoteroAutoComplete() { } -ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, previousResult, listener) { +ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParams, previousResult, listener) { var result = Cc["@mozilla.org/autocomplete/simple-result;1"] .createInstance(Ci.nsIAutoCompleteSimpleResult); result.setSearchString(searchString); @@ -54,35 +54,35 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p this._listener = listener; this._cancelled = false; - this._zotero.debug("Starting autocomplete search of type '" - + searchParam + "'" + " with string '" + searchString + "'"); + this._zotero.debug("Starting autocomplete search with data '" + + searchParams + "'" + " and string '" + searchString + "'"); + + searchParams = JSON.parse(searchParams); + if (!searchParams) { + throw new Error("Invalid JSON passed to autocomplete"); + } + var [fieldName, , subField] = searchParams.fieldName.split("-"); this.stopSearch(); var self = this; var statement; - // Allow extra parameters to be passed in - var pos = searchParam.indexOf('/'); - if (pos!=-1){ - var extra = searchParam.substr(pos + 1); - var searchParam = searchParam.substr(0, pos); - } - - var searchParts = searchParam.split('-'); - searchParam = searchParts[0]; - - switch (searchParam) { + switch (fieldName) { case '': break; case 'tag': var sql = "SELECT DISTINCT name AS val, NULL AS comment FROM tags WHERE name LIKE ?"; var sqlParams = [searchString + '%']; - if (extra){ + if (typeof searchParams.libraryID != 'undefined') { + sql += " AND libraryID=?"; + sqlParams.push(searchParams.libraryID); + } + if (searchParams.itemID) { sql += " AND name NOT IN (SELECT name FROM tags WHERE tagID IN (" + "SELECT tagID FROM itemTags WHERE itemID = ?))"; - sqlParams.push(extra); + sqlParams.push(searchParams.itemID); } statement = this._zotero.DB.getStatement(sql, sqlParams); @@ -103,22 +103,24 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p // 0 == search two-field creators // 1 == search single-field creators // 2 == search both - var [fieldMode, itemID] = extra.split('-'); - - if (fieldMode==2) - { + if (searchParams.fieldMode == 2) { var sql = "SELECT DISTINCT CASE fieldMode WHEN 1 THEN lastName " + "WHEN 0 THEN firstName || ' ' || lastName END AS val, NULL AS comment " + "FROM creators NATURAL JOIN creatorData WHERE CASE fieldMode " + "WHEN 1 THEN lastName " + "WHEN 0 THEN firstName || ' ' || lastName END " - + "LIKE ? ORDER BY val"; - var sqlParams = searchString + '%'; + + "LIKE ? "; + var sqlParams = [searchString + '%']; + if (typeof searchParams.libraryID != 'undefined') { + sql += " AND libraryID=?"; + sqlParams.push(searchParams.libraryID); + } + sql += "ORDER BY val"; } else { var sql = "SELECT DISTINCT "; - if (fieldMode==1){ + if (searchParams.fieldMode == 1) { sql += "lastName AS val, creatorID || '-1' AS comment"; } // Retrieve the matches in the specified field @@ -138,22 +140,35 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p } var fromSQL = " FROM creators NATURAL JOIN creatorData " - + "WHERE " + searchParts[2] + " LIKE ?1 " + "AND fieldMode=?2"; - var sqlParams = [searchString + '%', - fieldMode ? parseInt(fieldMode) : 0]; - if (itemID){ + + "WHERE " + subField + " LIKE ?1 " + "AND fieldMode=?2"; + var sqlParams = [ + searchString + '%', + searchParams.fieldMode ? searchParams.fieldMode : 0 + ]; + if (searchParams.itemID) { fromSQL += " AND creatorID NOT IN (SELECT creatorID FROM " + "itemCreators WHERE itemID=?3)"; - sqlParams.push(itemID); + sqlParams.push(searchParams.itemID); + } + if (typeof searchParams.libraryID != 'undefined') { + if (searchParams.libraryID) { + fromSQL += " AND libraryID=?4"; + sqlParams.push(searchParams.libraryID); + } + // The db query code doesn't properly replace numbered + // parameters with "IS NULL" + else { + fromSQL += " AND libraryID IS NULL"; + } } sql += fromSQL; // If double-field mode, include matches for just this field // as well (i.e. "Shakespeare"), and group to collapse repeats - if (fieldMode!=1){ + if (searchParams.fieldMode != 1) { sql = "SELECT * FROM (" + sql + " UNION SELECT DISTINCT " - + searchParts[2] + " AS val, creatorID || '-1' AS comment" + + subField + " AS val, creatorID || '-1' AS comment" + fromSQL + ") GROUP BY val"; } @@ -165,8 +180,8 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p case 'dateModified': case 'dateAdded': - var sql = "SELECT DISTINCT DATE(" + searchParam + ", 'localtime') AS val, NULL AS comment FROM items " - + "WHERE " + searchParam + " LIKE ? ORDER BY " + searchParam; + var sql = "SELECT DISTINCT DATE(" + fieldName + ", 'localtime') AS val, NULL AS comment FROM items " + + "WHERE " + fieldName + " LIKE ? ORDER BY " + fieldName; var sqlParams = [searchString + '%']; statement = this._zotero.DB.getStatement(sql, sqlParams); break; @@ -181,9 +196,9 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p break; default: - var fieldID = this._zotero.ItemFields.getID(searchParam); + var fieldID = this._zotero.ItemFields.getID(fieldName); if (!fieldID) { - this._zotero.debug("'" + searchParam + "' is not a valid autocomplete scope", 1); + this._zotero.debug("'" + fieldName + "' is not a valid autocomplete scope", 1); this.updateResults([], false, Ci.nsIAutoCompleteResult.RESULT_IGNORED); return; } @@ -191,7 +206,7 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p // We don't use date autocomplete anywhere, but if we're not // disallowing it altogether, we should at least do it right and // use the user part of the multipart field - var valueField = searchParam=='date' ? 'SUBSTR(value, 12, 100)' : 'value'; + var valueField = fieldName == 'date' ? 'SUBSTR(value, 12, 100)' : 'value'; var sql = "SELECT DISTINCT " + valueField + " AS val, NULL AS comment " + "FROM itemData NATURAL JOIN itemDataValues " @@ -199,10 +214,10 @@ ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam, p + " LIKE ?2 " var sqlParams = [fieldID, searchString + '%']; - if (extra){ + if (searchParams.itemID) { sql += "AND value NOT IN (SELECT value FROM itemData " + "NATURAL JOIN itemDataValues WHERE fieldID=?1 AND itemID=?3) "; - sqlParams.push(extra); + sqlParams.push(searchParams.itemID); } sql += "ORDER BY value"; statement = this._zotero.DB.getStatement(sql, sqlParams);