jMaki Injector

A JavaScript based client that allows you to bring in content from a separate URL (same domain only) and load any JavaScript/CSS from that page into the current page much like an iframe without the usability issues such as back button, history changing, and links being in a different context.

jMaki Injector Usage

The injector API is generally used under the covers by widget developers. In the case of the Dojo Tabbed View widget the injector is used to load content into a specific tab. In the case of the following example we have two tabs which are both defined in separate JSP files.

 <a:widget name="dojo.tabbedview"
        value="{tabs:[{label:'My Tab', content: 'Some static content'},
                      {label:'My Tab 2', url: 'tab2.jsp'} ]}" />

tab2.jsp contains the Yahoo Widget and a Flickr widgets.

<%@ taglib prefix="a" uri="" %>
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<a:widget name="yahoo.calendar" />
<a:widget name="flickr.favorites" args="{tags:'thekt', size:50, columns:2, count:4}"/>

The end result is a tabbed view that contains widgets from different libraries.

We can also use a Yahoo Tabbed View by changing the widget name and giving it the same arguments.

 <a:widget name="yahoo.tabbedview"
        value="{tabs:[{label:'My Tab', content: 'Some static content'},
                      {label:'My Tab 2', url: 'tab2.jsp'} ]}" />

The end result is a similar tabbed view with the same widgets.

This is the widget user's view of the jMaki Injector. Common use cases for this API included container widgets such as the Dojo and Yahoo Tabbed Views, the Dojo Accordion and any other container widget which you might want to contain other wdgets.

jMaki Injector API

As we saw above the jMaki Injector API can be used to bring content including widgets from a separate URL that is hosted on within the same domain. Common usecases for the Injector include the following:


Now lets look at the code used to interact with the injector. The following code snippet is from the Dojo Tabbed View and it uses the Injector to load content into a div which is set as a tab.

   var divId = widget.uuid + "_tab" + i;
   var _c = dojo.widget.createWidget("ContentPane",
  					 {label: _row.label, selected: i==1});
   _c.setContent("<div style='widget:100%;height:323px' id='" +
   divId +"'>Loading " + divId + "</div>");
   var _i = new jmaki.Injector();
   _i.inject({url:_row.url, injectionPoint: divId});

The key is the _i.inject({url:_row.url, injectionPoint: divId}); where a div is passed in along with a URL. The Injector API takes care of the rest.

What does the Injector do?

The Injector does the following in the following:

  1. The Injector loads the target URL
  2. The Injector parses the document retrieved from the URL removing all <script> and <style> references (e.g. <script type='text/javascript' src='foo'></script> or <link type='text/css' href='bar'></link> ) creating a list of the references are not already defined in the current page.
  3. The Injector then pareses the remaing document removing all embedded <script> and <style> elements creating a list of each in the proper order they were found in the page.
  4. The remaining document is set as the innnerHTML property of the injectionPoint or returned depending on which API was called (this is detailed further below).
  5. The list of <script> and <style> references obtained in step 2 are now injected into the current page.
  6. The list of embedded styles are added to the current page.
  7. Each embedded script obtained in step 3 is then evaluated in the global scope of the current page

There are cases where you want finner grained control over the loading of the scripts. Such control is provided and can be seen with the Yahoo Tabbed View use of the Injector.

  var divId = wargs.uuid + "_tab" + i;
  // calcualte height here
  var content = ("<div style='height:360px' id='" + divId +"'>Loading..." + "</div>");
  var _tv = tabView;
  var _r = new YAHOO.widget.Tab({
      label: _row.label,
      content: content,
      active: (i == 0)
  jmaki.injector.inject({url:_row.url, injectionPoint: divId});

Notice that the example we are creating a div with a well known ID that will be replaced by the Injector. The injector is smart enough to wait for the div to be loaded before any content or scripts are loaded.

Performance Considerations

Over using the injector may cause a performace bottleneck because all of the resources relating to a page that is injected must be loaded and parsed serially which can cause the browser to lock as JavaScript is not threaded. It is recommened to use an iframe instead if you are including large chuncks of content. The Dyanmic Container component which uses the Injector is set to use either injector or iframes based on an argument iframe : true.

Security Considerations

The Injector is multi-purpose and can be used to create some really advanced UIs. The Injector uses XMLHttpRequests to pull in content so you are limited to URLs within the same domain. Even in your own domain you should still use caution as the injector is in essence taking the scripts and styles out of page and injecting it into the current page for evey piece of content loaded. If one of those scripts becomes compromised everything in your page is at risk.

While the Injector could be written to work in conjunction with a proxy to pull in external content it is not recommended as it would pose a security risk. It would be unwise to allow untrusted external scripts into the global page.

Use Care not to Create Memory Leaks

As the Injector is a generic API that allows you to load and update content on demand you do need to use caution when loading and reloading a section of a page as it can lead to memory leaks. Use caution with the number of scripts being loaded. The Injector will only load any given library that is included as a reference once but all embedded scripts and styles will be added to the page. It is the developer's responsibilty to remove any references to elements and event handlers before refreshing a region.

Other Limitations

Scripts that write expect to be loaded with the inital page such as Google Maps and Yahoo Maps can not be used with the Injector as they will make document.write calls to the page after the page has been loaded causing the original page to disappear. Once a page has been loaded you can not call document.write. The only other option here would be to wrap Google or Yahoo Maps in an iframe if you need to dynamically load them.