source: main/trunk/greenstone3/web/interfaces/default/transform/layouts/webswing-collage.xsl@ 38991

Last change on this file since 38991 was 38991, checked in by anupama, 4 weeks ago

Dr Bainbridge set it was better to remove the applet shim and in place of JavaScript reading the applet params for any Collage user configuration options in the pageResponse XML, set those user config options as gsf:variables (which do more than xsl:variables by declaring javascript vars besides xslt vars, but need to take care we're not clashing with pre-declared var names). The webswing-collage.xsl Javascript code now uses the gsf:vars in preference to fallback defaults for user-configurable vars, while dynamic vars were already set by the JavaScript if the applet shim didn't set them. Applet shims are entirely phased out. As the Phind classifier doesn't have user config settings that are relevant to instantiating the Phind app, all the phind XSL/JavaScript code checking for any user settings on any applet shim are removed, simplifying the Phind XSL/JS code again. Rechecked the changes. Also checked again for proper shutdown on navigating away (so that part isn't accidentally broken) and checked the phind and collage app pages still respond correct when the webswing extension is deactivated.

File size: 17.1 KB
Line 
1<?xml version="1.0" encoding="UTF-8"?>
2<xsl:stylesheet version="1.0"
3 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4 xmlns:java="http://xml.apache.org/xslt/java"
5 xmlns:util="xalan://org.greenstone.gsdl3.util.XSLTUtil"
6 xmlns:gslib="http://www.greenstone.org/skinning"
7 xmlns:gsf="http://www.greenstone.org/greenstone3/schema/ConfigFormat"
8 extension-element-prefixes="java util"
9 exclude-result-prefixes="java util gsf">
10
11 <xsl:template name="webswing-embed-collage">
12 <link rel="stylesheet" href="/webswing-server/css/style.css" />
13 <div id="webswing-collage" class="webswing-element" data-webswing-instance="webswingInstance0" style="width: 800px; height: 400px;">
14
15 <div id="loading" class="ws-modal-container">
16 <div class="ws-login">
17 <div class="ws-login-content">
18 <div class="ws-spinner">
19 <div class="ws-spinner-dot-1"><xsl:comment>filler</xsl:comment></div>
20 <div class="ws-spinner-dot-2"><xsl:comment>filler</xsl:comment></div>
21 </div>
22 </div>
23 </div>
24 </div>
25
26 </div>
27
28 <gsf:script>
29 // https://www.webswing.org/docs/23.2/configure/applet.html
30 // https://www.webswing.org/docs/23.2/configure/swing.html
31 // https://www.webswing.org/docs/23.2/integrate/javascript-api?_h=customArgs%2Cargs#usage-with-customization-and-options
32 // https://www.webswing.org/docs/23.2/integrate/embed.html
33 // https://www.webswing.org/docs/20.1/integrate/urlparams.html
34 // https://www.webswing.org/docs/23.2/integrate/urlparams.html
35 // https://www.webswing.org/docs/20.1/integrate/customize.html
36 // https://www.webswing.org/docs/2.7/integrate/embed.html
37
38 var webswingInstance0 = {
39 options: {
40 autoStart: true,
41 //appletParams: {"collection":"smallbea", "library":"library"},
42 connectionUrl:'/webswing-server/collage',
43
44 customization: function(injector) {
45 injector.services.base.handleActionEvent = function(actionName, data, binaryData) {
46 //console.log("WebSwing actionEvent callback handler: called with actionName = " + actionName);
47
48 if (actionName === "openURL") {
49 var url = data;
50 // check if a target tab/window name has been specified
51 // TODO: Any better way of passing > 1 string between Java and JavaScript?
52 var index = url.indexOf(" - ");
53 if (index !== -1) {
54 var target = url.substring(index+3); // skip past " - " to get target name
55 url = url.substring(0, index);
56 // Note that target window name is not the same as target window title
57 // https://stackoverflow.com/questions/8051811/how-to-show-window-title-using-window-open
58 window.open(url, target);
59 } else {
60 window.open(url, '_blank');
61 }
62 } else if (actionName == "javaToWebswingJSConsoleLog") {
63 console.log("Got message from java:\n" + data);
64 }
65
66 }
67 }
68
69
70 }
71 };
72
73
74 // The applet jar files can just remain in web/applet where they are compiled up
75
76 if(!webswingInstance0.options.args) {
77 webswingInstance0.options.args="";
78 }
79
80 var verbosity = 3;
81
82 // Fill up webswingParams JSON record with fallbacks for non-dynamic values
83 // and values that are not user configurable
84 var webswingParams = {
85 //"webswing":1, // set in webswing.config.in to discourage editing
86 "verbosity": verbosity,
87 "imageMustNotHave":"interfaces/",
88 "imageType":".jpg%.png",
89 "maxDepth": 500,
90 "maxDisplay": 25,
91 "refreshDelay": 1500,
92 "isJava2":"auto",
93 "bgcolor":"#96c29a"
94 };
95
96
97 // get all available webswing config settings from gsf variables set in classifier.xsl
98 for (var webswingParam of Object.keys(webswingParams)) {
99 if(gs.variables[webswingParam]) { // if this exists as a gsf variable name
100 webswingParams[webswingParam] = gs.variables[webswingParam];
101 } else {
102 if(verbosity>=4) {
103 console.log("User configuration in pageResponse did not supply: " + webswingParam + ". Using fallback: " + webswingParams[webswingParam]);
104 }
105 }
106 }
107
108 // Setting the dynamic arguments to be passed to the webswing app
109 //https://stackoverflow.com/questions/25203124/how-to-get-base-url-with-jquery-or-javascript
110 var baseURL;
111 if(gs.requestInformation.baseURL) {
112 baseURL = window.location.protocol + gs.requestInformation.baseURL; // contains servlet
113 } else { // will this else block ever again need to be used?
114 baseURL = window.location.origin+window.location.pathname;
115 // webswingInstance0.options.args += "\"" + baseURL+ "?a=a&amp;rt=d&amp;s=GsdlCollageApplet&amp;c=smallbea\"";
116 var servlet_index = baseURL.indexOf("/"+gs.xsltParams.library_name);
117 if(servlet_index > 0) {
118 baseURL = baseURL.substring(0, servlet_index+1);
119 }
120 }
121
122 //var gs3CollImgPath = gs.xsltParams.library_name + "/sites/" + gs.xsltParams.site_name + "/collect/" + gs.cgiParams.c;
123 var gs3CollBrowsePath = gs.xsltParams.library_name + "/collection/" + gs.cgiParams.c + "/browse/" + gs.cgiParams.cl; // cl is classifier, e.g "CL3"
124
125 webswingParams["gsdlversion"] = 3,
126 webswingParams["baseurl"] = baseURL;
127 webswingParams["collection"] = gs.cgiParams.c;
128 webswingParams["library"] = gs.xsltParams.library_name;
129 webswingParams["documentroot"] = gs.xsltParams.servlet_context;
130 webswingParams["hrefMustHave"] = gs3CollBrowsePath;
131 webswingParams["classifier"] = gs.cgiParams.cl + ".1";
132
133 // So our JavaScript logging here obeys any user-supplied verbosity:
134 verbosity = webswingParams["verbosity"];
135
136 // Special case: width and height are attributes of the applet tag not subelements
137 // and may need parsing.
138 // Control the width and height of the Java application launched with webswing by
139 // passing the width and height params set on the webswing element, unless an applet
140 // element is available. In that case try to get the dimensions from the applet tag.
141 // https://stackoverflow.com/questions/21851633/get-height-from-style-attribute
142 var w = gs.variables["collagewidth"] ? gs.variables["collagewidth"] : document.getElementById("webswing-collage").style.width;
143 var h = gs.variables["collageheight"] ? gs.variables["collageheight"] : document.getElementById("webswing-collage").style.height;
144 webswingParams["width"] = stripUnitOffAttribute("px", w);
145 webswingParams["height"] = stripUnitOffAttribute("px", h);
146
147 // Webswing only uses the width and height set on the webswing-collage element
148 // Override this with any dimensions set on any applet element, as that is user-controlled
149 // from GLI. style.setProperty failed without the 'important' param to force the setting.
150 // https://stackoverflow.com/questions/5191478/changing-element-style-attribute-dynamically-using-javascript
151 document.getElementById("webswing-collage").style.setProperty("width", webswingParams["width"] + "px", "important");
152 document.getElementById("webswing-collage").style.setProperty("height", webswingParams["height"] + "px", "important");
153
154 if(verbosity >= 4) {
155 console.log("@@@ Final verbosity: " + webswingParams["verbosity"]
156 + ", width: " + webswingParams["width"] + ", height: " + webswingParams["height"]);
157 }
158 // Having collected all the webswingParams, we can finally build up the single webswing customArgs string
159 var _args = "";
160 for (var webswingParam of Object.keys(webswingParams)) {
161 _args += "--" + webswingParam + " \"" + webswingParams[webswingParam] + "\" "; // space at end to precede next arg
162 }
163 webswingInstance0.options.args += _args.substring(0, _args.length-1); // remove extra space at end
164
165
166 // Set this to false if running the webswing instance as a webswing application. This has
167 // nothing to do with if the applet element exists on the page: it can be on the page
168 // and you can still decide to run GsdlCollage as a webswing application. If switching
169 // between running as application and applet, remember to adjust webswing.config.in
170 // Either way, since we still push the deprecated applet element onto the page, we can use
171 // it as a shim: use JavaScript to read its configuration params and pass them to webswing.
172
173 var isWebswingRunAsApplet = true;
174
175 // When run as webswing applet (instead of as webswing application), the webswing var
176 // customargs becomes assigned as the value of key "xtraParams" in webswing.config.in
177 // and is a string of key-value pairs. And our Java code is able to successfully
178 // receive these key-valye pairs in the form k1::v1;;k2::v2;; (URL form of key-value
179 // pairs is not easy to pass in from JavaScript through webswing into Java).
180
181 if(isWebswingRunAsApplet) {
182 var xtraParams = webswingInstance0.options.args;
183 var hyphens_index = xtraParams.indexOf("--");
184 var spaceAfterKey = -1;
185 while(hyphens_index >= 0) {
186 // Locate start of value in each key-value pair
187 spaceAfterKey = xtraParams.indexOf(" ", hyphens_index);
188 if(spaceAfterKey >= 0) {
189 // insert (splice in) the key-value internal separator,creating "key::value"
190 xtraParams = [xtraParams.slice(0, spaceAfterKey), "::", xtraParams.slice(spaceAfterKey+1)].join('');
191 }
192 hyphens_index = xtraParams.indexOf("--", hyphens_index+2);
193 }
194 // now add separator *between* each key-value pairs with ;;
195 xtraParams = xtraParams.replaceAll(" --", ";;");
196 webswingInstance0.options.args = xtraParams.substring(2);
197 }
198
199 if(verbosity >= 4) {
200 console.log("args: " + webswingInstance0.options.args);
201 }
202
203 function getParam(name) {
204 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
205 var results = new RegExp("[\\?&amp;]" + name + "=([^&amp;#]*)").exec(location.href);
206 return results == null ? null : decodeURIComponent(results[1]);
207 }
208
209 function stripUnitOffAttribute(unit, attr) {
210 // common code, regardless of whether we use the applet element as a shim or not
211 var suffixIndex = 0;
212 if(attr) {
213 suffixIndex = attr.indexOf(unit);
214 if(suffixIndex>0) {
215 attr = attr.substring(0, suffixIndex);
216 }
217 }
218 return attr;
219 }
220 </gsf:script>
221
222
223 <script data-webswing-global-var="webswing">
224 <xsl:text disable-output-escaping="yes">
225 var unloaded = false;
226
227 (function (window, document) {
228 var loader = function () {
229
230 unloaded = false; // reset state, because we are reloading the webswing app
231
232 var baseUrl = '/webswing-server/collage';
233 baseUrl = baseUrl.indexOf("/", baseUrl.length - 1) !== -1 ? baseUrl : (baseUrl + "/");
234 var xmlhttp = new XMLHttpRequest();
235 xmlhttp.onreadystatechange = function () {
236 if (xmlhttp.readyState == XMLHttpRequest.DONE) {
237 var version = xmlhttp.status == 200 ? xmlhttp.responseText : "undefined";
238 var script = document.createElement("script"),
239 tag = document.getElementsByTagName("script")[0];
240 script.src = baseUrl + "javascript/webswing-embed.js?version=" + version;
241 tag.parentNode.insertBefore(script, tag);
242
243 if (xmlhttp.status != 200) { // possibly no webswing-server
244 document.getElementById("webswing-collage").style.setProperty("display", "none", "important");
245
246 document.getElementById("collage-displaying-info").style.setProperty("display", "block", "important");
247 document.getElementById("collage-displaying-info").textContent += " cannot be displayed as the webswing extension it requires has possibly been deactivated.";
248 }
249 }
250 };
251 xmlhttp.open("GET", baseUrl + "rest/version", true);
252 xmlhttp.send();
253 };
254
255 var navigatingAway = function () {
256 if(verbosity >= 4) {
257 console.log("*** navigatingAway called");
258 }
259
260 // Multiple eventlisteners are registered to call navigatingAway, as not all are
261 // triggered in all contexts (desktop vs mobile vs older browsers)
262 // https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event
263 // We work with multiple eventlisteners to ensure we cleanup webswing collage app when
264 // user navigates away from page. But we should only do the cleanup once, even if *all*
265 // event handlers got triggered. We use the unloaded var to ensure we cleanup only once
266 if(!unloaded) {
267
268 // Sadly, sending messages to Java from JavaScript using the webswing API does not
269 // work for us.
270 // So we do not succeed in calling our handler in Java for our custom action
271 // navigatingAway or even testJavaCall, like this (Java does not get the message):
272 //
273 // if(typeof webswingInstance0.setControl === 'function') {
274 // Allow sending user events to swing application
275 // https://www.webswing.org/docs/20.2/integrate/embed.html
276 // webswingInstance0.setControl(true);
277 //
278 // console.log("@@@ Asking webswing to stop the collage application");
279 // webswingInstance0.performAction({actionName: "navigatingAway"});
280 /// webswingInstance0.performAction({actionName: "testJavaCall"});
281 // // unload only once even if multiple listeners call navigatingAway() callback
282 // unloaded = true;
283 // }
284 //
285 // Fortunately the official webswing kill() function on the webswing
286 // instance is successfully sent and detected on the Java end.
287 // In fact, the behaviour on kill() is a little more complicated:
288 // kill() is able to stop the GsdlCollageApplet when collage runs as an
289 // application, because the default webswing behaviour is to generate a
290 // WindowClosing event, which GsdlCollage is written to respond to when run as an
291 // application).
292 // When GsdlCollageApplet is run as an applet, it cannot detect and respond to
293 // the regular WindowClosing event and the Java end needs to implement the webswing
294 // shutdown-handler to cleanly stop and exit.
295
296 if(typeof webswingInstance0.kill === 'function') { // it should exist
297 if(verbosity >= 3) {
298 console.log("@@@ Telling webswing to stop the collage application/applet");
299 }
300 webswingInstance0.kill();
301 unloaded = true; // do not unload again, if multiple listeners call navigatingAway() callback function
302 if(verbosity >= 4) {
303 console.log("@@@@ unloaded");
304 }
305 } // else cannot call method that does not exist
306 } else {
307 if(verbosity >= 4) {
308 console.log("@@@@ already unloaded.");
309 }
310 }
311
312 };
313 window.addEventListener ? window.addEventListener("load", loader, false) : window.attachEvent("onload", loader);
314
315
316 // When the user navigates away from this page or reloads it, we want to
317 // shutdown the webswing Java application/applet.
318
319 // https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event
320 // unload is discouraged as it is not always supported (some mobile devices),
321 // so we listen for pagehide events too. Visibilitychange events happen on minimising the
322 // window or another tab getting focus. So do not stop the app on mere visibilitychange.
323 // https://dev.to/amersikira/top-3-ways-to-easily-detect-when-a-user-leaves-a-page-using-javascript-2ce4
324
325 // Pagehide is also fired when user presses back button: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
326 // https://stackoverflow.com/questions/15925251/trigger-an-event-when-user-navigates-away
327
328 // Order of registering listeners may matter and be useful:
329 // https://stackoverflow.com/questions/31852179/javascript-event-listeners-firing-order
330 // https://stackoverflow.com/questions/2706109/are-javascript-dom-event-handlers-called-in-order-of-registration
331 // https://medium.com/@olofbaage/javascript-essentials-all-you-need-to-know-about-event-listeners-8ed889bffb8d
332 // Event support detection
333 // https://stackoverflow.com/questions/158673/onbeforeunload-support-detection
334 // https://stackoverflow.com/questions/2877393/detecting-support-for-a-given-javascript-event
335 // http://perfectionkills.com/detecting-event-support-without-browser-sniffing
336
337 // Does onUnload not leave enough time to get webswing to shutdown? OnBeforeUnload works
338 // https://caniuse.com/?search=beforeunload
339
340 window.addEventListener ? window.addEventListener("beforeunload", navigatingAway, false) : window.attachEvent("onbeforeunload", navigatingAway);
341 window.addEventListener ? window.addEventListener("pagehide", navigatingAway, false) : window.attachEvent("onpagehide", navigatingAway);
342 //window.addEventListener ? window.addEventListener("visibilitychange", navigatingAway, false) : window.attachEvent("onvisibilitychange", navigatingAway);
343
344 })(window, document);
345 </xsl:text>
346 </script>
347
348 </xsl:template>
349
350</xsl:stylesheet>
Note: See TracBrowser for help on using the repository browser.