Table of Contents
- Javascript DNA / jDNA
Unobtrusive, simple to use, asynchronous script loader and dependency resolver that will
- dramatically optimize the loading speed of many scripts
- bring order into big web apps
- allow you to define clean Javascript classes (prototypes) the way you always wanted to - without
define()
ormodule.exports
auxiliary trash - ability to resolve dependencies for all Javascript files CSS files and more
Q: Why another AMD solution? We have Dojo Toolkit, RequireJS, and ScriptManJS...
A: There was a need for modern, clean and intuitive loader. What every programer needs is a system that you can say to "I need classes
Class1
andClass2
for my code to work." System will somehow make that happen and then execute your dependent code. This is what everybody wants. And this is what most f(r)ameworks don't get right.Programmers are required to alter their precious code to enable loading frameworks. That I consider in most cases as pure evil. Framework should help you and you should not be required to help framework. Do you need to include
define()
at the end of your PHP class definition file? No! Why should you in Javascript?The desire was to build the system that will understand simple files containing clean (future ECMA6) class declarations or current Javascript prototype definitions without any
define()
andmodule.export
trash code. Something you are used to from other languages. Something where one can express dependencies using class names rather then cryptic ids or file paths... Something like
// Contents of file /just/anywhere/file.js
function Point(x, y) {
this.coord = [x, y];
}
Point.prototype.toString = function() {
return '[' + this.coord.join(' x ') + ']';
};
that can be intuitively required in the code:
dna(
'Point', // I need the Point prototype
function() { // Run this after you load the Point prototype
new dna.Point(10, 20);
}
);
There was nothing like that. Most solutions failed to meet this simple expectation of every programmer by making impractical design choices or implementing unnecessarily complex solutions to achieve simple goal.
If you want to try something simple, intuitive and very powerfull then Javascript DNA is here for you!
- Simple API - all you need is just one method
dna(...)
. That's really it. There is not more to it. - Bundles - optional script archives capable of accommodating dozens of scripts that are fast to download and don't carry unnecessary burden on browser's internal script parser.
- CSS resources - you can load javascripts as easy as CSS resources only when needed.
- 100% asynchronous - scripts are loaded out-of-order yet evaluation order is guaranteed.
- Out-of-order API - with our unique O₃ API (Ozone API) you can call DNA methods in any order, define your hooks with their requirements before feeding DNA with dependency information, call DNA even before it is loaded and more. No need to worry if, when or in what order you can use any of DNA features.
- Optimized - small and fast with minified size of just about 11kB.
- Easy debugging - shows correct source/lines in debuggers. Reporting problems in console. Global error handlers.
- Open plugin system - support for URL-rewritting, download and script evaluation plugins.
- jQuery based
Include DNA script on your page
<script src=".../dna.js"></script>
Define locations of your Javascript prototypes and their dependencies
dna({
'proto': 'MyPrototype1', // Prototype name that you use in your code requirements
'require': 'MyPrototype2', // It requires also another prototype defined elsewhere
'load': '/somewhere/func1.js' // There is MyPrototype1 expected to be defined.
}, {
'proto': 'MyPrototype2',
'load': '/somewhere/func2.js'
});
Create files with your javascript classes:
File /somewhere/func1.js
function MyPrototype1() {
new dna.MyPrototype2; // we expect DNA to resolve also MyPrototype2 requirement
}
File /somewhere/func2.js
function MyPrototype2() {
alert('Hello world!');
}
Execute your function after DNA exports explicitly required MyPrototype1
and its dependency MyPrototype2
as dna
properties:
dna('MyPrototype1', function() { new dna.MyPrototype1; });
dna( [ REQUIREMENT | CONFIGURATION | CONFIGURATION_URL | CALLBACK | ARRAY | SETTINGS ], ...):Promise
dna.push( REQUIREMENT | CONFIGURATION | CONFIGURATION_URL | CALLBACK | ARRAY | SETTINGS ):Number
REQUIREMENT
:String
is a string withid
,proto
,service
identifier that needs to be resolved before calling callbacks.CONFIGURATION
:Object
is an object with list of requirements and scripts to load. See more in Configuration section.CONFIGURATION_URL
:String
you can store your configuration(s) as an array of objects in an external JSON file. This will load configurations from the file. JSON URL must contain at least one character "/
" (e.g. "./dna.json
") Note: Listed URIs will be rewritten and downloaded using plugin system. JSON files can contain also string names of prototypes/services to be loaded right away.CALLBACK
:Function
any callback(s) to be executed when all requirements were resolved. Same as specifying callback using$(...).done(CALLBACK);
ARRAY
:Array
list of any combination of items of typeREQUIREMENT
|CONFIGURATION
|CONFIGURATION_URL
|CALLBACK
|ARRAY
|SETTINGS
.SETTINGS
:Object
see more in Settings and Core Plugin System section.
Returned values
Promise
- the call todna(...)
always returns jQuery Promise object that you can use to hook your callbacks onto immediately or anytime later.Number
- the call todna.push(...)
returns the size of queue of commands waiting for resolution.
Call dna.push()
only if you are not sure if DNA is loaded. See section Call DNA Before It Loads. Good practice is to call it with a callback without any dependencies specified. This will call the callback immediatelly after DNA is loaded.
var dna = dna || [];
dna.push(function() { // On DNA load
dna(…);
…;
});
Configuration Objects are used to define dependencies and requirements.
{
'id': ID,
'proto': PROTO,
'service': SERVICE,
'data': DNAME,
'require': REQUIRE,
'load': LOAD,
'eval': EVAL,
'context': CONTEXT
}
Where
ID
:Identifier
Optional. Unique super-identifier (unique across allid
,proto
,service
identifiers in all configurations).PROTO
:Identifier
Optional. A super-identifier. Name of theFunction
javascript object. Must start with an upper-case letter. This object will be available asdna
property (e.g.dna[PROTO]
) after successful resolution. See Prototype Aliases to see how to load multiple versions of the same script.SERVICE
:Identifier
Optional. A super-identifier. Name of the propertydna[SERVICE]
. Must start with a lower-case letter. Thedna[SERVICE]
will be populated with object created usingPROTO
Function
(in a nutshell it will dodna[SERVICE]=new dna[PROTO];
).DNAME
:Identifier
Optional. A super-identifier. Name of the propertydna.data[DNAME]
. Ifrequire
section contains any JSON URIs (e.g. "json:./my-config.json"), deserialized JSON objects will be merged in that property. Thedata
identifier can be specified more then once.REQUIRE
:URI|Identifier|Array
Optional. One or array ofid
,proto
orservice
identifiers that define dependencies or relative or absoluteCONFIGURATION_URL
of file with additional list of JSON-serializedCONFIGURATION
objects to be loaded. All dependencies referred by listed super-identifiers will be resolved prior to resolvingload
section of this particular configurationLOAD
:URI|Array
Optional. A list of absolute or relative (resolved to a containing.json
file or current document) URLs of Javascript or HTML (see Bundled Assets) files to be loaded and parsed/executed. Files are guaranteed to be executed in listed order with required dependencies executed first. Note: Listed URIs will be rewritten and downloaded using plugin system.EVAL
:String
Optional. Accepted values:dna
(default) or custom name. See more in Evaluation Engines section.dna
evaluates the script in closure scope and expects the script to define variable of name specified in configuration'sproto
property.deferred
your script is not expected to define variable of name specified inconfig.proto
property but you are expected to pass object representingconfig.proto
to Deferred object stored infactory
variable.- custom name expects you to specify your own factory to execute the code and return the result object. See more in Custom Evaluation Engines section.
CONTEXT
:String
Optional. Default:false
. Name of the context to evaluate the script. Currently supported values: "window
" orfalse
.false
(default) boolean causes the script evaluation in its own context.- "
window
" string causes evaluation inwindow
object context. STRING
Experimental - any name identifying a shared context. Scripts having the same context name will havethis
andcontext
set to the same private Object. See Named Context section for more information.
Note: At least one id
or proto
super-identifier must be specified in the single Configuration Object.
Just pass the Configuration Object or URL pointing to JSON file with Configuration Objects to dna()
method. See syntax section for more. Examples:
dna( '/dna.json' );
dna( {'proto': 'Test', 'load': '/file.js'} );
dna(
'/dna.json',
{'proto': 'Test', 'load': '/file.js'},
[ '/other.json', '/otherother.json' ]
);
You can also include other JSON configuration files from withing Configuration Object.
[
{
"id": "load-big-project",
"description": "Huge Project included on request.",
"require": "./my/big-project.json"
},
{
"proto": "MyObject",
"description": "My code depending on Huge Project using indirect `require`",
"require": ['load-big-project', 'ClassFromBigProject'],
"load": "object.js"
},
{
"id": "MyObject2",
"description": "My code depending on Huge Project using direct `require`",
"require": ["./my/big-project.json", "Class2FromBigProject"],
"load": "object2.js"
},
{
"id": "MyObject2",
"description": "My code depending on Huge Project using `load`",
"require": [],
"load": [
"config:./my/big-project.json",
"object2.js"
]
}
]
You can then load additional big-project.json
just by requiring
load-big-project
. E.g. dna("load-big-project").done(...);
or indirectly through require
statement using dna("MyObject").done(...);
. That
way you can confortably break huge projects in multiple JSON
configurations and set up dependencies between them.
It is also possible to load external JSON using config
scheme - see more in Inbuilt Config Scheme section.
You can use DNA to load JSON configuration files. Example of DNA configuration.
{
"service": "myService",
"proto": "MyClass",
"load": ["./myclass.js"],
"require": ["myConfig"]
},
{
"data": "myConfig",
"load": ["json:./configs/my.json"]
}
After running dna("myService")
following happens:
myclass.js
is fetched and it is expected to containMyClass
declaration that will be stored indna.MyClass
property.my.json
is downloaded and deserialized. Resulting object is merged intodna.data.myConfig
property.new dna.MyClass
is instantiated and stored indna.myService
property- promise is resolved
Sometimes you will need to load Javascript class that has the name that conflicts with other class. Usually it is the case of supporting different versions of the same class.
In that case you can use prototype alias to export the prototype in different property. Example:
dna({
'proto': 'MyStuff',
'load': '/lib/my-stuff-v1.js'
}, {
'proto': 'MyStuff=MyStuff@2',
'load': '/lib/my-stuff-v2.js'
});
dna('MyStuff@2', newerCodeCallback);
In this case the class MyStuff
from the file my-stuff-v1.js
will be exported as dna.MyStuff
while the same class from file my-stuff-v2.js
will be exported as dna["MyStuff@2"]
.
You can use also multiple aliases:
dna({
'proto': 'MyStuff=MyStuff@2=webdevelopers.eu:MyStuff@2',
'load': '/lib/my-stuff-v2.js'
});
in which case MyStuff
from my-stuff-v2.js
will be available as both dna["MyStuff@2"]
and dna["webdevelopers.eu:MyStuff@2"]
but not as dna.MyStuff
.
You can execute your scripts in various ways. You can even register your own Custom Evaluation Engines.
DNA comes with following evaluation engines.
Your script is expected to define variable matching the name specified in config's proto
property that holds the prototype object representing your module.
Example:
dna({
'proto': 'MyModue',
'load': '/mymodule.js',
'eval': 'dna' // default
});
Contents of /mymodule.js
is expected to define MyModule
variable holding the Object. For example:
function MyModule() {}
Sometimes you need to asynchronously load other parts of the module and you cannot define the prototype right away during script evaluation.
For that the deferred
engine is the right one as it expects you to pass the prototype object to Promise
when you are ready.
Example:
dna({
'proto': 'MyModue',
'load': '/mymodule.js'
'eval': 'deferred'
});
Contents of /mymodule.js
is expected to call factory.resolve(...);
when your module is ready.
// variable `factory` is already populated with Deferred object you are expected to resolve/reject.
doSomeAsyncInit
.done(function(myProto) {
// myProto prototype is the outcome of your module.
// It will be passed on to DNA to be registered in dna.MyModule property.
factory.resolve(myProto);
});
This Engine will allow you to include other extensive configurations on request. That way you can chain up .json configurations and modules that will be loaded on request or can be specified as dependencies for other modules.
dna({
'id': 'extensive:module',
'load': 'javascript: dna("/lot-of.json", "extensive:loader", function() { factory.resolve(); });',
'eval': 'deferred'
});
// will load /lot-of.json with additional configuration
// and initialize service dna['extensive:loader']
dna('extensive:module', function() {
// My extensive module is ready
});
Note: for the trick with "load": "javascript:..."
see Custom Downloader section.
Nowadays Javascript loader should download scripts asynchronously and out-of-order. DNA pushed it even further by making whole API fully out-of-order (O₃ API) to match your needs for worryless coding.
You can define callbacks before you load configurations. DNA will delay your callback's resolution until it gets enough information to resolve all dependencies.
// DNA is not loaded yet? Create surrogate object.
var dna = dna || [];
// Treat dna.push([ARGS]) as it were dna(ARGS)
dna.push(['MyService', doSomething]);
// Note - DNA is not loaded yet and just line before
// you specified dependency on MyService that was not defined either
dna.push({'proto': 'MyService', 'load': ['my1.js', 'my2.js']});
And when DNA is included everything falls in place automatically and doSomething()
will get executed.
You can pass object with settings to dna(SETTINGS)
method. Supported properties are
timeout
:Integer
number of milliseconds to wait before failing when trying to satisfy object requirements. Default: 5000rewrite
:object
see Core Plugin Systemfactory
:object
see Core Plugin Systemdownloader
:object
see Core Plugin System
Exampe:
dna({'timeout': 1000});
The main difference between asynchronous loaders is how loader
- interprets URLs
- downloads files
- evaluates scripts
Javascript DNA has core plugin system that allows you to define your own behavior for all of main components.
To register your plugins pass the plugin configuration object to dna:
dna({
'rewrite': rewritePlugins,
'downloader': downloaderPlugins,
'factory': factoryPlugins
});
To register your own URL rewritting callback use this syntax
dna({
'rewrite': function(currentURI, originalURI, baseURI) | [ function(currentURI, originalURI, baseURI), ... ]
});
Example:
if (server.development) {
dna({
'rewrite': function(currentURI, originalURI, baseURI) {
return currentURI.replace(/\.min\.js$/, '.js');
}
});
}
You can register multiple rewrite callbacks. They will be called in order of registration.
The resulting URI will be resolved to absolute URL if it is relative after all rewrite callbacks were applied.
You can also register your own URI downloader. That way you can download files not only from server but also from local storage, variables or other resources.
DNA has following native scheme downloaders
javascript
: able to exectue javascript URLs. Eg.'load': 'javascript: alert("Hello World!");'
css
: able to load CSS URLs. Eg.'load': 'css:./modules/my.css'
config
: able to load additional DNA configurations. Eg.'load': 'config:./modules/my.json'
remote
: able to load scripts from third-party domains. Eg.'load': 'remote:https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'
*
: default downloader that uses standard$.AJAX
call.
The inbuilt javascript
scheme hook allows you to embed javascripts into URLs.
dna({
'id': 'my-test',
'load': 'javascript: alert("Hello World!");'
}, 'my-test'});
Inbuilt css
sheme downloader allows you to embed CSS.
dna({
'id': 'my-test',
'load': 'css:./my.css'
}, 'my-test'});
Inbuilt config
sheme downloader allows you to load additional DNA configurations on demand.
dna({
'id': 'my-test',
'load': [
'config:my/dna.json',
'my/script.js'
}, 'my-test'});
You cannot specify requirement that referre to
Configuration Object loaded from load
section using config
scheme. Error example:
{
'id': 'my-test',
'description': 'ERROR: the requirement to load big-project.json will be never met.'
'require': 'ClassInBigProject',
'load': [
'config:my/big-project.json',
'my/script.js'
]
}
If you you want to specify requirement that yet-to-be-loaded from other JSON file you have to put both reference to that JSON file
and requirement from that file in require
statement. Correct example:
{
'id': 'my-test',
'require': ['my/big-project.json', 'ClassInBigProject'],
'load': 'my/script.js'
}
If the configuration object contains only id
property and
require
property that contains URLs of other JSON configurations
then such configuration can be overriden by other configuration with
the same id
.
For example you can have one configuration file
[
{
'id': 'my-test',
'require': './other.json'
}
]
that includes other.json
file
[
{
'id': 'my-test',
'proto': 'MyTest',
'load': './my-test.js'
}
]
and DNA will not complain about duplicate id
super-identifier and
the original configuration will be replaced with the one from
other.json
. That way you can split large JSON configurations into
multiple JSON files.
Inbuilt remote
sheme downloader allows you to load scripts from third-party domains.
dna({
'id': 'my-test',
'load': [
'load': 'remote:https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js',
'my/script.js'
}, 'my-test'});
You can register only one downloader for each URI scheme.
To register your own downloader use this syntax
dna({
'downloader': {
SCHEME: function(dfd, uri, config),
...
}
});
Your downloader is expected to call dfd.reject(ERROR)
or dfd.resolve(DATA_STRING)
after the download.
Example:
dna({
'downloader': {
'variable': function(dfd, uri, config) {
var contents = myCachedContents[uri.replace('variable:', '')];
if (contents) dfd.resolve(contents);
else dfd.reject(new Error('Cannot download URI "' + uri + '"!'));
}
}
});
dna({
'proto': 'Test',
'load': 'variable:myTest'
}, 'Test', callback);
Note: When your downloader returns false
then default $.AJAX
downloader will be called instead. If you return false
from your downloader then you are supposed not to resolve dfd
Deferred object.
You can specify your own function to execute downloaded scripts. That way you can bridge RequireJS or CommonJS or any other module format.
To specify execution handler use this syntax
dna({
'factory': {
EVAL: function(dfd, jString, protoName, config),
...
}
});
Your factory is expected to call dfd.reject(ERROR)
or dfd.resolve(FUNCTION)
after resolution.
Example:
dna({
'factory': {
'my-common-js': function(dfd, jString, protoName, config) {
var exports = {};
(function(exports) {
eval(jString);
}(exports));
dfd.resolve(exports[protoName]); // Return the exported object
},
'my-other-method': mySuperEvaluator
}
});
dna({
'proto': 'MyModule',
'load': '/lib/my.js',
'eval': 'my-common-js'
});
Note: Thanks to Ozone API if you try to require a class that has unknown eval
type then the request will be queued until apropriate eval
type is defined. O₃ API allows you to define custom factories anytime without worrying if any code requiring custom factory was called before it has been even defined.
In fact this should allow also ECMA6 bridge.
// app.js
import something as calculator from 'calculator';
console.log(calculator.sum(1, 2)); // => 3
where factory can search for import
statement, do sub-call to DNA to resolve the found dependency and remove the import
statement for compatibility with non-ECMA6 browsers... :-)
You can bundle multiple scripts into one XML or HTML file for optimized download.
Just create a document (e.g. my-bundle.html
) and put standard script
tags with id
attributes in it.
<script id="myScript">function MyObject() {alert('Hello World!');}<script>
When specifying load
property of the DNA Configuration object use the URL pointing to HTML file with hash part specifying the element ID.
{
'proto': 'MyObject',
'load': ['/my-bundle.html#myScript', '/my-unbundled.js']
}
DNA will download the my-bundle.html
file only once (and reuse it later for other included scripts) and then it will extract and execute script with the attribute id="myScript"
.
It is good idea to make sure your web server allows browser-side caching of XML/HTML files.
For developement you can have empty script
tags linking to external javascripts: <script id="myScript" src="/libs/myscript.js"></script>
.
DNA will figure out that the content of script
tag is missing and will use linked resource instead.
For production site you can populate the HTML with embeded scripts or use the web server PageSpeed Module or other tools to do it automatically for you.
dna.core.configs
exposes the list of all known configurations.
If your configuration contains custom data you may search for them in dna.core.configs
.
Example: Some configurations have property startup
set to true
and we want to run all configs marked that way.
Your startup service in your dna.json
config may look like this:
{
"service": "MyBootstrap",
"startup": true,
"load": "mybootstrap.js"
}
// Find all existing services that should be ran on start up
dna.core.configs
.filter((config) => config.startup) // filter only startup=true props
.forEach((config) => dna(config.service)); // start all services
Thanks to the nature of DNA configs may be loaded later after the code above was executed. To watch for newly added configs you may want to leverage [Notifying of New Configurations](#Notifying of New Configurations):
$(window).on("dna:config:new", function (ev, config) {
if (config.service && config.startup) {
dna(config.service);
}
});
The dna:config:new
event is fired on window
object on each newsly
added configuration object. To listen for newly added configuartions use this code:
$('window').on("dna:config:new", function (message) {
alert('New DNA config added: ' + JSON.stringify(message.data));
});
Load jQuery plugins
dna('/dna-configs/my.json', 'jquery:my');
Contents of my.json
(note the CSS scheme trick)
[
{
"id": "jquery:my",
"load": ["css:js/jquery.my.css", "js/jquery.my.js"]
}
]
Mixed confugration using JSON file and inline Configuration Objects + requiring service dna.svc2
and prototype dna.Svc1
:
dna(
'Svc1', 'svc2',
'/configs/svcs.json',
{'service': 'svc2', 'proto': 'Svc2', 'load': ['/js/base.js', '/js/svc2.js']}
)
.done(run)
.fail(ups);
Out-of-order calls (O₃ API): first require dna.Svc1
and dna.svc2
to run your callback run
and then later on load required configurations.
dna(['Svc1', 'svc2'], run);
dna('/configs/svcs.json');
dna({'service': 'svc2', 'proto': 'Svc2', 'load': ['/js/base.js', '/js/svc2.js']});
Making DNA calls before dna.js
gets loaded using asynchronous script
tag.
<script>
var dna = dna || [];
dna.push(function() { alert('DNA just loaded!'); });
dna.push([ 'svc', function(svc) { alert('Service `dna.svc` is ready!'); } ]);
</script>
...
<script async src="/dna.js"></script>
Contents of index.html
:
<script>
var dna = dna || [];
dna.push(function() { // on DNA load
dna('/app/config.json', 'myApp', function() { // load and start my app
dna.myApp.start();
});
});
</script>
...
<script src="/dna.js" async></script>
Contents of /app/config.json
(relative paths are resolved relatively to JSON's file directory /app/
):
[
{
'service': 'myApp',
'proto': 'MyApplication',
'require': 'app:base',
'load': './my.js'
}, {
'id': 'app:base',
'load': ['./base/jquery.js', '/lib/bootstrap.js'],
'context': 'window'
}
]
Contents of /app/my.js
:
function MyApplication() {
this.version = '0.1';
}
MyApplication.prototype.start = function() {
alert('Hello world!');
}
If you write piece of code for global distribution then make sure you create configuration with globally (worldwide) unique ids so programmers using your code can integrate it without changes to your configs.
Good idea is to prefix your super-identifiers with your domain name.
Example of your config.json
file:
[
{
'proto': 'Example=example.com:Example',
'require': ['example.com:Main', 'example.com:service'],
'load': './example.js'
}, {
'service': 'example.com:service',
'proto': 'ServiceProto=example.com:ServiceProto',
'require': 'example.com:Main',
'load': './service.js'
}, {
'proto': 'Main=example.com:Main',
'load': './main.js'
}
]
Code in the example will result in exports into dna["example.com:..."]
properties.
See more in Prototype Aliases section.
There are many ways how to leverage the strength of DNA in your project.
You can make DNA calls even before DNA was loaded.
Create simple []
array if DNA is not loaded yet and call the dna.push([arguments])
method on it as it was dna(arguments)
method.
<script>
var dna = dna || [];
dna.push([ 'service1', function(svc1) {...} ]);
dna.push(callbackOnDNAStart);
</script>
...
<script src=".../dna.js" async="async"></script>
Note: Multiple arguments must be passed to dna.push()
as an array.
There is one limitation though, the dna.push()
method does not return the Promise object so you must pass your on-success callbacks as arguments.
Store your configurations in JSON file and load it, don't forget that dna()
always returns the jQuery Promise object.
dna('/my-defs.json', 'MyObject1', 'MyObject2', myCallback)
.done(myOtherCallback)
.done(myOtherOtherCallback, oneMoreCallback)
.fail(myWTF);
You can use dna()
to load any script that was not directly written for DNA.
dna({
'id': 'jquery',
'load': '/libs/jquery.min.js',
'context': 'window'
}, {
'id': 'jquery:iPop',
'require': 'jquery',
'load': '/libs/jquery.ipop.js',
'context': 'window'
});
dna('jquery:iPop', callback);
Most of older scripts can be specified using id
attribute and executed using context
type window
. To support newer scripts (like AMD scripts) use custom factories that you can tailor to fit any framework and/or your special needs.
Sometimes selected scripts need to share the variables. Polluting global window
scope with variables is not the best solution.
With DNA you can use the experimental named contexts. Scripts sharing the same name of the context will have this
and variable context
set to their own shared Object.
dna({
'id': 'test:1',
'load': 'javascript: context.myVar1 = "var 1"; console.log("Script 1", context.myVar1, context.myVar2);',
'context': 'my-private'
}, {
'id': 'test:2',
'load': 'javascript: context.myVar2 = "var 2"; console.log("Script 2", context.myVar1, context.myVar2);',
'context': 'my-private'
},
'test:1', 'test:2');
If named context is not specified then with dna
eval mode each configuration has its own private Object set as context automatically.
Watch the Javascript Console.
- Document $(window) events 'dna:fail', 'dna:done', 'dna:always'