Javascript Loader

Status

 

Merged.

Introduction

 

The javascript effort for GateIn 3.3 has improved how javascript is managed in the server and has seen the introduction of a javascript loader to handle the loading of javascript on the client in an efficient manner.

 

This new effort improves the loading mechanism to provide three features:

 

  • More efficient loading mechanism performed on the client browser
  • Provide true module isolation between javascript modules to avoid global variable usage and conflicts
  • Integrate with existing javascript modules

 

The GateIn 3.3 loader performs the ordering of script loading on the server and is quite efficient, however it does not provide a real asynchronous loading mechanism. In addition the CommonJS effort provides a definition of a module system known as Modules/1.0 . This module system does have an asynchronous loader specification known as Asynchronous Module Definition (AMD).

 

GateIn will embed an AMD library (for now the Requires.js implementation has been chosen) and integrate it with the dependency mechanism implemented in GateIn 3.3.

 

Specification

 

GateIn defines since the 3.3 version a notion of module that coincides with the notion of javascript module. The modules are loaded by the loader library which is an evolution of previous version to define a notion of fetch mode (immediate or on-load).

Fetch modes

 

The current immediate and on-load fetch mode will be preserved but the following will change:

 

  • The immediate mode provide script loading outside of the AMD system. It means that a script is loaded by the browser as a <script> tag of the <head> section. Dependencies are still be managed by the XML declaration and those dependencies are used to order the script tags in the head section. The goal of this fetch mode is to honour the use case where a script has to be loaded directly by the browser without the module system. GateIn will not use this feature for its own javascript.
  • The on-load mode provide script loading managed by the AMD system.
  • In GateIn 3.3 it was possible to create dependencies between immediate and on-load (by promoting an on-load script as an immediate script), we need to remove this notion in this specification.

Asychronous Modules

Module declared as asynchronous have a special treatment, they are wrapped by a function that allows the module system to

 

  • Defer the execution of the script until all its dependencies are resolved
  • Define the dependencies the script depends on
  • Provide the module dependencies as function arguments

 

GateIn will take care of wrapping the modules with the necessary function required by the module system. This will be done at runtime when script are served and is based on the current dependencies declared by the script deployment descriptor.

 

XML declaration changes

 

The module can provide a default name when used in on-load fetch mode:

 

 

<module>
  <name>jquery</name>
  <as>$</as>
  ...
</module>

 

The dependency mechanism will have a new optional tag that is used to define the alias of module dependency in the javascript wrapper function:

 

<module>
  <name>base</name>
  ...
  <depends>
    <module>jquery</module>
    <as>$</as>
  </depends>
</module>

 

The <as> tag alias the name of the dependency when consumed in a module. Of course the <depends> tag overrides the default alias.

Loading order

 

The loading order should is the following

 

  1. Load immediate resources ordered by the dependency mechanism
  2. Load JSR 286 portlet headers
  3. Trigger asynchronous module loading

Transform scripts to requireJS module

 

We don't need to use global namespace (a global variable to workaround conflict problem) like eXo.core, eXo.webui ... anymore. Our JS dependencies will be injected by requireJS.

So Local javascripts that were pushed in <module> tag will be wrapped in requireJS : define function, to make this work properly, those files should be transformed to use as requireJS modules

 

You can expose your object as a requirejs module (jquery) or as a module's property (almost gatein javascripts).

The key thing to make this work is : _module variable - GateIn will wrap your script and define _module variable. Add or point this special _module variable to your object to expose it as requirejs module.

1/ To expose a JS object as a property of a requireJS module.

 

For example, if we have some JS modules declared in gatein-resources.xml like this

 

<module>
      <name>foo</name>    
      <script>
        <name>obj1</name>
        <path>/javascript/obj1.js</path>
      </script>
      <script>
        <name>obj2</name>
        <path>/javascript/obj2.js</path>
      </script>
</module>

 

 

/javascript/obj1.js content:

 

var obj1 = {
          //Definition for obj1
}
//Add obj1 as a property of _module
_module.obj1 = obj1;  

 

 

If we have bar module depends on foo module like this

 

<module>
      <name>bar</name>     
      <as>barAlias</as>
      <script>
         <name>zoo</name>
         <path>/javascript/zoo.js</path>
      </script>
      <depends>
         <module>foo</module>
         <as>fooAlias</as>
      <depends>
</module>

 

 

--> Now foo module will be injected to bar with alias "fooAlias". Here is "zoo.js" content:

 

var zoo = function() {
  //This is how you use injected dependencies
  fooAlias.obj1.doSomething();
  fooAlias.obj2;
}
//This is the 2nd way to transform your script to requireJS module
//Note that we don't add property for _module here
_module = zoo;

 

 

2/ In case we want to expose an js object as a requirejs module (NOT a property of a requireJS module):

Let's say we have bar module, we only have zoo.js, an you want to refer to your JS object like this bar.doSomething() instead of bar.zoo.doSomething().

So don't return an object but point _module to your object:

 

//zoo.js code ...
_module = zoo;