Thursday, April 18, 2013

Closure Compiling JavaScript

‹prev | My Chain | next›

The ICE Code Editor has gotten quite big. I have no doubt that the code size is large, but the sheer number of scripts that needs to be pulled is has gotten quite out of hand:
<!DOCTYPE html>
<html>
  <head>
    <title>Embedded ICE Code Editor</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="http://gamingjs.com/ice-beta/editor.css">
    <script src="http://gamingjs.com/ice-beta/js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
    <script src="http://gamingjs.com/ice-beta/js/ace/keybinding-emacs.js"></script>
    <script src="http://gamingjs.com/ice-beta/js/rawinflate.js"></script>
    <script src="http://gamingjs.com/ice-beta/js/rawdeflate.js"></script>
    <script src="http://gamingjs.com/ice-beta/js/appcache.js"></script>
  </head>
  <body>
    // Web page content & code here...
  </body>
  <script src="http://gamingjs.com/ice-beta/js/ice-editor.js"></script>
  <script src="http://gamingjs.com/ice-beta/js/ice-embeded.js"></script>
  <script>
    new ICE.Embedded(
      document.getElementById('code-001'),
      {preview_el: document.getElementById('preview-001')}
    );
  </script>
</html>
So tonight, I hope to bring the Closure JavaScript compiler to bear on the problem. If nothing else, I hope mash everything into one usable file.

I grab, unzip and copy the JAR to a common location:
➜  Downloads  wget http://closure-compiler.googlecode.com/files/compiler-latest.zip
➜  src  unzip ../Downloads/compiler-latest.zip
➜  src  cp compiler.jar ~/local/java 
With that, I am ready to try compiling:
➜  code-editor git:(api) ✗ java -jar ~/local/java/compiler.jar \
    --js js/rawdeflate.js \
         js/rawinflate.js \
         js/appcache.js \
         js/ace/* \
         js/ice-* \
    > js/ice.js
Unfortunately, I get a bunch of errors that prevent compilation. Even more unfortunately, the errors come from the ACE code editor. Some of them are parse errors. I'll deal with them next. The bulk of the errors are Internet Explorer woes:
js/ace/mode-html.js:366: ERROR - Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option.
                token: "string.regexp",
                ^

js/ace/mode-html.js:383: ERROR - Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option.
                token: "string.regexp",
                ^
...
Of course, Internet Explorer is completely incapable of doing any of the stuff in 3D Game Programming for Kids anyway (lacking WebGL among other things). In other words, potential IE problems are not problems at all. So I add the --jscomp_off internetExplorerChecks command line switch:
➜  code-editor git:(api) ✗ java -jar ~/local/java/compiler.jar \
    --jscomp_off internetExplorerChecks \
    --js js/rawdeflate.js \
         js/rawinflate.js \
         js/appcache.js \
         js/ace/* \
         js/ice-* \         
    > js/ice.js
That leaves me with just parse errors. I do not see much choice but to directly edit the ACE code. Happily, this does not involve much. I follow the warning suggestion:
js/ace/ace.js:9122: ERROR - Parse error. missing formal parameter
    this.findMatchingBracket = function(position, char) {
                                                  ^

js/ace/ace.js:9125: ERROR - Parse error. identifier is a reserved word
        var charBeforeCursor = char || this.getLine(position.row).charAt(position.column-1);
And change the char variable name to chr:
function BracketMatch() {

    this.findMatchingBracket = function(position, chr) {
        if (position.column == 0) return null;

        var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column-1);
        // ...
    }
}
That leaves me warnings, but no errors that would prevent compilation. I end up with a pretty hefty JavaScript file:
➜  code-editor git:(api) ✗ ls -lSh js
total 624K
-rw-r--r-- 1 chris chris 469K Apr 18 22:27 ice.js
-rw-r--r-- 1 chris chris  53K Sep  9  2012 rawdeflate.js
-rw-r--r-- 1 chris chris  26K Apr  5 00:43 editor.js
-rw-r--r-- 1 chris chris  20K Sep  9  2012 rawinflate.js
-rw-r--r-- 1 chris chris  18K Apr 15 23:03 ice-full.js
-rw-r--r-- 1 chris chris 5.6K Apr 17 23:44 ice-editor.js
-rw-r--r-- 1 chris chris 5.5K Apr 11 22:53 ice-store.js
drwxr-xr-x 2 chris chris 4.0K Apr 18 22:27 ace
-rw-r--r-- 1 chris chris 3.4K Apr 17 23:46 ice-embeded.js
-rw-r--r-- 1 chris chris  620 Mar 12 22:59 appcache.js
Since ACE itself (in a sub-directory) is 800k+, I think I can live with that. But it will not matter much if the code does not work.

Sadly, the code does not work.

I change the web page so that it only needs to include the compiled ice.js:
<!DOCTYPE html>
<html>
  <head>
    <title>code editor</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="editor.css">
  </head>
  <body>
    // Web page content & code here...
  </body>
  <script src="js/ice.js"></script>
  <script>
    new ICE.Embedded(
      document.getElementById('code-001'),
      {preview_el: document.getElementById('preview-001')}
    );
  </script>
</html>
But, when I reload, I get an error that the require() function from require.js is not defined:
Uncaught TypeError: Property 'require' of object [object Object] is not a function
My initial instinct is to despair. After a little while, I pull myself together enough to wonder if the order of the ACE files is causing the problem.

So I try being explicit about the order of things:
➜  code-editor git:(api) ✗ java -jar ~/local/java/compiler.jar \
    --jscomp_off internetExplorerChecks \
    --js js/ace/ace.js 
         js/ace/keybinding-emacs.js \
         js/ace/theme-textmate.js \
         js/ace/mode-javascript.js \
         js/ace/theme-chrome.js \
         js/ace/worker-javascript.js \
         js/rawdeflate.js \
         js/rawinflate.js \
         js/appcache.js  \
         js/ice-* \
    > js/ice.js
That changes the error message. But I still get an error in the JavaScript console:
Uncaught atempt to load ace worker into main window instead of webWorker
It seems that, however ACE defines this web worker code, it is somehow dependent on being a separate require.js file. So I recompile again, but without the web worker.

After reloading, I find that the worker library is required, but not at the correct URL:
GET http://localhost:3000/worker-javascript.js 404 (Not Found) 
The URL should include the js/ace sub-path. I will worry about getting that value correct another time. For now, I add the library to the top-level of my web server, reload and get:



Yay! It works.

All is not perfect, however. Moving the web working code to the top-level directory might work on a local server, but it is not an option when I publish this code to http://gamingjs.com. The other problem that I find is that, if I reload the page, the code goes missing:



The code is not actually missing completely. If I resize the browser, it triggers ACE to redraw the code. If I recall correctly, I already have code in ICE to force this redraw on load. Unfortunately, however that works does not account for loading the ICE code along with everything else.

Those two issues aside, this looks to be quite promising. I call it a day here and will pick back up with those two issues tomorrow.


Day #726

1 comment: