Tuesday, November 22, 2011

MAX-Javascript Includes II

Rationale

With the release of MAX 6 I'm restarting the process of rethinking what Im doing with Javascript, and aim to refactor things a bit more sensibly a la Crockford, maybe by hijacking bits of Google's Closure.

However, in the interim, I still need a cheap-n-cheerful include mechanism, so Im back with the one I started with, a very simple dump of all the lines of code from a file into a buffer, with an eval at the end. Ive renamed it crudeinclude since it is indeed pretty crude. Its probably not that well-written either, to be honest.

Implementation

Part of the post-MAX6-tidy means that I've split it into two parts so that I can use multiple versions of it with multiple versions of MAX without editing the javascript that actually does the work, and renamed the files with a 'sxa.jsextension' class-hierarchy prefix a la goog.closure. Hence there's a now a sxa.jsextensions.crudeinclude.js file file and a sxa.jsextensions._setenv_includes_.js, with the latter declaring 'environment variables' used by the include routine. Both of these .js files need to be located the Cycling '74\jsextensions folder so that they're loaded automatically when MAX starts up.

file path : This include mechanism is based on the cpp #define style, so there are two formats for the filename argument.

The first, the 'direct' form "localIncludeLibrary2", uses the same directory as the including code as the included file's expected root folder. The second format, the 'angle-bracket' format "<siteIncludeLibrary1>" uses a global 'library folder' as the included file's expected root folder. This library folder is defined by the path set in SXA_JS_INCLUDEPATH mock 'environment variable, which should normally be initialised when MAX starts via the code in sxa.jsextensions._setenv_includes_.js

file suffix : The code has been written to try and open a file suffixed with .c.js first, or, if that does not exist, then it defaults to a version with the .js suffix. This was implemented to allow a speed up in file reading and eval-ing; the .c.js is expected to be a 'minified' or 'compressed' version of the original javascript. Originally a simple comment-and-whitespace stripper was developed to do this, but something like JSMIN or the Google closure compiler could be used instead.

examples

If SXA_JS_INCLUDEPATH is set to U:\MAX6\global then the following filename arguments to crudeinclude when called from a javascript file U:\MAX\project\example\example.js will result in crudeinclude trying to read the following files in the order described:

"localLibrary" :
    U:\MAX\project\example\localLibrary.c.js
then
    U:\MAX\project\example\localLibrary.js

"<siteLibrary>" :
    U:\MAX6\global\siteLibrary.c.js
then
    U:\MAX6\global\siteLibrary.js

code

This javascript, sxa.jsextensions._setenv_includes_.js, does the setup:


1:  //sxa.jsextensions._setenv_includes_ v0.2  
2:  //------------------------------------------------------------------------  
3:  // 'environment variables' for sxa.jsextensions.crudeinclude  
4:  //------------------------------------------------------------------------  
5:  // v0.2 : 21.11.2011 : add SXA_JS_INCLUDEBUFFER variable  
6:  //------------------------------------------------------------------------  
7:  // useage : this file to be located in Cycling '74/jsxtensions  
8:  //  
9:  //----------------------------------------------------------------------------------------  
10:  var SXA_JS_INCLUDEPATH="U:_MAX6_\\site\\jsincludes\\";  
11:  var SXA_JS_INCLUDEBUFFER = 800;  

And this javascript, sxa.jsextensions.crudinclude.js, does all the work:

1:  //sxa.jsextensions.crudeinclude v0.5  
2:  //------------------------------------------------------------------------  
3:  // simple brute-force javascript include mechanism for jsextensions folder  
4:  //------------------------------------------------------------------------  
5:  // v0.5 : 23.11.2011 : tidying, better comments, add SXA_JS_INCLUDEBUFFER   
6:  // variable and changed '.h.js' form to '.c.js'  
7:  //------------------------------------------------------------------------  
8:  // useage : this file to be located in Cycling '74/jsxtensions  
9:  //  
10:  // crudeinclude takes the suffix-less name of a javascript file, and loads that  
11:  // file into a buffer for eval() to interpret.  
12:  // crudeinclude is modelled after the cpp #include directive so takes two forms,  
13:  // the angle-bracket form, which expects the source file to be in a folder defined  
14:  // by the SXA_JS_INCLUDEPATH 'pseudo-environment variable' and the direct form, which  
15:  // expects the source file to be in the same folder as the 'parent' javascript file  
16:  // doing the include.  
17:  // crudeinclude has been written to search for a file suffixed with .c.js first, -before-   
18:  // checking for a version with the .js suffix.  This was done to speed up file reading;  
19:  // the .c.js is expected to be a 'compressed' or 'compiled' version of the file   
20:  // (as per the Google closure compiler) which contains a stripped and cleaned version of the  
21:  // original javascript.    
22:  //   
23:  // buffer = crudeinclude("<siteInclude>");  // includes siteInclude.c.js located in path SXA_JS_INCLUDEBUFFER  
24:  // buffer = crudeinclude("<localInclude");  // includes localInclude.js located in same path as calling code  
25:  //  
26:  //------------------------------------------------------------------------  
27:  crudeinclude.local = 1;  
28:  function crudeinclude(filename){  
29:    var includes ="";  
30:    var compressedType = ".c.js";  
31:    var rawType = ".js";  
32:    length = filename.length;  
33:    // determine possible full path names for filename  
34:    if ((filename.substring(0,1) == "<")){                   // '<' indicates possible global library name  
35:     if ((filename.substring(length-1,length) == ">")){            // '>' confirms well-formed global library name  
36:       base_filename = SXA_JS_INCLUDEPATH + filename.substring(1,length-1);  
37:       compiled_filename = base_filename + compressedType;  
38:       raw_filename = base_filename + rawType;  
39:     }else{                                 //badly formed global  
40:       post("ERROR in include; malformed filename '",filename, "'\n");  
41:       return;  
42:     }  
43:    }else{ // ! == "<"  
44:     if ((filename.substring(length-1,length) == ">")){           //<..> not matched, so badly formed include  
45:       post("ERROR in include; malformed filename '",filename, "'\n");  
46:       return;  
47:     }else{ // ! == ">" so not <...> form, thus a local filename  
48:       compiled_filename = filename + compressedType;  
49:       raw_filename = filename + rawType;  
50:     }// done checking angle brackets   
51:    }// done determining path and name options  
52:    // open file to copy into a string  
53:    // prioritise the compiled version first  
54:    file = new File(compiled_filename, "read");  
55:    file.open();    
56:    if (!(file.isopen)){         
57:     file = new File(raw_filename, "read");  
58:     file.open();   
59:    }  
60:    if (file.isopen){  
61:     // read in lines of up to 120 chars at a time  
62:     //(compensates for strange filesize/buffer issue)  
63:     // this is probably slower,  
64:     //but strings get weirdly truncated otherwise.  
65:     fileposition = 0;  
66:     while (fileposition < file.eof){  
67:       includes += file.readline(SXA_JS_INCLUDEBUFFER);  
68:       includes += "\n";  
69:       fileposition = file.position;  
70:     }// end while  
71:     file.close();  
72:    }else{// file not open      
73:     post("ERROR in include; cannot open ", filename, "\n");  
74:    }  
75:    return(includes);  
76:  }// end function include   

Basically this returns a buffer containing the code from an external javascript file. The file can then be interpreted via eval from within the code of a js or jsui object.

useage : A simple way of using this include code in javascript would be


     eval(crudeinclude("<siteIncludeLibrary1>"));  
     eval(crudeinclude("localIncludeLibrary2"));  

or


     includeBuf = (crudeinclude("<siteIncludeLibrary1>"));  
     includeBuf += (crudeinclude("localIncludeLibrary2"));  
     eval(includeBuf);

No comments:

Post a Comment