fx-compat: long tag fixer (#2649)
* Manual tag splitting from tag selector * Only apply split to the tag in current library * Preserve tag type
This commit is contained in:
parent
b1595cdd1d
commit
13cc393840
12 changed files with 406 additions and 251 deletions
|
@ -642,6 +642,46 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
|
||||||
await Zotero.Tags.setColor(this.libraryID, io.name, io.color, io.position);
|
await Zotero.Tags.setColor(this.libraryID, io.name, io.color, io.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openTagSplitterWindow() {
|
||||||
|
const oldTagName = this.contextTag.name; // contextTag contains { name, width, color }
|
||||||
|
const dataIn = {
|
||||||
|
oldTag: this.contextTag.name,
|
||||||
|
isLongTag: false
|
||||||
|
};
|
||||||
|
const dataOut = { result: null };
|
||||||
|
|
||||||
|
window.openDialog(
|
||||||
|
'chrome://zotero/content/longTagFixer.xhtml',
|
||||||
|
'',
|
||||||
|
'chrome,modal,centerscreen',
|
||||||
|
dataIn, dataOut
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!dataOut.result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldTagID = Zotero.Tags.getID(oldTagName);
|
||||||
|
|
||||||
|
if (dataOut.result.op === 'split') {
|
||||||
|
const itemIDs = await Zotero.Tags.getTagItems(this.libraryID, oldTagID);
|
||||||
|
await Zotero.DB.executeTransaction(async () => {
|
||||||
|
for (const itemID of itemIDs) {
|
||||||
|
const item = await Zotero.Items.getAsync(itemID);
|
||||||
|
const tagType = item.getTagType(oldTagName);
|
||||||
|
for (const newTagName of dataOut.result.tags) {
|
||||||
|
item.addTag(newTagName, tagType);
|
||||||
|
}
|
||||||
|
item.removeTag(oldTagName);
|
||||||
|
await item.save();
|
||||||
|
}
|
||||||
|
await Zotero.Tags.purge(oldTagID);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported op: ' + dataOut.result.op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async openRenamePrompt() {
|
async openRenamePrompt() {
|
||||||
var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
|
var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
|
||||||
.getService(Ci.nsIPromptService);
|
.getService(Ci.nsIPromptService);
|
||||||
|
|
|
@ -1,191 +1,212 @@
|
||||||
/*
|
/*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright © 2009 Center for History and New Media
|
Copyright © 2022 Corporation for Digital Scholarship
|
||||||
George Mason University, Fairfax, Virginia, USA
|
Vienna, Virginia, USA
|
||||||
http://zotero.org
|
https://www.zotero.org
|
||||||
|
|
||||||
This file is part of Zotero.
|
This file is part of Zotero.
|
||||||
|
|
||||||
Zotero is free software: you can redistribute it and/or modify
|
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
|
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
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Zotero is distributed in the hope that it will be useful,
|
Zotero is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const HTML_NS = 'http://www.w3.org/1999/xhtml';
|
||||||
|
|
||||||
var Zotero_Long_Tag_Fixer = new function () {
|
var Zotero_Long_Tag_Fixer = new function () { // eslint-disable-line camelcase, no-unused-vars
|
||||||
var _oldTag = window.arguments[0];
|
const { oldTag, isLongTag } = window.arguments?.[0] ?? { isLongTag: true, oldTag: '' };
|
||||||
var _dataOut = window.arguments[1];
|
const dataOut = window.arguments?.[1] || {};
|
||||||
|
|
||||||
this.init = function () {
|
this.init = function () {
|
||||||
document.getElementById('zotero-old-tag').value = _oldTag;
|
const lastMode = Zotero.Prefs.get('lastLongTagMode') || 0;
|
||||||
document.getElementById('zotero-old-tag-delimiter').nextSibling.value = Zotero.getString('general.character.singular');
|
const delimiter = Zotero.Prefs.get('lastLongTagDelimiter');
|
||||||
|
|
||||||
var delimiter = Zotero.Prefs.get('lastLongTagDelimiter');
|
this.dialog = document.getElementById('zotero-long-tag-fixer');
|
||||||
document.getElementById('zotero-old-tag-delimiter').value = delimiter;
|
this.intro = document.getElementById('intro');
|
||||||
|
this.tabs = document.getElementById('zotero-new-tag-actions');
|
||||||
var lastMode = Zotero.Prefs.get('lastLongTagMode');
|
this.oldTagInput = document.getElementById('zotero-old-tag');
|
||||||
if (!lastMode) {
|
this.oldTag = document.getElementById('zotero-old-tag');
|
||||||
lastMode = 0;
|
this.delimiterLabel = document.getElementById('delimiter-label');
|
||||||
}
|
this.oldTagDelimiter = document.getElementById('zotero-old-tag-delimiter');
|
||||||
this.switchMode(lastMode);
|
this.listbox = document.getElementById('zotero-new-tag-list');
|
||||||
}
|
this.newTagInput = document.getElementById('zotero-new-tag-editor');
|
||||||
|
this.newTagCharacterCount = document.getElementById('zotero-new-tag-character-count');
|
||||||
|
this.zoteroNewTagInfo = document.getElementById('zotero-new-tag-characters');
|
||||||
|
|
||||||
|
document.addEventListener('dialogaccept', () => this.accept());
|
||||||
|
document.addEventListener('dialogcancel', () => this.cancel());
|
||||||
|
this.tabs.addEventListener('select', (ev) => {
|
||||||
|
if (ev.target === this.tabs.querySelector('tabpanels')) {
|
||||||
|
this.switchMode(ev.currentTarget.selectedIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialog.classList.toggle('is-long-tag', isLongTag);
|
||||||
|
|
||||||
|
this.oldTagDelimiter.addEventListener('input', () => this.onUpdateDelimiter());
|
||||||
|
this.newTagInput.addEventListener('input', ev => this.updateEditLength(ev.currentTarget.value.length));
|
||||||
|
|
||||||
|
this.oldTagInput.value = oldTag;
|
||||||
|
this.oldTagDelimiter.value = delimiter;
|
||||||
|
|
||||||
|
this.updateLabel();
|
||||||
|
this.switchMode(isLongTag ? lastMode : 0);
|
||||||
|
};
|
||||||
|
|
||||||
this.switchMode = function (index) {
|
this.switchMode = function (index) {
|
||||||
var dialog = document.getElementById('zotero-long-tag-fixer');
|
this.tabs.selectedIndex = index;
|
||||||
|
let buttonLabel = "";
|
||||||
document.getElementById('zotero-new-tag-actions').selectedIndex = index;
|
|
||||||
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
|
default:
|
||||||
case 0:
|
case 0:
|
||||||
var buttonLabel = "saveTags";
|
buttonLabel = 'saveTags';
|
||||||
this.updateTagList();
|
this.updateTagList();
|
||||||
document.getElementById('zotero-old-tag-delimiter').select();
|
this.oldTagDelimiter.select();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
var buttonLabel = "saveTag";
|
buttonLabel = 'saveTag';
|
||||||
document.getElementById('zotero-new-tag-editor').value = _oldTag;
|
this.newTagInput.value = oldTag;
|
||||||
this.updateEditLength(_oldTag.length)
|
this.updateEditLength(oldTag.length);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
var buttonLabel = "deleteTag";
|
buttonLabel = 'deleteTag';
|
||||||
dialog.getButton('accept').disabled = false;
|
this.dialog.getButton('accept').disabled = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('zotero-long-tag-fixer').getButton('accept').label = Zotero.getString('sync.longTagFixer.' + buttonLabel);
|
this.dialog.getButton('accept').label = Zotero.getString('sync.longTagFixer.' + buttonLabel);
|
||||||
window.sizeToContent();
|
window.sizeToContent();
|
||||||
Zotero.Prefs.set('lastLongTagMode', index);
|
if (isLongTag) {
|
||||||
}
|
Zotero.Prefs.set('lastLongTagMode', index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split tags and populate list
|
* Split tags and populate list
|
||||||
*/
|
*/
|
||||||
this.updateTagList = function () {
|
this.updateTagList = function () {
|
||||||
var listbox = document.getElementById('zotero-new-tag-list');
|
let tags = [];
|
||||||
while (listbox.childNodes.length) {
|
|
||||||
listbox.removeChild(listbox.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
var delimiter = document.getElementById('zotero-old-tag-delimiter').value;
|
const delimiter = document.getElementById('zotero-old-tag-delimiter').value;
|
||||||
if (delimiter) {
|
if (delimiter) {
|
||||||
Zotero.Prefs.set('lastLongTagDelimiter', delimiter);
|
Zotero.Prefs.set('lastLongTagDelimiter', delimiter);
|
||||||
var re = new RegExp("\\s*" + delimiter.replace(/([\.\-\[\]\(\)\?\*\+])/g, "\\$1") + "\\s*");
|
const re = new RegExp("\\s*" + delimiter.replace(/([\.\-\[\]\(\)\?\*\+])/g, "\\$1") + "\\s*");
|
||||||
var tags = _oldTag.split(re);
|
tags = [...new Set(oldTag.split(re).filter(t => t.length > 0))];
|
||||||
}
|
}
|
||||||
|
|
||||||
var acceptButton = document.getElementById('zotero-long-tag-fixer').getButton('accept');
|
const acceptButton = document.getElementById('zotero-long-tag-fixer').getButton('accept');
|
||||||
if (!delimiter || tags.length < 2) {
|
if (!delimiter || tags.length < 2) {
|
||||||
acceptButton.disabled = true;
|
acceptButton.disabled = true;
|
||||||
return;
|
// return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
acceptButton.disabled = false;
|
acceptButton.disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.sort();
|
tags.sort();
|
||||||
for (var i=0; i<tags.length; i++) {
|
|
||||||
if (i != 0 && tags[i] == tags[i-1]) {
|
while (this.listbox.childNodes.length) {
|
||||||
continue;
|
this.listbox.removeChild(this.listbox.lastChild);
|
||||||
}
|
|
||||||
if (!tags[i]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var li = listbox.appendItem(tags[i]);
|
|
||||||
li.setAttribute('type', 'checkbox');
|
|
||||||
li.setAttribute('checked', 'true');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
const li = document.createElement('richlistitem');
|
||||||
|
const div = document.createElement('div');
|
||||||
|
const checkbox = document.createElement('input');
|
||||||
|
checkbox.type = 'checkbox';
|
||||||
|
checkbox.checked = true;
|
||||||
|
checkbox.id = 'tag-' + tag;
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.setAttribute('for', 'tag-' + tag);
|
||||||
|
label.textContent = tag;
|
||||||
|
// Don't toggle checkbox for single-click on label
|
||||||
|
|
||||||
|
div.appendChild(checkbox);
|
||||||
|
div.appendChild(label);
|
||||||
|
li.appendChild(div);
|
||||||
|
this.listbox.append(li);
|
||||||
|
});
|
||||||
|
|
||||||
window.sizeToContent();
|
window.sizeToContent();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
this.updateLabel = function () {
|
||||||
|
this.delimiterLabel.innerHTML = this.oldTagDelimiter.value.length > 1
|
||||||
|
? Zotero.getString('general.character.plural')
|
||||||
|
: Zotero.getString('general.character.singular');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onUpdateDelimiter = function () {
|
||||||
|
this.updateLabel();
|
||||||
|
this.updateTagList();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.deselectAll = function () {
|
this.deselectAll = function () {
|
||||||
var lis = document.getElementById('zotero-new-tag-list').getElementsByTagName('listitem');
|
this.listbox.querySelectorAll('[type=checkbox]').forEach(checkbox => checkbox.checked = false);
|
||||||
for (var i=0; i<lis.length; i++) {
|
};
|
||||||
lis[i].checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.selectAll = function () {
|
this.selectAll = function () {
|
||||||
var lis = document.getElementById('zotero-new-tag-list').getElementsByTagName('listitem');
|
this.listbox.querySelectorAll('[type=checkbox]').forEach(checkbox => checkbox.checked = true);
|
||||||
for (var i=0; i<lis.length; i++) {
|
};
|
||||||
lis[i].checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.updateEditLength = function (len) {
|
this.updateEditLength = function (len) {
|
||||||
document.getElementById('zotero-new-tag-character-count').value = len;
|
this.newTagCharacterCount.innerText = len;
|
||||||
var invalid = len == 0 || len > Zotero.Tags.MAX_SYNC_LENGTH;
|
const invalid = len == 0 || len > Zotero.Tags.MAX_SYNC_LENGTH;
|
||||||
document.getElementById('zotero-new-tag-characters').setAttribute('invalid', invalid);
|
this.zoteroNewTagInfo.classList.toggle('invalid', invalid);
|
||||||
document.getElementById('zotero-long-tag-fixer').getButton('accept').disabled = invalid;
|
this.dialog.getButton('accept').disabled = invalid;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
this.cancel = function () {
|
this.cancel = function () {
|
||||||
_dataOut.result = false;
|
dataOut.result = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
this.save = function () {
|
this.accept = function () {
|
||||||
try {
|
try {
|
||||||
|
const result = {};
|
||||||
var result = {};
|
switch (this.tabs.selectedIndex) {
|
||||||
|
// Split
|
||||||
var index = document.getElementById('zotero-new-tag-actions').selectedIndex;
|
case 0:
|
||||||
|
result.op = 'split';
|
||||||
switch (index) {
|
result.tags = Array.from(this.listbox.querySelectorAll('[type=checkbox]'))
|
||||||
// Split
|
.filter(c => c.checked)
|
||||||
case 0:
|
.map(n => n.nextSibling.textContent);
|
||||||
// Get checked tags
|
break;
|
||||||
var listbox = document.getElementById('zotero-new-tag-list');
|
// Edit
|
||||||
var len = listbox.childElementCount;
|
case 1:
|
||||||
var newTags = [];
|
result.op = 'edit';
|
||||||
for (var i=0; i<len; i++) {
|
result.tag = this.newTagInput.value;
|
||||||
var li = listbox.childNodes[i];
|
break;
|
||||||
if (li.getAttribute('checked') == 'true') {
|
|
||||||
newTags.push(li.getAttribute('label'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.op = 'split';
|
// Delete
|
||||||
result.tags = newTags;
|
case 2:
|
||||||
break;
|
result.op = 'delete';
|
||||||
|
break;
|
||||||
// Edit
|
}
|
||||||
case 1:
|
dataOut.result = result;
|
||||||
result.op = 'edit';
|
|
||||||
result.tag = document.getElementById('zotero-new-tag-editor').value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Delete
|
|
||||||
case 2:
|
|
||||||
result.op = 'delete';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dataOut.result = result;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.debug(e);
|
Zotero.debug(e);
|
||||||
throw (e);
|
throw (e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
65
chrome/content/zotero/longTagFixer.xhtml
Normal file
65
chrome/content/zotero/longTagFixer.xhtml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||||
|
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||||
|
|
||||||
|
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||||
|
|
||||||
|
<window
|
||||||
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||||
|
title="&zotero.sync.error;"
|
||||||
|
onload="Zotero_Long_Tag_Fixer.init()">
|
||||||
|
<dialog
|
||||||
|
id="zotero-long-tag-fixer"
|
||||||
|
buttons="cancel,accept"
|
||||||
|
>
|
||||||
|
<html:div id="intro">
|
||||||
|
<label value="&zotero.sync.longTagFixer.followingTagTooLong;"/>
|
||||||
|
<html:textarea id="zotero-old-tag" readonly="readonly" />
|
||||||
|
<label>&zotero.sync.longTagFixer.syncedTagSizeLimit;</label>
|
||||||
|
<label value="&zotero.sync.longTagFixer.splitEditDelete;"/>
|
||||||
|
</html:div>
|
||||||
|
<tabbox id="zotero-new-tag-actions">
|
||||||
|
<tabs>
|
||||||
|
<tab label="&zotero.sync.longTagFixer.split;"/>
|
||||||
|
<tab label="&zotero.general.edit;"/>
|
||||||
|
<tab label="&zotero.general.delete;"/>
|
||||||
|
</tabs>
|
||||||
|
<tabpanels>
|
||||||
|
<!-- Split -->
|
||||||
|
<vbox class="split-tab">
|
||||||
|
<hbox class="delimiter-input-wrap">
|
||||||
|
<label>&zotero.sync.longTagFixer.splitAtThe;</label>
|
||||||
|
<html:input
|
||||||
|
type="text"
|
||||||
|
class="delimiter-input"
|
||||||
|
id="zotero-old-tag-delimiter" />
|
||||||
|
<label id="delimiter-label"></label>
|
||||||
|
</hbox>
|
||||||
|
<richlistbox id="zotero-new-tag-list" class="tag-list" flex="1" tabindex="0" />
|
||||||
|
<label value="&zotero.sync.longTagFixer.uncheckedTagsNotSaved;"/>
|
||||||
|
<hbox>
|
||||||
|
<button label="&zotero.general.deselectAll;" oncommand="Zotero_Long_Tag_Fixer.deselectAll()"/>
|
||||||
|
<button label="&zotero.general.selectAll;" oncommand="Zotero_Long_Tag_Fixer.selectAll()"/>
|
||||||
|
</hbox>
|
||||||
|
</vbox>
|
||||||
|
|
||||||
|
<!-- Edit -->
|
||||||
|
<vbox class="edit-tab">
|
||||||
|
<html:textarea id="zotero-new-tag-editor"/>
|
||||||
|
<html:div id="zotero-new-tag-characters">
|
||||||
|
<html:span id="zotero-new-tag-character-count" />
|
||||||
|
<html:span>&zotero.sync.longTagFixer.characters;</html:span>
|
||||||
|
</html:div>
|
||||||
|
</vbox>
|
||||||
|
|
||||||
|
<!-- Delete -->
|
||||||
|
<vbox align="center" pack="center">
|
||||||
|
<label value="&zotero.sync.longTagFixer.tagWillBeDeleted;"/>
|
||||||
|
</vbox>
|
||||||
|
</tabpanels>
|
||||||
|
</tabbox>
|
||||||
|
<script src="include.js"/>
|
||||||
|
<script src="longTagFixer.js"/>
|
||||||
|
</dialog>
|
||||||
|
</window>
|
|
@ -1,76 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero/skin/longTagFixer.css" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://zotero-platform-version/content/style.css"?>
|
|
||||||
|
|
||||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
|
||||||
|
|
||||||
<dialog id="zotero-long-tag-fixer" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
buttons="accept,cancel"
|
|
||||||
onload="Zotero_Long_Tag_Fixer.init()"
|
|
||||||
ondialogcancel="Zotero_Long_Tag_Fixer.cancel()"
|
|
||||||
ondialogaccept="Zotero_Long_Tag_Fixer.save()"
|
|
||||||
title="&zotero.sync.error;">
|
|
||||||
|
|
||||||
<script src="include.js"/>
|
|
||||||
<script src="longTagFixer.js"/>
|
|
||||||
|
|
||||||
<label value="&zotero.sync.longTagFixer.followingTagTooLong;"/>
|
|
||||||
<groupbox>
|
|
||||||
<textbox id="zotero-old-tag" multiline="true" rows="4" readonly="true" class="plain"/>
|
|
||||||
</groupbox>
|
|
||||||
<label>&zotero.sync.longTagFixer.syncedTagSizeLimit;</label>
|
|
||||||
|
|
||||||
<separator class="thin"/>
|
|
||||||
|
|
||||||
<label value="&zotero.sync.longTagFixer.splitEditDelete;"/>
|
|
||||||
|
|
||||||
<separator/>
|
|
||||||
|
|
||||||
<tabbox id="zotero-new-tag-actions">
|
|
||||||
<tabs oncommand="Zotero_Long_Tag_Fixer.switchMode(this.selectedIndex)">
|
|
||||||
<tab label="&zotero.sync.longTagFixer.split;"/>
|
|
||||||
<tab label="&zotero.general.edit;"/>
|
|
||||||
<tab label="&zotero.general.delete;"/>
|
|
||||||
</tabs>
|
|
||||||
<tabpanels>
|
|
||||||
<!-- Split -->
|
|
||||||
<vbox>
|
|
||||||
<hbox align="center">
|
|
||||||
<label>&zotero.sync.longTagFixer.splitAtThe;</label>
|
|
||||||
<textbox id="zotero-old-tag-delimiter" size="1"
|
|
||||||
oninput="this.nextSibling.value = Zotero.getString('general.character.' + (this.value.length > 1 ? 'plural' : 'singular')); Zotero_Long_Tag_Fixer.updateTagList();"/>
|
|
||||||
<label/>
|
|
||||||
</hbox>
|
|
||||||
|
|
||||||
<separator class="thin"/>
|
|
||||||
|
|
||||||
<listbox id="zotero-new-tag-list" rows="8"/>
|
|
||||||
|
|
||||||
<separator class="thin"/>
|
|
||||||
|
|
||||||
<label value="&zotero.sync.longTagFixer.uncheckedTagsNotSaved;"/>
|
|
||||||
|
|
||||||
<hbox>
|
|
||||||
<button label="&zotero.general.deselectAll;" oncommand="Zotero_Long_Tag_Fixer.deselectAll()"/>
|
|
||||||
<button label="&zotero.general.selectAll;" oncommand="Zotero_Long_Tag_Fixer.selectAll()"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
|
|
||||||
<!-- Edit -->
|
|
||||||
<vbox>
|
|
||||||
<textbox id="zotero-new-tag-editor" multiline="true" flex="1"
|
|
||||||
oninput="Zotero_Long_Tag_Fixer.updateEditLength(this.value.length)"/>
|
|
||||||
<hbox id="zotero-new-tag-characters">
|
|
||||||
<label id="zotero-new-tag-character-count"/>
|
|
||||||
<label value="&zotero.sync.longTagFixer.characters;"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
|
|
||||||
<!-- Delete -->
|
|
||||||
<vbox align="center" pack="center">
|
|
||||||
<label value="&zotero.sync.longTagFixer.tagWillBeDeleted;"/>
|
|
||||||
</vbox>
|
|
||||||
</tabpanels>
|
|
||||||
</tabbox>
|
|
||||||
</dialog>
|
|
|
@ -1214,62 +1214,54 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
.getService(Components.interfaces.nsIWindowMediator);
|
||||||
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
||||||
|
|
||||||
// Open long tag fixer for every long tag in every editable library we're syncing
|
// Open long tag fixer for library we're syncing
|
||||||
var editableLibraries = options.libraries
|
let oldTagIDs = yield Zotero.Tags.getLongTagsInLibrary(object.libraryID);
|
||||||
.filter(x => Zotero.Libraries.get(x).editable);
|
|
||||||
for (let libraryID of editableLibraries) {
|
for (let oldTagID of oldTagIDs) {
|
||||||
let oldTagIDs = yield Zotero.Tags.getLongTagsInLibrary(libraryID);
|
let oldTag = Zotero.Tags.getName(oldTagID);
|
||||||
for (let oldTagID of oldTagIDs) {
|
let dataOut = { result: null };
|
||||||
let oldTag = Zotero.Tags.getName(oldTagID);
|
lastWin.openDialog(
|
||||||
let dataOut = { result: null };
|
'chrome://zotero/content/longTagFixer.xhtml',
|
||||||
lastWin.openDialog(
|
'',
|
||||||
'chrome://zotero/content/longTagFixer.xul',
|
'chrome,modal,centerscreen',
|
||||||
'',
|
{ oldTag, isLongTag: true },
|
||||||
'chrome,modal,centerscreen',
|
dataOut
|
||||||
oldTag,
|
);
|
||||||
dataOut
|
// If dialog was cancelled, stop
|
||||||
);
|
if (!dataOut.result) {
|
||||||
// If dialog was cancelled, stop
|
return;
|
||||||
if (!dataOut.result) {
|
}
|
||||||
return;
|
const itemIDs = yield Zotero.Tags.getTagItems(object.libraryID, oldTagID);
|
||||||
}
|
|
||||||
switch (dataOut.result.op) {
|
switch (dataOut.result.op) {
|
||||||
case 'split':
|
case 'split':
|
||||||
for (let libraryID of editableLibraries) {
|
yield Zotero.DB.executeTransaction(async function () {
|
||||||
let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
|
for (let itemID of itemIDs) {
|
||||||
yield Zotero.DB.executeTransaction(async function () {
|
let item = await Zotero.Items.getAsync(itemID);
|
||||||
for (let itemID of itemIDs) {
|
let tagType = item.getTagType(oldTag);
|
||||||
let item = await Zotero.Items.getAsync(itemID);
|
for (let tag of dataOut.result.tags) {
|
||||||
for (let tag of dataOut.result.tags) {
|
item.addTag(tag, tagType);
|
||||||
item.addTag(tag);
|
|
||||||
}
|
|
||||||
item.removeTag(oldTag);
|
|
||||||
await item.save();
|
|
||||||
}
|
}
|
||||||
await Zotero.Tags.purge(oldTagID);
|
item.removeTag(oldTag);
|
||||||
});
|
await item.save();
|
||||||
}
|
}
|
||||||
|
await Zotero.Tags.purge(oldTagID);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
for (let libraryID of editableLibraries) {
|
yield Zotero.DB.executeTransaction(async function () {
|
||||||
let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
|
for (let itemID of itemIDs) {
|
||||||
yield Zotero.DB.executeTransaction(async function () {
|
let item = await Zotero.Items.getAsync(itemID);
|
||||||
for (let itemID of itemIDs) {
|
item.replaceTag(oldTag, dataOut.result.tag);
|
||||||
let item = await Zotero.Items.getAsync(itemID);
|
await item.save();
|
||||||
item.replaceTag(oldTag, dataOut.result.tag);
|
}
|
||||||
await item.save();
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
for (let libraryID of editableLibraries) {
|
yield Zotero.Tags.removeFromLibrary(object.libraryID, oldTagID);
|
||||||
yield Zotero.Tags.removeFromLibrary(libraryID, oldTagID);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1202,6 +1202,8 @@
|
||||||
oncommand="ZoteroPane.tagSelector.openColorPickerWindow(); event.stopPropagation();"/>
|
oncommand="ZoteroPane.tagSelector.openColorPickerWindow(); event.stopPropagation();"/>
|
||||||
<menuitem label="&zotero.tagSelector.renameTag;"
|
<menuitem label="&zotero.tagSelector.renameTag;"
|
||||||
oncommand="ZoteroPane.tagSelector.openRenamePrompt(); event.stopPropagation();"/>
|
oncommand="ZoteroPane.tagSelector.openRenamePrompt(); event.stopPropagation();"/>
|
||||||
|
<menuitem id="split-tag" label="&zotero.tagSelector.splitTag;"
|
||||||
|
oncommand="ZoteroPane.tagSelector.openTagSplitterWindow(event)"/>
|
||||||
<menuitem label="&zotero.tagSelector.deleteTag;"
|
<menuitem label="&zotero.tagSelector.deleteTag;"
|
||||||
oncommand="ZoteroPane.tagSelector.openDeletePrompt(); event.stopPropagation();"/>
|
oncommand="ZoteroPane.tagSelector.openDeletePrompt(); event.stopPropagation();"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
<!ENTITY zotero.tagSelector.assignColor "Assign Color…">
|
<!ENTITY zotero.tagSelector.assignColor "Assign Color…">
|
||||||
<!ENTITY zotero.tagSelector.renameTag "Rename Tag…">
|
<!ENTITY zotero.tagSelector.renameTag "Rename Tag…">
|
||||||
<!ENTITY zotero.tagSelector.deleteTag "Delete Tag…">
|
<!ENTITY zotero.tagSelector.deleteTag "Delete Tag…">
|
||||||
|
<!ENTITY zotero.tagSelector.splitTag "Split…">
|
||||||
|
|
||||||
<!ENTITY zotero.tagColorChooser.title "Choose a Tag Color and Position">
|
<!ENTITY zotero.tagColorChooser.title "Choose a Tag Color and Position">
|
||||||
<!ENTITY zotero.tagColorChooser.color "Color:">
|
<!ENTITY zotero.tagColorChooser.color "Color:">
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#zotero-new-tag-characters[invalid="true"] {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zotero-new-tag-character-count {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
|
@ -25,11 +25,14 @@
|
||||||
@import "components/autosuggest";
|
@import "components/autosuggest";
|
||||||
@import "components/button";
|
@import "components/button";
|
||||||
@import "components/clicky";
|
@import "components/clicky";
|
||||||
|
@import "components/collection-tree";
|
||||||
@import "components/createParent";
|
@import "components/createParent";
|
||||||
@import "components/dictionaryManager";
|
@import "components/dictionaryManager";
|
||||||
@import "components/editable";
|
@import "components/editable";
|
||||||
@import "components/exportOptions";
|
@import "components/exportOptions";
|
||||||
@import "components/icons";
|
@import "components/icons";
|
||||||
|
@import "components/item-tree";
|
||||||
|
@import "components/longTagFixer";
|
||||||
@import "components/mainWindow";
|
@import "components/mainWindow";
|
||||||
@import "components/notesList";
|
@import "components/notesList";
|
||||||
@import "components/progressMeter";
|
@import "components/progressMeter";
|
||||||
|
@ -38,9 +41,7 @@
|
||||||
@import "components/tabBar";
|
@import "components/tabBar";
|
||||||
@import "components/tagsBox";
|
@import "components/tagsBox";
|
||||||
@import "components/tagSelector";
|
@import "components/tagSelector";
|
||||||
@import "components/collection-tree";
|
|
||||||
@import "components/virtualized-table";
|
@import "components/virtualized-table";
|
||||||
@import "components/item-tree";
|
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
|
@ -2,3 +2,8 @@
|
||||||
// Utilities
|
// Utilities
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
@mixin text-truncate($text-overflow: ellipsis) {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: $text-overflow;
|
||||||
|
}
|
112
scss/components/_longTagFixer.scss
Normal file
112
scss/components/_longTagFixer.scss
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#zotero-long-tag-fixer {
|
||||||
|
min-width: 400px; // with intro disabled, dialog tends to get to narrow
|
||||||
|
|
||||||
|
tab {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@include variant("#zotero-long-tag-fixer.is-long-tag") {
|
||||||
|
display: revert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabpanels {
|
||||||
|
padding-top: 15px;
|
||||||
|
|
||||||
|
@include variant("#zotero-long-tag-fixer.is-long-tag") {
|
||||||
|
padding-top: 33px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tab[visuallyselected="true"]:not(:-moz-window-inactive) {
|
||||||
|
color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#intro {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@include variant("#zotero-long-tag-fixer.is-long-tag") {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
> label {
|
||||||
|
margin: .5em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> textarea {
|
||||||
|
margin: 1em;
|
||||||
|
height: 50px;
|
||||||
|
border: none;
|
||||||
|
padding: .5em;
|
||||||
|
background-color: $shade-1;
|
||||||
|
appearance: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-new-tag-actions {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-tab {
|
||||||
|
.tag-list {
|
||||||
|
height: calc(8 * (1em + 10px));
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
> richlistitem {
|
||||||
|
display: block;
|
||||||
|
height: calc(1em + 10px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
// width of the label is set to the width of the viewport minus sum of widths of
|
||||||
|
// paddings, margins and a checkbox to produce ellipsis truncation.
|
||||||
|
width: calc(100vw - 100px);
|
||||||
|
@include text-truncate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.delimiter-input {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delimiter-input-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list + label {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-tab {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
#zotero-new-tag-editor {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-new-tag-characters {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
padding: .5em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-new-tag-character-count {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1321,7 +1321,7 @@ describe("Zotero.Sync.Runner", function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
waitForDialog(null, 'accept', 'chrome://zotero/content/longTagFixer.xul');
|
waitForDialog(null, 'accept', 'chrome://zotero/content/longTagFixer.xhtml');
|
||||||
yield runner.sync({ libraries: [Zotero.Libraries.userLibraryID] });
|
yield runner.sync({ libraries: [Zotero.Libraries.userLibraryID] });
|
||||||
|
|
||||||
assert.isFalse(Zotero.Tags.getID(tag));
|
assert.isFalse(Zotero.Tags.getID(tag));
|
||||||
|
@ -1391,9 +1391,9 @@ describe("Zotero.Sync.Runner", function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
waitForDialog(function (dialog) {
|
waitForDialog(function (window) {
|
||||||
dialog.Zotero_Long_Tag_Fixer.switchMode(2);
|
window.Zotero_Long_Tag_Fixer.switchMode(2);
|
||||||
}, 'accept', 'chrome://zotero/content/longTagFixer.xul');
|
}, 'accept', 'chrome://zotero/content/longTagFixer.xhtml');
|
||||||
yield runner.sync({ libraries: [Zotero.Libraries.userLibraryID] });
|
yield runner.sync({ libraries: [Zotero.Libraries.userLibraryID] });
|
||||||
|
|
||||||
assert.isFalse(Zotero.Tags.getID(tag));
|
assert.isFalse(Zotero.Tags.getID(tag));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue