Composr Tutorial: JavaScript Reference

Written by Salman Abbas

Introduction

This tutorial guides you through using JavaScript in Composr.

Libraries

In order to avoid polluting the JavaScript environment with global variables and functions, Composr exports its public JavaScript API using "libraries" instead.
"Libraries" are plain JavaScript objects that are global and named with a dollar sign ($) prefix. Composr core consists of three main libraries: $cms, $util and $dom.

The $cms library

This library mainly consists of important functions specific to Composr, and some ported from PHP. It has further sub-libraries, like $cms.form — which has form validation functions.

Sample functions:

Code (JavaScript)

$cms.isGuest(); // Returns true if the user is not logged in.

$cms.isStaff(); // Returns true if the current user is a staff member.

$cms.isAdmin(); // Returns true if the current user is an administrator.
 
$cms.isMobile(); // Returns true if mobile mode is active, false for desktop mode.

$cms.configOption('js_overlays'); // Pass the name of a config option to get its value. Please note that this function only works for a subset of config options that are marked as public.

$cms.requireJavascript('captcha');// Equivalent of require_javascript() in PHP, returns an ES6 promise that's resolved once the script is loaded.

$cms.doAjaxRequest('some/url.php').then(function (xhr) { // Returns an ES6 promise that's resolved with the XHR object once the request is complete.
    var response = xhr.responseText;
    // Do something with the response
});
 

The $cms.ui library

This library consists of functions you can use to open nifty modals or lightboxes and manipulate Composr UI widgets.

Sample functions:

Code (JavaScript)

$cms.ui.alert('Notice goes here...!', 'Modal title'); // Shows an alert modal, returns an ES6 promise that's resolved once the modal is closed

$cms.ui.confirm('Question goes here', null, 'Modal title'); // Shows a confirmation modal, returns an ES6 promise that resolved with a boolean indicating whether yes or no was clicked.

$cms.ui.prompt('Question goes here', 'Default value', null, 'Modal title'); // Shows a prompt modal, returns an ES6 promise that's resolved with the input provided by the user.
 

The $cms.form library

This library consists of form validation and manipulation functions.

Sample functions:

Code (JavaScript)

$cms.form.checkForm(event, formElement); // Returns an ES6 promise that resolves with a boolean indicating form validity.

$cms.form.doAjaxFieldTest(checkUrl); // Returns an ES6 promise that resolves with a boolean indicating field validity.

$cms.form.areUploadsComplete(formElement); // Whether all on-going file uploads have completed for a form.
 

The $cms.behaviors library

Composr borrows the concept of behaviors from Drupal. You are encouraged to quickly learn about them before continuing with this tutorial: Drupal.behaviors.
Please note that unlike Drupal behaviors which depend on the jQuery.once plugin, we use our own $util.once() function.

The $util library

This library consists of utility functions. $util is inspired by Underscore.js.

Sample functions:

Code (JavaScript)

$util.random(); // Returns a random integer between zero and a trillion.

// String interpolation
$util.format('My name is {1}', ['Salman']); // "My name is Salman"

$util.isNumeric('123'); // Returns true if the string is numeric
 

Classes

Among other niceties $util provides a way to declare ES6 classes while keeping backwards compatibility:

Code (JavaScript)

/* Creating a class */
function Animal() {
    this.speak();
}

$util.properties(Animal.prototype, { // First parameter is a prototype object, second is the methods you want to add to it.
    speak: function () {
        console.log('Not implemented yet');
    }
});
 
The above is equivalent to the following ES6 code:

Code (JavaScript)

class Animal {
    constructor () {
        this.speak();
    }

    speak() {
        console.log('Not implemented yet');
    }
}
 
$util also allows for backwards-compatible sub-classing:

Code (JavaScript)

/* Creating a sub-class */
function Cow() {
    Cow.base(this, 'constructor', arguments); // Call the parent constructor
}

$util.inherits(Cow, Animal, { // First parameter is the new sub-class, second is the parent class and finally any instance methods for the sub-class
    speak: function () {
        console.log('Mooo!');
    }
});
 
The above is equivalent to the following ES6 code:

Code (JavaScript)

class Cow extends Animal {
    constructor() {
        super(); // Call the parent constructor
    }
       
    speak() {
        console.log('Mooo!');
    }
}
 

The $dom library

This library consists of DOM manipulation functions. $dom is inspired by jQuery, except it works on individual elements instead of collections of them.

Code (JavaScript)

$dom.ready.then(function () { // $dom.ready is an ES6 promise that's resolved when DOM is ready.
    // DOM ready
});

$dom.load.then(function () { // $dom.load is an ES6 promise that's resolved when all the page content has fully loaded (once the window "load" event has fired).
    // DOM fully loaded
});

$dom.$('.my-element'); // Returns a single element which has a class "my-element", null if not found.

$dom.$$('.some-element'); // Returns an array of all elements with the class "some-element"

$dom.$$$(someElement, '.child-element'); // (Three dollar signs) Returns an array of all elements within `someElement` that have the class "child-element" plus `someElement` too if it has the class "child-element".

// Event listening
$dom.on('.my-element', 'click', function () {
    // Listen to the click event on .my-element
});

// Event delegation
$dom.on('.parent-element', 'click', '.child-element', function () {
    // Listens to the click event on .parent-element but only calls this function when clicked inside .child-element
});
 

Templates/Views

In order to adhere with CSP guidelines, instead of using inline event handlers or script tags, Composr keeps all code related to Tempcode templates under either template functions in the $cms.templates library or as the more advanced view classes under $cms.views.
To make this possible the data-tpl ($cms.behaviors.initializeTemplates) and data-view ($cms.behaviors.initializeViews) behaviors along with the PARAMS_JSON tempcode directive are used.

Example for data-tpl:

Create a file named EXAMPLE.tpl under the templates_custom folder with the following contents:

Code (HTML)

<!-- Add a data-tpl attribute to the outer-most element naming a template function, optionally pass a parameters object using the data-tpl-params attribute -->
<div data-tpl="example" data-tpl-params="{ message: 'Button was clicked' }">
    <button type="button" class="js-click-show-alert-with-message">Click me!</button>
    <!-- If you look closely, to avoid adding an inline event handler here we have added a CSS class to the interactive element, we can later attach event listeners in the template function below. -->
    <!-- Composr convention is to prefix all CSS classes that trigger JS functionality with 'js-' followed by the event name it's triggered on ('click' here) and the remainder describes what is being done. -->
</div>
 

The corresponding JS for the above HTML would look like:

Code (JavaScript)

// The data-tpl attribute was set to "example" so we will create a function named so under the $cms.templates library.
$cms.templates.example = function (params, container) {
    // The first parameter provided to template functions is the params object passed to the data-tpl-params attribute, second is the container element having the data-tpl attribute.
    var message = params.message;
   
    // You can add any template initialization code here...
   
    // Attach an event listener using event delegation
    $dom.on(container, 'click', '.js-click-show-alert-with-message', function () {
        window.alert(message); // This will show an alert modal whenever the button in EXAMPLE.tpl is clicked
    });
};
 

Template functions suffice for most common use cases except for some templates which have lots of JS involved, where you can use OOP-based view classes instead.
Composr View classes are heavily inspired by Backbone views, you can learn more about them here: Backbone.View.

Example for data-view:

Create a file named EXAMPLE_VIEW.tpl under the templates_custom folder with the following contents:

Code (HTML)

<!-- Add a data-view attribute to the outer-most element naming a view class, optionally pass a parameters object using the data-view-params attribute -->
<div data-view="ExampleView" data-view-params="{ message: 'Button was clicked' }">
    <button type="button" class="js-click-show-alert-with-message">Click me!</button>
</div>
 

The corresponding JS for the above HTML would look like as follows:

Code (JavaScript)

// The data-view attribute was set to "ExampleView" so we need to create a class named so under the $cms.views library.
$cms.views.ExampleView = ExampleView; // Remember to add your view class to [tt]$cms.views[/tt].
function ExampleView(params) {
    ExampleView.base(this, 'constructor', arguments); // Call the parent constructor
   
    // You can access the view container element using this.el
    this.el.style.color = 'red';
   
    // You can access the view container element's child elements using this.$(), passing a selector to it
    var button = this.$('button'); // Button element
    // this.$$() for multiple elements
    var buttons = this.$$('button'); // Array of button elements
}

$util.inherits(ExampleView, $cms.View, { // Sub-class the $cms.View class
    // The events function allows for a convenient way to use event delegation, it must return an object mapping event names and selectors to method names that are to be called when the events are triggered.
    events: function () {
        return {
            'click .js-click-show-alert-with-message': 'showAlertWithMessage'
        };
    };
   
    showAlertWithMessage: function () {
        window.alert(this.params.message);
    }
});
 

Feedback

Please rate this tutorial:

Have a suggestion? Report an issue on the tracker.