...
Content Distribution Networks (CDNs)
...
Info | ||||||||
---|---|---|---|---|---|---|---|---|
Hold off on using CDN URLs until
|
Some popular libraries are hosted on CDNs. To decrease load times (browser accesses a different domain) and potentially improve performance (a different application may have downloaded the JS library from the CDN and have it in the browser's cache), a CDN can be used instead of obtaining the library from the Resource Server. The CDN must support HTTPS to access the library so user's don't get security warnings.
...
- They might make it more difficult to run uPortal off-line when you don't have a network connection and the browser doesn't have a cached version of the desired library. You can work around this by temporarily modifying the source code, or creating an entry in your machine's hosts file and hosting the javascript library locally (see below).
- Scripts hosted on the Resource Server in both non-minified and minified versions allow selecting the non-minified versions by disabling Aggregation in the uPortal Admin UI.
...
- If scripts are listed in a skin.xml file and included with the <rs:aggregatedResources path="skin.xml"/> tag you do get minified or non-minified versions accordingly.
CDNs are recommended for the following libraries. Libraries referenced on CDNs should also be placed in the resource server so they are available when off-line in an emergency (see above).
- jQuery, jQuery UI
- Bootstrap
...
- (though see Bootstrap below for use in portlets)
Preferred way to access resources from a CDN should use the resourceURL tag library to load them rather than accessing the CDNs directly. This allows the URL to be modified to a non-minified version when JS aggregation is disabled or running in off-line mode (though the resourceURL tag currently does not do this). E.g. access like:
Code Block |
---|
<script src="<rs:resourceURL value='//code.jquery.com/jquery-1.10.2.min.js'/>" type="text/javascript"></script> |
Loading Javascript
Below is an example of loading jQuery 1.10.2, jQuery TreeView 1.4.0 and assigning them under the myPortletName var with the portlet's namespace key. The myPortletName var with the portlet namespace property is used as a namespace for this portlet's JavaScript, allowing the portlet to declare its own functions without placing them in the global namespace.
...
is to put them in a skin.xml file and use the rs:aggregatedResources tag so you get the non-minified versions when desired. The below example assumes
Code Block | ||
---|---|---|
| ||
skin.xml: <resources xmlns="http://www.jasig.org/uportal/web/skin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jasig.org/uportal/web/skin https://source.jasig.org/schemas/resource-server/skin-configuration/skin-configuration-v1.2.xsd"> <js included="plain">//code.jquery.com/jquery-1.10.2.min.js'/>" type="text/javascript"></script> <script src="<rs:resourceURL value='/javascript/jquery-treeview/1.4.0/jquery.treeview.js'/>" type="text/javascript" ></script> <script src="${pageContext.request.contextPath}/javascript/myPortlet.js" type="text/javascript" ></script> <!-- alternate style <script src="<c:url value='/javascript/myPortlet.js'/>" type="text/javascript" ></script> --> <script type="text/javascript"> /* * Don't overwrite an existing myPortletName variable, just add to it */ var myPortletName = myPortletName || {}; myPortletName["${n}"] = myPortletName["${n}"] || {}; /* * Switch jQuery to extreme noConflict mode, keeping a reference to it in the myPortletName["${n}"] namespace */ myPortletName["${n}"].jQuery = jQuery.noConflict(true); /** * Use an anonymous function to initialize all JavaScript for this portlet. */ (function($) { // treeview init $('#${n}community ul.listing').treeview({js</js> <js included="aggregated">//code.jquery.com/jquery-1.10.2.min.js</js> <js included="plain">//code.jquery.com/ui/1.10.3/jquery-ui.js</js> <js included="aggregated">//code.jquery.com/ui/1.10.3/jquery-ui.min.js</js> </resources> JSP file: <%-- This example assumes the portlet.xml defines a portlet preference includeJsLibs=false that the installer can override to set to true if needed. The portletPreferencesValues variable comes from the defineObjects tag library. --%> <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %> <portlet:defineObjects/> <c:if test="${portletPreferencesValues['includeJsLibs'][0] != 'false'}"> <rs:aggregatedResources path="/resources.xml"/> </c:if> <script type="text/javascript"><rs:compressJs> var ${n} = {}; <%-- This is one way to namespace this portlet's version of jQuery --%> <c:choose> <c:when test="${portletPreferencesValues['includeJsLibs'][0] != 'false'}"> animated: {duration:300, easing:"swing"}, ${n}.jQuery = jQuery.noConflict(true) collapsed: true,</c:when> <c:otherwise> ${n}.jQuery unique: false= up.jQuery; });</c:otherwise> </c:choose> $('#${n}community ul.treeview ul').siblings('a').click(function(e) { e.preventDefault();.jQuery(function(){ $(this).siblings('div.hitarea').trigger('click'); $(this).blur(); }); })(myPortletName["${n}"].jQuery); </script> |
Using this approach the portlet is ensured it will not be affected by other versions of jQuery loaded on the same page or other JavaScript frameworks that affect $. The approach is described on jQuery's Using jQuery with Other Libraries page.
If you've got your javascript in a .js file -- one that can't resolve the server-side expression '#${n}container' -- you can launch something in the .js file from within the .jsp, passing a reference to the ${n}container div.
Code Block |
---|
// In a JSP file: <script> myPortletName.myObj["${n}"] = customJsLib.myCustomConstructor} </rs:compressJs></script> |
The alternative is to load them from the CDNs directly as you would in any webapp.
Code Block | ||
---|---|---|
| ||
<script src="//code.jquery.com/jquery-1.10.2.min.js" type="text/javascript"></script> |
Loading Javascript
Below is an example of loading jQuery 1.10.2, jQuery TreeView 1.4.0 and assigning them under the myPortletName var with the portlet's namespace key. The myPortletName var with the portlet namespace property is used as a namespace for this portlet's JavaScript, allowing the portlet to declare its own functions without placing them in the global namespace.
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- Store the portlet namespace string in the JSP page variable "n" -->
<c:set var="n"><portlet:namespace/></c:set>
<!-- Use a resource URL for common javascript libraries that multiple portlets may share and is present in the Resource Serving Webapp. -->
<script src="//code.jquery.com/jquery-1.10.2.min.js" type="text/javascript"></script>
<script src="<rs:resourceURL value='/rs/jquery-treeview/1.4.0/jquery.treeview.js'/>" type="text/javascript" ></script>
<script src="${pageContext.request.contextPath}/javascript/myPortlet.js" type="text/javascript" ></script>
<!-- alternate style <script src="<c:url value='/javascript/myPortlet.js'/>" type="text/javascript" ></script> -->
<script type="text/javascript">
/*
* Don't overwrite an existing myPortletName variable, just add to it
*/
var myPortletName = myPortletName || {};
myPortletName["${n}"] = myPortletName["${n}"] || {};
/*
* Switch jQuery to extreme noConflict mode, keeping a reference to it in the myPortletName["${n}"] namespace
*/
myPortletName["${n}"].jQuery = jQuery.noConflict(true);
/**
* Use an anonymous function to initialize all JavaScript for this portlet.
*/
(function($) {
// treeview init
$('#${n}community ul.listing').treeview({
animated: {duration:300, easing:"swing"},
collapsed: true,
unique: false
});
$('#${n}community ul.treeview ul').siblings('a').click(function(e) {
e.preventDefault();
$(this).siblings('div.hitarea').trigger('click');
$(this).blur();
});
})(myPortletName["${n}"].jQuery);
</script>
|
Using this approach the portlet is ensured it will not be affected by other versions of jQuery loaded on the same page or other JavaScript frameworks that affect $. The approach is described on jQuery's Using jQuery with Other Libraries page.
If you've got your javascript in a .js file -- one that can't resolve the server-side expression '#${n}container' -- you can launch something in the .js file from within the .jsp, passing a reference to the ${n}container div.
Code Block |
---|
// In a JSP file:
<script>
myPortletName.myObj["${n}"] = customJsLib.myCustomConstructor($('#${n}container'));
myPortletName.myObj["${n}"].init();
</script> |
...
Code Block | ||
---|---|---|
| ||
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
// Bootstrap javascript fails if included multiple times on a page.
// uPortal Bootstrap best practice: include bootstrap if and only if it is not present and save it to
// portlets object. Bootstrap functions could be manually invoked via portlets.bootstrapjQuery variable.
// All portlets using Bootstrap Javascript must use this approach. Portlet's jQuery should be included
// prior to this code block.
var portlets = portlets || {};
// If bootstrap is not present at uPortal jQuery nor a community bootstrap, dynamically load it.
up.jQuery().carousel || portlets.bootstrapjQuery || document.write('<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"><\/script>');
</script>
<script type="text/javascript">
// Must be in separate script tag to insure bootstrap was dynamically loaded.
portlets["${n}"] = {};
portlets["${n}"].jQuery = jQuery.noConflict(true);
// If bootstrap JS global variable was not defined, set it to the jQuery that has bootstrap attached to.
portlets.bootstrapjQuery = portlets.bootstrapjQuery || (up.jQuery().carousel ? up.jQuery : portlets["${n}"].jQuery);
</script> |
Development Tips
Tip | ||
---|---|---|
| ||
On your local and dev instance, you can permanently disable JavaScript and CSS aggregation by adding to your .bash_profile: export JAVA_OPTS="$JAVA_OPTS -Dorg.jasig.resourceserver.utils.aggr.aggregated_theme=false” Also see UE Development Tips was not defined, set it to the jQuery that has bootstrap attached to. portlets.bootstrapjQuery = portlets.bootstrapjQuery || (up.jQuery().carousel ? up.jQuery : portlets["${n}"].jQuery); </script> |
Development Tips
Tip | ||
---|---|---|
| ||
On your local and dev instance, you can permanently disable JavaScript and CSS aggregation by adding to your .bash_profile: export JAVA_OPTS="$JAVA_OPTS -Dorg.jasig.resourceserver.utils.aggr.aggregated_theme=false” Also see UE Development Tips |
Tip |
---|
To use JSTL expressions like ${portletPreferencesValues['includeJsLibs'][0] != 'false', make sure you have the portlet 2.0 version of the portlet tag, not the portlet 1.0 version; e.g.
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %>
<portlet:defineObjects/>
not
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet" %>
<portlet:defineObjects/> |
Info | ||||
---|---|---|---|---|
| ||||