diff --git a/.babelrc b/.babelrc index c11d720d86..2965585ef6 100644 --- a/.babelrc +++ b/.babelrc @@ -18,6 +18,7 @@ "syntax-jsx", "transform-react-jsx", "transform-react-display-name", + "transform-class-properties", [ "transform-es2015-modules-commonjs", { diff --git a/chrome/content/zotero/bindings/tagselector.xml b/chrome/content/zotero/bindings/tagselector.xml deleted file mode 100644 index 9db67dc840..0000000000 --- a/chrome/content/zotero/bindings/tagselector.xml +++ /dev/null @@ -1,1213 +0,0 @@ - - - - - - - - - - - - - - - - - - - - false - false - null - - null - null - - - "view" - - - - - - - - - - - - - - - - - - - - - null - - - - - - - - - false - null - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Zotero.Promise.check(this.mode)); - - let { div, emptyRegular } = this.createTagsList(this._tags); - - this._emptyRegular = emptyRegular; - tagsBox.innerHTML = ""; - tagsBox.appendChild(div); - this._dirty = false; - this._tagsDiv = null; - } - // Otherwise just update based on visibility - else { - // If only a few tags, regenerate buttons from scratch - if (this.filterToScope && this._scope.size <= 100) { - // If full set is currently displayed, store it for later - if (!this._tagsDiv) { - this._tagsDiv = tagsBox.firstChild; - } - - let tags = []; - this._scope.forEach(function (types, name) { - tags.push(...types.map(type => { - return { - tag: name, - type - }; - })); - }); - let { div, emptyRegular } = this.createTagsList(tags); - tagsBox.replaceChild(div, tagsBox.firstChild); - this._emptyRegular = emptyRegular; - } - // Otherwise swap in the stored buttons from the last full run, - // after updating their visibility - else { - let oldDiv = tagsBox.removeChild(tagsBox.firstChild); - if (!this._tagsDiv) { - this._tagsDiv = oldDiv; - } - - let elems = this._tagsDiv.childNodes; - let tagColors = Zotero.Tags.getColors(this.libraryID); - for (let i = 0; i < elems.length; i++) { - let elem = elems[i]; - let visible = this._updateClickableTag( - elem, elem.textContent, tagColors - ); - if (visible) { - this._emptyRegular = false; - } - } - tagsBox.appendChild(this._tagsDiv); - } - } - - //start tag cloud code - - var tagCloud = Zotero.Prefs.get('tagCloud'); - if (false && tagCloud) { - var labels = tagsBox.getElementsByTagName('label'); - - //loop through displayed labels and find number of linked items - var numlinked= []; - for (var i=0; i - - - - - - tag.tag)); - [...new Set(tagColors.keys())].filter(x => !regularTags.has(x)).forEach((x) => { - tags.push(Zotero.Tags.cleanData({ tag: x })); - }); - - // Sort by name - var t = new Date(); - Zotero.debug("Sorting tags"); - var collation = Zotero.getLocaleCollation(); - tags.sort(function (a, b) { - return collation.compareString(1, a.tag, b.tag); - }); - Zotero.debug(`Sorted tags in ${new Date() - t} ms`); - - var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); - var emptyRegular = true; - var lastTag; - for (let i = 0; i < tags.length; i++) { - let tagData = tags[i]; - - // Only show tags of different types once - if (tagData.tag === lastTag) { - continue; - } - lastTag = tagData.tag; - - let elem = this._insertClickableTag(div, tagData); - let visible = this._updateClickableTag( - elem, tagData.tag, tagColors - ); - if (visible) { - emptyRegular = false; - } - } - return { div, emptyRegular }; - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - val.split("/")[1] == 'tagColors')) { - yield this.refresh(true); - } - return; - } - - // Ignore anything other than deletes in duplicates view - if (this.collectionTreeRow.isDuplicates()) { - switch (event) { - case 'delete': - case 'trash': - break; - - default: - return; - } - } - - // Ignore item events other than 'trash' - if (type == 'item' && event != 'trash') { - return; - } - - var selectionChanged = false; - - // If a selected tag no longer exists, deselect it - if (event == 'delete' || event == 'trash' || event == 'modify') { - // TODO: necessary, or just use notifier value? - this._tags = yield Zotero.Tags.getAll(this.libraryID, this._types); - - for (let tag of this.selection) { - for (let tag2 of this._tags) { - if (tag == tag2) { - var found = true; - break; - } - } - if (!found) { - this.selection.delete(tag); - selectionChanged = true; - } - } - } - - if (event == 'add') { - if (type == 'item-tag') { - let tagObjs = ids - // Get tag name and type - .map(x => extraData[x]) - // Ignore tag adds for items not in the current library, if there is one - .filter(function (x) { - if (!this._libraryID) return true; - return x.libraryID == this._libraryID; - }.bind(this)); - - if (tagObjs.length) { - this.insertSorted(this.id('tags-box').firstChild, tagObjs); - // If full set isn't currently displayed, update it too - if (this._tagsDiv) { - this.insertSorted(this._tagsDiv, tagObjs); - } - } - } - // Don't add anything for item or collection-item; just update scope - - return this.updateScope(); - } - - var t = this.id('tags-search').inputField; - if (t.value) { - this.setSearch(t.value, true); - } - else { - this.setSearch(false, true); - } - this._dirty = true; - - // This is a hack, but set this to run after the refresh, - // since _emptyRegular isn't set until then - this.onRefresh = function () { - // If no regular tags visible after a delete, deselect all. - // This is necessary so that a selected tag that's removed - // from its last item doesn't cause all regular tags to - // disappear without anything being visibly selected. - if ((event == 'remove' || event == 'delete') && - this._emptyRegular && this.getNumSelected()) { - Zotero.debug('No tags visible after delete -- deselecting all'); - return this.clearAll(); - } - }.bind(this); - - // If the selection changed, update the items list - if (selectionChanged && this.onchange) { - return this.onchange(); - } - - // Otherwise, just update the tag selector - return this.updateScope(); - }, this); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { - Zotero.updateZoteroPaneProgressMeter( - Math.round(progress / progressMax * 100) - ); - } - ); - } - finally { - Zotero.hideZoteroPaneOverlays(); - } - } - }.bind(this))(); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - = Zotero.Tags.MAX_COLORED_TAGS && !tagColors.has(io.name)) { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "", Zotero.getString('pane.tagSelector.maxColoredTags', Zotero.Tags.MAX_COLORED_TAGS)); - return; - } - - io.tagColors = tagColors; - - window.openDialog( - 'chrome://zotero/content/tagColorChooser.xul', - "zotero-tagSelector-colorChooser", - "chrome,modal,centerscreen", io - ); - - // Dialog cancel - if (typeof io.color == 'undefined') { - return; - } - - yield Zotero.Tags.setColor(this.libraryID, io.name, io.color, io.position); - }.bind(this)); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/reactUI/containers/tag-selector.jsx b/chrome/content/zotero/reactUI/containers/tag-selector.jsx new file mode 100644 index 0000000000..4085cbac58 --- /dev/null +++ b/chrome/content/zotero/reactUI/containers/tag-selector.jsx @@ -0,0 +1,455 @@ +/* global Zotero: false */ +'use strict'; + +(function() { + +const React = require('react'); +const ReactDom = require('react-dom'); +const TagSelector = require('react-ui/components/tag-selector.js'); +const noop = Promise.resolve(); +const defaults = { + tags: [], + searchString: '', + shouldFocus: false, + onSelection: noop, + viewOnly: false +}; +const { Cc, Ci } = require('chrome'); + +ZoteroPane.React.TagSelector = class TagSelectorContainer extends React.Component { + constructor(props) { + super(props); + this.opts = Object.assign({}, defaults, props); + this.selectedTags = new Set(); + this.updateScope = Promise.resolve; + this.filterToScope = true; + this.showAutomatic = true; + this._notifierID = Zotero.Notifier.registerObserver( + this, + ['collection-item', 'item', 'item-tag', 'tag', 'setting'], + 'tagSelector' + ); + this._scope = null; + this._allTags = null; + this.state = null; + this.update(); + } + + componentWillReceiveProps(nextProps) { + this.opts = Object.assign({}, this.opts, nextProps); + this.update(); + } + + async notify(event, type, ids, extraData) { + if (type === 'setting') { + if (ids.some(val => val.split('/')[1] == 'tagColors')) { + await this.refresh(true); + } + return; + } + + // Ignore anything other than deletes in duplicates view + if (this.collectionTreeRow.isDuplicates()) { + switch (event) { + case 'delete': + case 'trash': + break; + + default: + return; + } + } + + // Ignore item events other than 'trash' + if (type == 'item' && event != 'trash') { + return; + } + + + // If a selected tag no longer exists, deselect it + if (event == 'delete' || event == 'trash' || event == 'modify') { + for (let tag of this.selectedTags) { + if (tag == extraData[ids[0]].old.tag) { + this.selectedTags.delete(tag); + break; + } + } + await this.refresh(true); + } + + if (event == 'add') { + if (type == 'item-tag') { + let tagObjs = ids + // Get tag name and type + .map(x => extraData[x]) + // Ignore tag adds for items not in the current library, if there is one + .filter(x => { + if (!this._libraryID) { + return true; + } + return x.libraryID == this._libraryID; + }); + + if (tagObjs.length) { + this.insertSorted(tagObjs); + } + } + } + + // Otherwise, just update the tag selector + return this.updateScope(); + } + + async refresh(fetch) { + let t = new Date; + + if(this._allTags === null) { + fetch = true; + } + + if (fetch) { + Zotero.debug('Reloading tags selector'); + } else { + Zotero.debug('Refreshing tags selector'); + } + + // If new data, rebuild boxes + if (fetch) { + let tagColors = Zotero.Tags.getColors(this.libraryID); + this._allTags = await Zotero.Tags.getAll(this.libraryID, this.showAutomatic ? [0, 1] : [0]); + // .tap(() => Zotero.Promise.check(this.mode)); + + // Add colored tags that aren't already real tags + let regularTags = new Set(this._allTags.map(tag => tag.tag)); + let coloredTags = Array.from(tagColors.keys()); + + coloredTags.filter(ct => !regularTags.has(ct)).forEach(x => + this._allTags.push(Zotero.Tags.cleanData({ tag: x })) + ); + + // Sort by name + this._allTags.sort(function (a, b) { + return Zotero.getLocaleCollation().compareString(1, a.tag, b.tag); + }); + + } + + this.update(); + + Zotero.debug('Loaded tag selector in ' + (new Date - t) + ' ms'); + + // var event = new Event('refresh'); + // this.dispatchEvent(event); + } + + update() { + var tags; + let flatScopeTags = Array.isArray(this._scope) ? this._scope.map(tag => tag.tag) : []; + + if('libraryID' in this) { + var tagColors = Zotero.Tags.getColors(this.libraryID); + } + + if(this.filterToScope && Array.isArray(this._scope)) { + tags = this._allTags.filter(tag => + flatScopeTags.includes(tag.tag) || (tagColors && tagColors.has(tag.tag)) + ); + } else { + tags = this._allTags ? this._allTags.slice(0) : []; + } + + if(this.opts.searchString) { + tags = tags.filter(tag => !!tag.tag.match(new RegExp(this.opts.searchString, 'i'))); + } + + this.opts.tags = tags.map(t => { + let name = t.tag; + let selected = this.selectedTags.has(name); + let color = tagColors && tagColors.has(name) ? tagColors.get(name).color : ''; + let disabled = !flatScopeTags.includes(name); + return { name, selected, color, disabled }; + }); + this.state ? this.setState(this.opts) : this.state = Object.assign({}, this.opts); + } + + render() { + return {} : this.onTagSelectedHandler.bind(this) } + onTagContext={ this.onTagContextHandler.bind(this) } + onSearch={ this.onSearchHandler.bind(this) } + onSettings={ this.onTagSelectorViewSettingsHandler.bind(this) } + />; + } + + setMode(mode) { + this.setState({viewOnly: mode == 'view'}); + } + + focusTextbox() { + this.opts.shouldFocus = true; + this.update(); + } + + toggle() { + this._isCollapsed = !this._isCollapsed; + } + + unregister() { + ReactDom.unmountComponentAtNode(this.domEl); + if (this._notifierID) { + Zotero.Notifier.unregisterObserver(this._notifierID); + } + } + + uninit() { + this.selectedTags = new Set(); + this.opts.searchString = ''; + this.update(); + } + + onTagContextHandler(tag, ev) { + let tagContextMenu = document.getElementById('tag-menu'); + ev.preventDefault(); + tagContextMenu.openPopup(ev.target, 'end_before', 0, 0, true); + this.contextTag = tag; + } + + onTagSelectorViewSettingsHandler(ev) { + let settingsContextMenu = document.getElementById('tag-selector-view-settings-menu'); + ev.preventDefault(); + settingsContextMenu.openPopup(ev.target, 'end_before', 0, 0, true); + } + + onTagSelectedHandler(tag) { + if(this.selectedTags.has(tag)) { + this.selectedTags.delete(tag); + } else { + this.selectedTags.add(tag); + } + + if('onSelection' in this.opts && typeof(this.opts.onSelection) === 'function') { + this.opts.onSelection(this.selectedTags).then(this.refresh.bind(this)); + } + } + + onSearchHandler(searchString) { + this.opts.searchString = searchString; + this.update(); + } + + getTagSelection() { + return this.selectedTags; + } + + clearTagSelection() { + this.selectedTags = new Set(); + return this.selectedTags; + } + + async openColorPickerWindow() { + var io = { + libraryID: this.libraryID, + name: this.contextTag.name + }; + + var tagColors = Zotero.Tags.getColors(this.libraryID); + if (tagColors.size >= Zotero.Tags.MAX_COLORED_TAGS && !tagColors.has(io.name)) { + var ps = Cc['@mozilla.org/embedcomp/prompt-service;1'] + .getService(Ci.nsIPromptService); + ps.alert(null, '', Zotero.getString('pane.tagSelector.maxColoredTags', Zotero.Tags.MAX_COLORED_TAGS)); + return; + } + + io.tagColors = tagColors; + + window.openDialog( + 'chrome://zotero/content/tagColorChooser.xul', + 'zotero-tagSelector-colorChooser', + 'chrome,modal,centerscreen', io + ); + + // Dialog cancel + if (typeof io.color == 'undefined') { + return; + } + + await Zotero.Tags.setColor(this.libraryID, io.name, io.color, io.position); + + this.refresh(); + } + + async openRenamePrompt() { + var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1'] + .getService(Ci.nsIPromptService); + + var newName = { value: this.contextTag.name }; + var result = promptService.prompt(window, + Zotero.getString('pane.tagSelector.rename.title'), + Zotero.getString('pane.tagSelector.rename.message'), + newName, '', {}); + + if (!result || !newName.value || this.contextTag.name == newName.value) { + return; + } + + if (this.selectedTags.has(this.contextTag.name)) { + var wasSelected = true; + this.selectedTags.delete(this.contextTag.name); + } + + if (Zotero.Tags.getID(this.contextTag.name)) { + await Zotero.Tags.rename(this.libraryID, this.contextTag.name, newName.value); + } + // Colored tags don't need to exist, so in that case + // just rename the color setting + else { + let color = Zotero.Tags.getColor(this.libraryID, this.contextTag.name); + if (!color) { + throw new Error("Can't rename missing tag"); + } + await Zotero.Tags.setColor(this.libraryID, this.contextTag.name, false); + await Zotero.Tags.setColor(this.libraryID, newName, color); + } + + if(wasSelected) { + this.selectedTags.add(newName.value); + } + + this.updateScope(); + } + + async openDeletePrompt() { + var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1'] + .getService(Ci.nsIPromptService); + + var confirmed = promptService.confirm(window, + Zotero.getString('pane.tagSelector.delete.title'), + Zotero.getString('pane.tagSelector.delete.message')); + + if (!confirmed) { + return; + } + + var tagID = Zotero.Tags.getID(this.contextTag.name); + + if (tagID) { + await Zotero.Tags.removeFromLibrary(this.libraryID, tagID); + } + // If only a tag color setting, remove that + else { + await Zotero.Tags.setColor(this.libraryID, this.contextTag.name, false); + } + + this.updateScope(); + } + + toggleFilterToScope(newValue) { + this.filterToScope = typeof(newValue) === 'undefined' ? !this.filterToScope : newValue; + this.refresh(); + } + + toggleShowAutomatic(newValue) { + this.showAutomatic = typeof(newValue) === 'undefined' ? !this.showAutomatic : newValue; + this.refresh(true); + } + + deselectAll() { + this.selectedTags = new Set(); + if('onSelection' in this.opts && typeof(this.opts.onSelection) === 'function') { + this.opts.onSelection(this.selectedTags).then(this.refresh.bind(this)); + } + } + + set scope(newScope) { + try { + this._scope = Array.from(newScope); + } catch(e) { + this._scope = null; + } + this.refresh(); + } + + get label() { + let count = this.selectedTags.size; + let mod = count === 1 ? 'singular' : count === 0 ? 'none' : 'plural'; + + return Zotero.getString('pane.tagSelector.numSelected.' + mod, [count]); + } + + onResize() { + const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar + + //Zotero.debug('Updating tag selector size'); + var zoteroPane = document.getElementById('zotero-pane-stack'); + var splitter = document.getElementById('zotero-tags-splitter'); + var tagSelector = document.getElementById('zotero-tag-selector'); + + // Nothing should be bigger than appcontent's height + var max = document.getElementById('appcontent').boxObject.height + - splitter.boxObject.height; + + // Shrink tag selector to appcontent's height + var maxTS = max - COLLECTIONS_HEIGHT; + var tsHeight = parseInt(tagSelector.getAttribute("height")); + if (tsHeight > maxTS) { + //Zotero.debug("Limiting tag selector height to appcontent"); + tagSelector.setAttribute('height', maxTS); + } + tagSelector.style.height = tsHeight + 'px'; + + var height = tagSelector.getBoundingClientRect().height; + + /*Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height); + Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height')); + Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height); + Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));*/ + + + // Don't let the Z-pane jump back down to its previous height + // (if shrinking or hiding the tag selector let it clear the min-height) + if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) { + //Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height); + zoteroPane.setAttribute('height', zoteroPane.boxObject.height); + } + + if (tagSelector.getAttribute('collapsed') == 'true') { + // 32px is the default Z pane min-height in overlay.css + height = 32; + } + else { + // tS.boxObject.height doesn't exist at startup, so get from attribute + if (!height) { + height = parseInt(tagSelector.getAttribute('height')); + } + // 121px seems to be enough room for the toolbar and collections + // tree at minimum height + height = height + COLLECTIONS_HEIGHT; + } + + //Zotero.debug('Setting Zotero pane minheight to ' + height); + zoteroPane.setAttribute('minheight', height); + + if (this.isShowing() && !this.isFullScreen()) { + zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height); + } + + // Fix bug whereby resizing the Z pane downward after resizing + // the tag selector up and then down sometimes caused the Z pane to + // stay at a fixed size and get pushed below the bottom + tagSelector.height++; + tagSelector.height--; + } + + static init(domEl, opts) { + var ref; + console.log(domEl.style); + ReactDom.render( ref = c } {...opts} />, domEl); + ref.domEl = domEl; + new MutationObserver(ref.onResize).observe(domEl, {attributes: true, attributeFilter: ['height']}); + return ref; + } +} +})(); diff --git a/chrome/content/zotero/reactUI/tagSelector.xul b/chrome/content/zotero/reactUI/tagSelector.xul new file mode 100644 index 0000000000..d1adca8208 --- /dev/null +++ b/chrome/content/zotero/reactUI/tagSelector.xul @@ -0,0 +1,58 @@ + + + %globalDTD; + %zoteroDTD; +]> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/chrome/content/zotero/reactUI/zoteroPane.js b/chrome/content/zotero/reactUI/zoteroPane.js new file mode 100644 index 0000000000..c77ce511d9 --- /dev/null +++ b/chrome/content/zotero/reactUI/zoteroPane.js @@ -0,0 +1,40 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2018 Center for History and New Media + George Mason University, Fairfax, Virginia, USA + http://zotero.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Zotero is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Zotero. If not, see . + + ***** END LICENSE BLOCK ***** +*/ + +'use strict'; + +ZoteroPane.React = { + init() { + var tagSelector = document.getElementById('zotero-tag-selector'); + ZoteroPane_Local.tagSelector = ZoteroPane.React.TagSelector.init(tagSelector, { + onSelection: ZoteroPane_Local.updateTagFilter.bind(ZoteroPane_Local) + }); + }, + + destroy() { + ZoteroPane_Local.tagSelector.unregister(); + } +} + diff --git a/chrome/content/zotero/reactUI/zoteroPane.xul b/chrome/content/zotero/reactUI/zoteroPane.xul new file mode 100644 index 0000000000..056cf67fcd --- /dev/null +++ b/chrome/content/zotero/reactUI/zoteroPane.xul @@ -0,0 +1,35 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/chrome/content/zotero/standalone/standalone.xul b/chrome/content/zotero/standalone/standalone.xul index dd381dc7f6..ad04b99844 100644 --- a/chrome/content/zotero/standalone/standalone.xul +++ b/chrome/content/zotero/standalone/standalone.xul @@ -287,4 +287,7 @@ + + + diff --git a/chrome/content/zotero/xpcom/notifier.js b/chrome/content/zotero/xpcom/notifier.js index 6ffda23b33..11a64f547e 100644 --- a/chrome/content/zotero/xpcom/notifier.js +++ b/chrome/content/zotero/xpcom/notifier.js @@ -73,6 +73,7 @@ Zotero.Notifier = new function(){ if (priority) { msg += " with priority " + priority; } + Zotero.debug(msg); _observers[hash] = { ref: ref, types: types, diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 1b597f2f6d..e5c1726ef6 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -58,8 +58,6 @@ var ZoteroPane = new function() this.document = document; - const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar - var self = this, _loaded = false, _madeVisible = false, titlebarcolorState, titleState, observerService, @@ -171,11 +169,6 @@ var ZoteroPane = new function() itemsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true); itemsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true); - var tagSelector = document.getElementById('zotero-tag-selector'); - tagSelector.onchange = function () { - return ZoteroPane_Local.updateTagFilter(); - }; - Zotero.Keys.windowInit(document); if (Zotero.restoreFromServer) { @@ -240,6 +233,7 @@ var ZoteroPane = new function() } catch (e) {} } + ZoteroPane.React.init(); if (Zotero.openPane) { Zotero.openPane = false; @@ -351,8 +345,7 @@ var ZoteroPane = new function() this.serializePersist(); } - var tagSelector = document.getElementById('zotero-tag-selector'); - tagSelector.unregister(); + ZoteroPane_Local.React.destroy(); if(this.collectionsView) this.collectionsView.unregister(); if(this.itemsView) this.itemsView.unregister(); @@ -1106,85 +1099,17 @@ var ZoteroPane = new function() // and focus filter textbox if (showing) { yield this.setTagScope(); - tagSelector.focusTextbox(); + ZoteroPane_Local.tagSelector.focusTextbox(); } // If hiding, clear selection else { - tagSelector.uninit(); + ZoteroPane_Local.tagSelector.uninit(); } }); this.updateTagSelectorSize = function () { - //Zotero.debug('Updating tag selector size'); - var zoteroPane = document.getElementById('zotero-pane-stack'); - var splitter = document.getElementById('zotero-tags-splitter'); - var tagSelector = document.getElementById('zotero-tag-selector'); - // Nothing should be bigger than appcontent's height - var max = document.getElementById('appcontent').boxObject.height - - splitter.boxObject.height; - - // Shrink tag selector to appcontent's height - var maxTS = max - COLLECTIONS_HEIGHT; - if (parseInt(tagSelector.getAttribute("height")) > maxTS) { - //Zotero.debug("Limiting tag selector height to appcontent"); - tagSelector.setAttribute('height', maxTS); - } - - var height = tagSelector.boxObject.height; - - - /*Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height); - Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height')); - Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height); - Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));*/ - - - // Don't let the Z-pane jump back down to its previous height - // (if shrinking or hiding the tag selector let it clear the min-height) - if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) { - //Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height); - zoteroPane.setAttribute('height', zoteroPane.boxObject.height); - } - - if (tagSelector.getAttribute('collapsed') == 'true') { - // 32px is the default Z pane min-height in overlay.css - height = 32; - } - else { - // tS.boxObject.height doesn't exist at startup, so get from attribute - if (!height) { - height = parseInt(tagSelector.getAttribute('height')); - } - // 121px seems to be enough room for the toolbar and collections - // tree at minimum height - height = height + COLLECTIONS_HEIGHT; - } - - //Zotero.debug('Setting Zotero pane minheight to ' + height); - zoteroPane.setAttribute('minheight', height); - - if (this.isShowing() && !this.isFullScreen()) { - zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height); - } - - // Fix bug whereby resizing the Z pane downward after resizing - // the tag selector up and then down sometimes caused the Z pane to - // stay at a fixed size and get pushed below the bottom - tagSelector.height++; - tagSelector.height--; - } - - - function getTagSelection() { - var tagSelector = document.getElementById('zotero-tag-selector'); - return tagSelector.selection ? tagSelector.selection : new Set(); - } - - - this.clearTagSelection = function () { - document.getElementById('zotero-tag-selector').deselectAll(); } @@ -1193,7 +1118,7 @@ var ZoteroPane = new function() */ this.updateTagFilter = Zotero.Promise.coroutine(function* () { if (this.itemsView) { - yield this.itemsView.setFilter('tags', getTagSelection()); + yield this.itemsView.setFilter('tags', ZoteroPane_Local.tagSelector.getTagSelection()); } }); @@ -1212,23 +1137,23 @@ var ZoteroPane = new function() * * Passed to the items tree to trigger on changes */ - this.setTagScope = Zotero.Promise.coroutine(function* () { - var collectionTreeRow = this.getCollectionTreeRow(); + this.setTagScope = async function () { + var collectionTreeRow = self.getCollectionTreeRow(); var tagSelector = document.getElementById('zotero-tag-selector'); - if (this.tagSelectorShown()) { + if (self.tagSelectorShown()) { Zotero.debug('Updating tag selector with current tags'); if (collectionTreeRow.editable) { - tagSelector.mode = 'edit'; + ZoteroPane_Local.tagSelector.setMode('edit'); } else { - tagSelector.mode = 'view'; + ZoteroPane_Local.tagSelector.setMode('view'); } - tagSelector.collectionTreeRow = collectionTreeRow; - tagSelector.updateScope = () => this.setTagScope(); - tagSelector.libraryID = collectionTreeRow.ref.libraryID; - tagSelector.scope = yield collectionTreeRow.getChildTags(); + ZoteroPane_Local.tagSelector.collectionTreeRow = collectionTreeRow; + ZoteroPane_Local.tagSelector.updateScope = self.setTagScope; + ZoteroPane_Local.tagSelector.libraryID = collectionTreeRow.ref.libraryID; + ZoteroPane_Local.tagSelector.scope = await collectionTreeRow.getChildTags(); } - }); + }; this.onCollectionSelected = function () { @@ -1280,7 +1205,7 @@ var ZoteroPane = new function() }*/ collectionTreeRow.setSearch(''); - collectionTreeRow.setTags(getTagSelection()); + collectionTreeRow.setTags(ZoteroPane_Local.tagSelector.getTagSelection()); this._updateToolbarIconsForRow(collectionTreeRow); diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul index adff5bfeae..6ba72fabb1 100644 --- a/chrome/content/zotero/zoteroPane.xul +++ b/chrome/content/zotero/zoteroPane.xul @@ -27,6 +27,7 @@ + %globalDTD; @@ -35,7 +36,8 @@ ]> + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml">