-
Notifications
You must be signed in to change notification settings - Fork 11
/
ror.js
280 lines (272 loc) · 13.4 KB
/
ror.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
console.log("ror.js..");
var rorSelector = "span[data-cvoc-protocol='ror']";
var rorInputSelector = "input[data-cvoc-protocol='ror']";
var rorRetrievalUrl = "https://api.ror.org/organizations";
var rorIdStem = "https://ror.org/";
var rorPrefix = "ror";
//Max chars that displays well for a child field
var rorMaxLength = 31;
$(document).ready(function() {
expandRors();
updateRorInputs();
});
function expandRors() {
// Check each selected element
$(rorSelector).each(function() {
var rorElement = this;
if (rorElement != null) {
// If it hasn't already been processed
if (!$(rorElement).hasClass('expanded')) {
//Child field case - if non-managed display, the string before this is name (affiliation) and we need to remove the duplicate affiliation string
//This is true for Dataverse author field - may not be true elsewhere - tbd
let useParens = true;
let truncate = false;
let prev = $(rorElement)[0].previousSibling;
if (prev != null && prev.tagName != 'BR') {
let val = prev.nodeValue;
if (val !== null) {
let index = val.indexOf('(');
if (index != -1) {
$(rorElement)[0].previousSibling.data = val.substring(0, val.indexOf('('));
}
}
} else {
useParens = false;
truncate = true;
}
// Mark it as processed
$(rorElement).addClass('expanded');
var id = rorElement.textContent;
if (!id.startsWith(rorIdStem)) {
$(rorElement).html(getRorDisplayHtml(id, null, ['No ROR Entry'], false, useParens));
} else {
//Remove the URL prefix - "https://ror.org/".length = 16
id = id.substring(rorIdStem.length);
//Check for cached entry
let value = getValue(rorPrefix, id);
if (value.name != null) {
$(rorElement).html(getRorDisplayHtml(value.name, rorIdStem + id, value.altNames, false, useParens));
} else {
// Try it as an ROR entry (could validate that it has the right form or can just let the GET fail)
$.ajax({
type: "GET",
url: rorRetrievalUrl + "/" + id,
dataType: 'json',
headers: {
'Accept': 'application/json',
},
success: function(ror, status) {
// If found, construct the HTML for display
var name = ror.name;
var altNames = ror.acronyms;
$(rorElement).html(getRorDisplayHtml(name, rorIdStem + id, altNames, false, true));
//Store values in localStorage to avoid repeating calls to CrossRef
storeValue(rorPrefix, id, name + "#" + altNames);
},
failure: function(jqXHR, textStatus, errorThrown) {
// Generic logging - don't need to do anything if 404 (leave
// display as is)
if (jqXHR.status != 404) {
console.error("The following error occurred: " + textStatus, errorThrown);
}
}
});
}
}
}
}
});
}
function getRorDisplayHtml(name, url, altNames, truncate = true, addParens = false) {
if (typeof (altNames) == 'undefined') {
altNames = [];
}
if (truncate && (name.length >= rorMaxLength)) {
// show the first characters of a long name
// return item.text.substring(0,25) + "…";
altNames.unshift(name);
name = name.substring(0, rorMaxLength) + "…";
}
if (url != null) {
name = name + '<a href="' + url + '" target="_blank" rel="nofollow" >' + '<img alt="ROR logo" src="https://raw.githubusercontent.com/ror-community/ror-logos/main/ror-icon-rgb.svg" height="20" class="ror"/></a>';
}
if (addParens) {
name = ' (' + name + ')';
}
return $('<span></span>').append(name).attr("title", altNames);
}
function updateRorInputs() {
// For each input element within rorInputSelector elements
$(rorInputSelector).each(function() {
var rorInput = this;
if (!rorInput.hasAttribute('data-ror')) {
// Random identifier
let num = Math.floor(Math.random() * 100000000000);
// Hide the actual input and give it a data-ror number so we can
// find it
$(rorInput).hide();
$(rorInput).attr('data-ror', num);
// Todo: if not displayed, wait until it is to then create the
// select 2 with a non-zero width
// Add a select2 element to allow search and provide a list of
// choices
var selectId = "rorAddSelect_" + num;
$(rorInput).after(
'<select id=' + selectId + ' class="form-control add-resource select2" tabindex="0" >');
$("#" + selectId).select2({
theme: "classic",
tags: $(rorInput).attr('data-cvoc-allowfreetext'),
delay: 500,
templateResult: function(item) {
// No need to template the searching text
if (item.loading) {
return item.text;
}
// markMatch bolds the search term if/where it appears in
// the result
var $result = markMatch2(item.text, term);
return $result;
},
templateSelection: function(item) {
// For a selection, format as in display mode
//Find/remove the id number
var name = item.text;
var pos = item.text.search(/, [a-z0-9]{9}/);
if (pos >= 0) {
name = name.substr(0, pos);
var idnum = item.text.substr(pos + 2);
var altNames = [];
pos = idnum.indexOf(', ');
if (pos > 0) {
altNames = idnum.substr(pos + 2).split(',');
idnum = idnum.substr(0, pos);
}
return getRorDisplayHtml(name, rorIdStem + idnum, altNames);
}
return getRorDisplayHtml(name, null, ['No ROR Entry']);
},
language: {
searching: function(params) {
// Change this to be appropriate for your application
return 'Search by name or acronym…';
}
},
placeholder: rorInput.hasAttribute("data-cvoc-placeholder") ? $(rorInput).attr('data-cvoc-placeholder') : "Select or enter...",
minimumInputLength: 3,
allowClear: true,
ajax: {
// Use an ajax call to ROR to retrieve matching results
url: rorRetrievalUrl,
data: function(params) {
term = params.term;
if (!term) {
term = "";
}
var query = {
query: term,
}
return query;
},
// request json
headers: {
'Accept': 'application/json'
},
processResults: function(data, params) {
//console.log("Data dump BEGIN");
//console.log(data);
//console.log("Data dump END");
return {
results: data['items']
// Sort the list
// Prioritize active orgs
.sort((a, b) => Number(b.status === 'active') - Number(a.status === 'active'))
// Prioritize those with this acronym
.sort((a, b) => Number(b.acronyms.includes(params.term)) - Number(a.acronyms.includes(params.term)))
// Prioritize previously used entries
.sort((a, b) => Number(getValue(rorPrefix, b['id'].replace(rorIdStem, '')).name != null) - Number(getValue(rorPrefix, a['id'].replace(rorIdStem, '')).name != null))
.map(
function(x) {
return {
text: x.name + ", " + x.id.replace(rorIdStem, '') + ', ' + x.acronyms,
id: x.id
}
})
};
}
}
});
//Add a tab stop and key handling to allow the clear button to be selected via tab/enter
const observer = new MutationObserver((mutationList, observer) => {
var button = $('#' + selectId).parent().find('.select2-selection__clear');
console.log("BL : " + button.length);
button.attr("tabindex", "0");
button.on('keydown', function(e) {
if (e.which == 13) {
$('#' + selectId).val(null).trigger('change');
}
});
});
observer.observe($('#' + selectId).parent()[0], {
childList: true,
subtree: true
}
);
// If the input has a value already, format it the same way as if it
// were a new selection
var id = $(rorInput).val();
if (id.startsWith(rorIdStem)) {
id = id.substring(rorIdStem.length);
$.ajax({
type: "GET",
url: rorRetrievalUrl + "/" + id,
dataType: 'json',
headers: {
'Accept': 'application/json'
},
success: function(ror, status) {
var name = ror.name;
//Display the name and id number in the selection menu
var text = name + ", " + ror.id.replace(rorIdStem, '') + ', ' + ror.acronyms;
var newOption = new Option(text, id, true, true);
$('#' + selectId).append(newOption).trigger('change');
},
failure: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status != 404) {
console.error("The following error occurred: " + textStatus, errorThrown);
}
}
});
} else {
// If the initial value is not in ROR, just display it as is
var newOption = new Option(id, id, true, true);
newOption.altNames = ['No ROR Entry'];
$('#' + selectId).append(newOption).trigger('change');
}
// Could start with the selection menu open
// $("#" + selectId).select2('open');
// When a selection is made, set the value of the hidden input field
$('#' + selectId).on('select2:select', function(e) {
var data = e.params.data;
// For entries from ROR, the id and text are different
//For plain text entries (legacy or if tags are allowed), they are the same
if (data.id != data.text) {
// we want just the ror url
$("input[data-ror='" + num + "']").val(data.id);
} else {
// Tags are allowed, so just enter the text as is
$("input[data-ror='" + num + "']").val(data.id);
}
});
// When a selection is cleared, clear the hidden input
$('#' + selectId).on('select2:clear', function(e) {
$("input[data-ror='" + num + "']").attr('value', '');
});
//When the field is selected via keyboard, move the focus and cursor to the new input
$('#' + selectId).on('select2:open', function(e) {
$(".select2-search__field").focus()
$(".select2-search__field").attr("id", selectId + "_input")
document.getElementById(selectId + "_input").select();
});
}
});
}