composing stick stores posts as xml snippets that, after being sent through a simple markup translator, can be dropped on a page with any content (especially other posts). care is taken to make sure that posts do not interfere with each other or change any global state.

but lately I've been using a fair amount of javascript in posts, and I wanted a way to prevent loading libraries multiple times. if a library is not written well, reloading its source file could destroy its internal state. some sort of include guard is needed for posts to continue to be able to stand alone.

I think I've found a solution. put the following into a file called include_once.js:

String.prototype.trim = function () {
  return this.replace(/^\s*/, "").replace(/\s*$/, "");
}

String.prototype.basename = function() {
  return this.replace(/^.*\//, '');
}

Node.prototype.insertAfter = function(newNode, refNode) {
  if(refNode.nextSibling) {
    return this.insertBefore(newNode, refNode.nextSibling);
  } else {
    return this.appendChild(newNode);
  }
}

var scripts = document.getElementsByTagName('script');
var tracked_files = tracked_files || {};
// unfortunately, Javascript doesn't do __FILE__
var this_file = "include_once.js"

for(var i = 0, ii = scripts.length; i < ii; i++) {
  if(!scripts[i].src || scripts[i].iterated) continue;
  scripts[i].iterated = "yes";

  /* if there's a # in the filename and the preceding portion is this file's name,
   * then include the string after the # as the script to guard.
   */
  var files = scripts[i].src.split('#', 2);
  if(files.length == 2 && files[0].basename().trim() == this_file) {
    //console.log("called to include: "+files[1]);

    // only load each script once.
    if(tracked_files[files[1]]) continue;
    tracked_files[files[1]] = files[1];

    // first time!
    var newcontent = document.createElement('script'); 
    newcontent.src = files[1];
    newcontent.type = "text/javascript";
    newcontent.charset = "utf-8";
    newcontent.iterated = "yes";
    scripts[i].parentNode.insertAfter(newcontent, scripts[i]);
    //console.log("included file: "+files[1]);
  }
}

use it like this:

<!-- comments show DOM state after the preceding include_once.js has been run -->
<script src="include_once.js#a.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="a.js" type="text/javascript" charset="utf-8"></script> -->
<script src="include_once.js#a.js" type="text/javascript" charset="utf-8"></script>
<script src="include_once.js#b.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="b.js" type="text/javascript" charset="utf-8"></script> -->
<script src="include_once.js#b.js" type="text/javascript" charset="utf-8"></script>
<script src="include_once.js#b.js" type="text/javascript" charset="utf-8"></script>
<script src="include_once.js#a.js" type="text/javascript" charset="utf-8"></script>

this code works reliably in Firefox, but it doesn't work consistently under Safari. haven't tested with other browsers.

this post continues a trend of doing stupid things with Javascript.