Skip to content

Using ajile

mike lee edited this page Jan 27, 2023 · 21 revisions

ajile ♻️ asynchronous javascript importing & loading extension ✅ enables asynchronous javascript importing, loading & module management in web browsers, webviews & other javascript & DOM-enabled environments.

Getting Started

Getting started with ajile is simple, just add this <script> tag to any HTML page and set its src attribute to ajile's actual location:

<script src="path/to/ajile/com.iskitz.ajile.js" type="text/javascript"></script>

Once that's done, ajile's ready for use!

Loading Scripts

ajile provides both Manual and Automatic Script Loading.

Manual Script Loading

Manual script loading is done via ajile's Load() API:

Load ("//ajile.net/play/api/scripts/LoadExample.js");

In the example above, the LoadExample.js script is dynamically loaded into the current page. The Load() API allows any inline or external script to programmatically load any other inline or external script. It eliminates the need to manually modify pages each time their script list changes. It allows using JavaScript rather than HTML to define exactly which scripts are loaded at runtime.

Automatic Script Loading

Automatic script loading uses Shared and Page Loader scripts and is enabled by default to get you up and running quickly.

TIP: Shared and Page Loaders separate HTML and JavaScript which is an important first step towards developing with the Model-View-Controller (MVC) software architecture pattern.

Shared Loader

Shared Loader is the script used to store code shared by multiple pages. It's the index.js script at ajile's location (i.e. path/to/ajile/index.js) that's automatically loaded whenever a page using ajile is launched.

The shared loader is enabled by default but can be disabled by setting the mvcshareoff launch option in ajile's <script> tag's src attribute:

<script src="path/to/ajile/com.iskitz.ajile.js?mvcshareoff" type="text/javascript"></script>

To re-enable the shared loader, remove the mvcshareoff launch option or replace it with mvcshare.

TIP: The shared loader is useful for loading code that's shared by multiple pages. Social widgets, analytics, and GUI libraries are examples of such shared code. When used, the shared loader is the only script that needs to be updated when the list of scripts being used by multiple pages changes. It removes the need to directly modify each affected page.

Page Loader

A Page loader is a page-specific script that's automatically loaded whenever its page is launched. It uses the same name and location as its page (i.e. /my/Page.js for /my/Page.htm) and is the starting point for all of its page's scripts.

Page loaders are enabled by default but can be disabled by setting the mvcoff launch option in ajile's script tag's src attribute:

<script src="path/to/ajile/com.iskitz.ajile.js?mvcoff" type="text/javascript"></script>

To re-enable page loaders remove the mvcoff launch option or replace it with mvc.

FYI: ajile's API Examples page (api/index.htm) uses its own page loader (api/index.js) for automatic script loading.

Modules

ajile provides APIs for defining namespaces, modules and their dependencies.

Namespacing

The Namespace() API enables defining namespaces.

Namespace ("com.iskitz.ajile.examples");    // Creates the following globally accessible
com;                                        // com namespace,
com.iskitz;                                 // com.iskitz namespace,
com.iskitz.ajile;                           // com.iskitz.ajile namespace, and
com.iskitz.ajile.examples;                  // com.iskitz.ajile.examples namespace objects.

Defining

The Namespace() API shown in the Namespacing section above creates namespaces that can be used for storing modules.

Namespace ("com.iskitz.ajile.examples");                    // Creates a global namespace.

com.iskitz.ajile.examples.NamespaceExample = function()     // Adds a module to that namespace.
{
    alert("This is the NamespaceExample!");                 // The module shows a popup message.
};

In the example above, the com.iskitz.ajile.examples namespace is created and the NamespaceExample module, a function, is added to it. A reversed domain name is used as its namespace to assure that the module is uniquely named. The ability to identify individual modules is especially important when using multiple modules from varied sources within the same page or site.

When working with modules within an Intranet or non-Internet environment, it may be sufficient to use simpler namespaces. ajile provides the flexibility to create namespaces of any desired depth.

Packaging

Once a module is created as shown in the Defining a module section above, it needs to be packaged in order to be included or imported. ajile supports packaging By File Name and By File Path.

By File Name

To package the NamespaceExample module shown in the Defining a module section above, by file name, save the module's source code in a file named: com.iskitz.ajile.examples.NamespaceExample.js

Next, place that file in the same directory as the ajile module:

path/to/ajile/com.iskitz.ajile.examples.NamespaceExample.js

FYI: ajile supports replacing all (.) characters in a packaged module's filename with any valid operating system filename character but requires that one (.) remain immediately before the file's extension i.e.: com-iskitz-ajile-examples-NamespaceExample.js, com_iskitz_ajile_examples_NamespaceExample.js

By File Path

To package the NamespaceExample module shown in the Defining a module section above, by file path, save the NamespaceExample module's source code in a similarly named file within the ajile module's directory:

path/to/ajile/com/iskitz/ajile/examples/NamespaceExample.js

Importing

Namespaces are effective for interoperability but can complicate development by requiring the use of long module names. ajile's solution to this challenge is the Import() API.

The Import() API provides these key benefits

  • Simple names to reference uniquely named JavaScript modules.
  • Simple definition of JavaScript module dependencies.
  • Simple programmatic loading of external JavaScript modules.

Example

Import ("com.iskitz.ajile.examples.Complex");

function testImport()
{
    var complex = new Complex();
    complex.sayHello();
}

The Import() API in the code above indicates that there is a dependency that must be imported. The externally defined com.iskitz.ajile.examples.Complex.js JavaScript module is automatically imported and made accessible via its short name, Complex. The imported Complex module is then used by the testImport function.

The Complex Module

Namespace ("com.iskitz.ajile.examples");
Import    ("com.iskitz.ajile.examples.Simple");

com.iskitz.ajile.examples.Complex = function()
{
    var simple = new Simple();

    this.sayHello = function sayHello()
    {
        var message = "Hello World!\n\nThis is a " + this.toString()
                    + " object that imported and is\nusing a "
                    + simple.toString() + " object!"
                    ;
        alert(message);
    };

    this.toString = function toString()
    {
        return "[Complex]";
    };
};

The Import() API used in the code above automatically imports the externally defined com.iskitz.ajile.examples.Simple.js JavaScript module then makes it available via its short name Simple.

The Simple Module

Namespace ("com.iskitz.ajile.examples");

com.iskitz.ajile.examples.Simple = function()
{
    this.toString = function toString()
    {
        return "[Simple]";
    };
};

Importing with Aliases

When importing multiple modules it is possible to encounter naming conflicts. ajile provides the ImportAs() API as a solution to this problem.

The ImportAs() API provides two benefits in addition to those provided by the Import() API:

  • Flexibility to import a module using any un-used short name.
  • Ability to handle naming conflicts by assigning aliases to similarly named modules.

Example: Handling module name conflicts

Import   ("com.iskitz.ajile.examples.Complex");
ImportAs ("AComplex", "com.iskitz.ajile.examples.ambiguity.Complex");

function testAmbiguity()
{
    var complex = new Complex();
    complex.sayHello();

    var aComplex = new AComplex();
    aComplex.sayHello();

    return false;
}

In the code above, two modules with the identical short name Complex are imported. The Import() API automatically imports the externally defined com.iskitz.ajile.examples.Complex.js JavaScript module as is. The ImportAs() API also automatically imports the externally defined com.iskitz.ajile.examples.ambiguity.Complex.js JavaScript module but performs the extra step of assigning it the AComplex alias.

By assigning the AComplex alias to the imported com.iskitz.ajile.examples.ambiguity.Complex module, the testAmbiguity() function is able to reference both modules without encountering naming conflicts.

The AComplex Module

Namespace ("com.iskitz.ajile.examples.ambiguous");
Import    ("com.iskitz.ajile.examples.Simple");

com.iskitz.ajile.examples.ambiguous.Complex = function()
{
    var simple = new Simple();

    this.sayHello = function sayHello()
    {
        var message = "Hello World!\n\nThis is an ambiguous " + this.toString()
                    + " object that\nimported and is using a "
                    + simple.toString() + " object!"
                    ;
        alert(message);
    };

    this.toString = function toString()
    {
        return "[Complex]";
    };
};