The Importer: Cinnamon Tutorials

## The Importer

To access code of other JavaScript files, cjs has got the `imports` object.

In cjs in combination of Cinnamon you can use following statements to import statements:

    1
    2
    3
    4
    imports.*
    imports.gi.*
    imports.ui.*
    imports.misc.*

### `imports.*`

This is the normal form of importing modules.

You can think of this object like nested objects which properties are the JavaScript files or directories. All functions, variables (var, let, const) in a JavaScript file can be accessed like this file is an object.

To clearify, an example:

    1
    2
    3
    4
    5
    6
    7
    8
    //Direct access to file a.js
    const A = imports.a;
    //Directories must be also typed in, in order to get file c.js in directory b
    const C = imports.b.c;

    log(A.foo); //"Property foo"
    log(A.bar()); //"Method bar"
    log(C.baz); //"Property baz"

`a.js`

    1
    2
    3
    4
    5
    let foo = "Property foo";

    function bar(){
      return "Method bar";
    }

`c.js` in a directory named b

    1
    let baz = "Property baz";

In every case, you can include cjs core modules. Those provides you useful functions or (less often) bindings to C libraries.

Examples are:

    1
    2
    3
    4
    const Cairo = imports.cairo; //Cairo graphics
    const Lang = imports.lang; //useful JavaScript functions for extensing the language
    const Gettext = imports.gettext; //Gettext translation
    const TweenEquations = imports.tweener.equations; //Tween equations for animations

As you can see, it is common to assign the import to a constant in UpperCamelCase which looks like the imported module.

To view the source of those cjs modules, you should visit the GitHub page.

### `imports.gi.*`

As Cinnamon uses C libraries like Clutter, Muffin and more, there is a little problem: How can be those libraries used in cjs?

For this, there is GObject Introspection. For short, it allows you to use C libraries in cjs, Python and other languages.

C libraries are included like this:

    1
    2
    const St = imports.gi.St; //Shell Toolkit, the normal way to display widgets on the Cinnamon screen
    const Cinnamon = imports.gi.Cinnamon; //Cinnamon C libraries, e.g. AppSystem

Note: Not like normal `imports.*`, `imports.gi.*` imports needs to have the first letter after `gi.` be in upper case.

### `imports.ui.*`

Those imports under `imports.ui.*` are core Cinnamon modules.

Some important modules:

    1
    2
    const PopupMenu = imports.ui.popupMenu; //High-level classes for building menus for applets or context menus
    const Applet = imports.ui.applet; //Base applet classes

The source is in `/usr/share/cinnamon/js/ui/`

### `imports.misc.*`

Those imports under `imports.misc.*` are belonging to Cinnamon, but aren't tied to it that much like `imports.ui.*`.

    1
    2
    const Util = imports.misc.util; //useful functions
    const Interfaces = imports.misc.interfaces; //DBus stuff

The source is in `/usr/share/cinnamon/js/misc/`

### Importing xlet modules

When you want to split a big xlet code into smaller files, you'll need to import them. A simple way is using `AppletManager.applets[uuid]`:

    1
    2
    3
    4
    const uuid = "xlet@uuid";
    const dir = imports.ui.appletManager.applets[uuid]; //get the directory for the uuid

    //do imports with dir.*

The disadvantage on this method is that the constant dir is only valid for this file. You'll need a lot of copy&paste in order to import thing from every module.

### The `imports.searchPath`

Now some advanced stuff: the `imports.searchPath`. It is an array defaulting to `["resource:////gnome/gjs/modules", "/usr/share/cinnamon/js/"]`. Those two values are neccesary to let you the access to the modules mentioned above.

If you modify it (pushing a directory string to it), cjs also looks there. This is also very useful for big xlets which are split up in many modules.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const uuid = "xlet@uuid";
    const path = imports.ui.appletManager.appletMeta[uuid].path; //get the path for the uuid

    if(imports.searchath.indexOf(path) === -1)
      imports.searchPath.push(path);

    //do imports

    imports.searchPath.splice(imports.searchath.indexOf(path), 1);

The advantage of this method is that you can use just `imports.*` for all of your files and not only for one file.

It is very recommed to check that only one instance of the searchPath exists (see the if clause before push) and to remove this one (splice method). Otherwise, possible confusement of other parts of the Cinnamon enviroment can occurence.

### `__init__.js`

When you import stuff by modifying the `imports.searchPath` you can also use `__init__.js`. It is a normal JavaScript file, but every function or variable can be accessed directly via `import.*`.

Examples are often used functions, like a modified `_()` function for translating your xlet.

`__init__.js`

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const Gettext = imports.gettext;

    const uuid = "xlet@uuid";

    Gettext.bindtextdomain(uuid, GLib.get_home_dir() + "/.local/share/locale");

    function _(str){
      return Gettext.dgettext(uuid, str);
    }

In your other files:

    1
    2
    const uuid = imports.uuid;
    const _ = imports._;

This is just a little need extension to the `imports.searchPath` modification. There is no harm renaming `__init__.js` to something other (like `util.js`) and using `imports.util.*`.

### Order of imprting

The importer goes through the elements of `imports.searchPath`. On each element, the following order is used to import stuff:

- value in `__init__.js`
- a file
- a directory