Sort descendant collections alphabetically in advanced search window

https://forums.zotero.org/discussion/50679/
This commit is contained in:
Dan Stillman 2015-07-21 00:44:38 -04:00
parent 984789d304
commit a452af6c3a
3 changed files with 149 additions and 129 deletions

View file

@ -405,130 +405,130 @@
]]> ]]>
</constructor> </constructor>
<method name="onConditionSelected"> <method name="onConditionSelected">
<body> <body><![CDATA[
<![CDATA[ return Zotero.spawn(function* () {
// Skip if correct condition already selected // Skip if correct condition already selected
if (this.id('conditionsmenu').value==this.selectedCondition){ if (this.id('conditionsmenu').value==this.selectedCondition){
return; return;
}
var conditionsMenu = this.id('conditionsmenu');
var operatorsList = this.id('operatorsmenu');
this.selectedCondition = conditionsMenu.value;
this.selectedOperator = operatorsList.value;
var condition = Zotero.SearchConditions.get(conditionsMenu.value);
var operators = condition['operators'];
// Display appropriate operators for condition
var selectThis;
for(var i = 0, len = operatorsList.firstChild.childNodes.length; i < len; i++)
{
var val = operatorsList.firstChild.childNodes[i].getAttribute('value');
var hidden = !operators[val];
operatorsList.firstChild.childNodes[i].setAttribute('hidden', hidden);
if (!hidden && (selectThis == null || this.selectedOperator == val))
{
selectThis = i;
} }
} var conditionsMenu = this.id('conditionsmenu');
operatorsList.selectedIndex = selectThis; var operatorsList = this.id('operatorsmenu');
// Generate drop-down menu instead of textbox for certain conditions
switch (conditionsMenu.value){
case 'collection':
var rows = [];
var libraryID = this.parent.search.libraryID;
var cols = Zotero.getCollections(false, true, libraryID); // TODO: Replace with Zotero.Collections.getByLibrary()
for (var i in cols) {
// Indent subcollections
var indent = '';
if (cols[i].level) {
for (var j=1; j<cols[i].level; j++) {
indent += ' ';
}
indent += '- ';
}
rows.push([indent + cols[i].name, 'C' + cols[i].key]);
}
this.createValueMenu(rows);
break;
case 'savedSearch': this.selectedCondition = conditionsMenu.value;
var rows = []; this.selectedOperator = operatorsList.value;
var libraryID = this.parent.search.libraryID;
var searches = Zotero.Searches.getAll(libraryID);
for (var i in searches) {
if (searches[i].id != this.parent.search.id) {
rows.push([searches[i].name, 'S' + searches[i].key]);
}
}
this.createValueMenu(rows);
break;
case 'itemType': var condition = Zotero.SearchConditions.get(conditionsMenu.value);
var t = Zotero.ItemTypes.getTypes(); var operators = condition['operators'];
// Sort by localized name
var types = [];
for (var i=0; i<t.length; i++) {
types.push({
id: t[i].id,
name: t[i].name,
localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
});
}
var collation = Zotero.getLocaleCollation();
types.sort(function(a, b) {
return collation.compareString(1, a.localized, b.localized);
});
for (var i in types) {
types[i][0] = types[i].localized;
types[i][1] = types[i].name;
delete types[i]['name'];
delete types[i]['id'];
}
this.createValueMenu(types);
break;
case 'fileTypeID': // Display appropriate operators for condition
var types = Zotero.FileTypes.getTypes(); var selectThis;
for (var i in types) { for(var i = 0, len = operatorsList.firstChild.childNodes.length; i < len; i++)
types[i][0] = Zotero.getString('fileTypes.' + types[i]['name']); {
types[i][1] = types[i]['id']; var val = operatorsList.firstChild.childNodes[i].getAttribute('value');
delete types[i]['name']; var hidden = !operators[val];
delete types[i]['id']; operatorsList.firstChild.childNodes[i].setAttribute('hidden', hidden);
} if (!hidden && (selectThis == null || this.selectedOperator == val))
this.createValueMenu(types);
break;
default:
if (operatorsList.value=='isInTheLast')
{ {
this.id('value-date-age').value = this.value; selectThis = i;
} }
}
// Textbox operatorsList.selectedIndex = selectThis;
else {
// If switching from menu to textbox, clear value // Generate drop-down menu instead of textbox for certain conditions
if (this.id('valuefield').hidden){ switch (conditionsMenu.value){
this.id('valuefield').value = ''; case 'collection':
var rows = [];
var libraryID = this.parent.search.libraryID;
var cols = yield Zotero.Collections.getByLibrary(libraryID, true);
for (var i in cols) {
// Indent subcollections
var indent = '';
if (cols[i].level) {
for (var j=1; j<cols[i].level; j++) {
indent += ' ';
}
indent += '- ';
}
rows.push([indent + cols[i].name, 'C' + cols[i].key]);
} }
// If switching between textbox conditions, get loaded value for new one this.createValueMenu(rows);
else { break;
this.id('valuefield').value = this.value;
case 'savedSearch':
var rows = [];
var libraryID = this.parent.search.libraryID;
var searches = Zotero.Searches.getAll(libraryID);
for (var i in searches) {
if (searches[i].id != this.parent.search.id) {
rows.push([searches[i].name, 'S' + searches[i].key]);
}
}
this.createValueMenu(rows);
break;
case 'itemType':
var t = Zotero.ItemTypes.getTypes();
// Sort by localized name
var types = [];
for (var i=0; i<t.length; i++) {
types.push({
id: t[i].id,
name: t[i].name,
localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
});
}
var collation = Zotero.getLocaleCollation();
types.sort(function(a, b) {
return collation.compareString(1, a.localized, b.localized);
});
for (var i in types) {
types[i][0] = types[i].localized;
types[i][1] = types[i].name;
delete types[i]['name'];
delete types[i]['id'];
}
this.createValueMenu(types);
break;
case 'fileTypeID':
var types = Zotero.FileTypes.getTypes();
for (var i in types) {
types[i][0] = Zotero.getString('fileTypes.' + types[i]['name']);
types[i][1] = types[i]['id'];
delete types[i]['name'];
delete types[i]['id'];
}
this.createValueMenu(types);
break;
default:
if (operatorsList.value=='isInTheLast')
{
this.id('value-date-age').value = this.value;
} }
// Update field drop-down if applicable // Textbox
this.id('valuefield').update(conditionsMenu.value, this.mode); else {
} // If switching from menu to textbox, clear value
} if (this.id('valuefield').hidden){
this.id('valuefield').value = '';
this.onOperatorSelected(); }
]]> // If switching between textbox conditions, get loaded value for new one
</body> else {
this.id('valuefield').value = this.value;
}
// Update field drop-down if applicable
this.id('valuefield').update(conditionsMenu.value, this.mode);
}
}
this.onOperatorSelected();
}.bind(this));
]]></body>
</method> </method>
<method name="onOperatorSelected"> <method name="onOperatorSelected">
<body> <body>
@ -597,8 +597,8 @@
<method name="initWithParentAndCondition"> <method name="initWithParentAndCondition">
<parameter name="parent"/> <parameter name="parent"/>
<parameter name="condition"/> <parameter name="condition"/>
<body> <body><![CDATA[
<![CDATA[ return Zotero.spawn(function* () {
this.parent = parent; this.parent = parent;
this.conditionID = condition['id']; this.conditionID = condition['id'];
@ -638,11 +638,11 @@
this.dontupdate = false; this.dontupdate = false;
} }
this.onConditionSelected(); yield this.onConditionSelected();
this.id('conditionsmenu').focus(); this.id('conditionsmenu').focus();
]]> }.bind(this));
</body> ]]></body>
</method> </method>
<!-- Gets the value from the field and updates the associated search condition --> <!-- Gets the value from the field and updates the associated search condition -->
<method name="updateSearch"> <method name="updateSearch">

View file

@ -690,17 +690,21 @@ Zotero.Collection.prototype.getChildren = Zotero.Promise.coroutine(function* (re
// 0 == collection // 0 == collection
// 1 == item // 1 == item
var sql = 'SELECT collectionID AS id, ' var sql = 'SELECT collectionID AS id, collectionName AS name, '
+ "0 AS type, collectionName AS collectionName, key " + "0 AS type, collectionName AS collectionName, key "
+ 'FROM collections WHERE parentCollectionID=?1'; + 'FROM collections WHERE parentCollectionID=?1';
if (!type || type == 'item') { if (!type || type == 'item') {
sql += ' UNION SELECT itemID AS id, 1 AS type, NULL AS collectionName, key ' sql += ' UNION SELECT itemID AS id, NULL AS name, 1 AS type, NULL AS collectionName, key '
+ 'FROM collectionItems JOIN items USING (itemID) WHERE collectionID=?1'; + 'FROM collectionItems JOIN items USING (itemID) WHERE collectionID=?1';
if (!includeDeletedItems) { if (!includeDeletedItems) {
sql += " AND itemID NOT IN (SELECT itemID FROM deletedItems)"; sql += " AND itemID NOT IN (SELECT itemID FROM deletedItems)";
} }
} }
var children = yield Zotero.DB.queryAsync(sql, this.id); var children = yield Zotero.DB.queryAsync(sql, this.id);
children.sort(function (a, b) {
if (a.name === null || b.name === null) return 0;
return Zotero.localeCompare(a.name, b.name)
});
for(var i=0, len=children.length; i<len; i++) { for(var i=0, len=children.length; i<len; i++) {
// This seems to not work without parseInt() even though // This seems to not work without parseInt() even though

View file

@ -13,13 +13,29 @@ describe("Zotero.Collections", function () {
}) })
it("should get all collections in a library in recursive mode", function* () { it("should get all collections in a library in recursive mode", function* () {
var col1 = yield createDataObject('collection'); yield createDataObject('collection', { libraryID: (yield getGroup()).libraryID });
var col2 = yield createDataObject('collection');
var col3 = yield createDataObject('collection', { parentID: col2.id }); var libraryID = Zotero.Libraries.userLibraryID;
var cols = yield Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID, true); var col1 = yield createDataObject('collection', { name: "C" });
assert.isAbove(cols.length, 2); var col2 = yield createDataObject('collection', { name: "A" });
assert.includeMembers(cols.map(col => col.id), [col1.id, col2.id, col3.id]); var col3 = yield createDataObject('collection', { name: "D", parentID: col2.id });
assert.ok(cols.every(col => col.libraryID == Zotero.Libraries.userLibraryID)); var col4 = yield createDataObject('collection', { name: "B", parentID: col2.id });
var col5 = yield createDataObject('collection', { name: "E", parentID: col2.id });
var col6 = yield createDataObject('collection', { name: "G", parentID: col3.id });
var col7 = yield createDataObject('collection', { name: "F", parentID: col3.id });
var cols = yield Zotero.Collections.getByLibrary(libraryID, true);
assert.isAbove(cols.length, 6);
var ids = cols.map(col => col.id);
assert.includeMembers(
ids, [col1.id, col2.id, col3.id, col4.id, col5.id, col6.id, col7.id]
);
assert.isBelow(ids.indexOf(col2.id), ids.indexOf(col4.id), "A before child B");
assert.isBelow(ids.indexOf(col4.id), ids.indexOf(col3.id), "B before D");
assert.isBelow(ids.indexOf(col3.id), ids.indexOf(col7.id), "D before child F");
assert.isBelow(ids.indexOf(col7.id), ids.indexOf(col6.id), "F before G");
assert.isBelow(ids.indexOf(col6.id), ids.indexOf(col5.id), "G before D sibling E");
assert.isBelow(ids.indexOf(col5.id), ids.indexOf(col1.id), "E before A sibling C");
assert.ok(cols.every(col => col.libraryID == libraryID));
}) })
}) })