Skip to content

Loading of Resources

Andre Kless edited this page Mar 1, 2018 · 228 revisions

Last updated February 1, 2018 by akless - content is up-to-date for ccm v15.0.2

Table Of Contents

Introduction

The ccm Framework provides a service for asynchronous loading of resources. It could be used with the method ccm.load. With ccm.load you can load resources like HTML, CSS, Images, JSON data and JavaScript on-demand and cross-domain. On a single ccm.load call several resources can be loaded at once. It can be flexibly controlled which resources are loaded in serial and which in parallel. The results are passed to the callback.

Syntax

ccm.load( resource1, resource2, ..., resourceX, callback )

Parameter Values

Parameter Description
resource1, resource2, ..., resourceX The resources to be loaded.
callback Callback when all resources are loaded.

Technical Details

Return Value

  • Result(s) of the ccm.load call
    (only if there were no asynchronous operations)

JavaScript Version:

  • ECMAScript 6

Detailed Explanation

In the following, all aspects of how to use ccm.load are described in detail.

Simplest Case: Loading by URL

In the simplest case, only an URL is passed as a parameter for a resource to be loaded:

ccm.load( 'style.css' );

The URL of the resource can be a relative or an absolute path. The resource does not have to be within the same domain and can be loaded cross-domain.

Loading by Resource Data Object

Instead of an URL, an object can be passed, which then contains other information besides the URL, via which the loading of the resource is even more flexible controllable. For example, when loading a resource, it can be specified in which context it is loaded. The CSS contained in a CSS file can now also be loaded into a specific Shadow DOM:

const shadow = document.createElement( 'div' );
shadow.attachShadow( { mode: 'open' } );

ccm.load( { url: 'style.css', context: shadow } );

See here, here and here for more examples of loading CSS.

Here is a list of settings that can be made for loading a resource via such an object:

Property Description
url Required. URL of the resource.
context Context in which the resource should be loaded (default is <head>).
method HTTP method to use: 'GET', 'POST' or 'JSONP' (default is GET).
params HTTP parameters to send (in the case of a data exchange).
attr HTML attributes to be set for the HTML tag that loads the resource.
ignore_cache Ignore any result already cached by ccm.
type Resource is loaded as css, image, data or js (default is data).

Result of a ccm.load Call

ccm.load returns only a return value if no asynchronous operations have occurred. This is only the case if all resources to be loaded were already loaded before and all results come directly from the ccm cache. In any case, the results are passed to the callback as the first parameter. The following example loads the content "Hello, <b>World</b>!" of a HTML file:

const first = ccm.load( 'hello.html', result => {
  const second = ccm.load( 'hello.html', result => {} );
} );

first is undefined because the HTML content has to be loaded asynchronously. second is "Hello, <b>World</b>!" because the content is already cached. In any case result contains the expected value.

If loading of at least one resource fails, ccm.load will not return a result and the callback will not be called.

Loading of Multiple Resources at Once

On a single ccm.load call several resources can be loaded at once:

ccm.load( 'hello.html', 'style.css', 'image.png', console.log );

When multiple resources are loaded, the callback receives an array instead of a single value as the result. The array contains the results in the order in which the ccm.load parameters were passed.

In this case, the result in the browser console is:
[ "Hello, <b>World</b>!", "style.css", "image.png" ].

If loading a resource does not supply anything specific, the default result is the URL of the resource. This applies, for example, when loading CSS and images.

Parallel and Serial Loading of Resources

It can be flexibly controlled which resources are loaded serially and which ones in parallel. By default, resources are loaded in parallel. When resources are to be loaded one after another, they simply need to be passed as an array:

ccm.load( [ 'hello.html', 'style.css' ] );

In the example, the two resources are now loaded serially.

The serial and parallel loading can be flexibly controlled as deep as desired. With each deeper array level you switch between serial and parallel loading:

ccm.load(
  'hello.html',     // Array Level 0: Parallel
  [
    'style.css',    // Array Level 1: Serial
    'image.png',
    [
      'data.json',  // Array Level 2: Parallel
      'script.js'
    ],
    'logo.gif'
  ],
  'picture.jpg'
);

The example loads the resources in the following timeline:

Resource Timeline
hello.html ******------------------
style.css ******------------------
image.png ------******------------
data.json ------------******------
script.js ------------******------
logo.gif ------------------******
picture.jpg ******------------------

Loading of JavaScript

Loading a JavaScript file will execute the JavaScript code contained in the file. As a result of this resource ccm.load returns the URL as usual. But the loaded JavaScript code can also set the result individually:

/* script.js */
ccm.files[ 'script.js' ] = { foo: 'bar' };
ccm.load( 'script.js', console.log );

Loading this JavaScript file with ccm.load results in: { foo: 'bar' }.

ccm.load returns as a result of loading a JavaScript file what the contained JavaScript code puts in ccm.files[ 'filename' ], where filename is the filename of the JavaScript file. Otherwise, the result is the URL. Using this convention, a JavaScript file can provide data across domains. Publicly fetched data in the namespace ccm.files will be immediately deleted by ccm.

In case of a minimized JavaScript file, ".min" in filename can be omitted:

/* script.min.js */
ccm.files['script.js']={foo:'bar'};
ccm.load( 'script.min.js', console.log );

See here, here and here for more examples of loading JavaScript.

Loading of Data

Data can be loaded via ccm.load:

ccm.load( 'hello.php', console.log );
/* hello.php */
<?php
echo 'Hello, World!';
?>

The example returns as result in the browser console: "Hello, World!"

A Resource Data Object can be used to specify HTTP parameters to be transmitted:

ccm.load( {
  url: 'echo.php',
  params: {         // sets HTTP parameters
    name: 'John'
  }
} , console.log );
/* echo.php */
<?php
echo 'Hello, '.filter_input( INPUT_GET, 'name', FILTER_SANITIZE_STRING );
?>

Result in the browser console: "Hello, John!"

The Resource Data Object can also be used to specify whether GET or POST should be used as HTTP method:

ccm.load( {
  url: 'hello.php',
  method: 'POST'    // sets HTTP method
} , console.log );

By default, GET is used.

JSONP can be used for data exchange. In this case, it is always a GET request. JSONP is only necessary if the data is to be loaded cross-domain:

ccm.load( {
  url: 'https://other.domain.com/data.php',
  method: 'JSONP'   // turns on JSONP
} , console.log );
/* data.php */
<?php
$callback = filter_input( INPUT_GET, 'callback', FILTER_SANITIZE_STRING );
echo $callback.'({"foo":"bar"});';
?>

Result in the browser console: { "foo": "bar" }

JSONP is not required if Cross-origin Resource Sharing (CORS) is already working.
See here for another example of a data exchange.

Loading a Resource Regardless of its File Extension

Normally ccm.load automatically recognizes at the file extension how the resource should be loaded. If a type is specified in the Resource Data Object, the file extension of the resource is ignored and the resource is loaded as the specified type:

ccm.load( {
  url: 'style.php',
  type: 'css'        // resource is loaded as CSS file
} );
/* style.php */
<?php
header( 'Content-Type: text/css' );
?>
b { color: red; }

Although the resource does not have the file extension .css, it will be loaded like a CSS file. The <head> now contains: <link rel="stylesheet" type="text/css" href="style.php">.

If type is not specified and the file extension is unknown, a data exchange is assumed.
See here and here for another examples of loading a resource regardless of its file extension.

Other Aspects

All Resource Data Objects passed to ccm.load are cloned to prevent them from being changed externally.

When loading data or loading a CSS file in a specific context, the cache is always ignored.

In a Resource Data Object, the reference to a ccm Instance can also be passed for the property context. The resource is then loaded into the instance's Shadow DOM.

When a resource is loaded into a specific context, care must be taken that this context has DOM contact. The chosen context should not be an on-the-fly element or part of it. This is required so that the HTML element used to load the resource is evaluated by the browser.

ccm.load always works with callbacks and never with "wait x ms and try again". There are therefore no unnecessary waiting times. If a resource is loaded that is already being loaded by another ccm.load call, the resource will not be requested again. Instead, it waits and is informed immediately by callback when the resource is successfully loaded.

More Examples

Loading the Content of an HTML File with a Relative Path

ccm.load( 'unit_tests/hello.html', console.log );
/* hello.html */
Hello, <b>World</b>!

Result in the browser console: "Hello, <b>World</b>!"

Loading the Content of an JSON File with an Absolute Path

ccm.load( 'https://akless.github.io/ccm/unit_tests/dummy/data.json', console.log );
/* data.json */
{ "foo": "bar" }

Result in the browser console: { "foo": "bar" }

If you want to load the static data from a JSON file cross-domain, the data should instead be provided via a JavaScript file (see here and here). Then the static data is loaded with JSONP and the HTTP request is not blocked by the Same-origin Policy (SOP) of the browser. JSONP is not required if Cross-origin Resource Sharing (CORS) is already working.

Loading of a CSS File in the <head> Context

ccm.load( 'https://akless.github.io/ccm/unit_tests/dummy/style.css', console.log );

The CSS rules contained in the CSS file are now active inside the global DOM.

The <head> now contains:
<link rel="stylesheet" type="text/css" href="https://akless.github.io/ccm/unit_tests/dummy/style.css">

The result in the browser console is the URL of the CSS file:
https://akless.github.io/ccm/unit_tests/dummy/style.css

Loading of a CSS File in a Shadow DOM Context

const shadow = document.createElement( 'div' );
shadow.attachShadow( { mode: 'open' } );

ccm.load( {
  url: 'https://akless.github.io/ccm/unit_tests/dummy/style.css',
  context: shadow
} );

This time the CSS rules contained in the CSS file are now active inside the Shadow DOM and the <link> element is appended to the shadow-root of shadow.

Loading of a CSS File in the Shadow DOM of a ccm Instance

ccm.instance( 'https://akless.github.io/ccm-components/blank/ccm.blank.js', instance => {

  ccm.load( {
    url: 'https://akless.github.io/ccm/unit_tests/dummy/style.css',
    context: instance
  } );

} );

The CSS rules contained in the CSS file are now active inside the Shadow DOM of the ccm Instance and the <link> element is appended to its shadow-root.

Preloading of an Image

ccm.load( 'https://akless.github.io/ccm/unit_tests/dummy/image.png', callback );

From the moment the callback is called, the loaded image is in the browser cache.

Loading of a JavaScript File

This example loads the JavaScript library jQuery v3.2.1.

ccm.load( 'https://code.jquery.com/jquery-3.2.1.js', console.log );

The code contained in the loaded JavaScript file is executed.

The <head> now contains:
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>

The result in the browser console is the URL of the JavaScript file:
https://code.jquery.com/jquery-3.2.1.js

Loading of a JavaScript File with Subresource Integrity

This is an example of setting HTML attributes for the HTML tag that loads a resource.

ccm.load( {
  url: 'https://akless.github.io/ccm/unit_tests/dummy/script.js',
  attr: {
    integrity: 'sha384-QoLtnRwWkKw2xXw4o/pmW2Z1Zwst5f16sRMbRfP/Ova1nnEN6t2xUwiLOZ7pbbDW',
    crossorigin: 'anonymous'
  }
} );

The <head> now contains:

<script src="https://akless.github.io/ccm/unit_tests/dummy/script.js"  
    integrity="sha384-QoLtnRwWkKw2xXw4o/pmW2Z1Zwst5f16sRMbRfP/Ova1nnEN6t2xUwiLOZ7pbbDW"
    crossorigin="anonymous"></script>

Loading of Static Data through a JavaScript File

ccm.load( 'https://akless.github.io/ccm/unit_tests/dummy/script.js', console.log );
/* script.js */
ccm.files[ 'script.js' ] = { foo: 'bar' };

Result in the browser console: { "foo": "bar" }

Loading of Data from a Server Interface with JSONP

This example performs a ccm demo login where you can authenticate yourself with any username and password.

ccm.load( {
  url: 'https://ccm.inf.h-brs.de',
  method: 'JSONP',
  params: {
    realm: 'ccm'
  }
}, console.log );

The result in the browser console is { "id": "username", token: "password" }, where username is the selected username and password is a server-side generated security token.

Dynamic Loading of an Image

This example preloads an image that comes from a PHP interface.

ccm.load( {
  url: 'https://kaul.inf.h-brs.de/ccm/dummy/image.php',
  type: 'image'
} );
<?php
header( 'Content-Type: image/png' );
readfile( 'image.png' );
?>

If the PHP interface would additionally implement user authentication, images could be made available to specific user groups. An HTTP parameter could also be used to control which image should be delivered.

Dynamic Loading of JavaScript

This example loads JavaScript that comes from a PHP interface.

ccm.load( {
  url: 'https://kaul.inf.h-brs.de/ccm/dummy/js.php',
  type: 'js'
}. console.log );
/* js.php */
ccm.files[ 'js.php' ] = { foo: '<? echo 'bar'; ?>' };

The <head> now contains: <script src="https://kaul.inf.h-brs.de/ccm/dummy/js.php">.
Result in the browser console: { "foo": "bar" }