Skip to content

Commit 64d9bbb

Browse files
committed
Merge branch 'develop'
current state: Alpha Deployment
2 parents 4b60975 + 6361233 commit 64d9bbb

5 files changed

Lines changed: 442 additions & 370 deletions

File tree

Code.gs

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/* Copyright 2014 University of Passau
2-
Licensed under the Apache License, Version 2.0 (the "License");
3-
you may not use this file except in compliance with the License.
4-
You may obtain a copy of the License at
5-
http://www.apache.org/licenses/LICENSE-2.0
6-
Unless required by applicable law or agreed to in writing, software
7-
distributed under the License is distributed on an "AS IS" BASIS,
8-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9-
See the License for the specific language governing permissions and
10-
limitations under the License.
11-
*/
2+
Licensed under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License.
4+
You may obtain a copy of the License at
5+
http://www.apache.org/licenses/LICENSE-2.0
6+
Unless required by applicable law or agreed to in writing, software
7+
distributed under the License is distributed on an "AS IS" BASIS,
8+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
See the License for the specific language governing permissions and
10+
limitations under the License.
11+
*/
12+
1213
/**
1314
* Creates a menu entry in the Google Docs UI when the document is opened.
1415
*
@@ -40,11 +41,20 @@ function onInstall(e) {
4041
* Opens a sidebar in the document containing the add-on's user interface.
4142
*/
4243
function showSidebar() {
43-
var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
44-
.setTitle('Recommendations');
44+
var ui = HtmlService.createTemplateFromFile('Sidebar').evaluate()
45+
.setTitle('E-Explorer');
4546
DocumentApp.getUi().showSidebar(ui);
4647
}
4748

49+
/**
50+
* Returns the contents of a HTML file.
51+
* @param {string} file The name of the file to retrieve.
52+
* @return {string} The file's content.
53+
*/
54+
function include(file) {
55+
return HtmlService.createTemplateFromFile(file).evaluate().getContent();
56+
}
57+
4858
/**
4959
* Gets the text the user has selected. If there is no selection,
5060
* this function displays an error message.
@@ -79,33 +89,25 @@ function getSelectedText() {
7989
}
8090
}
8191
}
92+
8293
if (text.length == 0) {
8394
throw 'Please select some text.';
8495
}
96+
8597
return text;
8698
} else {
8799
throw 'Please select some text.';
88100
}
89101
}
90102

91103
/**
92-
* Gets the recommendations from the entered text.
93-
*
94-
* @param {String} text The text entered by the user.
95-
*
96-
* @return {String} The response as JSON string.
97-
*/
98-
function getRecommandationsFromInput(text) {
99-
return callProxy(getTerms([text]));
100-
}
101-
102-
/**
103-
* Gets the recommendations from the selected user text.
104+
* Fetches the recommendations for the given text.
104105
*
106+
* @param {Array<String>} text for which the recommendations should be fetched
105107
* @return {String} The response as JSON string.
106108
*/
107-
function getRecommendations() {
108-
return callProxy(getTerms(getSelectedText()));
109+
function fetchRecommendations(text) {
110+
return callProxy(getTerms(text));
109111
}
110112

111113
/**
@@ -117,7 +119,7 @@ function getRecommendations() {
117119
*/
118120
function getTerms(text) {
119121
var terms = [];
120-
122+
121123
// Split the text into terms
122124
for(t in text) {
123125
var tmp = text[t].split(" ");
@@ -126,7 +128,7 @@ function getTerms(text) {
126128
terms.push(tmp[i].replace(/\s/g, "").replace(/[\.,#-\/!$%\^&\*;:{}=\-_`~()]/g,""));
127129
}
128130
}
129-
131+
130132
return terms;
131133
}
132134

@@ -139,18 +141,18 @@ function getTerms(text) {
139141
*/
140142
function callProxy(terms) {
141143
// privacy proxy URL
142-
var url = "http://eexcess.joanneum.at/eexcess-privacy-proxy/api/v1/recommend";
144+
var url = "http://eexcess.joanneum.at/eexcess-privacy-proxy/api/v1/recommend";
143145
// federated recommender
144146
//var url = "http://eexcess.joanneum.at/eexcess-federated-recommender-web-service-1.0-SNAPSHOT/recommender/recommend";
145-
147+
146148
// POST payload
147149
var data = { "numResults" : 60, "contextKeywords" : [] };
148-
150+
149151
// Fill the context array
150152
for(i in terms) {
151153
data["contextKeywords"].push({ "weight" : 1.0 / terms.length, "text" : terms[i] });
152154
}
153-
155+
154156
// Options object, that specifies the method, content type and payload of the HTTPRequest
155157
var options = {
156158
"method" : "POST",
@@ -168,4 +170,4 @@ function callProxy(terms) {
168170
throw err;
169171
}
170172

171-
}
173+
}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
## How this addon works
44

5-
1. Go to: https://docs.google.com/document/d/1hqLocWCyKMQQr254gDO_BWfm6tDBmqMFz7IPqOvbKhA/edit
5+
1. Go to: https://docs.google.com/document/d/1hqLocWCyKMQQr254gDO_BWfm6tDBmqMFz7IPqOvbKhA/edit
66
2. Login with your Google Account to be able to use this addon
77
3. Activate the plugin (Top Menu: Addons -> EEXCESS -> Start)
8-
4. Select a piece of text and click the Get Recommendations button inside the sidebar. After a short while the recommendations will be displayed above the Get Recommendations button.
8+
4. Select a piece of text or use the search bar to search for recommendations. After a short while the recommendations will be displayed in the sidebar.
99

1010
## Source Code
1111

Scripts.html

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
2+
3+
<script>
4+
// constants
5+
var REFRESH_INTERVAL = 1000; // update selected text inspector every 1 second
6+
var TIMEOUT = 20000; // timout of 20s for fetching recommendations
7+
8+
// global variables
9+
var lastSelectedText;
10+
var lastSearchedText;
11+
12+
/**
13+
* On document load.
14+
*/
15+
$(function() {
16+
// init search bar
17+
$("#eexcess-search").keyup(function (e) {
18+
if (e.keyCode == 13) { // Enter keycode
19+
$('#btn-search').click();
20+
}
21+
});
22+
23+
$('#btn-search').click(getRecommendationsFromInput);
24+
25+
inspectSelectedText();
26+
});
27+
28+
/**
29+
* Runs a server-side function to get the recommendations for the user-entered text and update
30+
* the sidebar UI with the results from the privacy proxy.
31+
*/
32+
function getRecommendationsFromInput() {
33+
removeInfo();
34+
removeError();
35+
36+
var keyword = $("#eexcess-search").val();
37+
if(keyword == "") {
38+
showError("Please enter some text.");
39+
} else {
40+
this.disabled = true;
41+
fetchAndDisplayRecommendations(keyword, this);
42+
}
43+
}
44+
45+
function inspectSelectedText() {
46+
google.script.run
47+
.withSuccessHandler(
48+
function(selectedText) {
49+
if (selectedText.length > 0 && !arraysEqual(selectedText, lastSelectedText)) {
50+
lastSelectedText = selectedText;
51+
$("#eexcess-search").val(selectedText);
52+
$('#btn-search').click();
53+
}
54+
})
55+
.withFailureHandler(
56+
function() {
57+
// ignore errors
58+
})
59+
.getSelectedText();
60+
61+
window.setTimeout(inspectSelectedText, REFRESH_INTERVAL);
62+
}
63+
64+
/**
65+
* Runs a server-side function to fetch the recommendations for the given text and updates the sidebar UI with the
66+
* results from the privacy proxy or displays and error message in the sidebar.
67+
*
68+
* @param {String} or {Array<String>} text text for which the recommendations should be fetched
69+
* @param button disabled button which triggered the action or undefined if action was not triggered by a button
70+
*/
71+
function fetchAndDisplayRecommendations(text, button){
72+
// Remove error messages TODO remove after error messages displayed in recommendations list
73+
removeError();
74+
75+
// Clear previously loaded recommendations and displayed errors
76+
$("#eexcess-recommendations-list").empty();
77+
78+
// Insert Ajax loader icon
79+
showAjaxLoader();
80+
81+
var responded = false;
82+
var timeout = false;
83+
lastSearchedText = text;
84+
85+
setTimeout(function() {
86+
if(!responded) {
87+
timeout = true;
88+
removeAjaxLoader();
89+
90+
if (lastSearchedText == text)
91+
showError("The server timed out waiting for the request.");
92+
93+
if (button)
94+
button.disabled = false;
95+
}
96+
}, TIMEOUT);
97+
98+
google.script.run
99+
.withSuccessHandler(
100+
function(recommendations, button) {
101+
if(!timeout && lastSearchedText == text){
102+
responded = true;
103+
removeAjaxLoader();
104+
// Display Results
105+
showResultList(recommendations);
106+
107+
if (button)
108+
button.disabled = false;
109+
}
110+
})
111+
.withFailureHandler(
112+
function(errorMsg, button) {
113+
if(!timeout && lastSearchedText == text) {
114+
removeAjaxLoader();
115+
showError(errorMsg);
116+
117+
if (button)
118+
button.disabled = false;
119+
}
120+
})
121+
.withUserObject(button)
122+
.fetchRecommendations([text]);
123+
}
124+
125+
/**
126+
* Compares two 1-dimensional arrays by their content.
127+
*
128+
* @param a array 1
129+
* @param b array 2
130+
*/
131+
function arraysEqual(a, b) {
132+
if (a === b)
133+
return true;
134+
135+
if (a == null || b == null)
136+
return false;
137+
138+
if (a.length != b.length)
139+
return false;
140+
141+
for (var i = 0; i < a.length; ++i) {
142+
if (a[i] !== b[i])
143+
return false;
144+
}
145+
146+
return true;
147+
}
148+
149+
/**
150+
* Inserts the result list
151+
*
152+
* @param recommendations The results
153+
*/
154+
function showResultList(recommendations) {
155+
var container = $("#eexcess-recommendations-list");
156+
var titleLength = 30;
157+
var descriptionLength = 80;
158+
// Parsing the JSON string
159+
var o = JSON.parse(recommendations);
160+
if(o.totalResults > 0) {
161+
// iterate the results and append the list elements
162+
$.each(o.result, function() {
163+
var title = this.title;
164+
if(this.title.length > titleLength) {
165+
title = jQuery.trim(title).substring(0, titleLength).split(" ").slice(0, -1).join(" ") + "...";
166+
}
167+
var previewImage = "http://dummyimage.com/60x60/999/fff.png&text=+";
168+
169+
if(this.previewImage) {
170+
previewImage = this.previewImage;
171+
}
172+
173+
var description = "No description available.";
174+
175+
if(this.description) {
176+
description = this.description;
177+
}
178+
179+
var titleDescription = description;
180+
if(description.length > descriptionLength) {
181+
description = jQuery.trim(description).substring(0, descriptionLength).split(" ").slice(0, -1).join(" ") + "...";
182+
}
183+
184+
var template =
185+
'<li>' +
186+
'<a href="' + this.uri + '" target="_blank" class="inner">' +
187+
'<div class="li-img">' +
188+
'<img src="' + previewImage + '" alt="Image Alt Text" />' +
189+
'</div>' +
190+
'<div class="li-text">' +
191+
'<h4 class="li-head" title="' + this.title + '">' + title + '</h4>' +
192+
'<p class="li-provider">Provider: ' + this.facets.provider + '</p>' +
193+
'<p class="li-summary" title="' + titleDescription + '">' + description + '</p>' +
194+
'</div>' +
195+
'</a>' +
196+
'</li>';
197+
container.append(template);
198+
})
199+
} else {
200+
showError("No results found");
201+
}
202+
}
203+
204+
/**
205+
* Inserts a div that contains an error message after a given element.
206+
*
207+
* @param msg The error message to display.
208+
* @param element The element after which to display the error.
209+
*/
210+
function showError(msg) {
211+
removeError();
212+
var div = $('<div id="error" class="error">' + msg + '</div>');
213+
$("#eexcess-recommendations-list").after(div);
214+
}
215+
216+
/**
217+
* Removes previously added error messages.
218+
*/
219+
function removeError() {
220+
$("#error").remove();
221+
}
222+
223+
/**
224+
* Removes the initially added info message.
225+
*/
226+
function removeInfo() {
227+
$("#info").remove();
228+
}
229+
230+
/**
231+
* Inserts the ajax loader icon.
232+
*/
233+
function showAjaxLoader() {
234+
if ($("#ajax-loader-container").length == 0) {
235+
var div = $('<div id="ajax-loader-container"><img src="http://mics.fim.uni-passau.de/wp-content/uploads/2014/10/ajax-loader.gif" width="16px" height="16px" alt="ajax loader"/></div>');
236+
$("#eexcess-recommendations-list").after(div);
237+
}
238+
}
239+
240+
/**
241+
* Removes the ajax loader icon (spinner) from the DOM.
242+
*/
243+
function removeAjaxLoader() {
244+
$('#ajax-loader-container').remove();
245+
}
246+
</script>

0 commit comments

Comments
 (0)