Including Extra Javascript Files

Sometimes when writing javascript for use with Max/MSP I find I need to reuse bits of code that I’ve already created for a different project. Most of the time a simple search, copy and paste suffices but in some situations it would be better to have the ability to import other javascript files to get access to all of the functions they contain. This would also be useful when trying to organise where you files are stored. I don’t always want my javascripts in the Max search path (too many files here will cause the program to take ages to load), and I don’t always want them in the same directory as my main patch (which is the default place Max will look for a javascript/abstraction/image). The following code will import a specified javascript file into the current one.

You can use absolute or relative paths, the comment in the code gives you examples of how to navigate through directories in both directions. Please be a bit careful as the code takes advantage of the eval() function so if one of the files you include contains a mistake or something that is badly formatted then this will be included as it exists already. You should save this file in the Cycling ’74 > jsextensions folder so it will be available any time you need it.

/*
* lh.jsextensions
*
* first argument must be "this" to set scope
* second argument specifies the file to include
* relative paths must start wih a "/" separator
* this function uses eval() so please be careful
*
* include(this,"/local.js"); // same directory
* include(this,"/lib/functions/below.js"); // navigate lower
* include(this,"/../../above.js"); // navigate higher
* include(this,"/../../sub1/sub2/both.js"); // combine both
* include(this,"Macintosh HD:/Users/directory/sub-directory/other.js"); // absolute path
*/

function include(scope,dest) {
	var mem = "";
	var struct = /((\/\.\.)*)(.+$)/;
	var parent = /(.+)\/[^.]+\.\w+$/;
	var levels = struct.exec(dest)[1].length/3;
	var name = struct.exec(dest)[3];
	var target;
	if (dest.charAt(0) != "/") {
		target = dest;
	} else if (!levels) {
		target = scope.patcher.filepath.match(parent)[1]+name;
	} else {
		target = scope.patcher.filepath;
		for (i=-1; i<levels; i++) {
			target = target.slice(0,target.lastIndexOf("/",target.length-2));
		}
		target += name;
	}
	var f = new File(target,"read","TEXT");
	f.open();
	if (f.isopen) {
		while(f.position<f.eof) {
			mem += f.readline(800)+"\n";
		}
		f.close()
	} else {
		var er_file = target.slice(target.lastIndexOf("/",target.length-2)+1);
		var er_path = target.slice(0,target.lastIndexOf("/",target.length-2)+1);
		post("Error importing file \""+er_file+"\" from \""+er_path+"\"\n");
	}
	scope.eval(mem);
}

// EOF
About these ads

6 Comments

  1. Posted May 18, 2011 at 00:07 | Permalink | Reply

    wow, really nice articles running here!

    i was wondering how to do this in max, but couldn’t find in the manual.

    tks for sharing!

  2. skot
    Posted November 30, 2011 at 14:20 | Permalink | Reply

    Luke,

    Are you still able to do this in Max 6? Looks like they’ve disabled the eval() function…

    Javascript TypeError: scope.eval is not a function, line 506

    • Posted December 1, 2011 at 22:48 | Permalink | Reply

      I don’t have Max6 yet so I cannot check, however there are numerous JSON parsing libraries available that you could use instead. I think there are even a few examples posted to the forum. If you download that example it should be fairly easy to copy the relvant code across to use instead. Let me know how it goes!

  3. Posted February 28, 2012 at 03:29 | Permalink | Reply

    I found that the following seems to work as a replacement for that scope.eval(mem)

    var myFunction = new Function(mem);
    myFunction();

    However, it is not invoked in the context of ‘scope’ and it’s not clear to me whether anything important is lost by this.

  4. Posted June 24, 2013 at 11:35 | Permalink | Reply

    Try…

    var myFunction = new Function(mem);
    myFunction.apply(scope);

    I had to do this to get my includes to be recognised. Did you have it working without this? Also I need to define my functions in the included JS with lambda-style syntax:

    someNewFunction = function (a, b) { …

    …rather than…

    function someNewFunction(a,b) { …

    Manually having to specify the scope isn’t ideal as it isn’t really about scope but about giving include() access to the global scope’s “patcher” property. Any other scope, such as one from within an anonymous function I believe will cause an error. And of course, it’s also about making sure the included functions are part of the global namespace.

    There doesn’t seem to be a way to avoid the parameter by inferring the global scope directly from within include(). I tried “this” (different than “scope”) and “this.caller” (error)…

    • Mei
      Posted February 18, 2014 at 13:31 | Permalink | Reply

      Hi Hari,

      Do you mean you simply replaced the original line “scope.eval(mem)” with the following?
      var myFunction = new Function(mem);
      myFunction.apply(scope);

      I tried exactly the same thing with this include function. There is no more error message, but my main js file still doesn’t recognize my included file. Am I missing anything?

      Any hints will be very much appreciated!

Post a Comment

Required fields are marked *

*
*

Follow

Get every new post delivered to your Inbox.