├── .gitignore
├── test.sh
├── screenshots
├── screen1.png
└── screen2.png
├── chrome
├── locale
│ ├── en-US
│ │ ├── overlay.dtd
│ │ └── zoteroscholarcitations.properties
│ ├── ru-RU
│ │ ├── overlay.dtd
│ │ └── zoteroscholarcitations.properties
│ └── it-IT
│ │ ├── overlay.dtd
│ │ └── zoteroscholarcitations.properties
└── content
│ ├── overlay.xul
│ └── scripts
│ └── zoteroscholarcitations.js
├── chrome.manifest
├── install.rdf
├── README.md
└── test
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | mocha --ui tdd
4 |
--------------------------------------------------------------------------------
/screenshots/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beloglazov/zotero-scholar-citations/HEAD/screenshots/screen1.png
--------------------------------------------------------------------------------
/screenshots/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beloglazov/zotero-scholar-citations/HEAD/screenshots/screen2.png
--------------------------------------------------------------------------------
/chrome/locale/en-US/overlay.dtd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/chrome/locale/ru-RU/overlay.dtd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/chrome/locale/it-IT/overlay.dtd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/chrome/locale/en-US/zoteroscholarcitations.properties:
--------------------------------------------------------------------------------
1 | captchaString=Please enter the Captcha on the page that will now open and then re-try updating the citations, or wait a while to get unblocked by Google if the Captcha is not present.
2 | citedPrefixString=Cited by
3 |
--------------------------------------------------------------------------------
/chrome/locale/it-IT/zoteroscholarcitations.properties:
--------------------------------------------------------------------------------
1 | captchaString=Per piacere inserisci il codice Captcha nella pagina che si aprirà ora e riprova ad aggiornare le citazioni, oppure attendi che Google venga sbloccato se il codice Captcha non è presente.
2 | citedPrefixString=Citato da
3 |
--------------------------------------------------------------------------------
/chrome/locale/ru-RU/zoteroscholarcitations.properties:
--------------------------------------------------------------------------------
1 | captchaString=Пожалуйста, введите Captcha код с изображения на странице, которая сейчас откроется, затем попробуйте ещё раз обновить цитирования. Если страница с кодом не отображается, подождите некоторое время для разблокировки сервисом Google Scholar.
2 | citedPrefixString=Цит.:
3 |
--------------------------------------------------------------------------------
/chrome.manifest:
--------------------------------------------------------------------------------
1 | content zoteroscholarcitations chrome/content/
2 |
3 | overlay chrome://zotero/content/zoteroPane.xul chrome://zoteroscholarcitations/content/overlay.xul
4 |
5 | locale zoteroscholarcitations en-US chrome/locale/en-US/
6 | locale zoteroscholarcitations ru-RU chrome/locale/ru-RU/
7 | locale zoteroscholarcitations it-IT chrome/locale/it-IT/
8 |
9 |
--------------------------------------------------------------------------------
/chrome/content/overlay.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
12 |
13 |
14 |
17 |
18 |
19 |
26 |
27 |
33 |
34 |
42 |
43 |
--------------------------------------------------------------------------------
/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
20 | 2
21 |
22 |
23 |
24 | zotero@chnm.gmu.edu
25 | 3.0b1
26 | 5.0.*
27 |
28 |
29 |
30 |
31 | ru-RU
32 | Zotero Scholar Citations
33 | Плагин для Zotero для автоматической выборки числа цитирований из Google Scholar.
34 |
35 |
36 |
37 |
38 | it-IT
39 | Zotero Scholar Citations
40 | Plugin di Zotero per il recupero automatico del numero di citazioni in Google Scholar.
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zotero Scholar Citations
2 |
3 | This is an add-on for Zotero, a research source management tool. The add-on automatically fetches numbers of citations of your Zotero items from Google Scholar and makes it possible to sort your items by the citations. Moreover, it allows batch updating the citations, as they may change over time.
4 |
5 | When updating multiple citations in a batch, it may happen that citation queries are blocked by Google Scholar for multiple automated requests. If a blockage happens, the add-on opens a browser window and directs it to http://scholar.google.com/, where you should see a Captcha displayed by Google Scholar, which you need to enter to get unblocked and then re-try updating the citations. It may happen that Google Scholar displays a message like the following "We're sorry... but your computer or network may be sending automated queries. To protect our users, we can't process your request right now." In that case, the only solution is to wait for a while until Google unblocks you.
6 |
7 | Currently, Zotero doesn't have any special field for the number of citations, that's why it is stored in the "Extra" field. To sort by this field you have to add it in the source listing table.
8 |
9 | *IMPORTANT:* in version 1.8 the field for storing the number of citations has been changed from "Call Number" to "Extra" -- please update your column configuration.
10 |
11 | The add-on supports both versions of Zotero:
12 |
13 | 1. Zotero Standalone:
14 | - Download the add-on from https://github.com/beloglazov/zotero-scholar-citations/raw/master/builds/zotero-scholar-citations-1.9.3-fx.xpi
15 | - In Zotero Standalone go to Tools -> Add-ons -> click the settings button in the top-right corner -> Install Add-on From File -> select the downloaded file and restart Zotero.
16 | 2. Zotero for Firefox:
17 | - Install the Firefox add-on from https://addons.mozilla.org/en-us/firefox/addon/zotero-scholar-citations/
18 |
19 | Read about how the add-on was made: http://blog.beloglazov.info/2009/10/zotero-citations-from-scholar-en.html
20 |
21 | # License
22 |
23 | Copyright (C) 2011-2013 Anton Beloglazov
24 |
25 | Distributed under the Mozilla Public License (MPL).
26 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | var zsc = require('../chrome/content/scripts/zoteroscholarcitations.js');
2 | var assert = require('assert');
3 | var sinon = require('sinon');
4 | var request = require('sync-request');
5 |
6 | var items = [
7 | {
8 | 'citations': 400,
9 | 'title': 'Energy-aware resource allocation heuristics for efficient management of data centers for cloud computing',
10 | 'date': '2012',
11 | 'creators': [{
12 | 'ref': {
13 | 'firstName': 'Anton',
14 | 'lastName': 'Beloglazov'
15 | }
16 | }, {
17 | 'ref': {
18 | 'firstName': 'Jemal',
19 | 'lastName': 'Abawajy'
20 | }
21 | }, {
22 | 'ref': {
23 | 'firstName': 'Rajkumar',
24 | 'lastName': 'Buyya'
25 | }
26 | }]
27 | },
28 | {
29 | 'citations': 50,
30 | 'title': 'Optimal value of information in graphical models',
31 | 'date': '2009',
32 | 'creators': [{
33 | 'ref': {
34 | 'firstName': 'Carlos',
35 | 'lastName': 'Guestrin',
36 | }
37 | }, {
38 | 'ref': {
39 | 'firstName': 'CMU',
40 | 'lastName': 'EDU',
41 | }
42 | }]
43 | }];
44 |
45 |
46 | function createMockItem(item) {
47 | var mock = {
48 | citations: item.citations,
49 | getField: sinon.stub(),
50 | getCreators: sinon.stub()
51 | };
52 | mock.getField.withArgs('title').returns(item.title);
53 | mock.getField.withArgs('date').returns(item.date);
54 | mock.getCreators.returns(item.creators);
55 | return mock;
56 | }
57 |
58 | function fetchCitations(item) {
59 | var url = zsc.generateItemUrl(item);
60 | var res = request('GET', url);
61 | var content = res.body.toString('utf-8');
62 | return parseInt(zsc.getCitationCount(content));
63 | }
64 |
65 |
66 | suite('Zotero Scholar Citations', function() {
67 | this.timeout(0);
68 |
69 | test('fillZeros', function() {
70 | assert.equal(zsc.fillZeros(''), '00000');
71 | assert.equal(zsc.fillZeros('1'), '00001');
72 | assert.equal(zsc.fillZeros('32'), '00032');
73 | });
74 |
75 | test('fetchCitations', function() {
76 | items.forEach(function (item) {
77 | var mock = createMockItem(item);
78 | assert(fetchCitations(mock) > mock.citations);
79 | });
80 | });
81 |
82 |
83 | });
84 |
85 |
--------------------------------------------------------------------------------
/chrome/content/scripts/zoteroscholarcitations.js:
--------------------------------------------------------------------------------
1 | if (typeof Zotero === 'undefined') {
2 | Zotero = {};
3 | }
4 | Zotero.ScholarCitations = {};
5 |
6 |
7 | Zotero.ScholarCitations.init = function() {
8 | Zotero.ScholarCitations.resetState();
9 |
10 | stringBundle = document.getElementById('zoteroscholarcitations-bundle');
11 | Zotero.ScholarCitations.captchaString = 'Please enter the Captcha on the page that will now open and then re-try updating the citations, or wait a while to get unblocked by Google if the Captcha is not present.';
12 | Zotero.ScholarCitations.citedPrefixString = ''
13 | if (stringBundle != null) {
14 | Zotero.ScholarCitations.captchaString = stringBundle.getString('captchaString');
15 | }
16 |
17 | // Register the callback in Zotero as an item observer
18 | var notifierID = Zotero.Notifier.registerObserver(
19 | Zotero.ScholarCitations.notifierCallback, ['item']);
20 |
21 | // Unregister callback when the window closes (important to avoid a memory leak)
22 | window.addEventListener('unload', function(e) {
23 | Zotero.Notifier.unregisterObserver(notifierID);
24 | }, false);
25 | };
26 |
27 | Zotero.ScholarCitations.notifierCallback = {
28 | notify: function(event, type, ids, extraData) {
29 | if (event == 'add') {
30 | Zotero.ScholarCitations.updateItems(Zotero.Items.get(ids));
31 | }
32 | }
33 | };
34 |
35 | Zotero.ScholarCitations.resetState = function() {
36 | Zotero.ScholarCitations.current = -1;
37 | Zotero.ScholarCitations.toUpdate = 0;
38 | Zotero.ScholarCitations.itemsToUpdate = null;
39 | Zotero.ScholarCitations.numberOfUpdatedItems = 0;
40 | };
41 |
42 | Zotero.ScholarCitations.updateSelectedEntity = function(libraryId) {
43 | if (!ZoteroPane.canEdit()) {
44 | ZoteroPane.displayCannotEditLibraryMessage();
45 | return;
46 | }
47 |
48 | var collection = ZoteroPane.getSelectedCollection();
49 | var group = ZoteroPane.getSelectedGroup();
50 |
51 | if (collection) {
52 | var items = [];
53 | collection.getChildItems(false).forEach(function (item) {
54 | items.push(Zotero.Items.get(item.id));
55 | });
56 | Zotero.ScholarCitations.updateItems(items);
57 | } else if (group) {
58 | if (!group.editable) {
59 | alert("This group is not editable!");
60 | return;
61 | }
62 | var items = [];
63 | group.getCollections().forEach(function(collection) {
64 | collection.getChildItems(false).forEach(function(item) {
65 | items.push(Zotero.Items.get(item.id));
66 | })
67 | });
68 | Zotero.ScholarCitations.updateItems(items);
69 | } else {
70 | Zotero.ScholarCitations.updateAll();
71 | }
72 | };
73 |
74 | Zotero.ScholarCitations.updateSelectedItems = function() {
75 | Zotero.ScholarCitations.updateItems(ZoteroPane.getSelectedItems());
76 | };
77 |
78 | Zotero.ScholarCitations.updateAll = function() {
79 | var items = [];
80 | Zotero.Items.getAll().forEach(function (item) {
81 | if (item.isRegularItem() && !item.isCollection()) {
82 | var libraryId = item.getField('libraryID');
83 | if (libraryId == null ||
84 | libraryId == '' ||
85 | Zotero.Libraries.isEditable(libraryId)) {
86 | items.push(item);
87 | }
88 | }
89 | });
90 | Zotero.ScholarCitations.updateItems(items);
91 | };
92 |
93 | Zotero.ScholarCitations.updateItems = function(items) {
94 | if (items.length == 0 ||
95 | Zotero.ScholarCitations.numberOfUpdatedItems < Zotero.ScholarCitations.toUpdate) {
96 | return;
97 | }
98 |
99 | Zotero.ScholarCitations.resetState();
100 | Zotero.ScholarCitations.toUpdate = items.length;
101 | Zotero.ScholarCitations.itemsToUpdate = items;
102 | Zotero.ScholarCitations.updateNextItem();
103 | };
104 |
105 | Zotero.ScholarCitations.updateNextItem = function() {
106 | Zotero.ScholarCitations.numberOfUpdatedItems++;
107 |
108 | if (Zotero.ScholarCitations.current == Zotero.ScholarCitations.toUpdate - 1) {
109 | Zotero.ScholarCitations.resetState();
110 | return;
111 | }
112 |
113 | Zotero.ScholarCitations.current++;
114 | Zotero.ScholarCitations.updateItem(
115 | Zotero.ScholarCitations.itemsToUpdate[Zotero.ScholarCitations.current]);
116 | };
117 |
118 | Zotero.ScholarCitations.generateItemUrl = function(item) {
119 | var baseUrl = 'https://scholar.google.com/';
120 | var url = baseUrl +
121 | 'scholar?hl=en&as_q=' +
122 | encodeURIComponent(item.getField('title')).replace(/ /g, '+') +
123 | '&as_occt=title&num=1';
124 |
125 | var creators = item.getCreators();
126 | if (creators.length > 0) {
127 | url += '&as_sauthors=' +
128 | encodeURIComponent(creators[0].lastName).replace(/ /g, '+');
129 | } else {
130 | var date = item.getField('date');
131 | if (date != '') {
132 | url += '&as_ylo=' + date + '&as_yhi=' + date;
133 | }
134 | }
135 |
136 | return url;
137 | };
138 |
139 | Zotero.ScholarCitations.updateItem = function(item) {
140 | var req = new XMLHttpRequest();
141 | var url = Zotero.ScholarCitations.generateItemUrl(item);
142 | req.open('GET', url, true);
143 |
144 | req.onreadystatechange = function() {
145 | if (req.readyState == 4) {
146 | if (req.status == 200 && req.responseText.search("RecaptchaOptions") == -1) {
147 | if (item.isRegularItem() && !item.isCollection()) {
148 | var citations = Zotero.ScholarCitations.getCitationCount(
149 | req.responseText);
150 | try {
151 | var old = item.getField('extra')
152 | if (old.length == 0 || old.search(/^\d{5}$/) != -1) {
153 | item.setField('extra', citations);
154 | } else if (old.search(/^\d{5} *\n/) != -1) {
155 | item.setField(
156 | 'extra',
157 | old.replace(/^\d{5} */, citations + ' '));
158 | } else if (old.search(/^\d{5} *[^\n]+/) != -1) {
159 | item.setField(
160 | 'extra',
161 | old.replace(/^\d{5} */, citations + ' \n'));
162 | } else if (old.search(/^\d{5}/) != -1) {
163 | item.setField(
164 | 'extra',
165 | old.replace(/^\d{5}/, citations));
166 | } else {
167 | item.setField('extra', citations + ' \n' + old);
168 | }
169 | item.save();
170 | } catch (e) {}
171 | }
172 | Zotero.ScholarCitations.updateNextItem();
173 | } else if (req.status == 200 ||
174 | req.status == 403 ||
175 | req.status == 503) {
176 | alert(Zotero.ScholarCitations.captchaString);
177 | req2 = new XMLHttpRequest();
178 | req2.open('GET', url, true);
179 | req2.onreadystatechange = function() {
180 | if (req2.readyState == 4) {
181 | if (typeof Zotero.launchURL !== 'undefined') {
182 | Zotero.launchURL(url);
183 | } else if (typeof Zotero.openInViewer !== 'undefined') {
184 | Zotero.openInViewer(url);
185 | } else if (typeof ZoteroStandalone !== 'undefined') {
186 | ZoteroStandalone.openInViewer(url);
187 | } else {
188 | window.gBrowser.loadOneTab(
189 | url, {inBackground: false});
190 | }
191 | Zotero.ScholarCitations.resetState();
192 | }
193 | }
194 | req2.send(null);
195 | }
196 | }
197 | };
198 |
199 | req.send(null);
200 | };
201 |
202 | Zotero.ScholarCitations.fillZeros = function(number) {
203 | var output = '';
204 | var cnt = 5 - number.length;
205 | for (var i = 0; i < cnt; i++) {
206 | output += '0';
207 | }
208 | output += number;
209 | return output;
210 | };
211 |
212 | Zotero.ScholarCitations.getCitationCount = function(responseText) {
213 | if (responseText == '') {
214 | return '00000';
215 | }
216 |
217 | var citeStringLength = 15;
218 | var lengthOfCiteByStr = 9;
219 | var citeArray = new Array();
220 |
221 | var citeExists = responseText.search('Cited by');
222 | if (citeExists == -1) {
223 | return '00000';
224 | }
225 |
226 | var tmpString = responseText.substr(citeExists, citeStringLength);
227 | var end = tmpString.indexOf('<') - lengthOfCiteByStr;
228 | return Zotero.ScholarCitations.fillZeros(
229 | tmpString.substr(lengthOfCiteByStr, end));
230 | };
231 |
232 | if (typeof window !== 'undefined') {
233 | window.addEventListener('load', function(e) {
234 | Zotero.ScholarCitations.init();
235 | }, false);
236 | }
237 |
238 | module.exports = Zotero.ScholarCitations;
239 |
--------------------------------------------------------------------------------