diff --git a/test/tests/utilitiesTest.js b/test/tests/utilitiesTest.js index 17ba91f..4033f7f 100644 --- a/test/tests/utilitiesTest.js +++ b/test/tests/utilitiesTest.js @@ -361,4 +361,179 @@ describe("Zotero.Utilities", function() { assert.equal(newHTML, html); }); }); + + describe("getAutomaticAttachmentPreferences()", function () { + describe("Zotero.Prefs unavailable", function () { + it("should return defaults", function () { + let { enabledTypes, typeOrder } = Zotero.Utilities.getAutomaticAttachmentPreferences(); + assert.deepEqual(Array.from(enabledTypes), ['pdf', 'epub', 'html']); + assert.deepEqual(typeOrder, ['pdf', 'epub', 'html']); + }); + }); + + describe("Zotero.Prefs available", function () { + let automaticAttachmentTypes = new Set(['epub']); + let automaticAttachmentTypesOrder = ['html', 'epub', 'pdf']; + + before(function () { + Zotero.Prefs = { + get: function (pref) { + if (pref === 'automaticAttachmentTypes') { + return Array.from(automaticAttachmentTypes).join(','); + } + else if (pref === 'automaticAttachmentTypes.order') { + return automaticAttachmentTypesOrder.join(','); + } + return null; + } + }; + }); + + after(function () { + delete Zotero.Prefs; + }); + + it("should return pref values", function () { + let { enabledTypes, typeOrder } = Zotero.Utilities.getAutomaticAttachmentPreferences(); + assert.deepEqual(Array.from(enabledTypes), ['epub']); + assert.deepEqual(typeOrder, ['html', 'epub', 'pdf']); + }); + }); + }); + + describe("shouldSaveAttachmentOfType()", function () { + let automaticAttachmentTypes = new Set(['epub']); + let automaticAttachmentTypesOrder = ['html', 'epub', 'pdf']; + + before(function () { + Zotero.Prefs = { + get: function (pref) { + if (pref === 'automaticAttachmentTypes') { + return Array.from(automaticAttachmentTypes).join(','); + } + else if (pref === 'automaticAttachmentTypes.order') { + return automaticAttachmentTypesOrder.join(','); + } + return null; + } + }; + }); + + after(function () { + delete Zotero.Prefs; + }); + + it("should use pref values", function () { + assert.isTrue(Zotero.Utilities.shouldSaveAttachmentOfType('epub')); + assert.isFalse(Zotero.Utilities.shouldSaveAttachmentOfType('pdf')); + assert.isFalse(Zotero.Utilities.shouldSaveAttachmentOfType('html')); + }); + + it("should throw on an unknown type", function () { + assert.throws(() => Zotero.Utilities.shouldSaveAttachmentOfType('nonexistent--type')); + }); + }); + + describe("filterAttachmentsToSave()", function () { + let automaticAttachmentTypes = new Set(['html', 'epub']); + let automaticAttachmentTypesOrder = ['html', 'epub', 'pdf']; + + before(function () { + Zotero.Prefs = { + get: function (pref) { + if (pref === 'automaticAttachmentTypes') { + return Array.from(automaticAttachmentTypes).join(','); + } + else if (pref === 'automaticAttachmentTypes.order') { + return automaticAttachmentTypesOrder.join(','); + } + return null; + } + }; + }); + + after(function () { + delete Zotero.Prefs; + }); + + it("should keep non-attachments", function () { + assert.deepEqual( + Zotero.Utilities.filterAttachmentsToSave([ + { itemType: 'book', title: 'Other stuff' }, + { itemType: 'attachment', mimeType: 'application/pdf' } + ]), + [ + { itemType: 'book', title: 'Other stuff' } + ] + ); + }); + + it("should keep attachments of unknown types", function () { + assert.deepEqual( + Zotero.Utilities.filterAttachmentsToSave([ + { itemType: 'attachment', mimeType: 'application/pdf' }, + { mimeType: 'text/x-unknown' } + ]), + [ + { mimeType: 'text/x-unknown' } + ] + ); + }); + + it("should keep only attachments of the first matching type", function () { + assert.deepEqual( + Zotero.Utilities.filterAttachmentsToSave([ + { mimeType: 'application/pdf' }, + { mimeType: 'text/html' }, + { mimeType: 'text/html' }, + { mimeType: 'application/epub+zip' }, + { mimeType: 'application/epub+zip' } + ]), + [ + { mimeType: 'text/html' }, + { mimeType: 'text/html' } + ] + ); + }); + + it("should recognize multiple HTML MIME types and keep unknown type", function () { + assert.deepEqual( + Zotero.Utilities.filterAttachmentsToSave([ + { mimeType: 'text/html' }, + { mimeType: 'text/html' }, + { mimeType: 'application/xhtml+xml' }, + { mimeType: 'application/epub+zip' }, + { mimeType: 'application/octet-stream' } + ]), + [ + { mimeType: 'text/html' }, + { mimeType: 'text/html' }, + { mimeType: 'application/xhtml+xml' }, + { mimeType: 'application/octet-stream' } + ] + ); + }); + + it("should recognize attachment with 'document' property as HTML", function () { + assert.deepEqual( + Zotero.Utilities.filterAttachmentsToSave([ + { mimeType: 'text/html' }, + { document: {} }, + { mimeType: 'application/epub+zip' } + ]), + [ + { mimeType: 'text/html' }, + { document: {} } + ] + ); + }); + + it("should return the exact same objects", function () { + let obj = { mimeType: 'text/html' }; + assert.equal( + Zotero.Utilities.filterAttachmentsToSave([obj])[0], + obj + ); + }); + }); }); diff --git a/utilities.js b/utilities.js index 6808118..cd98cf9 100644 --- a/utilities.js +++ b/utilities.js @@ -1830,6 +1830,81 @@ var Utilities = { } return root.innerHTML; }, + + getAutomaticAttachmentPreferences() { + let enabledTypes = Zotero.Prefs && Zotero.Prefs.get('automaticAttachmentTypes').split(','); + let typeOrder = Zotero.Prefs && Zotero.Prefs.get('automaticAttachmentTypes.order').split(','); + + // If either pref isn't available (not just empty), use defaults + enabledTypes = new Set(enabledTypes || ['pdf', 'epub', 'html']); + typeOrder = typeOrder || ['pdf', 'epub', 'html']; + + return { enabledTypes, typeOrder }; + }, + + shouldSaveAttachmentOfType(type) { + if (!['pdf', 'epub', 'html'].includes(type)) { + throw new Error('Unknown type: ' + type); + } + + let { enabledTypes } = this.getAutomaticAttachmentPreferences(); + return enabledTypes.has(type); + }, + + /** + * Given an array of items in translator JSON format, return the items that + * should be saved according to the user's preferences. Non-attachment items + * are always returned. + * + * @typedef {{ mimeType: string, url?: string, document?: Document, itemType?: string }} TranslatorItem + * @param {TranslatorItem[]} itemsJSON + * @returns {TranslatorItem[]} + */ + filterAttachmentsToSave(itemsJSON) { + let { enabledTypes, typeOrder } = this.getAutomaticAttachmentPreferences(); + + let attachmentTypes = itemsJSON.map((attachment) => { + if (typeof attachment.itemType === 'string' && attachment.itemType !== 'attachment') { + return null; + } + if (attachment.mimeType === 'application/pdf') { + return 'pdf'; + } + if (attachment.mimeType === 'application/epub+zip') { + return 'epub'; + } + if (attachment.document + || (Zotero.MIME + ? Zotero.MIME.isWebPageType(attachment.mimeType) + : ['text/html', 'application/xhtml+xml'].includes(attachment.mimeType)) + ) { + return 'html'; + } + return null; + }); + + // We want to keep everything that isn't an identifiable file attachment + let isNotKnownTypeFileAttachment = (attachment, i) => { + return attachmentTypes[i] === null + || attachmentTypes[i] === 'html' && attachment.snapshot === false; + }; + + for (let type of typeOrder) { + if (!enabledTypes.has(type)) { + continue; + } + if (attachmentTypes.includes(type)) { + return itemsJSON.filter((attachment, i) => { + if (isNotKnownTypeFileAttachment(attachment, i)) { + return true; + } + return attachmentTypes[i] === type || attachmentTypes[i] === null; + }); + } + } + + return itemsJSON.filter(isNotKnownTypeFileAttachment); + }, // /** // * Provides unicode support and other additional features for regular expressions