I have several WebSockets endpoints such as,
wss://localhost:8181/ContextPath/Push
All of such endpoint URLs are hard-coded in separate, external JavaScript files (.js
). These JavaScript files are included in respective XHTML files as and when required. The host name and the context path should be evaluated programmatically instead of hard-coding all over the place where they are required.
The host name (localhost:8181
) can be obtained in JavaScript using document.location.host
but there is no standard/canonical way in JavaScript to obtain a context path where the application runs.
I am doing something like the following.
A global JavaScript variable is declared on the master template as follows.
<f:view locale="#{bean.locale}" encoding="UTF-8" contentType="text/html">
<f:loadBundle basename="messages.ResourceBundle" var="messages"/>
<ui:param name="contextPath" value="#{request.contextPath}"/>
<ui:insert name="metaData"></ui:insert>
<h:head>
<script type="text/javascript">var contextPath = "#{contextPath}";</script>
</h:head>
<h:body id="body">
</h:body>
</f:view>
</html>
The JavaScript files in which the host name and the context path are hard-coded are included in respective template clients or any of sections of the template north, south, east and west as follows.
<html lang="#{bean.language}"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:form>
<h:outputScript library="default" name="js/websockets.js" target="head"/>
</h:form>
For the sake of viewpoint only, websockets.js
looks like the following (you can simply ignore it).
if (window.WebSocket) {
// The global variable "contextPath" is unavailable here
// because it is declared afterwards in the generated HTML.
var ws = new WebSocket("wss://"+document.location.host + contextPath + "/Push");
ws.onmessage = function (event) {
// This handler is invoked, when a message is received through a WebSockets channel.
};
$(window).on('beforeunload', function () {
ws.close();
});
} else {}
Now, the global JavaScript variable contextPath
declared in the master template is expected to be available in the included JavaScript file namely websockets.js
. This is however untrue.
What happens is that the included JavaScript file namely websockets.js
where the global variable contextPath
is attempted to be accessed, is placed before the hard-coded <script>
tag in the generated HTML <head>
tag in the master template.
In other words, the global JavaScript variable contextPath
is actually attempted to use in the included file websockets.js
before being declared.
Anyway, how to get rid of hard-coding the context path in external JavaScript files?
The sole purpose of doing this is that unlike CSS files, EL isn't evaluated in external JavaScript files. Therefore, #{}
thing will not work unless it is placed in an XHTML file.
data-baseuri
. Is there any caveat using this approach though I ignore old/buggy/obscure browsers? In the meanwhile, I came across this link. Since PrimeFaces 3.0, it is possible to define a<f:facet>
withname="first"
,name="middle"
andname="last"
which allows resources to be placed in the said order in the generated HTML<head>
element. (Is thereplace()
function here is a special function to replace protocol names? It does not appear to be a simple character replacement function). – Maybe