From db0fa3c33e9db6a793aef04d9521fea848c31e5c Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Wed, 6 Aug 2014 17:38:05 -0400 Subject: [PATCH] Async DB megacommit Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details. WARNING: This includes backwards-incompatible schema changes. An incomplete list of other changes: - Schema overhaul - Replace main tables with new versions with updated schema - Enable real foreign key support and remove previous triggers - Don't use NULLs for local libraryID, which broke the UNIQUE index preventing object key duplication. All code (Zotero and third-party) using NULL for the local library will need to be updated to use 0 instead (already done for Zotero code) - Add 'compatibility' DB version that can be incremented manually to break DB compatibility with previous versions. 'userdata' upgrades will no longer automatically break compatibility. - Demote creators and tags from first-class objects to item properties - New API syncing properties - 'synced'/'version' properties to data objects - 'etag' to groups - 'version' to libraries - Create Zotero.DataObject that other objects inherit from - Consolidate data object loading into Zotero.DataObjects - Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data) - Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey() - New function Zotero.serial(fn), to wrap an async function such that all calls are run serially - New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func) - Add tag selector loading message - Various API and name changes, since everything was breaking anyway Known broken things: - Syncing (will be completely rewritten for API syncing) - Translation architecture (needs promise-based rewrite) - Duplicates view - DB integrity check (from schema changes) - Dragging (may be difficult to fix) Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways. --- chrome/content/zotero/advancedSearch.js | 49 +- .../content/zotero/bindings/attachmentbox.xml | 426 +- chrome/content/zotero/bindings/itembox.xml | 400 +- chrome/content/zotero/bindings/noteeditor.xml | 264 +- chrome/content/zotero/bindings/relatedbox.xml | 54 +- .../zotero/bindings/styled-textbox.xml | 2 + chrome/content/zotero/bindings/tagsbox.xml | 594 +- .../content/zotero/bindings/tagselector.xml | 507 +- .../content/zotero/bindings/zoterosearch.xml | 3 +- chrome/content/zotero/browser.js | 11 +- chrome/content/zotero/downloadOverlay.js | 4 +- chrome/content/zotero/duplicatesMerge.js | 2 +- chrome/content/zotero/fileInterface.js | 2 +- .../zotero/integration/addCitationDialog.js | 4 +- chrome/content/zotero/itemPane.js | 19 +- chrome/content/zotero/locateMenu.js | 107 +- chrome/content/zotero/note.js | 63 +- chrome/content/zotero/overlay.js | 9 +- .../zotero/preferences/preferences_export.js | 54 +- .../zotero/preferences/preferences_search.js | 26 +- chrome/content/zotero/recognizePDF.js | 16 +- chrome/content/zotero/selectItemsDialog.js | 2 +- .../content/zotero/standalone/standalone.js | 4 +- chrome/content/zotero/tagColorChooser.js | 70 +- chrome/content/zotero/tagColorChooser.xul | 2 +- chrome/content/zotero/xpcom/attachments.js | 1314 ++-- .../zotero/xpcom/collectionTreeView.js | 1326 +++-- .../content/zotero/xpcom/data/cachedTypes.js | 2 + .../content/zotero/xpcom/data/collection.js | 1350 ++--- .../content/zotero/xpcom/data/collections.js | 182 +- chrome/content/zotero/xpcom/data/creator.js | 560 -- chrome/content/zotero/xpcom/data/creators.js | 416 +- .../content/zotero/xpcom/data/dataObject.js | 394 ++ .../content/zotero/xpcom/data/dataObjects.js | 453 +- chrome/content/zotero/xpcom/data/group.js | 218 +- chrome/content/zotero/xpcom/data/groups.js | 59 +- chrome/content/zotero/xpcom/data/item.js | 5171 ++++++++-------- .../content/zotero/xpcom/data/itemFields.js | 6 +- chrome/content/zotero/xpcom/data/items.js | 771 +-- chrome/content/zotero/xpcom/data/libraries.js | 40 +- chrome/content/zotero/xpcom/data/relation.js | 75 +- chrome/content/zotero/xpcom/data/relations.js | 176 +- chrome/content/zotero/xpcom/data/tags.js | 1104 ++-- chrome/content/zotero/xpcom/db.js | 747 ++- chrome/content/zotero/xpcom/duplicates.js | 48 +- chrome/content/zotero/xpcom/file.js | 102 +- chrome/content/zotero/xpcom/fulltext.js | 680 ++- chrome/content/zotero/xpcom/http.js | 24 +- chrome/content/zotero/xpcom/id.js | 63 +- chrome/content/zotero/xpcom/integration.js | 70 +- chrome/content/zotero/xpcom/itemTreeView.js | 1418 ++--- .../content/zotero/xpcom/libraryTreeView.js | 29 +- chrome/content/zotero/xpcom/mime.js | 30 +- .../content/zotero/xpcom/mimeTypeHandler.js | 4 +- chrome/content/zotero/xpcom/notifier.js | 49 +- chrome/content/zotero/xpcom/quickCopy.js | 7 +- chrome/content/zotero/xpcom/report.js | 2 +- chrome/content/zotero/xpcom/schema.js | 490 +- chrome/content/zotero/xpcom/search.js | 1784 +++--- .../content/zotero/xpcom/server_connector.js | 8 +- chrome/content/zotero/xpcom/storage.js | 81 +- chrome/content/zotero/xpcom/storage/queue.js | 9 +- .../zotero/xpcom/storage/queueManager.js | 2 +- .../content/zotero/xpcom/storage/request.js | 4 +- chrome/content/zotero/xpcom/storage/webdav.js | 40 +- chrome/content/zotero/xpcom/storage/zfs.js | 24 +- chrome/content/zotero/xpcom/style.js | 12 +- chrome/content/zotero/xpcom/syncedSettings.js | 34 +- .../zotero/xpcom/translation/translate.js | 18 +- .../xpcom/translation/translate_item.js | 154 +- .../zotero/xpcom/translation/translator.js | 2 +- .../zotero/xpcom/translation/translators.js | 8 +- chrome/content/zotero/xpcom/utilities.js | 40 +- .../zotero/xpcom/utilities_internal.js | 55 +- chrome/content/zotero/xpcom/zotero.js | 146 +- chrome/content/zotero/zoteroPane.js | 1556 ++--- chrome/content/zotero/zoteroPane.xul | 16 +- chrome/locale/en-US/zotero/zotero.dtd | 1 + chrome/locale/en-US/zotero/zotero.properties | 2 + components/zotero-autocomplete.js | 17 +- components/zotero-protocol-handler.js | 6 +- components/zotero-service.js | 13 +- defaults/preferences/zotero.js | 1 + resource/bluebird.js | 5261 +++++++++++++++++ resource/concurrent-caller.js | 111 +- resource/q.js | 1980 ------- resource/schema/triggers.sql | 1406 +---- resource/schema/userdata.sql | 192 +- 88 files changed, 17286 insertions(+), 15771 deletions(-) delete mode 100644 chrome/content/zotero/xpcom/data/creator.js create mode 100644 chrome/content/zotero/xpcom/data/dataObject.js create mode 100644 resource/bluebird.js delete mode 100644 resource/q.js diff --git a/chrome/content/zotero/advancedSearch.js b/chrome/content/zotero/advancedSearch.js index 4bb5cefe70..7550f9e745 100644 --- a/chrome/content/zotero/advancedSearch.js +++ b/chrome/content/zotero/advancedSearch.js @@ -53,32 +53,24 @@ var ZoteroAdvancedSearch = new function() { _searchBox.updateSearch(); _searchBox.active = true; - // A minimal implementation of Zotero.CollectionTreeView - var itemGroup = { + // A minimal implementation of Zotero.CollectionTreeRow + var collectionTreeRow = { + ref: _searchBox.search, isSearchMode: function() { return true; }, - getItems: function () { - var search = _searchBox.search.clone(); + getItems: Zotero.Promise.coroutine(function* () { + var search = yield _searchBox.search.clone(); // Hack to create a condition for the search's library -- // this logic should really go in the search itself instead of here // and in collectionTreeView.js var conditions = search.getSearchConditions(); if (!conditions.some(function (condition) condition.condition == 'libraryID')) { - let libraryID = _searchBox.search.libraryID; - // TEMP: libraryIDInt - if (libraryID) { - search.addCondition('libraryID', 'is', libraryID); - } - else { - let groups = Zotero.Groups.getAll(); - for (let i=0; i - - 29 ) || firstSpace > 29) { - title.setAttribute('crop', 'end'); - title.setAttribute('value', val); - } - // Create a element, essentially - else { - title.removeAttribute('value'); - title.appendChild(document.createTextNode(val)); - } - - if (this.editable) { - title.className = 'zotero-clicky'; + 29 ) || firstSpace > 29) { + title.setAttribute('crop', 'end'); + title.setAttribute('value', val); + } + // Create a element, essentially + else { + title.removeAttribute('value'); + title.appendChild(document.createTextNode(val)); + } + + if (this.editable) { + title.className = 'zotero-clicky'; + + // For the time being, use a silly little popup + title.addEventListener('click', this.editTitle, false); + } + + var isImportedURL = this.item.attachmentLinkMode == + Zotero.Attachments.LINK_MODE_IMPORTED_URL; + + // Metadata for URL's + if (this.item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL + || isImportedURL) { + + // URL + if (this.displayURL) { + var urlSpec = this.item.getField('url'); + urlField.setAttribute('value', urlSpec); + urlField.setAttribute('hidden', false); + if (this.clickableLink) { + urlField.onclick = function (event) { + ZoteroPane_Local.loadURI(this.value, event) + }; + urlField.className = 'zotero-text-link'; + } + else { + urlField.className = ''; + } + urlField.hidden = false; } else { - urlField.className = ''; + urlField.hidden = true; + } + + // Access date + if (this.displayAccessed) { + this._id("accessed-label").value = Zotero.getString('itemFields.accessDate') + + Zotero.getString('punctuation.colon'); + this._id("accessed").value = Zotero.Date.sqlToDate( + this.item.getField('accessDate'), true + ).toLocaleString(); + accessed.hidden = false; + } + else { + accessed.hidden = true; } - urlField.hidden = false; } + // Metadata for files else { urlField.hidden = true; - } - - // Access date - if (this.displayAccessed) { - this._id("accessed-label").value = Zotero.getString('itemFields.accessDate') - + Zotero.getString('punctuation.colon'); - this._id("accessed").value = Zotero.Date.sqlToDate( - this.item.getField('accessDate'), true - ).toLocaleString(); - accessed.hidden = false; - } - else { accessed.hidden = true; } - } - // Metadata for files - else { - urlField.hidden = true; - accessed.hidden = true; - } - - if (this.item.attachmentLinkMode - != Zotero.Attachments.LINK_MODE_LINKED_URL - && this.displayFileName) { - var fileName = this.item.getFilename(); - if (fileName) { - this._id("fileName-label").value = Zotero.getString('pane.item.attachments.filename') - + Zotero.getString('punctuation.colon'); - this._id("fileName").value = fileName; - fileNameRow.hidden = false; + if (this.item.attachmentLinkMode + != Zotero.Attachments.LINK_MODE_LINKED_URL + && this.displayFileName) { + var fileName = this.item.getFilename(); + + if (fileName) { + this._id("fileName-label").value = Zotero.getString('pane.item.attachments.filename') + + Zotero.getString('punctuation.colon'); + this._id("fileName").value = fileName; + fileNameRow.hidden = false; + } + else { + fileNameRow.hidden = true; + } } else { fileNameRow.hidden = true; } - } - else { - fileNameRow.hidden = true; - } - - // Page count - if (this.displayPages) { - var pages = Zotero.Fulltext.getPages(this.item.id); - var pages = pages ? pages.total : null; - if (pages) { - this._id("pages-label").value = Zotero.getString('itemFields.pages') - + Zotero.getString('punctuation.colon'); - this._id("pages").value = pages; - pagesRow.hidden = false; + + // Page count + if (this.displayPages) { + var pages = yield Zotero.Fulltext.getPages(this.item.id); + var pages = pages ? pages.total : null; + if (pages) { + this._id("pages-label").value = Zotero.getString('itemFields.pages') + + Zotero.getString('punctuation.colon'); + this._id("pages").value = pages; + pagesRow.hidden = false; + } + else { + pagesRow.hidden = true; + } } else { pagesRow.hidden = true; } - } - else { - pagesRow.hidden = true; - } - - if (this.displayDateModified) { - this._id("dateModified-label").value = Zotero.getString('itemFields.dateModified') - + Zotero.getString('punctuation.colon'); - var mtime = this.item.attachmentModificationTime; - if (mtime) { - this._id("dateModified").value = new Date(mtime).toLocaleString(); - } - // Use the item's mod time as a backup (e.g., when sync - // passes in the mod time for the nonexistent remote file) - else { - this._id("dateModified").value = Zotero.Date.sqlToDate( - this.item.getField('dateModified'), true - ).toLocaleString(); - } - dateModifiedRow.hidden = false; - } - else { - dateModifiedRow.hidden = true; - } - - // Full-text index information - if (this.displayIndexed) { - this.updateItemIndexedState(); - indexStatusRow.hidden = false; - } - else { - indexStatusRow.hidden = true; - } - - // Note editor - var noteEditor = this._id('attachment-note-editor'); - if (this.displayNote) { - if (this.displayNoteIfEmpty || this.item.getNote() != '') { - Zotero.debug("setting links on top"); - noteEditor.linksOnTop = true; - noteEditor.hidden = false; - - // Don't make note editable (at least for now) - if (this.mode == 'merge' || this.mode == 'mergeedit') { - noteEditor.mode = 'merge'; - noteEditor.displayButton = false; - } - else { - noteEditor.mode = this.mode; - } - noteEditor.parent = null; - noteEditor.item = this.item; - } - } - else { - noteEditor.hidden = true; - } - - if (this.displayButton) { - selectButton.label = this.buttonCaption; - selectButton.hidden = false; - selectButton.setAttribute('oncommand', - 'document.getBindingParent(this).clickHandler(this)'); - } - else { - selectButton.hidden = true; - } - ]]> - + if (this.displayDateModified) { + this._id("dateModified-label").value = Zotero.getString('itemFields.dateModified') + + Zotero.getString('punctuation.colon'); + var mtime = this.item.attachmentModificationTime; + if (mtime) { + this._id("dateModified").value = new Date(mtime).toLocaleString(); + } + // Use the item's mod time as a backup (e.g., when sync + // passes in the mod time for the nonexistent remote file) + else { + this._id("dateModified").value = Zotero.Date.sqlToDate( + this.item.getField('dateModified'), true + ).toLocaleString(); + } + dateModifiedRow.hidden = false; + } + else { + dateModifiedRow.hidden = true; + } + + // Full-text index information + if (this.displayIndexed) { + yield this.updateItemIndexedState(); + indexStatusRow.hidden = false; + } + else { + indexStatusRow.hidden = true; + } + + // Note editor + var noteEditor = this._id('attachment-note-editor'); + if (this.displayNote) { + if (this.displayNoteIfEmpty || this.item.getNote() != '') { + Zotero.debug("setting links on top"); + noteEditor.linksOnTop = true; + noteEditor.hidden = false; + + // Don't make note editable (at least for now) + if (this.mode == 'merge' || this.mode == 'mergeedit') { + noteEditor.mode = 'merge'; + noteEditor.displayButton = false; + } + else { + noteEditor.mode = this.mode; + } + noteEditor.parent = null; + noteEditor.item = this.item; + } + } + else { + noteEditor.hidden = true; + } + + + if (this.displayButton) { + selectButton.label = this.buttonCaption; + selectButton.hidden = false; + selectButton.setAttribute('oncommand', + 'document.getBindingParent(this).clickHandler(this)'); + } + else { + selectButton.hidden = true; + } + }, this); + ]]> @@ -464,43 +466,43 @@ Update Indexed: (Yes|No|Partial) line --> - - - + diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml index 285203fa42..a5b4f8fa75 100644 --- a/chrome/content/zotero/bindings/itembox.xml +++ b/chrome/content/zotero/bindings/itembox.xml @@ -347,7 +347,7 @@ // createValueElement() adds the itemTypeID as an attribute // and converts it to a localized string for display if (fieldName == 'itemType') { - val = this.item.getField('itemTypeID'); + val = this.item.itemTypeID; } else { val = this.item.getField(fieldName); @@ -530,8 +530,8 @@ max = num; } for (var i = 0; i < max; i++) { - this.addCreatorRow(this.item.getCreator(i).ref, - this.item.getCreator(i).creatorTypeID); + let data = this.item.getCreator(i); + this.addCreatorRow(data, data.creatorType); // Display "+" button on all but last row if (i == max - 2) { @@ -553,7 +553,7 @@ this._displayAllCreators = true; if (this._addCreatorRow) { - this.addCreatorRow(false, this.item.getCreator(max-1).creatorTypeID, true); + this.addCreatorRow(false, this.item.getCreator(max-1).creatorType, true); this._addCreatorRow = false; this.disableCreatorAddButtons(); } @@ -659,8 +659,8 @@ - - + + @@ -668,26 +668,34 @@ // getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect() // may need need to be adjusted if this DOM structure changes - if (!creator) { - creator = { - firstName: '', - lastName: '', - fieldMode: Zotero.Prefs.get('lastCreatorFieldMode') - }; + var fieldMode = Zotero.Prefs.get('lastCreatorFieldMode'); + var firstName = ''; + var lastName = ''; + if (creatorData) { + fieldMode = creatorData.fieldMode; + firstName = creatorData.firstName; + lastName = creatorData.lastName; } - if (creator.fieldMode == 1) { - var firstName = ''; - var lastName = creator.lastName ? creator.lastName : this._defaultFullName; + // Sub in placeholder text for empty fields + if (fieldMode == 1) { + if (lastName === "") { + lastName = this._defaultFullName; + } } else { - var firstName = creator.firstName ? creator.firstName : this._defaultFirstName; - var lastName = creator.lastName ? creator.lastName : this._defaultLastName; + if (firstName === "") { + firstName = this._defaultFirstName; + } + if (lastName === "") { + lastName = this._defaultLastName; + } } // Use the first entry in the drop-down for the default type if none specified - var typeID = creatorTypeID ? - creatorTypeID : this._creatorTypeMenu.childNodes[0].getAttribute('typeid'); + var typeID = creatorTypeIDOrName + ? Zotero.CreatorTypes.getID(creatorTypeIDOrName) + : this._creatorTypeMenu.childNodes[0].getAttribute('typeid'); var typeBox = document.createElement("hbox"); typeBox.setAttribute("typeid", typeID); @@ -738,7 +746,7 @@ tabindex + 1 ) ); - if (creator.fieldMode) { + if (fieldMode) { firstlast.lastChild.setAttribute('hidden', true); } @@ -782,7 +790,7 @@ this.disableButton(addButton); } else { - this._enablePlusButton(addButton, typeID, creator.fieldMode); + this._enablePlusButton(addButton, typeID, fieldMode); } hbox.appendChild(addButton); @@ -797,7 +805,7 @@ this.addDynamicRow(typeBox, hbox, true); // Set single/double field toggle mode - if (creator.fieldMode) { + if (fieldMode) { this.switchCreatorMode(hbox.parentNode, 1, true); } else { @@ -1169,16 +1177,11 @@ @@ -1373,8 +1376,10 @@ var [field, creatorIndex, creatorField] = fieldName.split('-'); if (field == 'creator') { - var c = this.item.getCreator(creatorIndex); - var value = c ? c.ref[creatorField] : ''; + var value = this.item.getCreator(creatorIndex)[creatorField]; + if (value === undefined) { + value = ""; + } var itemID = this.item.id; } else { @@ -1487,72 +1492,73 @@ --> - - - + // Otherwise let the autocomplete popup handle matters + }, this); + ]]> @@ -1719,8 +1725,8 @@ //Shift existing creators for (var i=initNumCreators-1; i>=creatorIndex; i--) { - var shiftedCreator = this.item.getCreator(i); - this.item.setCreator(nameArray.length+i,shiftedCreator.ref,shiftedCreator.creatorTypeID); + let shiftedCreatorData = this.item.getCreator(i); + this.item.setCreator(nameArray.length + i, shiftedCreatorData); } } @@ -1753,7 +1759,7 @@ } var val = this.item.getCreator(creatorIndex); - val = val ? val.ref[creatorField] : null; + val = val ? val[creatorField] : null; if (!val) { // Reset to '(first)'/'(last)'/'(name)' @@ -1977,10 +1983,8 @@ var label2 = label1.parentNode.lastChild; var fields = { - lastName: label1.firstChild ? label1.firstChild.nodeValue - : label1.value, - firstName: label2.firstChild ? label2.firstChild.nodeValue - : label2.value, + lastName: label1.firstChild ? label1.firstChild.nodeValue : label1.value, + firstName: label2.firstChild ? label2.firstChild.nodeValue : label2.value, fieldMode: label1.getAttribute('fieldMode') ? parseInt(label1.getAttribute('fieldMode')) : 0, creatorTypeID: parseInt(typeID), @@ -1996,6 +2000,9 @@ fields.lastName = ''; } + Zotero.debug("RETURNING FIELDS"); + Zotero.debug(fields); + return fields; ]]> @@ -2006,14 +2013,11 @@ - - - + }.bind(this)); + ]]> + + @@ -2207,24 +2113,6 @@ - - @@ -2329,14 +2217,14 @@ - - - + @@ -2455,7 +2343,7 @@ var item = document.getBindingParent(this).item; var exists = item.hasCreatorAt(index); if (exists) { - var fieldMode = item.getCreator(index).ref.fieldMode; + var fieldMode = item.getCreator(index).name !== undefined ? 1 : 0; } var hideTransforms = !exists || !!fieldMode; return !hideTransforms;"> diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml index 61dee77bdd..73f91126b7 100644 --- a/chrome/content/zotero/bindings/noteeditor.xml +++ b/chrome/content/zotero/bindings/noteeditor.xml @@ -98,11 +98,11 @@ - - + + @@ -112,20 +112,22 @@ - + yield this.refresh(); + }, this); + ]]> @@ -155,109 +157,112 @@ - - - + @@ -378,11 +383,11 @@ ]]> - - + + - - - + }, this); + ]]> - - - + }, this); + ]]> - - 0) { var x = this.boxObject.screenX; var y = this.boxObject.screenY; @@ -434,13 +440,13 @@ else { this.id('seeAlso').add(); } - ]]> - + }, this); + ]]> - - - + }, this) + ]]> @@ -479,9 +485,11 @@ var zp = lastWin.ZoteroPane; } - var parentID = this.item.getSource(); - zp.clearQuicksearch(); - zp.selectItem(parentID); + Zotero.spawn(function* () { + var parentID = this.item.parentID; + yield zp.clearQuicksearch(); + zp.selectItem(parentID); + }, this); ]]> diff --git a/chrome/content/zotero/bindings/relatedbox.xml b/chrome/content/zotero/bindings/relatedbox.xml index cba1bfbce6..076513ddd7 100644 --- a/chrome/content/zotero/bindings/relatedbox.xml +++ b/chrome/content/zotero/bindings/relatedbox.xml @@ -73,27 +73,31 @@ - + }, this); + ]]> - + }, this); + ]]> - - - + }, this); + ]]> - - - + }); + ]]> @@ -257,7 +263,7 @@

'+html+"

"; } + Zotero.debug("SETTING CONTENT TO " + html); + this._editor.setContent(html); return val; ]]> diff --git a/chrome/content/zotero/bindings/tagsbox.xml b/chrome/content/zotero/bindings/tagsbox.xml index 8011488058..2a1bc2bc66 100644 --- a/chrome/content/zotero/bindings/tagsbox.xml +++ b/chrome/content/zotero/bindings/tagsbox.xml @@ -90,26 +90,25 @@ - - - + }, this); + ]]> @@ -134,6 +133,7 @@ + - - - + this._tagColors = yield Zotero.Tags.getColors(this.item.libraryID) + + var rows = this.id('tagRows'); + while(rows.hasChildNodes()) { + rows.removeChild(rows.firstChild); + } + var tags = this.item.getTags(); + + // Sort tags alphabetically + var collation = Zotero.getLocaleCollation(); + tags.sort(function (a, b) collation.compareString(1, a.tag, b.tag)); + + for (let i=0; i
- + - + @@ -362,7 +367,7 @@ if (event.button) { return; } - document.getBindingParent(this).clickHandler(this); + document.getBindingParent(this).clickHandler(this, 1, valueText); }, false); valueElement.className += ' zotero-clicky'; } @@ -402,8 +407,7 @@ - - - + ]]> - - - + + this._tabDirection = event.shiftKey ? -1 : 1; + target.onblur = null; + yield this.blurHandler(target); + this._focusField(); + return false; + } + + return true; + }.bind(this)); + ]]> "view" @@ -118,9 +122,16 @@
@@ -177,7 +188,9 @@ this._initialized = false; this.unregister(); this.selection = {}; - this.doCommand(); + if (this.onchange) { + this.onchange(); + } ]]> @@ -198,33 +211,29 @@ =0; i--) { let name = colorTags[positions[i]]; - let ids = Zotero.Tags.getIDs(name, self.libraryID); orderedTags.unshift({ - id: ids ? ids.join('-') : null, - name: name, + tag: name, type: 0, hasColor: true }); @@ -247,40 +254,41 @@ var lastTag; for (let i=0; i @@ -501,80 +493,81 @@ - - @@ -600,8 +593,8 @@ - - - + if (this.onchange) { + this.onchange(); + } + }, this); + ]]> - - - + @@ -682,9 +675,11 @@ label.setAttribute('selected', 'true'); } - this.doCommand(); - this.updateNumSelected(); + + if (this.onchange) { + this.onchange(); + } ]]> @@ -692,76 +687,45 @@ - - @@ -778,30 +742,21 @@ Zotero.getString('pane.tagSelector.delete.title'), Zotero.getString('pane.tagSelector.delete.message')); - if (confirmed) { - Zotero.DB.beginTransaction(); - - // Add other ids with same tag - var ids = Zotero.Tags.getIDs(name, this.libraryID); - var tagIDs = []; - for each(var id in ids) { - if (tagIDs.indexOf(id) == -1) { - tagIDs.push(id); - } + if (!confirmed) { + return; + } + + return Zotero.DB.executeTransaction(function* () { + yield Zotero.Tags.load(this.libraryID); + var tagID = Zotero.Tags.getID(this.libraryID, name); + if (tagID) { + yield Zotero.Tags.erase(this.libraryID, tagID); } - - if (tagIDs.length) { - Zotero.Tags.erase(tagIDs); - Zotero.Tags.purge(tagIDs); - } - - Zotero.DB.commitTransaction() - - // If only a tag color setting, remove that - if (!tagIDs.length) { - Zotero.Tags.setColor(this.libraryID, name, false); - } - + }.bind(this)); + + // If only a tag color setting, remove that + if (!tagID) { + Zotero.Tags.setColor(this.libraryID, name, false); } ]]> @@ -812,7 +767,7 @@ - @@ -931,7 +880,7 @@ } - this.onDrop = function (event) { + this.onDrop = Zotero.Promise.method(function (event) { var node = event.target; node.setAttribute('draggedOver', false); @@ -941,39 +890,18 @@ return; } - Zotero.DB.beginTransaction(); - - ids = ids.split(','); - var items = Zotero.Items.get(ids); - - // Find a manual tag if there is one - var tagID = null; - var tagIDs = node.getAttribute('tagID'); - tagIDs = tagIDs ? node.getAttribute('tagID').split(/\-/) : []; - var tagTypes = node.getAttribute('tagType').split(/\-/); - for (var i=0; i @@ -1001,7 +929,10 @@ - diff --git a/chrome/content/zotero/bindings/zoterosearch.xml b/chrome/content/zotero/bindings/zoterosearch.xml index 69368ef851..23d6995799 100644 --- a/chrome/content/zotero/bindings/zoterosearch.xml +++ b/chrome/content/zotero/bindings/zoterosearch.xml @@ -177,8 +177,7 @@ this.onLibraryChange(libraryID); } - // TODO: libraryIDInt - this.searchRef.libraryID = libraryID ? libraryID : null; + this.searchRef.libraryID = libraryID; ]]> diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js index af9fcecf23..e96f82ead5 100644 --- a/chrome/content/zotero/browser.js +++ b/chrome/content/zotero/browser.js @@ -110,7 +110,7 @@ var Zotero_Browser = new function() { if (!Zotero || Zotero.skipLoading) { // Zotero either failed to load or is reloading in Connector mode // In case of the latter, listen for the 'zotero-loaded' event (once) and retry - var zoteroInitDone_deferred = Q.defer(); + var zoteroInitDone_deferred = Zotero.Promise.defer(); var obs = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); var observer = { @@ -123,14 +123,14 @@ var Zotero_Browser = new function() { zoteroInitDone = zoteroInitDone_deferred.promise; } else { - zoteroInitDone = Q(); + zoteroInitDone = Zotero.Promise.resolve(); } - var chromeLoaded = Q.defer(); + var chromeLoaded = Zotero.Promise.defer(); window.addEventListener("load", function(e) { chromeLoaded.resolve() }, false); // Wait for Zotero to init and chrome to load before proceeding - Q.all([ + Zotero.Promise.all([ zoteroInitDone.then(function() { ZoteroPane_Local.addReloadListener(reload); reload(); @@ -139,8 +139,7 @@ var Zotero_Browser = new function() { ]) .then(function() { Zotero_Browser.chromeLoad() - }) - .done(); + }); } /** diff --git a/chrome/content/zotero/downloadOverlay.js b/chrome/content/zotero/downloadOverlay.js index 728de44ff0..b576ecb353 100644 --- a/chrome/content/zotero/downloadOverlay.js +++ b/chrome/content/zotero/downloadOverlay.js @@ -63,7 +63,7 @@ var Zotero_DownloadOverlay = new function() { .getMostRecentWindow("navigator:browser"); var libraryID, collection; try { - if(win.ZoteroPane.getItemGroup().filesEditable) { + if(win.ZoteroPane.getCollectionTreeRow().filesEditable) { libraryID = win.ZoteroPane.getSelectedLibraryID(); collection = win.ZoteroPane.getSelectedCollection(); } @@ -147,7 +147,7 @@ var Zotero_DownloadOverlay = new function() { var zoteroSelected = document.getElementById('zotero-radio').selected; var zp = Zotero.getActiveZoteroPane(), canSave = true; try { - canSave = zp.getItemGroup().filesEditable; + canSave = zp.getCollectionTreeRow().filesEditable; } catch(e) { Zotero.logError(e); }; diff --git a/chrome/content/zotero/duplicatesMerge.js b/chrome/content/zotero/duplicatesMerge.js index 435264db94..e48fa47a76 100644 --- a/chrome/content/zotero/duplicatesMerge.js +++ b/chrome/content/zotero/duplicatesMerge.js @@ -145,7 +145,7 @@ var Zotero_Duplicates_Pane = new function () { this.merge = function () { var itembox = document.getElementById('zotero-duplicates-merge-item-box'); - Zotero.ItemGroupCache.clear(); + Zotero.CollectionTreeCache.clear(); Zotero.Items.merge(itembox.item, _otherItems); } } diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index 2ac4056356..49e49a26a3 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -210,7 +210,7 @@ var Zotero_File_Interface = new function() { } var translation = new Zotero.Translate.Import(); - (file ? Q(file) : translation.getTranslators().then(function(translators) { + (file ? Zotero.Promise.resolve(file) : translation.getTranslators().then(function(translators) { const nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); diff --git a/chrome/content/zotero/integration/addCitationDialog.js b/chrome/content/zotero/integration/addCitationDialog.js index 34b5e0bfcf..730a51d172 100644 --- a/chrome/content/zotero/integration/addCitationDialog.js +++ b/chrome/content/zotero/integration/addCitationDialog.js @@ -141,9 +141,9 @@ var Zotero_Citation_Dialog = new function () { // If we're in a different library, switch libraries var id = io.citation.citationItems[0].id; - var itemGroup = collectionsView._getItemAtRow(collectionsView.selection.currentIndex); + var collectionTreeRow = collectionsView.selectedTreeRow; var item = Zotero.Items.get(id); - if(item.libraryID != itemGroup.ref.libraryID) { + if(item.libraryID != collectionTreeRow.ref.libraryID) { collectionsView.selectLibrary(item.libraryID); } var selected = itemsView.selectItem(id); diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js index a2e1981131..79c2de5cc2 100644 --- a/chrome/content/zotero/itemPane.js +++ b/chrome/content/zotero/itemPane.js @@ -50,7 +50,7 @@ var ZoteroItemPane = new function() { /* * Load a top-level item */ - this.viewItem = function (item, mode, index) { + this.viewItem = Zotero.Promise.coroutine(function* (item, mode, index) { if (!index) { index = 0; } @@ -77,7 +77,7 @@ var ZoteroItemPane = new function() { switch (index) { case 0: case 2: - box.blurOpenField(); + yield box.blurOpenField(); // DEBUG: Currently broken //box.scrollToTop(); break; @@ -94,10 +94,13 @@ var ZoteroItemPane = new function() { _notesList.removeChild(_notesList.firstChild); } - var notes = Zotero.Items.get(item.getNotes()); + yield item.loadChildItems(); + let notes = yield Zotero.Items.getAsync(item.getNotes()); if (notes.length) { - for(var i = 0; i < notes.length; i++) { + for (var i = 0; i < notes.length; i++) { + let note = notes[i]; let id = notes[i].id; + yield note.loadItemData(); var icon = document.createElement('image'); icon.className = "zotero-box-icon"; @@ -105,7 +108,7 @@ var ZoteroItemPane = new function() { var label = document.createElement('label'); label.className = "zotero-box-label"; - var title = Zotero.Notes.noteToTitle(notes[i].getNote()); + var title = note.getNoteTitle(); title = title ? title : Zotero.getString('pane.item.notes.untitled'); label.setAttribute('value', title); label.setAttribute('flex','1'); //so that the long names will flex smaller @@ -144,12 +147,14 @@ var ZoteroItemPane = new function() { else { box.mode = 'edit'; } + + yield [item.loadItemData(), item.loadCreators()]; box.item = item; - } + }); this.addNote = function (popup) { - ZoteroPane_Local.newNote(popup, _lastItem.id); + ZoteroPane_Local.newNote(popup, _lastItem.key); } diff --git a/chrome/content/zotero/locateMenu.js b/chrome/content/zotero/locateMenu.js index 183fd8c3b5..f19cedb3a3 100644 --- a/chrome/content/zotero/locateMenu.js +++ b/chrome/content/zotero/locateMenu.js @@ -143,14 +143,14 @@ var Zotero_LocateMenu = new function() { * @param {Boolean} showIcons Whether menu items should have associated icons * @param {Boolean} addExtraOptions Whether to add options that start with "_" below the separator */ - function _addViewOptions(locateMenu, selectedItems, showIcons, addExtraOptions) { + var _addViewOptions = Zotero.Promise.coroutine(function* (locateMenu, selectedItems, showIcons, addExtraOptions) { var optionsToShow = {}; // check which view options are available for each(var item in selectedItems) { for(var viewOption in ViewOptions) { if(!optionsToShow[viewOption]) { - optionsToShow[viewOption] = ViewOptions[viewOption].canHandleItem(item); + optionsToShow[viewOption] = yield ViewOptions[viewOption].canHandleItem(item); } } } @@ -178,7 +178,7 @@ var Zotero_LocateMenu = new function() { ViewOptions[viewOption], showIcons), lastNode); } } - } + }); /** * Get available locate engines that can handle a set of items @@ -343,26 +343,29 @@ var Zotero_LocateMenu = new function() { ViewOptions.pdf = new function() { this.icon = "chrome://zotero/skin/treeitem-attachment-pdf.png"; this._mimeTypes = ["application/pdf"]; - this.canHandleItem = function(item) !!_getFirstAttachmentWithMIMEType(item, this._mimeTypes); - this.handleItems = function(items, event) { + this.canHandleItem = function (item) { + return _getFirstAttachmentWithMIMEType(item, this._mimeTypes).then((item) => !!item); + } + + this.handleItems = Zotero.Promise.coroutine(function* (items, event) { var attachments = []; for each(var item in items) { - var attachment = _getFirstAttachmentWithMIMEType(item, this._mimeTypes); + var attachment = yield _getFirstAttachmentWithMIMEType(item, this._mimeTypes); if(attachment) attachments.push(attachment.id); } ZoteroPane_Local.viewAttachment(attachments, event); - } + }); - function _getFirstAttachmentWithMIMEType(item, mimeTypes) { - var attachments = (item.isAttachment() ? [item] : Zotero.Items.get(item.getBestAttachments())); + var _getFirstAttachmentWithMIMEType = Zotero.Promise.coroutine(function* (item, mimeTypes) { + var attachments = (item.isAttachment() ? [item] : (yield item.getBestAttachments())); for each(var attachment in attachments) { - if(mimeTypes.indexOf(attachment.attachmentMIMEType) !== -1 + if (mimeTypes.indexOf(attachment.attachmentContentType) !== -1 && attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) return attachment; } return false; - } + }); }; /** @@ -372,14 +375,16 @@ var Zotero_LocateMenu = new function() { */ ViewOptions.online = new function() { this.icon = "chrome://zotero/skin/locate-view-online.png"; - this.canHandleItem = function(item) _getURL(item) !== false; - this.handleItems = function(items, event) { - var urls = [_getURL(item) for each(item in items)]; - ZoteroPane_Local.loadURI([url for each(url in urls) if(url)], event); + this.canHandleItem = function (item) { + return _getURL(item).then((val) => val !== false); } + this.handleItems = Zotero.Promise.coroutine(function* (items, event) { + var urls = yield [_getURL(item) for each(item in items)]; + ZoteroPane_Local.loadURI([url for each(url in urls) if(url)], event); + }); - function _getURL(item) { + var _getURL = Zotero.Promise.coroutine(function* (item) { // try url field for item and for attachments var urlField = item.getField('url'); if(urlField) { @@ -391,6 +396,7 @@ var Zotero_LocateMenu = new function() { } if(item.isRegularItem()) { + yield item.loadChildItems(); var attachments = item.getAttachments(); if(attachments) { // look through url fields for non-file:/// attachments @@ -412,7 +418,7 @@ var Zotero_LocateMenu = new function() { } return false; - } + }); }; /** @@ -436,29 +442,32 @@ var Zotero_LocateMenu = new function() { */ ViewOptions.file = new function() { this.icon = "chrome://zotero/skin/treeitem-attachment-file.png"; - this.canHandleItem = function(item) !!_getFile(item); - this.handleItems = function(items, event) { + this.canHandleItem = function (item) { + return _getFile(item).then((item) => !!item); + } + + this.handleItems = Zotero.Promise.coroutine(function* (items, event) { var attachments = []; for each(var item in items) { - var attachment = _getFile(item); + var attachment = yield _getFile(item); if(attachment) attachments.push(attachment.id); } ZoteroPane_Local.viewAttachment(attachments, event); - } + }); - function _getFile(item) { - var attachments = (item.isAttachment() ? [item] : Zotero.Items.get(item.getBestAttachments())); + var _getFile = Zotero.Promise.coroutine(function* (item) { + var attachments = item.isAttachment() ? [item] : (yield item.getBestAttachments()); for each(var attachment in attachments) { - if(!ViewOptions.snapshot.canHandleItem(attachment) - && !ViewOptions.pdf.canHandleItem(attachment) + if (!(yield ViewOptions.snapshot.canHandleItem(attachment)) + && !(yield ViewOptions.pdf.canHandleItem(attachment)) && attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) { return attachment; } } return false; - } + }); }; /** @@ -471,28 +480,28 @@ var Zotero_LocateMenu = new function() { this.icon = "chrome://zotero/skin/locate-external-viewer.png"; this.useExternalViewer = true; - this.canHandleItem = function(item) { + this.canHandleItem = Zotero.Promise.coroutine(function* (item) { return (this.useExternalViewer ^ Zotero.Prefs.get('launchNonNativeFiles')) - && _getBestNonNativeAttachment(item); - } + && (yield _getBestNonNativeAttachment(item)); + }); - this.handleItems = function(items, event) { + this.handleItems = Zotero.Promise.coroutine(function* (items, event) { var attachments = []; for each(var item in items) { - var attachment = _getBestNonNativeAttachment(item); + var attachment = yield _getBestNonNativeAttachment(item); if(attachment) attachments.push(attachment.id); } ZoteroPane_Local.viewAttachment(attachments, event, false, this.useExternalViewer); - } + }); - function _getBestNonNativeAttachment(item) { - var attachments = (item.isAttachment() ? [item] : Zotero.Items.get(item.getBestAttachments())); + var _getBestNonNativeAttachment = Zotero.Promise.coroutine(function* (item) { + var attachments = item.isAttachment() ? [item] : (yield item.getBestAttachments()); for each(var attachment in attachments) { if(attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) { - var file = attachment.getFile(); - if(file) { - var ext = Zotero.File.getExtension(file); + var path = yield attachment.getFilePath(); + if (path) { + var ext = Zotero.File.getExtension(Zotero.File.pathToFile(path)); if(!attachment.attachmentMIMEType || Zotero.MIME.hasNativeHandler(attachment.attachmentMIMEType, ext) || !Zotero.MIME.hasInternalHandler(attachment.attachmentMIMEType, ext)) { @@ -503,7 +512,7 @@ var Zotero_LocateMenu = new function() { } } return false; - } + }); }; /** @@ -529,27 +538,27 @@ var Zotero_LocateMenu = new function() { this.icon = "chrome://zotero/skin/locate-show-file.png"; this.useExternalViewer = true; - this.canHandleItem = function(item) { - return !!_getBestFile(item); + this.canHandleItem = function (item) { + return _getBestFile(item).then(function (item) !!item); } - this.handleItems = function(items, event) { + this.handleItems = Zotero.Promise.coroutine(function* (items, event) { for each(var item in items) { - var attachment = _getBestFile(item); + var attachment = yield _getBestFile(item); if(attachment) { ZoteroPane_Local.showAttachmentInFilesystem(attachment.id); } } - } + }); - function _getBestFile(item) { + var _getBestFile = Zotero.Promise.coroutine(function* (item) { if(item.isAttachment()) { if(item.attachmentLinkMode === Zotero.Attachments.LINK_MODE_LINKED_URL) return false; return item; } else { - return Zotero.Items.get(item.getBestAttachment()); + return yield item.getBestAttachment(); } - } + }); }; /** @@ -559,8 +568,8 @@ var Zotero_LocateMenu = new function() { */ ViewOptions._libraryLookup = new function() { this.icon = "chrome://zotero/skin/locate-library-lookup.png"; - this.canHandleItem = function(item) item.isRegularItem(); - this.handleItems = function(items, event) { + this.canHandleItem = function (item) Zotero.Promise.resolve(item.isRegularItem()); + this.handleItems = Zotero.Promise.method(function (items, event) { var urls = []; for each(var item in items) { if(!item.isRegularItem()) continue; @@ -568,6 +577,6 @@ var Zotero_LocateMenu = new function() { if(url) urls.push(url); } ZoteroPane_Local.loadURI(urls, event); - } + }); }; } \ No newline at end of file diff --git a/chrome/content/zotero/note.js b/chrome/content/zotero/note.js index 0de5944135..d788d48f14 100644 --- a/chrome/content/zotero/note.js +++ b/chrome/content/zotero/note.js @@ -27,6 +27,13 @@ var noteEditor; var notifierUnregisterID; function onLoad() { + Zotero.spawn(function* () { + Zotero.debug('=-=-='); + var bar = yield Zotero.Promise.delay(1000).return('DONE'); + Zotero.debug(bar); + Zotero.debug('-----'); + }); + noteEditor = document.getElementById('zotero-note-editor'); noteEditor.mode = 'edit'; noteEditor.focus(); @@ -39,38 +46,40 @@ function onLoad() { } var itemID = io.itemID; var collectionID = io.collectionID; - var parentItemID = io.parentItemID; + var parentItemKey = io.parentItemKey; - if (itemID) { - var ref = Zotero.Items.get(itemID); - - var clearUndo = noteEditor.item ? noteEditor.item.id != ref.id : false; - - noteEditor.item = ref; - - // If loading new or different note, disable undo while we repopulate the text field - // so Undo doesn't end up clearing the field. This also ensures that Undo doesn't - // undo content from another note into the current one. - if (clearUndo) { - noteEditor.clearUndo(); - } - - document.title = ref.getNoteTitle(); - } - else { - if (parentItemID) { - var ref = Zotero.Items.get(parentItemID); - noteEditor.parent = ref; + return Zotero.spawn(function* () { + if (itemID) { + var ref = yield Zotero.Items.getAsync(itemID); + + var clearUndo = noteEditor.item ? noteEditor.item.id != ref.id : false; + + noteEditor.item = ref; + + // If loading new or different note, disable undo while we repopulate the text field + // so Undo doesn't end up clearing the field. This also ensures that Undo doesn't + // undo content from another note into the current one. + if (clearUndo) { + noteEditor.clearUndo(); + } + + document.title = ref.getNoteTitle(); } else { - if (collectionID && collectionID != '' && collectionID != 'undefined') { - noteEditor.collection = Zotero.Collections.get(collectionID); + if (parentItemKey) { + var ref = Zotero.Items.getByLibraryAndKey(parentItemKey); + noteEditor.parentItem = ref; } + else { + if (collectionID && collectionID != '' && collectionID != 'undefined') { + noteEditor.collection = Zotero.Collections.get(collectionID); + } + } + noteEditor.refresh(); } - noteEditor.refresh(); - } - - notifierUnregisterID = Zotero.Notifier.registerObserver(NotifyCallback, 'item'); + + notifierUnregisterID = Zotero.Notifier.registerObserver(NotifyCallback, 'item'); + }); } function onUnload() diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js index c865dd20d6..937fbcd684 100644 --- a/chrome/content/zotero/overlay.js +++ b/chrome/content/zotero/overlay.js @@ -60,7 +60,7 @@ var ZoteroOverlay = new function() var self = this; - Q.fcall(function () { + Zotero.Promise.try(function () { if (!Zotero || Zotero.skipLoading) { throw true; } @@ -227,12 +227,17 @@ var ZoteroOverlay = new function() * the foreground. */ this.toggleDisplay = function(makeVisible, dontRefocus) - { + { if (!Zotero || Zotero.skipLoading) { ZoteroPane.displayStartupError(); return; } + // Don't do anything if pane is already showing + if (makeVisible && ZoteroPane.isShowing()) { + return; + } + if(makeVisible || makeVisible === undefined) { if(Zotero.isConnector) { // If in connector mode, bring Zotero Standalone to foreground diff --git a/chrome/content/zotero/preferences/preferences_export.js b/chrome/content/zotero/preferences/preferences_export.js index fc2871ddcc..af6d7b0ac3 100644 --- a/chrome/content/zotero/preferences/preferences_export.js +++ b/chrome/content/zotero/preferences/preferences_export.js @@ -181,7 +181,7 @@ Zotero_Preferences.Export = { }, - refreshQuickCopySiteList: function () { + refreshQuickCopySiteList: Zotero.Promise.coroutine(function* () { var treechildren = document.getElementById('quickCopy-siteSettings-rows'); while (treechildren.hasChildNodes()) { treechildren.removeChild(treechildren.firstChild); @@ -189,37 +189,31 @@ Zotero_Preferences.Export = { var sql = "SELECT key AS domainPath, value AS format FROM settings " + "WHERE setting='quickCopySite' ORDER BY domainPath COLLATE NOCASE"; - var siteData = Zotero.DB.query(sql); + var siteData = yield Zotero.DB.queryAsync(sql); - if (!siteData) { - return; + for (var i=0; i