JBoss Community Archive (Read Only)

GateIn Portal 3.8

JavaScript Cookbook

In the previous chapter, we have explained the GateIn Portal module system and the script integration. The goal of this section is to provide a list of common use cases and how they can be addressed by GateIn. The section has two parts: the module cookbook and the script cookbook.

GateIn Module Cookbook

GateIn Module (GMD) with a self-executing function

Let us show how jQuery Highlight Plugin (source) can be declared as a GateIn Module. The source code of this example can be found in the /examples/portlets/amd-js directory of Gatein source tree. Highlight Plugin being a jQuery plugin, is a perfect candidate for packaging as a GateIn module because it already follows the self-invoking pattern that consumes the jquery dependency as $:

(function($) {
  ...
}(jQuery)

To use jQuery Highlight Plugin in GateIn Portal you need to declare it in gatein-resources.xml file of your project as follows:

<module>
  <name>highlight</name>
  <script>
    <path>/highlight/highlight.js</path>
  </script>
  <depends>
    <module>jquery</module>
    <as>jQuery</as>
  </depends>
</module>
  • The module is named highlight and uses the /highlight/highligh.js source code bundled in the war file

  • The depends tag creates a dependency on the jquery module. The dependency is aliased as $ using the as tag to match the Highlight.js self-executing function argument $.

The jquery plugin comes out of the box with GateIn Portal and one does not need to worry about declaring it. The jquery module is simply named jquery and provides the jQuery version 1.7.1.

GateIn Module (GMD) with AMD define()

As we have mentioned already, GateIn Portal uses the AMD loader RequireJS internally to handle JavaScript modules. Therefore, also the modules using the AMD define() format are supported in GateIn Portal:

define("module", ["dependency1",...,"dependencyN"], function(dep1,...,depN) {
});

Note that some parts of the above define() can be overridden by the XML declaration in gatein-resources.xml:

  • The "module" name will be ignored and replaced by the module name used in gatein-resources.xml.

  • The module dependencies from "dependency1" to "dependencyN" have to be declared with the same name in XML (we will see later that we can use the as tag to override the dependency name).

Assuming that the dependencies dependency1 to dependencyN have been declared in XML, such module definition can be declared with the following XML in gatein-resources.xml:

<module>
  <name>MyModule</name>
  ...
  <depends>
    <module>dependency1</module>
  </depends>
  ...
  <depends>
    <module>dependencyN</module>
  </depends>
</module>

Built-in jQuery module

GateIn Portal provides the jQuery library 1.7.1 as a jquery module, the configuration of this module can be found in the eXoResources.war file. To reuse this jQuery version, one just needs to declare a dependence on it:

<portlet>
  <name>RequireJSPortlet</name>
  <module>
    <depends>
      <module>jquery</module>
    </depends>
</portlet>

The default jquery module alias is $ so if you are using it, it should be named $ in the self-executing function:

(function($) {
  ...
})($);

If your library uses a different name such as jQuery the XML as tag should be used:

<portlet>
  <name>RequireJSPortlet</name>
  <module>
    <depends>
      <module>jquery</module>
      <as>jQuery</as>
    </depends>
</portlet>

With the following self-executing function:

(function($) {
  ...
}(jQuery);

Using a custom jQuery version

If you are not satisfied by the jQuery version provided by GateIn you can deploy any other version that suits your needs. It is not uncommon that a product built on top of GateIn Portal depends on a third party JavaScript framework that in turn depends on other versions of jQuery. Deploying other jQuery version can thus be unavoidable at some point. Having multiple jQuery instances in a common web page usually leads to conflicting global variables. The beauty of the Portal module system is that hosting multiple versions of the same JavaScript library is possible without such conflicts.

Consider the following example using jQuery version 1.6.4. For jQueryPortlet to be able to use the legacy jQuery 1.6.4, is as easy as the following gatein-resources.xml configuraion:

<module>
  <name>jquery-1.6.4</name>
  <script>
    <adapter>
(function() {
  <include>/javascript/jquery-1.6.4.js</include>
  return jQuery.noConflict(true);
})();
    </adapter>
  </script>
</module>
<portlet>
  <name>jQueryPortlet</name>
  ...
  <depends>
    <module>jquery-1.6.4</module>
  </depends>
</portlet>

jQuery Plugins

In this section, we are going to see how to configure a jQuery plugin and how to use it in jQueryPluginPortlet portlet. Let us have a plugin as minimal as the following:

(function($) {
 $.fn.doesPluginWork = function() {
    alert('YES, it works!');
 };
})(jQuery);

The plugin is then declared as a module in gatein-resources.xml:

<module>
  <name>jquery-plugin</name>
  <as>jqPlugin</as>
  <script>
    <path>/jqueryPlugin/jquery-plugin.js</path>
  </script>
  <depends>
    <module>jquery</module>
    <as>jQuery</as>
  </depends>
</module>

Finally we use this plugin in our portlet:

<portlet>
  <name>jQueryPluginPortlet</name>
  <module>
    <script>
      <path>/jqueryPlugin/jqueryPluginPortlet.js</path>
    </script>
    <depends>
      <module>jquery</module>
      <as>$</as>
    </depends>
    <depends>
      <module>jquery-plugin</module>
    </depends>
  </module>
</portlet>

One important point to have in mind is that our portlet module should depend on the jquery and the jquery-plugin modules even it will not use the plugin itself:

  • declaring the dependency on jquery allows to use the jQuery object

  • delcaring the dependency on jquery-plugin ensures that the plugin will be loaded in the jquery dependency before it is injected in the portlet module

Overriding the name of a dependency in an AMD module

We have shown previously how to declare an AMD define() style GateIn module. We have also noted that the module dependency names in gatein-resources.xml must match the AMD dependencies declared in the define() function arguments. In case of a mismatch, <as> element in gatein-resources.xml can be used to rename the dependencies.

Let us say that there is a foo.js file defining an AMD module named foo with two dependencies "dep1", "dep2":

define("foo", ["dep1", "dep2"], function(a1, a2) {
  // The module
});

Now let us suppose that the modules are declared as module1 and module2 in a gatein-resources.xml file that is perhaps not 100% under our control. As we can see, the names do not match. To fix this, we can use the <as> to rename the dependencies:

<module>
  <name>foo</name>
  <script>
    <path>/path/to/foo.js</path>
  </script>
  <depends>
    <module>module1</module>
    <as>dep1</as>
  </depends>
 <depends>
   <module>module2</module>
   <as>dep2</as>
 </depends>
</module>

CommonJS modules

CommonJS defines its own module format, although it is not supported natively by GateIn Portal, the adapter format can be used to wrap them as AMD define() style modules.

Here are two simple CommonJS modules:

math.js
exports.add = function() {
  var sum = 0, i = 0, args = arguments, l = args.length;
  while (i < l) {
    sum += args[i++];
  }
  return sum;
};
increment.js
var add = require('math').add;
exports.inc = function(val) {
  return add(val, 1);
};

As we can see, in increment.js, the CommonJS require() function is invoked, which is in conflict with define() provided by RequireJS. To make this work in the RequireJS environment, these modules need to be wrapped and injected the predefined modules: require, exports and module provided by Requirejs (details here). The good news is that developers do not need to do this themselves as GateIn will wrap the code for them based on the configuration using the adapter format:

<module>
  <name>math</name>
  <script>
    <adapter>
      define(["require", "exports"], function(require, exports) {
      <include>/commonjs/math.js</include>
      });
    </adapter>
  </script>
  <depends>
    <module>require</module>
  </depends>
  <depends>
    <module>exports</module>
  </depends>
</module>
<module>
  <name>increment</name>
  <script>
    <adapter>
      define(["require", "exports", "math"], function(require, exports) {
      <include>/commonjs/increment.js</include>
      });
    </adapter>
  </script>
  <depends>
    <module>require</module>
  </depends>
  <depends>
    <module>exports</module>
  </depends>
  <depends>
    <module>math</module>
  </depends>
</module>

Mustache.js module

Mustache.js is a popular JavaScript template engine. Mustache was written to be executed in several kinds of environments: as a global object, as a CommonJS module or as a native AMD module. If "module" and "exports" dependencies are available Mustache will register it as a CommonJS module. Mustache can also be made consumable for GateIn Portal thanks to the adapter format:

<module>
  <name>mustache</name>
  <script>
    <adapter>
define(["require", "exports", "module"], function(require, exports, module) {
  <include>/requirejs/js/plugins/mustache.js</include>
});
    </adapter>
  </script>
  <depends>
    <module>require</module>
  </depends>
  <depends>
    <module>exports</module>
  </depends>
  <depends>
    <module>module</module>
  </depends>
</module>

We need to use adapter tag here and declare the require, exports and module dependencies of the CommonJS module. Now any module can have Mustache instance injected just by declaring mustache as its dependence:

<module>
  <name>foo</name>
  ...
  <depends>
    <module>mustache</module>
  </depends>
</module>
(function(mustache){
  //code that use Mustache
  mustache.render(template);
})(mustache);

Resource loading with Text.js

RequireJS provides a support for loader plugins allowing a module to be a plugin and use the AMD system for loading web resources in a very efficient manner.

Mustache.js is a JavaScript template engine and as such it needs a template file to parse and generate an HTML output. When there are many templates or the template has a large size, embedding template in the page is not a good choice for performance reasons. It would be better to use Text.js to load the separate template files and inject them as dependencies.

Text.js is an AMD module which also depends on module - the predefined dependency provided by the AMD loader. Thanks to the AMD support of GateIn Portal, it is quite straightforward to declare it:

<module>
  <name>text</name>
  <script>
    <path>/requirejs/js/plugins/text.js</path>
  </script>
  <depends>
    <module>module</module>
  </depends>
</module>

Now we can use the mustache and text modules to load templates and render them in our own module:

<portlet>
  <name>foo</name>
  <module>
    ...
    <depends>
      <module>mustache</module>
    </depends>
    <depends>
      <module>text</module>
      <as>tmpl</as>
      <resource>/path/to/template.html</resource>
    </depends>
  </module>
</portlet>

Given that there is the text module with a <resource> tag in the dependency list, Text.js will load that resource template and inject it with the name tmpl. Here is the JavaScript of the portlet:

function(mustache, tmpl) {
  var html = mustache.render(tmpl);
  //append rendered html to DOM
})(mustache, tmpl);

Script cookbook

Accessing a module from a script

Sometimes it is required to access a module from a script at runtime. RequireJS provides such capability by using the require function to execute a function in a managed context:

require([“SHARED/ModuleA”], function(a) {
  // Codes of interacting with module A
  a.doSomething();
});

Note that we have used the name of the GateIn module as seen by RequireJS, in this case "PORTLET/ModuleA". The prefix in upper case is the GateIn module scope. The possible values are SHARED, PORTLET and PORTAL.

Expose the built-in jQuery globally

As explained previously, the built-in jQuery is currently declared as a GateIn module. By default, jQuery is not available in the window object of the browser. To make it available there, you need to declare a shared script like this:

require( [“SHARED/jquery”], function($) {
  // the ‘$’ in window.$ is alias, you can make the other for yourself.
  window.$ = $;
});

... and in gatein-resources.xml:

<scripts>
  <name>imediatejs</name>
  <script>
    <path>/myfolder/imediateJScript.js</path>
  </script>
</scripts>

A portlet can then provide its own script that depends on this script:

<portlet>
  <name>foo</name>
  <script>
    <name>portletjs</name>
    <path>/myfolder/portlet.js</path>
  </script>
  <depends>
    <scripts>imediatejs</scripts>
  </depends>
</scripts>

With the following JavaScript:

$("#foo").html("<h1>hello global jQuery</h1>");

Define a custom global jQuery

We can define a globally available custom jQuery quite easily. For this we reuse existing recipes seen previously and combine them:

  • Use a custom jQuery version

  • Expose GateIn version of jQuery globally

Plugin for a global jQuery

There are several ways to implement this recipe. However, the most important point to keep in mind is to make sure that the global jQuery is available before the global jQuery plugin is loaded.

We have seen before how the PORTLET scope can be used to load a module when a portlet is loaded on a page. Here, we will switch to the PORTAL scope, the main difference being that the loading of our plugin will be bound to a specific portal rather than a specific portlet.

At first, we create our jQuery plugin as a script named myPlugin.js and we integrate our plugin:

require([“SHARED/jquery”], function($) {
  $.fn.myPluginFunction = function() {
    // Your work here;
  };
});

Then, we bind the script to the site classic and also reuse the immediatejs script seen before:

<portal>
  <name>classic</name>
  <scripts>
    <script>
      <name>myPlugin</name>
      <path>/myfolder/myPlugin.js</path>
    </script>
    <script>
      <name>imediatejs</name>
      <path>/myfolder/imediateJScript.js</path>
    </script>
  </scripts>
</portal>

Now, our plugin is globally available and we can use it:

<script type=”text/javascript”>
$(‘#foo’).myPluginFunction();
</script>
JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 13:20:44 UTC, last content change 2014-04-14 15:14:43 UTC.