diff --git a/chrome.manifest b/chrome.manifest index f8b8f8493f..8fb8da2b74 100644 --- a/chrome.manifest +++ b/chrome.manifest @@ -21,6 +21,7 @@ locale zotero gl-ES chrome/locale/gl-ES/zotero/ locale zotero he-IL chrome/locale/he-IL/zotero/ locale zotero hr-HR chrome/locale/hr-HR/zotero/ locale zotero hu-HU chrome/locale/hu-HU/zotero/ +locale zotero id-ID chrome/locale/id-ID/zotero/ locale zotero is-IS chrome/locale/is-IS/zotero/ locale zotero it-IT chrome/locale/it-IT/zotero/ locale zotero ja-JP chrome/locale/ja-JP/zotero/ diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index 4367a36351..06a48b7c3c 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -241,30 +241,11 @@ var Zotero_File_Interface = new function() { * Imports from clipboard */ this.importFromClipboard = function () { - var clip = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); - if (!clip.hasDataMatchingFlavors(["text/unicode"], 1, clip.kGlobalClipboard)) { + var str = Zotero.Utilities.Internal.getClipboard("text/unicode"); + if(!str) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); ps.alert(null, "", Zotero.getString('fileInterface.importClipboardNoDataError')); - return; - } - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - trans.addDataFlavor("text/unicode"); - clip.getData(trans, clip.kGlobalClipboard); - var str = {}; - try { - trans.getTransferData("text/unicode", str, {}); - str = str.value.QueryInterface(Components.interfaces.nsISupportsString).data; - } - catch (e) { - Zotero.debug(e); - return; - } - if (!str) { - Zotero.debug("No clipboard text to import"); - return; } var translate = new Zotero.Translate.Import(); diff --git a/chrome/content/zotero/integration/quickFormat.js b/chrome/content/zotero/integration/quickFormat.js index 1371cacc34..f3c0962a1c 100644 --- a/chrome/content/zotero/integration/quickFormat.js +++ b/chrome/content/zotero/integration/quickFormat.js @@ -22,6 +22,7 @@ ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://gre/modules/Services.jsm"); var Zotero_QuickFormat = new function () { const pixelRe = /^([0-9]+)px$/ @@ -36,10 +37,8 @@ var Zotero_QuickFormat = new function () { keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0, separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging, panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator, - panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false; - - // A variable that contains the timeout object for the latest onKeyPress event - var eventTimeout = null; + panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false, + searchTimeout; const SHOWN_REFERENCES = 7; @@ -119,6 +118,8 @@ var Zotero_QuickFormat = new function () { qfiDocument = qfi.contentDocument; qfb.addEventListener("keypress", _onQuickSearchKeyPress, false); qfe = qfiDocument.getElementById("quick-format-editor"); + qfe.addEventListener("drop", _onBubbleDrop, false); + qfe.addEventListener("paste", _onPaste, false); } } @@ -179,11 +180,17 @@ var Zotero_QuickFormat = new function () { var range = selection.getRangeAt(0); var node = range.startContainer; - if(node !== range.endContainer || node.nodeType !== Node.TEXT_NODE ) { - return false; + if(node !== range.endContainer) return false; + if(node.nodeType === Node.TEXT_NODE) return node; + + // Range could be referenced to the body element + if(node === qfe) { + var offset = range.startOffset; + if(offset !== range.endOffset) return false; + node = qfe.childNodes[Math.min(qfe.childNodes.length-1, offset)]; + if(node.nodeType === Node.TEXT_NODE) return node; } - - return node; + return false; } /** @@ -192,7 +199,7 @@ var Zotero_QuickFormat = new function () { */ function _getEditorContent(clear) { var node = _getCurrentEditorTextNode(); - return node ? node.textContent : false; + return node ? node.wholeText : false; } /** @@ -641,14 +648,18 @@ var Zotero_QuickFormat = new function () { // It's entirely unintuitive why, but after trying a bunch of things, it looks like using // a XUL label for these things works best. A regular span causes issues with moving the // cursor. - var bubble = qfiDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label"); + var bubble = qfiDocument.createElement("span"); bubble.setAttribute("class", "quick-format-bubble"); - bubble.setAttribute("value", str); + bubble.setAttribute("draggable", "true"); + bubble.textContent = str; bubble.addEventListener("click", _onBubbleClick, false); bubble.addEventListener("dragstart", _onBubbleDrag, false); - bubble.addEventListener("dragend", _onBubbleDrop, false); bubble.citationItem = citationItem; - qfe.insertBefore(bubble, (nextNode ? nextNode : null)); + if(nextNode && nextNode instanceof Range) { + nextNode.insertNode(bubble); + } else { + qfe.insertBefore(bubble, (nextNode ? nextNode : null)); + } // make sure that there are no rogue
s var elements = qfe.getElementsByTagName("br"); @@ -725,12 +736,10 @@ var Zotero_QuickFormat = new function () { } } - var qfeHeight = qfe.scrollHeight; - - if(qfeHeight > 30) { + if(qfe.scrollHeight > 30) { qfe.setAttribute("multiline", true); qfs.setAttribute("multiline", true); - qfs.style.height = (4+qfeHeight)+"px"; + qfs.style.height = (4+qfe.scrollHeight)+"px"; window.sizeToContent(); } else { delete qfs.style.height; @@ -971,6 +980,50 @@ var Zotero_QuickFormat = new function () { io.accept(); } } + + /** + * Get bubbles within the current selection + */ + function _getSelectedBubble(right) { + var selection = qfiWindow.getSelection(), + range = selection.getRangeAt(0); + qfe.normalize(); + + // Check whether the bubble is selected + // Not sure whether this ever happens anymore + var container = range.startContainer; + if(container !== qfe) { + if(container.citationItem) { + return container; + } else if(container.nodeType === Node.TEXT_NODE && container.wholeText == "") { + if(container.parentNode === qfe) { + var node = container; + while((node = container.previousSibling)) { + if(node.citationItem) { + return node; + } + } + } + } + return null; + } + + // Check whether there is a bubble anywhere to the left of this one + var offset = range.startOffset, + childNodes = qfe.childNodes, + node = childNodes[offset-(right ? 0 : 1)]; + if(node && node.citationItem) return node; + return null; + } + + /** + * Reset timer that controls when search takes place. We use this to avoid searching after each + * keypress, since searches can be slow. + */ + function _resetSearchTimer() { + if(searchTimeout) clearTimeout(searchTimeout); + searchTimeout = setTimeout(_quickFormat, 250); + } /** * Handle return or escape @@ -987,29 +1040,48 @@ var Zotero_QuickFormat = new function () { } else if(keyCode === event.DOM_VK_TAB || event.charCode === 59 /* ; */) { event.preventDefault(); _bubbleizeSelected(); - } else if(keyCode === event.DOM_VK_BACK_SPACE) { + } else if(keyCode === event.DOM_VK_BACK_SPACE || keyCode === event.DOM_VK_DELETE) { + var bubble = _getSelectedBubble(keyCode === event.DOM_VK_DELETE); + + if(bubble) { + event.preventDefault(); + bubble.parentNode.removeChild(bubble); + } + _resize(); - - if(Zotero_QuickFormat.eventTimeout) clearTimeout(Zotero_QuickFormat.eventTimeout); - Zotero_QuickFormat.eventTimeout = setTimeout(_quickFormat, 250); - + _resetSearchTimer(); + } else if(keyCode === event.DOM_VK_LEFT || keyCode === event.DOM_VK_RIGHT) { + var right = keyCode === event.DOM_VK_RIGHT, + bubble = _getSelectedBubble(right); + if(bubble) { + event.preventDefault(); + + var nodeRange = qfiDocument.createRange(); + nodeRange.selectNode(bubble); + nodeRange.collapse(!right); + + var selection = qfiWindow.getSelection(); + selection.removeAllRanges(); + selection.addRange(nodeRange); + } + } else if(keyCode === event.DOM_VK_UP) { var selectedItem = referenceBox.selectedItem; var previousSibling; - //Seek the closet previous sibling that is not disabled - while((previousSibling = selectedItem.previousSibling) && previousSibling.getAttribute("disabled")){ + // Seek the closet previous sibling that is not disabled + while((previousSibling = selectedItem.previousSibling) && previousSibling.hasAttribute("disabled")) { selectedItem = previousSibling; } - //If found, change to that + // If found, change to that if(previousSibling) { referenceBox.selectedItem = previousSibling; - //If there are separators before this item, ensure that they are visible + // If there are separators before this item, ensure that they are visible var visibleItem = previousSibling; - while(visibleItem.previousSibling && visibleItem.previousSibling.getAttribute("disabled")){ + while(visibleItem.previousSibling && visibleItem.previousSibling.hasAttribute("disabled")) { visibleItem = visibleItem.previousSibling; } referenceBox.ensureElementIsVisible(visibleItem); @@ -1018,54 +1090,20 @@ var Zotero_QuickFormat = new function () { } else if(keyCode === event.DOM_VK_DOWN) { if((Zotero.isMac ? event.metaKey : event.ctrlKey)) { // If meta key is held down, show the citation properties panel - var selection = qfiWindow.getSelection(); - var range = selection.getRangeAt(0); - - // Check whether the bubble is selected - var endContainer = range.endContainer; - if(endContainer !== qfe) { - if(range.endContainer.citationItem) { - _showCitationProperties(range.endContainer); - } else if(endContainer.nodeType === Node.TEXT_NODE) { - if(endContainer.parentNode === qfe) { - var node = endContainer; - while((node = endContainer.previousSibling)) { - if(node.citationItem) { - _showCitationProperties(node); - event.preventDefault(); - return; - } - } - } - } - event.preventDefault(); - return; - } - - // Check whether there is a bubble in the range - var endOffset = range.endOffset; - var childNodes = qfe.childNodes; - for(var i=Math.min(endOffset, childNodes.length-1); i>=0; i--) { - var node = childNodes[i]; - if(node.citationItem) { - _showCitationProperties(node); - event.preventDefault(); - return; - } - } - + var bubble = _getSelectedBubble(); + + if(bubble) _showCitationProperties(bubble); event.preventDefault(); } else { var selectedItem = referenceBox.selectedItem; var nextSibling; - //Seek the closet next sibling that is not disabled - while((nextSibling = selectedItem.nextSibling) && nextSibling.getAttribute("disabled")){ + // Seek the closet next sibling that is not disabled + while((nextSibling = selectedItem.nextSibling) && nextSibling.hasAttribute("disabled")) { selectedItem = nextSibling; } - //If found, change to that - + // If found, change to that if(nextSibling){ referenceBox.selectedItem = nextSibling; referenceBox.ensureElementIsVisible(nextSibling); @@ -1073,9 +1111,7 @@ var Zotero_QuickFormat = new function () { }; } } else { - // Use a timeout so that _quickFormat gets called after update - if(Zotero_QuickFormat.eventTimeout) clearTimeout(Zotero_QuickFormat.eventTimeout); - Zotero_QuickFormat.eventTimeout = setTimeout(_quickFormat, 250); + _resetSearchTimer(); } } @@ -1083,34 +1119,74 @@ var Zotero_QuickFormat = new function () { * Adds a dummy element to make dragging work */ function _onBubbleDrag(event) { - // just in case - var el = qfiDocument.getElementById("zotero-drag"); - if(el) el.parentNode.removeChild(el); - - var dt = event.dataTransfer; - dragging = event.target.citationItem; - dt.setData("text/html", ''); + dragging = event.currentTarget; + event.dataTransfer.setData("text/plain", ''); event.stopPropagation(); } + + /** + * Get index of bubble in citations + */ + function _getBubbleIndex(bubble) { + var nodes = qfe.childNodes, oldPosition = -1, index = 0; + for(var i=0, n=nodes.length; i