From 3de1789f264930d581cd2e96b1a2d2df2b131e7e Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sun, 4 May 2008 08:32:48 +0000 Subject: [PATCH] Initial Zotero 1.5 Megacommit Apologies for the massive (and, due to data_access.js splitting, difficult-to-follow) commit. Please note that external code that accesses the data layer may need to be tweaked for compatibility. Here's a comprehensive-as-possible changelog: - Added server sync functionality (incomplete) - Overhaul of data layer - Split data_access.js into separate files (item.js, items.js, creator.js, etc.) - Made creators and collections first-class objects, similar to items - Constructors now take id as first parameter, e.g. new Zotero.Item(1234, 'book'), to allow explicit id setting and id changing - Made various data layer operations (including attachment fields) require a save() rather than making direct DB changes - Better handling of unsaved objects - Item.setCreator() now takes creator objects instead of creator ids, and Item.save() will auto-save unsaved creators - clone() now works on unsaved objects - Newly created object instances are now disabled after save() to force refetch of globally accessible instance using Zotero.(Items|Creators|etc.).get() - Added secondary lookup key to data objects - Deprecated getID() and getItemType() methods in favor of .id and .itemTypeID properties - toArray() deprecated in favor of serialize(), which has a somewhat modified format - Added support for multiple creators with identical data -- currently unimplemented in interface and most of data layer - Added Item.diff() for comparing item metadata - Database changes - Added SQLite triggers to enforce foreign key constraints - Added Zotero.DB.transactionVacuum flag to run a VACUUM after a transaction - Added Zotero.DB.transactionDate, .transactionDateTime, and transactionTimestamp to retrieve consistent timestamps for entire transaction - Properly store 64-bit integers - Set PRAGMA locking_mode=EXCLUSIVE on database - Set SQLite page size to 4096 on new databases - Set SQLite page cache to 8MB - Do some database cleanup and integrity checking on migration from 1.0 branch - Removed IF NOT EXISTS from userdata.sql CREATE statements -- userdata.sql is now processed only on DB initialization - Removed itemNoteTitles table and moved titles into itemNotes - Abstracted metadata edit box and note box into flexible XBL bindings with various modes, including read-only states - Massive speed-up of item tree view - Several fixes from 1.0 branch for Fx3 compatibility - Added Notifier observer to log delete events for syncing - Zotero.Utilities changes - New methods getSQLDataType() and md5() - Removed onError from Zotero.Utilities.HTTP.doGet() - Don't display more than 1024 characters in doPost() debug output - Don't display passwords in doPost() debug output - Added Zotero.Notifier.untrigger() -- currently unused - Added Zotero.reloadDataObjects() to reset all in-memory objects - Added |chars| parameter to Zotero.randomString(len, chars) - Added Zotero.Date.getUnixTimestamp() and Date.toUnixTimestamp(JSDate) - Adjusted zotero-service.js to simplify file inclusion Various things (such as tags) are temporarily broken. --- .../mac/{itemPane.css => itembox.css} | 0 .../unix/{itemPane.css => itembox.css} | 0 .../content/zotero-platform/win/itemPane.css | 31 - .../content/zotero-platform/win/itembox.css | 31 + chrome/content/zotero/bindings/itembox.xml | 2076 +++++++ chrome/content/zotero/bindings/merge.xml | 443 ++ chrome/content/zotero/bindings/noteeditor.xml | 262 +- chrome/content/zotero/browser.js | 20 +- chrome/content/zotero/fileInterface.js | 3 +- chrome/content/zotero/itemPane.js | 1469 +---- chrome/content/zotero/itemPane.xul | 52 +- chrome/content/zotero/merge.js | 214 + chrome/content/zotero/merge.xul | 53 + chrome/content/zotero/note.js | 50 +- chrome/content/zotero/note.xul | 2 +- chrome/content/zotero/overlay.js | 105 +- chrome/content/zotero/overlay.xul | 12 +- .../zotero/preferences/preferences.xul | 31 + chrome/content/zotero/xpcom/attachments.js | 201 +- chrome/content/zotero/xpcom/cite.js | 6 +- .../zotero/xpcom/collectionTreeView.js | 46 +- .../content/zotero/xpcom/data/cachedTypes.js | 273 + .../content/zotero/xpcom/data/collection.js | 930 +++ .../content/zotero/xpcom/data/collections.js | 184 + chrome/content/zotero/xpcom/data/creator.js | 451 ++ chrome/content/zotero/xpcom/data/creators.js | 346 ++ chrome/content/zotero/xpcom/data/item.js | 3392 +++++++++++ .../content/zotero/xpcom/data/itemFields.js | 391 ++ chrome/content/zotero/xpcom/data/items.js | 577 ++ chrome/content/zotero/xpcom/data/notes.js | 43 + chrome/content/zotero/xpcom/data/tags.js | 418 ++ chrome/content/zotero/xpcom/data_access.js | 5191 +---------------- chrome/content/zotero/xpcom/db.js | 77 +- chrome/content/zotero/xpcom/file.js | 2 +- chrome/content/zotero/xpcom/fulltext.js | 44 +- chrome/content/zotero/xpcom/id.js | 286 + chrome/content/zotero/xpcom/itemTreeView.js | 276 +- chrome/content/zotero/xpcom/notifier.js | 48 +- chrome/content/zotero/xpcom/progressWindow.js | 3 + chrome/content/zotero/xpcom/quickCopy.js | 1 - chrome/content/zotero/xpcom/schema.js | 286 +- chrome/content/zotero/xpcom/search.js | 36 +- chrome/content/zotero/xpcom/sync.js | 1852 ++++++ chrome/content/zotero/xpcom/translate.js | 61 +- chrome/content/zotero/xpcom/utilities.js | 77 +- chrome/content/zotero/xpcom/zotero.js | 55 +- .../skin/default/zotero/bindings/itembox.css | 104 + .../default/zotero/bindings/noteeditor.css | 8 +- chrome/skin/default/zotero/itemPane.css | 85 - chrome/skin/default/zotero/merge.css | 97 + chrome/skin/default/zotero/overlay.css | 6 - chrome/skin/default/zotero/prefs-sync.png | Bin 0 -> 2024 bytes chrome/skin/default/zotero/zotero.css | 14 +- components/zotero-autocomplete.js | 14 +- components/zotero-service.js | 106 +- defaults/preferences/zotero.js | 6 +- system.sql | 14 +- triggers.sql | 659 +++ userdata.sql | 160 +- 59 files changed, 14186 insertions(+), 7494 deletions(-) rename chrome/content/zotero-platform/mac/{itemPane.css => itembox.css} (100%) rename chrome/content/zotero-platform/unix/{itemPane.css => itembox.css} (100%) delete mode 100644 chrome/content/zotero-platform/win/itemPane.css create mode 100644 chrome/content/zotero-platform/win/itembox.css create mode 100644 chrome/content/zotero/bindings/itembox.xml create mode 100644 chrome/content/zotero/bindings/merge.xml create mode 100644 chrome/content/zotero/merge.js create mode 100644 chrome/content/zotero/merge.xul create mode 100644 chrome/content/zotero/xpcom/data/cachedTypes.js create mode 100644 chrome/content/zotero/xpcom/data/collection.js create mode 100644 chrome/content/zotero/xpcom/data/collections.js create mode 100644 chrome/content/zotero/xpcom/data/creator.js create mode 100644 chrome/content/zotero/xpcom/data/creators.js create mode 100644 chrome/content/zotero/xpcom/data/item.js create mode 100644 chrome/content/zotero/xpcom/data/itemFields.js create mode 100644 chrome/content/zotero/xpcom/data/items.js create mode 100644 chrome/content/zotero/xpcom/data/notes.js create mode 100644 chrome/content/zotero/xpcom/data/tags.js create mode 100644 chrome/content/zotero/xpcom/id.js create mode 100644 chrome/content/zotero/xpcom/sync.js create mode 100644 chrome/skin/default/zotero/bindings/itembox.css delete mode 100644 chrome/skin/default/zotero/itemPane.css create mode 100644 chrome/skin/default/zotero/merge.css create mode 100644 chrome/skin/default/zotero/prefs-sync.png create mode 100644 triggers.sql diff --git a/chrome/content/zotero-platform/mac/itemPane.css b/chrome/content/zotero-platform/mac/itembox.css similarity index 100% rename from chrome/content/zotero-platform/mac/itemPane.css rename to chrome/content/zotero-platform/mac/itembox.css diff --git a/chrome/content/zotero-platform/unix/itemPane.css b/chrome/content/zotero-platform/unix/itembox.css similarity index 100% rename from chrome/content/zotero-platform/unix/itemPane.css rename to chrome/content/zotero-platform/unix/itembox.css diff --git a/chrome/content/zotero-platform/win/itemPane.css b/chrome/content/zotero-platform/win/itemPane.css deleted file mode 100644 index bc168b7d7d..0000000000 --- a/chrome/content/zotero-platform/win/itemPane.css +++ /dev/null @@ -1,31 +0,0 @@ -#zotero-editpane-dynamic-fields row > hbox, -#zotero-editpane-dynamic-fields row > vbox -{ - margin-top: 0 !important; - margin-bottom: 0 !important; - padding-top: 0 !important; - padding-bottom: 0 !important; -} - -#zotero-editpane-dynamic-fields row > hbox > hbox -{ - -moz-box-align: center; -} - -#zotero-editpane-dynamic-fields row hbox hbox label -{ - margin-top: 0; - margin-bottom: 0; -} - -#zotero-editpane-dynamic-fields row > toolbarbutton -{ - margin-right: 5px; - -moz-image-region: rect(2px, 14px, 18px, 0px); -} - -#zotero-editpane-dynamic-fields row vbox[fieldname=abstractNote], -#zotero-editpane-dynamic-fields row vbox[fieldname=extra] -{ - margin-left: 1px; -} diff --git a/chrome/content/zotero-platform/win/itembox.css b/chrome/content/zotero-platform/win/itembox.css new file mode 100644 index 0000000000..68ec1016a5 --- /dev/null +++ b/chrome/content/zotero-platform/win/itembox.css @@ -0,0 +1,31 @@ +row > hbox, +row > vbox +{ + margin-top: 0 !important; + margin-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +row > hbox > hbox +{ + -moz-box-align: center; +} + +row hbox hbox label +{ + margin-top: 0; + margin-bottom: 0; +} + +row > toolbarbutton +{ + margin-right: 5px; + -moz-image-region: rect(2px, 14px, 18px, 0px); +} + +row vbox[fieldname=abstractNote], +row vbox[fieldname=extra] +{ + margin-left: 1px; +} diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml new file mode 100644 index 0000000000..cca2eae640 --- /dev/null +++ b/chrome/content/zotero/bindings/itembox.xml @@ -0,0 +1,2076 @@ + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + + + + + + "view" + + + + + + + + + + + + + + + + + [] + + + .visibleFields'); + } + + this._visibleFields = val; + ]]> + + + + + [] + + + .clickableFields'); + } + + this._clickableFields = val; + ]]> + + + + + [] + + + .editableFields'); + } + + this._editableFields = val; + ]]> + + + + + + [] + + + .fieldOrder'); + } + + this._fieldOrder = val; + ]]> + + + + + + + + + + + + + + + + + + 10 + 0 + 1000 + 0 + 0 + + + + + + + + " button + testView: try { + var viewButton = document.getElementById('view-button'); + + viewButton.removeAttribute('viewSnapshot'); + viewButton.removeAttribute('viewURL'); + viewButton.setAttribute('label', + Zotero.getString('pane.item.goToURL.online.label')); + viewButton.setAttribute('tooltiptext', + Zotero.getString('pane.item.goToURL.online.tooltip')); + + var spec = false, validURI = false; + + var uri = Components.classes["@mozilla.org/network/standard-url;1"]. + createInstance(Components.interfaces.nsIURI); + + // First try to find a snapshot matching the item's URL field + var snapID = this.item.getBestSnapshot(); + if (snapID) { + spec = Zotero.Items.get(snapID).getLocalFileURL(); + uri.spec = spec; + if (!uri.scheme || uri.scheme != 'file') { + snapID = false; + spec = false; + } + } + + // If that fails, try the URL field itself + if (!spec) { + spec = this.item.getField('url'); + uri.spec = spec; + if (!(uri.scheme && (uri.host || uri.scheme == 'file'))) { + spec = false; + } + } + + if (!spec) { + break testView; + } + + validURI = true; + + if (snapID) { + viewButton.setAttribute('label', + Zotero.getString('pane.item.goToURL.snapshot.label')); + viewButton.setAttribute('tooltiptext', + Zotero.getString('pane.item.goToURL.snapshot.tooltip')); + viewButton.setAttribute('viewSnapshot', snapID); + } + else { + viewButton.setAttribute('viewURL', spec); + } + } + catch (e) { + Zotero.debug(e); + } + viewButton.setAttribute('disabled', !validURI); + + // Enable/disable "Locate =>" (OpenURL) button + switch (this.item.itemTypeID) + { + // DEBUG: handle descendents of these types as well? + case Zotero.ItemTypes.getID('book'): + case Zotero.ItemTypes.getID('bookSection'): + case Zotero.ItemTypes.getID('journalArticle'): + case Zotero.ItemTypes.getID('thesis'): + var openURL = true; + break; + + default: + var openURL = false; + } + document.getElementById('openurl-button').setAttribute('disabled', !openURL); + + this._id('go-buttons').hidden = false; + } + else { + this._id('go-buttons').hidden = true; + } + + // Item type menu + if (this.showTypeMenu) { + // Build item type menu if it hasn't been built yet + if (!this._itemTypeMenu.firstChild.hasChildNodes()) { + var itemTypes = Zotero.ItemTypes.getTypes(); + for (var i=0; i0 ? this._tabIndexMinFields + i : 1) : 0; + this._tabIndexMaxInfoFields = Math.max(this._tabIndexMaxInfoFields, tabindex); + + if (fieldIsClickable && + !this.item.isPrimaryField(fieldName) && + Zotero.ItemFields.isFieldOfBase(Zotero.ItemFields.getID(fieldName), 'date')) { + this.addDateRow(fieldNames[i], this.item.getField(fieldName, true), tabindex); + continue; + } + } + + var valueElement = this.createValueElement( + val, fieldName, tabindex + ); + + var label = document.createElement("label"); + label.setAttribute('fieldname', fieldName); + + var prefix = ''; + // Add '(...)' before 'Abstract:' for collapsed abstracts + if (fieldName == 'abstractNote') { + if (val && !Zotero.Prefs.get('lastAbstractExpand')) { + prefix = '(...) '; + } + } + + if (fieldName) { + label.setAttribute("value", prefix + + Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, fieldName) + ":"); + } + + if (fieldName == 'url' && val) { + label.setAttribute("isButton", true); + // TODO: make getFieldValue non-private and use below instead + label.setAttribute("onclick", "ZoteroPane.loadURI(this.nextSibling.firstChild ? this.nextSibling.firstChild.nodeValue : this.nextSibling.value, event)"); + label.setAttribute("tooltiptext", Zotero.getString('pane.item.goToURL.online.tooltip')); + } + else if (fieldName == 'abstractNote') { + label.setAttribute("onclick", + "if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); } " + + "else { document.getBindingParent(this).toggleAbstractExpand(this); }"); + } + else { + label.setAttribute("onclick", + "if (this.nextSibling.inputField) { this.nextSibling.inputField.blur(); }"); + } + + this.addDynamicRow(label, valueElement); + + if (fieldName && this._selectField == fieldName) { + this.showEditor(valueElement); + } + } + this._selectField = false; + + // + // Creators + // + + // Creator type menu + if (this.editable) { + while (this._creatorTypeMenu.hasChildNodes()) { + this._creatorTypeMenu.removeChild(this._creatorTypeMenu.firstChild); + } + + var creatorTypes = Zotero.CreatorTypes.getTypesForItemType(this.item.itemTypeID); + var localized = {}; + for (var i=0; i 0) { + for (var i = 0, len=this.item.numCreators(); i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hbox->label->label->toolbarbutton + var button = row.lastChild.lastChild.previousSibling.previousSibling; + var hbox = button.previousSibling; + var lastName = hbox.firstChild; + var comma = hbox.firstChild.nextSibling; + var firstName = hbox.lastChild; + + // Switch to single-field mode + if (fieldMode == 1) { + button.setAttribute('image', 'chrome://zotero/skin/textfield-dual.png'); + button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two')); + lastName.setAttribute('fieldMode', '1'); + button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(this.parentNode.parentNode, 0)"); + lastName.setAttribute('flex', '1'); + + // Remove firstname field from tabindex + var tab = parseInt(firstName.getAttribute('ztabindex')); + firstName.setAttribute('ztabindex', -1); + if (this._tabIndexMaxCreators == tab) { + this._tabIndexMaxCreators--; + } + + // Hide first name field and prepend to last name field + firstName.setAttribute('hidden', true); + comma.setAttribute('hidden', true); + + if (!initial) { + var first = this._getFieldValue(firstName); + if (first && first != this._defaultFirstName) { + var last = this._getFieldValue(lastName); + this._setFieldValue(lastName, first + ' ' + last); + } + } + + if (this._getFieldValue(lastName) == this._defaultLastName) { + this._setFieldValue(lastName, this._defaultFullName); + } + } + // Switch to two-field mode + else { + button.setAttribute('image', 'chrome://zotero/skin/textfield-single.png'); + button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one')); + lastName.setAttribute('fieldMode', '0'); + button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(this.parentNode.parentNode, 1)"); + lastName.setAttribute('flex', '0'); + + // Add firstname field to tabindex + var tab = parseInt(lastName.getAttribute('ztabindex')); + firstName.setAttribute('ztabindex', tab + 1); + if (this._tabIndexMaxCreators == tab) + { + this._tabIndexMaxCreators++; + } + + if (!initial) { + // Move all but last word to first name field and show it + var last = this._getFieldValue(lastName); + if (last && last != this._defaultFullName) { + var lastNameRE = /(.*?)[ ]*([^ ]+[ ]*)$/; + var parts = lastNameRE.exec(last); + if (parts[2] && parts[2] != last) + { + this._setFieldValue(lastName, parts[2]); + this._setFieldValue(firstName, parts[1]); + } + } + } + + if (!this._getFieldValue(firstName)) { + this._setFieldValue(firstName, this._defaultFirstName); + } + + if (this._getFieldValue(lastName) == this._defaultFullName) { + this._setFieldValue(lastName, this._defaultLastName); + } + + firstName.setAttribute('hidden', false); + comma.setAttribute('hidden', false); + } + + // Save the last-used field mode + Zotero.debug("Switching lastCreatorFieldMode to " + fieldMode); + Zotero.Prefs.set('lastCreatorFieldMode', fieldMode); + + if (!initial) + { + var index = button.getAttribute('fieldname').split('-')[1]; + var fields = this.getCreatorFields(row); + fields.fieldMode = fieldMode; + this.modifyCreator(index, fields); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + elements inside a vbox + if (fieldName == 'extra' || abstractAsVbox) { + var lines = valueText.split("\n"); + for (var i = 0; i < lines.length; i++) { + var descriptionNode = document.createElement("description"); + var linetext = document.createTextNode(lines[i]); + descriptionNode.appendChild(linetext); + valueElement.appendChild(descriptionNode); + } + } + // 29 == arbitrary length at which to chop uninterrupted text + else if ((firstSpace == -1 && valueText.length > 29 ) || firstSpace > 29 + || (fieldName && + (fieldName.substr(0, 7) == 'creator') || fieldName == 'abstractNote')) { + if (fieldName == 'abstractNote') { + valueText = valueText.replace(/[\t\n]/g, ' '); + } + valueElement.setAttribute('crop', 'end'); + valueElement.setAttribute('value',valueText); + } + else { + // Wrap to multiple lines + valueElement.appendChild(document.createTextNode(valueText)); + } + + return valueElement; + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +