├── .gitignore
├── chrome
├── skin
│ └── default
│ │ └── overlay.css
├── locale
│ └── en-US
│ │ └── zoteroquicklook.dtd
└── content
│ ├── Bridge.exe
│ ├── include.js
│ ├── includeDialog.js
│ ├── ZoteroQuickLook
│ └── coq.scpt
│ ├── includeQuickFormat.js
│ ├── addCitationDialogOverlay.xul
│ ├── quickFormatOverlay.xul
│ ├── zoteroquicklookdialog.js
│ ├── overlay.xul
│ ├── zoteroquicklook.pl
│ ├── Bridge.cs
│ ├── zoteroquicklookquickformat.js
│ └── zoteroquicklook.js
├── defaults
└── preferences
│ └── defaults.js
├── chrome.manifest
├── install.rdf
├── update.rdf
├── update-altwindows.rdf
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | builds
2 | github.token
--------------------------------------------------------------------------------
/chrome/skin/default/overlay.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chrome/locale/en-US/zoteroquicklook.dtd:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chrome/content/Bridge.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mronkko/ZoteroQuickLook/HEAD/chrome/content/Bridge.exe
--------------------------------------------------------------------------------
/defaults/preferences/defaults.js:
--------------------------------------------------------------------------------
1 | pref("extensions.zoteroquicklook.usefilenameworkaround", true);
2 | pref("extensions.zoteroquicklook.customviewcommand", "");
3 |
--------------------------------------------------------------------------------
/chrome/content/include.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | Zotero.debug("ZoteroQuickLook loading",3);
4 |
5 | Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("chrome://zoteroquicklook/content/zoteroquicklook.js");
6 |
7 |
8 | Zotero.Schema.schemaUpdatePromise.then(() => {
9 | Zotero.ZoteroQuickLook.init();
10 | Zotero.debug("ZoteroQuickLook loaded",3);
11 | });
12 |
--------------------------------------------------------------------------------
/chrome/content/includeDialog.js:
--------------------------------------------------------------------------------
1 | Zotero.debug("ZoteroQuickLookDialog loading",3);
2 |
3 | Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("chrome://zoteroquicklook/content/zoteroquicklookdialog.js");
4 |
5 |
6 | window.addEventListener('load', function(e) { Zotero.ZoteroQuickLookDialog.init(); }, false);
7 |
8 | Zotero.debug("ZoteroQuickLookDialog loaded",3);
9 |
10 |
--------------------------------------------------------------------------------
/chrome/content/ZoteroQuickLook/coq.scpt:
--------------------------------------------------------------------------------
1 | try
2 | do shell script "PIPE=\"/Users/Shared/.zoteroIntegrationPipe_$LOGNAME\"; if [ ! -e \"$PIPE\" ]; then PIPE='~/.zoteroIntegrationPipe'; fi; if [ -e \"$PIPE\" ]; then echo 'MacWord2008 quickLook '" & quoted form of POSIX path of (path to current application) & " > \"$PIPE\"; else exit 1; fi;"
3 | on error
4 | display alert "Word could not communicate with Zotero. Please ensure that Firefox is open and try again." as critical
5 | end try
6 |
--------------------------------------------------------------------------------
/chrome/content/includeQuickFormat.js:
--------------------------------------------------------------------------------
1 | Zotero.debug("ZoteroQuickLookQuickFormat loading",3);
2 |
3 | Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("chrome://zoteroquicklook/content/zoteroquicklookquickformat.js");
4 |
5 |
6 | window.addEventListener('load', function(e) { Zotero.ZoteroQuickLookQuickFormat.init(); }, false);
7 |
8 | Zotero.debug("ZoteroQuickLookQuickFormat loaded",3);
9 |
10 |
--------------------------------------------------------------------------------
/chrome/content/addCitationDialogOverlay.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/chrome/content/quickFormatOverlay.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/chrome/content/zoteroquicklookdialog.js:
--------------------------------------------------------------------------------
1 | Zotero.ZoteroQuickLookDialog = {
2 |
3 | init: function() {
4 |
5 | Zotero.debug("ZoteroQuickLookDialog starts init",3);
6 |
7 | document.getElementById('zotero-items-tree').addEventListener("keypress",this.onKey,false);
8 |
9 | Zotero.debug("ZoteroQuickLookDialog finished init",3)
10 |
11 | },
12 | onKey: function(event) {
13 | return Zotero.ZoteroQuickLook.handleKeyPress(event,itemsView.getSelectedItems());
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/chrome.manifest:
--------------------------------------------------------------------------------
1 | content zoteroquicklook chrome/content/
2 | locale zoteroquicklook en-US chrome/locale/en-US/
3 | skin zoteroquicklook default chrome/skin/default/
4 |
5 | overlay chrome://browser/content/browser.xul chrome://zoteroquicklook/content/overlay.xul
6 | overlay chrome://zotero/content/zoteroPane.xul chrome://zoteroquicklook/content/overlay.xul
7 | overlay chrome://zotero/content/integration/addCitationDialog.xul chrome://zoteroquicklook/content/addCitationDialogOverlay.xul
8 | overlay chrome://zotero/content/integration/quickFormat.xul chrome://zoteroquicklook/content/quickFormatOverlay.xul
9 |
--------------------------------------------------------------------------------
/chrome/content/overlay.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | zoteroquicklook@gmail.com
5 | 2
6 | ZoteroQuickLook
7 | 1.4.2
8 | Mikko Ronkko
9 | Brenton M. Wiernik
10 | Quick Look Utility for Zotero.
11 | https://raw.githubusercontent.com/mronkko/ZoteroQuickLook/master/update.rdf
12 | true
13 |
14 |
15 | zotero@chnm.gmu.edu
16 | 4.0
17 | 5.*
18 |
19 |
20 |
21 |
22 | juris-m@juris-m.github.io
23 | 4.0
24 | 5.*
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/chrome/content/zoteroquicklook.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | use File::Spec;
4 | use IPC::Open2;
5 |
6 | print "\n\nThis file contains debug information\n\n";
7 |
8 | my @files=();
9 |
10 | foreach $argnum (0 .. $#ARGV) {
11 | my $path=$ARGV[$argnum];
12 |
13 | #If path contains * wildcards, expand these
14 | if($path =~ /\*/){
15 | #If path contains spaces put quotes around it
16 | if($path =~ / /){
17 | $path="\"$path\"";
18 | }
19 |
20 | push(@files,glob($path));
21 | }
22 | else{
23 | push(@files,$path);
24 | }
25 | }
26 |
27 | #
28 | # Because Gloobus and OS X QuickLook are quite different applications, we must run one with exec and the other with system.
29 | #
30 |
31 |
32 | if (scalar @files > 0) {
33 | if (-e '/usr/bin/qlmanage') { exec('/usr/bin/qlmanage','-p',@files); }
34 | elsif (-e '/usr/bin/sushi') {
35 | # we pass an entire string, instead of a list of args, to `system` because dbus-send needs to run in an actual shell
36 | $cmd = 'dbus-send --print-reply --dest=org.gnome.NautilusPreviewer /org/gnome/NautilusPreviewer org.gnome.NautilusPreviewer.ShowFile string:"file://%s" int32:0 boolean:false';
37 | # sushi only support viewing single file
38 | system sprintf($cmd,File::Spec->rel2abs($files[0]));
39 | }
40 | elsif (-e '/usr/bin/gloobus-preview') { system('/usr/bin/gloobus-preview',@files); }
41 | else { print "No QuickLook application found"; }
42 | }
43 |
--------------------------------------------------------------------------------
/update.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 1.4.2
9 |
10 |
11 | zotero@chnm.gmu.edu
12 | 4.0
13 | 5.*
14 | https://github.com/mronkko/ZoteroQuickLook/releases/download/1.4.2/zoteroquicklook.zoteroplugin
15 |
16 |
17 |
18 |
19 | juris-m@juris-m.github.io
20 | 4.0
21 | 5.*
22 | https://github.com/mronkko/ZoteroQuickLook/releases/download/1.4.2/zoteroquicklook.zoteroplugin
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/update-altwindows.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 1.4.2
9 |
10 |
11 | zotero@chnm.gmu.edu
12 | 4.0
13 | 5.*
14 | https://github.com/mronkko/ZoteroQuickLook/releases/download/1.4.2/zoteroquicklook.zoteroplugin
15 |
16 |
17 |
18 |
19 | juris-m@juris-m.github.io
20 | 4.0
21 | 5.*
22 | https://github.com/mronkko/ZoteroQuickLook/releases/download/1.4.2/zoteroquicklook.zoteroplugin
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/chrome/content/Bridge.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Pipes;
4 | using System.Security.Principal;
5 | using System.Windows.Forms;
6 |
7 | namespace Bridge
8 | {
9 | internal static class Program
10 | {
11 | private static void Main(string[] args)
12 | {
13 | if (args.Length == 0)
14 | {
15 | MessageBox.Show("Usage: Bridge.exe \"\"");
16 | return;
17 | }
18 |
19 | SendMessage(Toggle, args[0]);
20 | }
21 |
22 | private static readonly string PipeName = "QuickLook.App.Pipe." + WindowsIdentity.GetCurrent().User?.Value;
23 | private const string Toggle = "QuickLook.App.PipeMessages.Toggle";
24 |
25 | private static void SendMessage(string pipeMessage, string path = null)
26 | {
27 | if (path == null)
28 | path = "";
29 |
30 | try
31 | {
32 | using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
33 | {
34 | client.Connect(1000);
35 |
36 | using (var writer = new StreamWriter(client))
37 | {
38 | writer.WriteLine($"{pipeMessage}|{path}");
39 | writer.Flush();
40 | }
41 | }
42 | }
43 | catch (Exception e)
44 | {
45 | MessageBox.Show("QuickLook cannot be reached. Please run/install QuickLook (http://pooi.moe/QuickLook/) or specify a custom view command instead.");
46 | }
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/chrome/content/zoteroquicklookquickformat.js:
--------------------------------------------------------------------------------
1 | Zotero.ZoteroQuickLookQuickFormat = {
2 |
3 | init: function() {
4 |
5 | Zotero.debug("ZoteroQuickLookQuickFormat starts init",3);
6 |
7 | //Zotero Quick Look takes priority in event listeners, so we need to remove and readd the original.
8 | var originalListener=Zotero_QuickFormat._onQuickSearchKeyPress;
9 |
10 | document.getElementById('quick-format-entry').addEventListener("keypress",this.onKey,true);
11 |
12 | //Override ESC key for the panel to prevent it closing if quick look is open. In this case the first esc press should close the quicklook and second should close the panel
13 |
14 | document.getElementById("quick-format-reference-list").addEventListener("keypress",this.captureEsc,true);
15 | Zotero.debug("ZoteroQuickLookQuickFormat finished init",3)
16 |
17 | },
18 | onKey: function(event) {
19 |
20 | var key = String.fromCharCode(event.which);
21 |
22 | // Only capture cmd+y and arrow keys (no space) for the QuickFormat dialog
23 |
24 | Zotero.debug("ZoteroQuickLook: Got key code "+event.keyCode+" char is "+key);
25 |
26 | if ((key == 'y' && event.metaKey && !(event.ctrlKey || event.altKey)) || event.keyCode==38 || event.keyCode==40 ) {
27 |
28 | //If we are not in the browse mode, do nothing on arrows
29 | if((event.keyCode==38 || event.keyCode==40) &&
30 | ! (Zotero.ZoteroQuickLook.isActive() || Zotero.ZoteroQuickLook.isBrowseMode)) return;
31 |
32 | Zotero.debug("ZoteroQuickLook: Got key cmd+y, up arrow or down arrow. Start looking for item.");
33 |
34 | var referenceBox = document.getElementById("quick-format-reference-list");
35 |
36 | if(! referenceBox.hasChildNodes() || !referenceBox.selectedItem) return;
37 |
38 | //This is an ugly workaround to capture Esc. If quick look is open, we do not want to close on Esc
39 | //referenceBox.selectedItem.addEventListener("keypress",this.captureEsc,false);
40 |
41 | var items = Zotero.Items.get([referenceBox.selectedItem.getAttribute("zotero-item")]);
42 |
43 | return Zotero.ZoteroQuickLook.handleKeyPress(event,items);
44 | }
45 | //Esc
46 | else if(event.keyCode==27){
47 | //If QuickLook is open, stop event propagation
48 | Zotero.debug("ZoteroQuickLook: Got esc");
49 |
50 | if (Zotero.ZoteroQuickLook.isActive()){
51 | event.stopPropagation();
52 | event.preventDefault();
53 | Zotero.ZoteroQuickLook.handleKeyPress(event,null);
54 | return false;
55 | }
56 |
57 | return true;
58 | }
59 | },
60 |
61 | captureEsc: function(event){
62 |
63 | Zotero.debug("ZoteroQuickLook: Capturing Esc");
64 |
65 | if (Zotero.ZoteroQuickLook.isActive()){
66 | event.stopPropagation();
67 | event.preventDefault();
68 | Zotero.ZoteroQuickLook.handleKeyPress(event,null);
69 | return false;
70 | }
71 | return true;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ZoteroQuickLook
2 | ===============
3 |
4 | This open source project implements QuickLook in Zotero.
5 |
6 | # Status
7 |
8 | This plugin is not currently being actively maintained. It may not work with current or beta versions of Zotero. If someone is interested in taking over maintenance of the plugin, please contact @bwiernik.
9 |
10 | # Installing
11 |
12 | To install ZoteroQuickLook, download the latest version of `zoteroquicklook.zoteroplugin` from the Releases section of this GitHub repo. In **Zotero** (not in Firefox), open Tools -> Add-ons. Then drag the `zoteroquicklook.zoteroplugin` file onto the Zotero Add-ons window.
13 |
14 | ## Installing on Mac
15 | On Mac, ZoteroQuickLook uses macOS's native QuickLook functionality. No additional steps are needed.
16 |
17 | ## Installing on Linux
18 |
19 | On Linux, you must install [Gloobus-Preview](https://launchpad.net/gloobus-preview), a QuickLook-like preview software. On Ubuntu you can do this by running the following commands in terminal:
20 |
21 | ```
22 | sudo add-apt-repository ppa:gloobus-dev/gloobus-preview
23 | sudo apt-get update
24 | sudo apt-get upgrade
25 | sudo apt-get install gloobus-preview
26 | ```
27 |
28 | For other distributions and versions the installation might be different.
29 |
30 | If you do not like Gloobus, you can define a custom QuickLook command:
31 | 1. Install the software you want to use for previewing files. The software must be able to take a file name as a command line parameter.
32 | 1. Open the Config Editor in Zotero's Advanced preferences pane.
33 | 1. Search for `extensions.zoteroquicklook.customviewcommand`. Set the value for this config to the full path of the executable that you want to use to show the files.
34 |
35 | ## Installing on Windows
36 |
37 | On Windows, you must first install a QuickLook tool. By default, ZoteroQuickLook is set up to work with [QuickLook](https://github.com/QL-Win/QuickLook/releases). Note that you must install QuickLook using either the `.msi` installer or through the Windows Store. If you use the `.zip` version of QuickLook, you must also set up a custom view command.
38 |
39 | Alternative QuickLook tools for Windows include [Seer](http://1218.io), [WinQuickLook](https://github.com/shibayan/WinQuickLook), and the paid software [MaComfort](https://leonardo.re/macomfort/). In my experience, the default option, QuickLook, is the most stable and powerful and least resource intensive option. You can choose another QuickLook program if you like. The software must be able to take a file name as a command line parameter.
40 |
41 | After installing a QuickLook program, set ZoteroQuickLook to use this program:
42 | 1. Open the Config Editor in Zotero's Advanced preferences pane.
43 | 1. Search for `extensions.zoteroquicklook.customviewcommand`. Set the value for this config to the full path of the executable that you want to use to show the files.
44 |
45 | ### Alternative build for Windows
46 |
47 | Installing the extension on Windows sometimes fails with the error message: "ZoteroQuickLook cannot be installed because Zotero cannot modify the needed file." This is a Windows specific problem caused by the 256 character filename length limitation. This limit is reached during unpacking the xpi archive when installing the extension. Mozilla has fixed the issue by eliminating unpacking of extensions by default and running them directly from the xpi archive. However, this solution does not work for ZoteroQuickLook because on Mac and Linux, the extension uses executable files that need to be unpacked so that they can be run. These files are not used on Windows and there is a parallel version of ZoteroQuickLook that supports running directly from the xpi file. This version is called `zoteroquicklook-windows.xpi` and is available from the Releases section of this GitHub repo.
48 |
49 | You can read more about the problem on [Stack Overflow](http://stackoverflow.com/questions/7872489/addon-cannot-be-installed-by-an-error-of-not-be-able-to-modify-the-needed-file) and the pages linked to from there.
50 |
51 | # Getting support
52 |
53 | Please post any questions or bugs to the Zotero forums. Include "ZoteroQuickLook" in the thread title. Also include the following information:
54 | - Operating system and version (e.g., macOS 10.13.2)
55 | - Zotero version
56 | - ZoteroQuickLook version
57 | - Detailed description of your problem
58 | - A link to a log file (see the next section)
59 | - If on windows, whether loading the alternative Windows build fixes the issue
60 |
61 | ## Log files
62 |
63 | Log file will sometimes be helpful in diagnosing possible issues, so you need to submit a log file when asking for support.
64 |
65 | To create a log file, in Zotero, follow these steps:
66 | 1. Click Help -> Debug Output Logging -> Restart with Logging Enabled….
67 | 1. After Zotero restarts, click Help -> View Output.
68 | 1. Copy the content of the log that appears and paste it to (gist.github.com).
69 | 1. Click "Create secret gist", then copy paste the URL of the gist to your thread on the Zotero forums.
70 |
71 | The important information is in the beginning of the log. Here are example lines from the beginning of a log file. Make sure that your log file starts with similar lines to ensure that the log is complete.
72 |
73 | ```
74 | zotero(3): Using data directory /Users/user/Zotero/
75 |
76 | zotero(3): IPC: Initializing pipe at /Users/user/Zotero/pipes/1376326753918
77 |
78 | zotero(3): Loading in full mode
79 |
80 | zotero(3): Opening database 'zotero'
81 | ```
82 |
--------------------------------------------------------------------------------
/chrome/content/zoteroquicklook.js:
--------------------------------------------------------------------------------
1 | //This is hopefully not needed in the future.
2 | const INTEGRATION_TYPE_ITEM = 1;
3 |
4 | Zotero.ZoteroQuickLook = {
5 | initialized: false,
6 | proc:null,
7 | customviewcommand:null,
8 | isBrowseMode:false,
9 | viewerExecutable:null,
10 | viewerBaseArguments:null,
11 |
12 | init: async function () {
13 | if (document.getElementById('zotero-itemmenu') == null) {
14 | setTimeout(() => { this.init(); }, 1000);
15 | return;
16 | }
17 | document.getElementById('zotero-itemmenu').addEventListener("popupshowing", this.showQuickLookMenu, false);
18 | document.getElementById('zotero-items-tree').addEventListener("keydown",this.onKey,false);
19 |
20 | if (!this.initialized) {
21 | Zotero.debug("ZoteroQuickLook: starts init",3);
22 |
23 | //Trim the preference to avoid problems of extra spaces
24 | this.customviewcommand = this.getPref('customviewcommand').replace(/^\s+|\s+$/g, '');
25 |
26 | //Check that the custom view command exists and show an alert if it does not.
27 |
28 | if(this.customviewcommand!=""){
29 | this.viewerExecutable = Zotero.File.pathToFile(this.customviewcommand);
30 | this.viewerBaseArguments=[''];
31 |
32 | if(this.viewerExecutable.exists() === false){
33 | alert("You have specified a non-existing file ("+this.customviewcommand
34 | +") as a custom view command for Zotero Quick Look. The default view command will be used for this session.");
35 | this.customviewcommand="";
36 | }
37 | }
38 |
39 | // Get the path of the embedded Perl script (Mac/Linux) or QuickLook bridge executable (Win)
40 | if (this.customviewcommand == "") {
41 | await new Promise(function (resolve) {
42 | var MY_ID = "zoteroquicklook@gmail.com";
43 | Components.utils.import("resource://gre/modules/AddonManager.jsm");
44 | AddonManager.getAddonByID(MY_ID, async function (addon) {
45 | let scriptURI = addon.getResourceURI("chrome/content");
46 | await this.initScripts(scriptURI.spec);
47 | resolve();
48 | }.bind(this));
49 | }.bind(this));
50 | } else {
51 | this.initExecutable();
52 | }
53 |
54 | // Initialize the word processor integration
55 | // Disabled due to removal of script menu in Word 2016+
56 | //Zotero.ZoteroQuickLook.initIntegration();
57 |
58 | Zotero.debug("ZoteroQuickLook: finished init",3);
59 |
60 | this.initialized = true;
61 | }
62 | },
63 |
64 | /**
65 | * Initializes external scripts
66 | */
67 | initScripts: async function (scriptURL) {
68 | if (!Zotero.isWin) {
69 | let path = await this.copyURLToTempDir(scriptURL + "/zoteroquicklook.pl");
70 | Zotero.debug("ZoteroQuickLook: Copying zoteroquicklook.pl file to: " + path);
71 | Zotero.ZoteroQuickLook.initExecutable(path);
72 | } else {
73 | let path = await this.copyURLToTempDir(scriptURL + "/Bridge.exe");
74 | Zotero.debug("ZoteroQuickLook: Copying Bridge.exe file to: " + path);
75 | Zotero.ZoteroQuickLook.initExecutable(path);
76 | }
77 |
78 | /*
79 | // Check if the word processor integration for Zotero is installed and install the quicklook word processor script
80 | // Disabled due to removal of script menu in Word 2016+
81 | if (Zotero.isMac) {
82 | let zoteroScriptsPath = Zotero.File.pathToFile(
83 | "~/Library/Application Support/Microsoft/Office/Word Script Menu Items/Zotero"
84 | );
85 | if (zoteroScriptsPath.exists()) {
86 | Zotero.debug("ZoteroQuickLook: Found Zotero word processor integration scripts");
87 |
88 | var zoteroQL = Zotero.File.pathToFile(
89 | "~/Library/Application Support/Microsoft/Office/Word Script Menu Items/Zotero/ZoteroQuickLook/coq.scpt"
90 | );
91 | if (!zoteroQL.exists()){
92 | Zotero.debug("ZoteroQuickLook: Did not find ZoteroQuickLook integration script, attempting to install.");
93 |
94 | let sourceScript = await this.copyURLToTempDir(scriptURL + "ZoteroQuickLook/coq.scpt");
95 |
96 | Zotero.debug("ZoteroQuickLook: Compiling script. "
97 | + "Source: " + sourceScript + " "
98 | + "Target: " + zoteroScriptsPath.path + "/ZoteroQuickLook/coq.scpt"
99 | );
100 |
101 | var userAgent = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime).OS;
102 |
103 | Zotero.debug("ZoteroQuickLook: Determining OS version: " + userAgent);
104 |
105 | await Zotero.Utilities.Internal.exec(
106 | "/usr/bin/osacompile",
107 | [
108 | "-t", "osas",
109 | "-c", "ToyS",
110 | "-o", zoteroScriptsPath.path + "/ZoteroQuickLook/coq.scpt",
111 | sourceScript
112 | ]
113 | );
114 |
115 | await OS.File.remove(sourceScript);
116 |
117 | }
118 | else{
119 | Zotero.debug("ZoteroQuickLook: Found ZoteroQuickLook integration script.");
120 | }
121 | }
122 | else{
123 | Zotero.debug("ZoteroQuickLook: Did not find Zotero word processor integration scripts");
124 | }
125 | }
126 | */
127 | },
128 |
129 | /*
130 | initIntegration: function(){
131 |
132 | Zotero.Integration.Interface.prototype.quickLook = function() {
133 |
134 | Zotero.debug("ZoteroQuickLook: Executing integration function");
135 |
136 | var me = this;
137 |
138 | if(Zotero.ZoteroQuickLook.isActive()){
139 | Zotero.ZoteroQuickLook.closeQuickLook();
140 | return;
141 | }
142 | else{
143 | return this._getSession(true, false).then(function() {
144 | var field = me._doc.cursorInField(me._session.data.prefs['fieldType'])
145 | if(!field) {
146 | throw new Zotero.Exception.Alert("integration.error.notInCitation", [],
147 | "integration.error.title");
148 | }
149 |
150 | var code = field.getCode();
151 | fieldGetter = new Zotero.Integration.Fields(me._session, me._doc);
152 | var [type, content] = fieldGetter.getCodeTypeAndContent(code);
153 |
154 | if(type !== 1) {
155 | throw new Zotero.Exception.Alert("integration.error.notInCitation", [],
156 | "integration.error.title");
157 | }
158 |
159 | var citation = me._session.unserializeCitation(content);
160 | me._session.lookupItems(citation);
161 |
162 | var zoteroItems = [];
163 |
164 | for (let citationItem of citation.citationItems) {
165 | zoteroItem = false;
166 | if(citationItem.uris) {
167 | [zoteroItem, ] = me._session.uriMap.getZoteroItemForURIs(citationItem.uris);
168 | } else if(citationItem.key) {
169 | zoteroItem = Zotero.Items.getByKey(citationItem.key);
170 | }
171 | if(zoteroItem) zoteroItems.push(zoteroItem);
172 | }
173 |
174 | Zotero.ZoteroQuickLook.openQuickLook(zoteroItems);
175 | return;
176 | });
177 | }
178 | }
179 | },
180 | */
181 |
182 | initExecutable: function(scriptLocation) {
183 | Zotero.debug("ZoteroQuickLook: Script location is " + scriptLocation, 3);
184 |
185 | //Initialize the command that is used.
186 |
187 | //TODO: The script fails when custom view command is bogus.
188 |
189 | if(Zotero.ZoteroQuickLook.customviewcommand!=""){
190 | this.viewerExecutable = Zotero.File.pathToFile(Zotero.ZoteroQuickLook.customviewcommand);
191 | if(this.viewerExecutable.exists() === false){
192 | alert("The custom view command " + Zotero.ZoteroQuickLook.customviewcommand + " does not exits.");
193 | }
194 |
195 | if (/maComfort.exe/g.exec(this.viewerExecutable.path)){
196 | this.viewerBaseArguments=['-ql'];
197 | }
198 |
199 | }
200 |
201 |
202 | // Mac and Linux use the same wrapper file for calling QuickLook or Gloobus
203 |
204 | else if(Zotero.isMac || Zotero.isLinux){
205 |
206 |
207 | if(Zotero.isLinux){
208 | const linux_bins = ["/usr/bin/sushi", "/usr/bin/gloobus-preview"];
209 | const linux_existing_bins = linux_bins.filter(bin => Zotero.File.pathToFile(bin).exists());
210 | if (linux_existing_bins.length == 0) {
211 | alert("All supported software are mssing [" + linux_bins.toString() + "]. Please install either one or specify a custom view command instead.");
212 | return;
213 | }
214 | // take the first existing bin as higher priority
215 | this.viewerExecutable = Zotero.File.pathToFile(linux_existing_bins[0]);
216 | }
217 |
218 | if (this.getPref("usefilenameworkaround")) {
219 |
220 |
221 | Zotero.debug("Path to perl script is " + scriptLocation,3);
222 |
223 | //Run the script with perl to avoid permission issues
224 | this.viewerExecutable = Zotero.File.pathToFile("/usr/bin/perl");
225 |
226 | this.viewerBaseArguments=[scriptLocation];
227 | }
228 | else{
229 | if(Zotero.isLinux){
230 | this.viewerExecutable = Zotero.File.pathToFile("/usr/bin/gloobus-preview");
231 | }
232 | else{
233 | this.viewerExecutable = Zotero.File.pathToFile("/usr/bin/qlmanage");
234 | this.viewerBaseArguments=['-p'];
235 | }
236 |
237 | }
238 |
239 | }
240 |
241 | else if(Zotero.isWin){
242 | /* TODO: Checking for existence of Windows Store Apps isn't working
243 | // Check if QuickLook is installed.
244 | localappdata = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("LocalAppData", Components.interfaces.nsIFile).path;
245 | qlMsiLocation = Zotero.File.pathToFile(localappdata + "\\Programs\\QuickLook\\QuickLook.exe");
246 | winApps = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile).initWithPath("C:\\Program Files\\WindowsApps\\").directoryEntries;
247 | winAppsArray = [];
248 | while(winApps.hasMoreElements()) {
249 | var entry = winApps.getNext();
250 | entry.QueryInterface(Components.interfaces.nsIFile);
251 | winAppsArray.push(entry);
252 | }
253 | qlPublisher = /PaddyXu\.QuickLook/g ;
254 | qlWinStoreInstalled = winAppsArray.some(e => qlPublisher.test(e));
255 | if(qlMsiLocation.exists() === false && qlWinStoreInstalled === false){
256 | alert("QuickLook not found. Please install QuickLook (http://pooi.moe/QuickLook/) or specify a custom view command instead.");
257 | return;
258 | }
259 | */
260 |
261 | this.viewerExecutable = Zotero.File.pathToFile(scriptLocation);
262 | this.viewerBaseArguments=[''];
263 | }
264 | },
265 |
266 | getPref: function (pref) {
267 | return Zotero.Prefs.get('extensions.zoteroquicklook.' + pref, true);
268 | },
269 |
270 | copyURLToTempDir: async function (url) {
271 | var tmpDir = OS.Path.join(Zotero.getTempDirectory().path, 'ZoteroQuickLook')
272 | await OS.File.makeDir(tmpDir);
273 | var filename = url.match(/\/([^\/]+)$/)[1];
274 | var path = OS.Path.join(tmpDir, filename);
275 | await Zotero.File.download(url, path);
276 | return path;
277 | },
278 |
279 | cleanFileName: function(filename) {
280 | //TODO is this still needed?
281 | //This is a workaround for firefox bug. See https://www.zotero.org/trac/ticket/957
282 | //The workaround can be disabled with a hidden preference.
283 | //This feature is not supported on Windows and enabling it would just cause problems.
284 | if (this.getPref("usefilenameworkaround") && ! Zotero.isWin){
285 | filename=filename.replace(/[^A-Z0-9.:\/\\_\- ]/gi,'*');
286 | }
287 | return filename;
288 | },
289 |
290 | closeQuickLook: function(){
291 | Zotero.debug("ZoteroQuickLook: is killing quicklook viewer.");
292 | Zotero.ZoteroQuickLook.proc.kill();
293 | Zotero.ZoteroQuickLook.proc=null;
294 | },
295 |
296 | /*
297 |
298 | Checks if quicklook is active.
299 |
300 | */
301 |
302 | isActive: function(){
303 | //On windows checking for the process to be active is not currently supported.
304 | //For some reason the software freezes on Linux if we try to check Zotero.ZoteroQuickLook.proc.isRunning
305 | return ! Zotero.isLinux && Zotero.ZoteroQuickLook.proc!==null && Zotero.ZoteroQuickLook.proc.isRunning;
306 | },
307 |
308 | /*
309 |
310 | Cleans old notes from cahce directory if found.
311 |
312 | */
313 |
314 | cleanOldNotes: function(){
315 |
316 | // Delete the ZoteroQuickLook directory if found
317 | var file = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("TmpD", Components.interfaces.nsIFile);
318 | file.append("ZoteroQuickLook");
319 | if(file.exists()) { // if a cache directory exists, remove it
320 | file.remove(true);
321 | }
322 | },
323 | /*
324 |
325 | Checks the attachment file or writes a content of a note to a file and then pushes this to args.
326 |
327 | */
328 |
329 | pushItemToArgs: async function(args,item){
330 |
331 | if(item.isAttachment()){
332 |
333 | if (item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
334 | return;
335 | }
336 |
337 | let isLinkedFile = !item.isImportedAttachment();
338 | let path = item.getFilePath();
339 | if (!path) {
340 | ZoteroPane_Local.showAttachmentNotFoundDialog(
341 | item.id,
342 | path,
343 | {
344 | noLocate: true,
345 | notOnServer: true,
346 | linkedFile: isLinkedFile
347 | }
348 | );
349 | return;
350 | }
351 | let fileExists = await OS.File.exists(path);
352 |
353 | // If the file is an evicted iCloud Drive file, launch that to trigger a download.
354 | // As of 10.13.6, launching an .icloud file triggers the download and opens the
355 | // associated program (e.g., Preview) but won't actually open the file, so we wait a bit
356 | // for the original file to exist and then continue with regular file opening below.
357 | //
358 | // To trigger eviction for testing, use Cirrus from https://eclecticlight.co/downloads/
359 | if (!fileExists && Zotero.isMac && isLinkedFile) {
360 | // Get the path to the .icloud file
361 | let iCloudPath = Zotero.File.getEvictedICloudPath(path);
362 | if (await OS.File.exists(iCloudPath)) {
363 | // Launching qlmanage should trigger an iCloud download
364 | Zotero.debug("ZoteroQuickLook: Triggering download of iCloud file");
365 | await args.push(Zotero.ZoteroQuickLook.cleanFileName(path));
366 | return;
367 | }
368 | }
369 |
370 | if (fileExists) {
371 | await args.push(Zotero.ZoteroQuickLook.cleanFileName(path));
372 | return;
373 | }
374 |
375 | if (isLinkedFile || !Zotero.Sync.Storage.Local.getEnabledForLibrary(item.libraryID)) {
376 | this.showAttachmentNotFoundDialog(
377 | itemID,
378 | path,
379 | {
380 | noLocate: noLocateOnMissing,
381 | notOnServer: false,
382 | linkedFile: isLinkedFile
383 | }
384 | );
385 | return;
386 | }
387 |
388 | try {
389 | await Zotero.Sync.Runner.downloadFile(item);
390 | }
391 | catch (e) {
392 | // TODO: show error somewhere else
393 | Zotero.debug(e, 1);
394 | ZoteroPane_Local.syncAlert(e);
395 | return;
396 | }
397 |
398 | if (!await item.getFilePathAsync()) {
399 | ZoteroPane_Local.showAttachmentNotFoundDialog(
400 | item.id,
401 | path,
402 | {
403 | noLocate: noLocateOnMissing,
404 | notOnServer: true
405 | }
406 | );
407 | return;
408 | } else {
409 | // Try previeviewing file after download
410 | await args.push(Zotero.ZoteroQuickLook.cleanFileName(path));
411 | }
412 |
413 | }
414 | else if(item.isNote()){
415 |
416 | // Write the content of the note to a temporary file
417 |
418 | var file = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("TmpD", Components.interfaces.nsIFile);
419 | file.append("ZoteroQuickLook");
420 | // If the directory does not exists, create it
421 | if( !file.exists() || !file.isDirectory() ) { // if it doesn't exist, create
422 | file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
423 | }
424 | file.append(item.getNoteTitle()+".html");
425 | file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
426 |
427 | //Source https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO
428 |
429 | // file is nsIFile, data is a string
430 | var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
431 | createInstance(Components.interfaces.nsIFileOutputStream);
432 | // use 0x02 | 0x10 to open file for appending.
433 | foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
434 | // write, create, truncate
435 | // In a c file operation, we have no need to set file mode with or operation,
436 | // directly using "r" or "w" usually.
437 |
438 | // if you are sure there will never ever be any non-ascii text in data you can
439 | // also call foStream.writeData directly
440 | var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
441 | converter.init(foStream, "UTF-8", 0, 0);
442 | converter.writeString(item.getNote());
443 | converter.close(); // this closes foStream
444 |
445 | args.push(Zotero.ZoteroQuickLook.cleanFileName(file.path));
446 | }
447 | },
448 |
449 | /*
450 |
451 | Opens the quick look window with the currently active items.
452 |
453 | */
454 |
455 | openQuickLook: async function(items) {
456 |
457 | Zotero.debug("ZoteroQuickLook: opening viewer",3);
458 |
459 | var args=this.viewerBaseArguments.slice();
460 |
461 | // A boolean indicating if we have notes this far.
462 | var notesFound=false;
463 | var filesFound=false;
464 |
465 | if (!Zotero.isWin || this.customviewcommand !== "") {
466 |
467 | } else {
468 |
469 | }
470 |
471 | // Combine all filenames into an array
472 | // Note that for default Windows behavior, only the first time will be displayed
473 |
474 | for (item in items){
475 |
476 | if (items[item].isAttachment() || items[item].isNote()){
477 | if(items[item].isNote() &! notesFound){
478 | this.cleanOldNotes();
479 | notesFound=true;
480 | }
481 | await this.pushItemToArgs(args,items[item]);
482 | filesFound=true;
483 | }
484 |
485 | //See if it has children and add them. Best attachment comes first.
486 | //Notes come after attachments
487 |
488 | else{
489 |
490 | var attachments=items[item].getAttachments(false);
491 | var notes=items[item].getNotes(false);
492 |
493 | if(notes!==false &! notesFound){
494 | this.cleanOldNotes();
495 | notesFound=true;
496 | }
497 |
498 | children=new Array();
499 | if(attachments!=false) children=children.concat(attachments);
500 | if(notes!=false) children=children.concat(notes);
501 |
502 | for (childID in children){
503 | var child = Zotero.Items.get(children[childID]);
504 | await this.pushItemToArgs(args,child);
505 | filesFound=true;
506 | }
507 |
508 | }
509 | }
510 |
511 |
512 | ///If no files are specified, exit.
513 |
514 | if (! filesFound ) {
515 | Zotero.debug("ZoteroQuickLook: thinks that no files are selected",3);
516 | return false;
517 | }
518 |
519 | // Custom view commmand does not have base arguments but other view commands have one base argument.
520 |
521 | var argsString="";
522 |
523 | for( i in args){
524 | argsString=argsString+" "+args[i];
525 | }
526 |
527 | var baseArgs = this.viewerBaseArguments.slice();
528 | var baseArgsString="";
529 |
530 | for( i in baseArgs){
531 | baseArgsString = baseArgsString+" "+baseArgs[i];
532 | }
533 |
534 | // If no file arguments were added to the base arguments, exit.
535 | if (argsString == baseArgsString) {
536 | Zotero.debug("ZoteroQuickLook: Only linked URLs are selected",3);
537 | return false;
538 | }
539 |
540 | //Write to debug what is called
541 | Zotero.debug("ZoteroQuickLook: calling a shell command: " +this.viewerExecutable.path +argsString,3);
542 |
543 | Zotero.ZoteroQuickLook.proc = Components.classes["@mozilla.org/process/util;1"].
544 | createInstance(Components.interfaces.nsIProcess);
545 | Zotero.ZoteroQuickLook.proc.init(Zotero.ZoteroQuickLook.viewerExecutable);
546 | Zotero.ZoteroQuickLook.proc.runw(false, args, args.length);
547 | return true;
548 | },
549 |
550 | onKey: function(event) {
551 | return Zotero.ZoteroQuickLook.handleKeyPress(event,ZoteroPane.getSelectedItems());
552 | },
553 |
554 | /*
555 |
556 | This function is the actual key listener that decides what to do when a key press event is
557 | received. It calls the functions to open or close the quicklook window.
558 |
559 | */
560 | handleKeyPress: function(event,items){
561 |
562 | var key = String.fromCharCode(event.which);
563 |
564 | if ((key == ' ' && !(event.ctrlKey || event.altKey || event.metaKey)) || (key == 'y' && event.metaKey && !(event.ctrlKey || event.altKey))) {
565 |
566 | //Toggle the quicklook
567 | if(Zotero.ZoteroQuickLook.isActive()){
568 | Zotero.ZoteroQuickLook.closeQuickLook();
569 | }
570 | else{
571 | Zotero.ZoteroQuickLook.openQuickLook(items);
572 | }
573 | Zotero.ZoteroQuickLook.isBrowseMode=false;
574 | }
575 | // Esc
576 | else if(event.keyCode==27){
577 | if(Zotero.ZoteroQuickLook.isActive()){
578 | Zotero.ZoteroQuickLook.closeQuickLook();
579 | }
580 | Zotero.ZoteroQuickLook.isBrowseMode=false;
581 | }
582 | // 38 is arrow up and 40 is arrow down. If quick look is active, we will close it and open it again with the new selection.
583 | else if((event.keyCode==38 || event.keyCode==40)&& !(event.ctrlKey || event.altKey || event.metaKey) && (Zotero.ZoteroQuickLook.isActive() || Zotero.ZoteroQuickLook.isBrowseMode)) {
584 | Zotero.debug("ZoteroQuickLook: is browsing");
585 | if (! Zotero.ZoteroQuickLook.isBrowseMode) Zotero.ZoteroQuickLook.closeQuickLook();
586 | success=Zotero.ZoteroQuickLook.openQuickLook(items);
587 | // If the items were not found, the viewer stays closed. However, if we are browsing through a list of items, we want to reopen the viewer when we hit the next item that has an attachment.
588 | Zotero.ZoteroQuickLook.isBrowseMode = ! success;
589 | Zotero.debug("ZoteroQuickLook: has browse mode set to " + Zotero.ZoteroQuickLook.isBrowseMode,3);
590 |
591 | }
592 | return;
593 |
594 | },
595 |
596 | /*
597 |
598 | A small function that determines if the quicklook option should be visible in the context menu
599 | for the currently active items.
600 |
601 | */
602 |
603 | showQuickLookMenu: function(event) {
604 | var doshow = false;
605 |
606 | var items = ZoteroPane.getSelectedItems();
607 |
608 | doshow = items.length==1 && items[0].isAttachment() && ! Zotero.ZoteroQuickLook.isActive();
609 |
610 |
611 | document.getElementById("zoteroquicklook").hidden = !doshow;
612 | }
613 |
614 |
615 |
616 | };
617 |
618 | function mydump(arr,level) {
619 | var dumped_text = "";
620 | if(!level) level = 0;
621 |
622 | var level_padding = "";
623 | for(var j=0;j \"" + value + "\"\n";
634 | }
635 | }
636 | } else {
637 | dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
638 | }
639 | return dumped_text;
640 | }
641 |
--------------------------------------------------------------------------------