Full Code of lexandera/Aardwolf for AI

master 67611b7cdb81 cached
29 files
89.3 KB
23.4k tokens
69 symbols
1 requests
Download .txt
Repository: lexandera/Aardwolf
Branch: master
Commit: 67611b7cdb81
Files: 29
Total size: 89.3 KB

Directory structure:
gitextract_3aaz7ybb/

├── .gitignore
├── LICENSE
├── README.md
├── app.js
├── config/
│   └── config.defaults.js
├── js/
│   └── aardwolf.js
├── package.json
├── rewriter/
│   ├── coffeerewriter.js
│   ├── jsrewriter.js
│   ├── jstokenizer.js
│   ├── multirewriter.js
│   └── templates/
│       ├── debug-template.coffee
│       ├── debug-template.js
│       ├── exception-template.coffee
│       └── exception-template.js
├── samples/
│   ├── calc-coffee.html
│   ├── calc.coffee
│   ├── calc.html
│   └── calc.js
├── server/
│   ├── debug-file-server.js
│   ├── rewriter-server.js
│   ├── server-util.js
│   └── server.js
└── ui/
    ├── css/
    │   ├── buttons.css
    │   └── ui.css
    ├── index.html
    └── js/
        ├── coffeetokenizer.js
        ├── jstokenizer.js
        └── ui.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/config/config.local.js
/node_modules/
/nbproject/
cache.json
samples_output
.idea
atlassian-ide-plugin.xml


================================================
FILE: LICENSE
================================================
Copyright (C) 2011 by Aleksander Kmetec

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.md
================================================
Aardwolf
========

Aardwolf is a remote JavaScript debugger for Android / iOS / Windows Phone 7 / BlackBerry OS 6+ and is written in JavaScript. It's available under the MIT license.

Home page: http://lexandera.com/aardwolf/

Currently it supports:

* breakpoints
* code evaluation at breakpoint
* break on next
* step/continue execution control
* stack listing
* exception reporting (also for exceptions thrown in async calls)
* JavaScript console remoting


It consists of the following parts:

* a server for communication between the mobile device and the UI
* a code rewriter which injects debug info into your existing source code
* a debug library which can break execution of your scripts, report execution progress, evaluate code, etc.
* a UI for setting breakpoints, stepping through code and seeing the current position within the script


In order to run the examples you will need:

* Node.js. Get it here: http://nodejs.org/#download
* An Android 2.x/iOS/WindowsPhone7 device or emulator (although running them from a Firefox/Chrome/Safari window will also work)


Setting it up
----------------------------------------------------------------------------------------------------

* Begin by installing node.js and Git
* Get the Aardwolf source code from GitHub: 
`git clone git://github.com/lexandera/Aardwolf.git`
* Download the required libraries by running "npm link" in the checked-out directory
* Start the server by running "node app.js -h <ip-or-hostname-of-your-computer>"
* After the server starts up, open http://localhost:8000 in your desktop browser. The debugger UI should appear.
* Open http://ip-or-hostname-of-your-computer:8500/calc.html on your phone and wait for the page to load. The line "Mobile device connected." should appear in the UI's output pane.
* You're now debugging the "calculator" example script.


If you're having problems opening the example, make sure that access to the port 8500 on your computer is not blocked by a firewall and that the address you entered into the config file can really be accessed from your phone. This is where your phone will load the samples from, so it must work.

You will get best results by connecting both you computer and your phone to the same WiFi network.


CoffeeScript support
----------------------------------------------------------------------------------------------------

Aardwolf now also contains extrememly basic CoffeeScript support. It probably can't handle any serious real-world code, but it's a good starting point if someone wishes to fork the source and work on it.

The steps for debugging the CoffeeScript example are the same as the steps described above, except:

* Replace calc.html with calc-coffee.html in the final step when opening the example.


Debugging your own code
----------------------------------------------------------------------------------------------------

The procedure is the same as above, except:

* When starting the server, add an additional parameter called -d or --file-dir, like this:  
    `node app.js -h <ip-or-hostname-of-your-computer> -d </path/to/www/root>`
* In your HTML page include the aardwolf.js debug library as the very first JS file and change the paths of included files to point to the files modified by Aardwolf:
    <pre>
    &lt;script type="text/javascript" src="http://ip-or-hostname-of-your-computer:8500/aardwolf.js"&gt; &lt;/script&gt;
    &lt;script type="text/javascript" src="http://ip-or-hostname-of-your-computer:8500/some-script.js"&gt; &lt;/script&gt;
    &lt;script type="text/javascript" src="http://ip-or-hostname-of-your-computer:8500/some-other-script.js"&gt; &lt;/script&gt;
    </pre>
* Reload the debugger UI first, then reload the page you just modified. The line "Mobile device connected." should appear in the UI's output pane.
* You should now be able to evaluate code remotely, set breakpoints, etc.


Debugging processed or minified code
----------------------------------------------------------------------------------------------------

If you wish to debug code which gets concatenated into a single file, minified, or transformed in some other way, you can still use Aardwolf, but you'll need to make a minor change in the part of your application which reads the code before it gets transformed.

It is important that Aardwolf can access source files before they are processed. Therefore you will need to set it up just as described in the previous section, with the '-d' parameter pointing to the directory containing unprocessed files, then change the processing code in you application so it reads files served by Aardwolf instead of reading them straight from the filesystem.

For example, if your code looks something like this:

    jscode += readFile('some-script.js');
    jscode += readFile('some-other-script.js');

you would need to change it to something like this:
    
    jscode += readFile('http://aardwolf-host:8500/aardwolf.js'); // Don't forget to include this!
    jscode += readFile('http://aardwolf-host:8500/some-script.js');
    jscode += readFile('http://aardwolf-host:8500/some-other-script.js');

In most languages, making the modification should be pretty straightforward. PHP's `file_get_contents($url)` and Clojure's `(slurp url)` will handle the change from local paths to URLs transparently. In Scala you can use `io.Source.fromURL(url).mkString`, Ruby has the 'OpenURI' module and in NodeJS you should be able to read remote files using the 'request' module.

Now you should be ready to debug processed code. And since Aardwolf has access to the original files, its UI will display the original, unprocessed code for easier debugging.


How it works
----------------------------------------------------------------------------------------------------

Breaking code execution and evaluating code at that point is enabled by code rewriting. Aardwolf's server contains a rather simple code rewriter which inserts debug hooks in front of every statement in the source code. These debug statements look like this:

    Aardwolf.updatePosition(  
        "/calc.js", // File path  
        7,          // Line number  
        false,      // Is current line a "debugger;" statement?  
        function(aardwolfEval) {       // This closure captures the current scope and makes it  
            return eval(aardwolfEval); // possible to pass it into another function.  
        }  
    );  

The first two parameters – file path and line number – should be self explanatory. Every time `Aardwolf.updatePosition()` is called, the given file and line number are checked against a list of breakpoints, and if a match is found, script execution is halted by performing a synchronous XMLHttpRequest to the server.

The third parameter signals whether the current line contains a `debugger;` statement. If it does, we must break execution even if there is no breakpoint set on that line.

Finally, the last parameter is a closure which captures the scope it's defined in and allows us to pass it around. When a string is passed to this function for evaluation, it will be eval'd in the same scope where this closure was defined, thus enabling us to evaluate code at the point where script execution was halted.

================================================
FILE: app.js
================================================
'use strict';

var argv = require('optimist').argv;
var fs = require('fs');
var config = require('./config/config.defaults.js');

if (argv['h'])         { config.serverHost        = argv['h']; }
if (argv['host'])      { config.serverHost        = argv['host']; }

if (argv['p'])         { config.serverPort        = argv['p']; }
if (argv['port'])      { config.serverPort        = argv['port']; }

if (argv['d'])         { config.fileServerBaseDir = argv['d']; }
if (argv['file-dir'])  { config.fileServerBaseDir = argv['file-dir']; }

if (argv['file-port']) { config.fileServerPort    = argv['file-port']; }

if (argv['o'])		   { config.outputDir = argv['o']; }

if (argv['v'])		   { config.verbose = true; }

if (argv['white-list']){ config.whiteList = argv['white-list'].split(','); }

try {
    /* Makes sure the path exists and gets rid of any trailing slashes. */
    config.fileServerBaseDir = fs.realpathSync(config.fileServerBaseDir);
} catch (e) {
    console.error(e.message);
    process.exit(1);
}

Array.prototype.equals = function (otherArray) {
	return !(this < otherArray || otherArray < this);
}

var ips = scanAvailableIPs();

if (config.serverHost && ips.indexOf(config.serverHost) < 0) {
	console.error('Configured host', config.serverHost, 'is not valid. You don\'t have that IP');
	console.error('Available IPs are:', ips);
	process.exit(1);
}

if (!config.serverHost) {

	if (ips.length > 1) {
		console.log("Cannot decide which IP to use, please specify one of these");
		ips.forEach(function(ip, i) {
			console.log('[', i + 1, ']:', ip);
		})

		var prompt = require('prompt');
		prompt.start();
		prompt.get({name: 'selection', validator: /^\d{1}$/, empty: false}, function(err, result) {
			var ip = ips[result.selection - 1];
			console.log('Choosen option', ip);
			config.serverHost = ip;
			startApp();
		})
	} else {
		config.serverHost = ips[0];
		startApp();
	}
} else {
	startApp();
}

function scanAvailableIPs() {
	var os = require('os');
	var interfaces = os.networkInterfaces(),
		ips = [];
	for (var dev in interfaces) {
		interfaces[dev].forEach(function(details){
			if (details.family == 'IPv4' && details.address != '127.0.0.1') {
				ips.push(details.address);
			}
		});
	}
	return ips;
}

function startApp() {
	var server = require('./server/server.js');
	server.run();

	if (config.runDebugServer) {
		var debugFileServer = require('./server/debug-file-server.js');
		debugFileServer.run();
	}

	if (config.runOfflineRewriter) {
		var rewriterServer = require('./server/rewriter-server.js');
		rewriterServer.run();
	}
}







================================================
FILE: config/config.defaults.js
================================================
'use strict';

/*
 *
 * To change defaults set in this file create a file called config.local.js
 * in the same directory and override values like this:
 *
 *     var config = require('../config/config.defaults.js');
 *     config.setting = 'new_value';
 *
 */

var config = {};
var path = require('path');
var fs = require('fs');


/* Verbose mode */
config.verbose = false;

/* Run the debug file server or not */
config.runDebugServer = true;

/* Run the offline rewriter process or not */
config.runOfflineRewriter = false;

/* Hostname or IP of the local machine */
config.serverHost = ''; // Can be retrieved automatically or asked to the user

/* port on which the server listens for requests */
config.serverPort = 8000;

/* Full path to directory holding source files you wish to debug */
config.fileServerBaseDir = path.join(__dirname, '../samples');

/* Port on which files will be served */
config.fileServerPort = 8500;

/* Output folder in which to put debugging-enabled files */
config.outputDir = path.join(__dirname, '../samples_output');

/* Files which won't be copied to the output folder */
config.ignoreFiles = ['.git', '.svn'];

/* Files which won't be processed by the debugger */
config.blackList = [];

/* Force this files as the only ones to be processed by the debugger */
config.whiteList = [];

/* Index file of the application */
config.indexFile = '/index.html';

/* After which tag insert aardwolf script in the modified index file */
config.whereToInsertAardwolf = '<head>';

/* Aardwolf script tag */
config.aardwolfScript = '<script type="text/javascript" src="aardwolf.js"></script>';

module.exports = config;

/* Load overrides from config.local.js if it exists */
var localConf = path.join(__dirname, 'config.local.js');
if (fs.existsSync(localConf)) {
    require(localConf);
}


================================================
FILE: js/aardwolf.js
================================================
/*
 * Aardwold mobile runtime library.
 *
 * Do not enable JS strict mode for this file as it
 * will disable some functionality this library depends on.
 */
window.Aardwolf = new (function() {
    var serverHost = '__SERVER_HOST__';
    var serverPort = '__SERVER_PORT__';
    var serverUrl = 'http://' + serverHost + ':' + serverPort;
    var breakpoints = {};
    var shouldBreak = function() { return false; };
    var asyncXHR = null;
    var lastFile = '';
    var lastLine = '';

	var listenTimeout = null;

	var consoleRedirected = false;

    function listenToServer() {
        try {
            dropCommandConnection();

            asyncXHR = new XMLHttpRequest();
            asyncXHR.open('GET', serverUrl + '/mobile/incoming', true);
            asyncXHR.onreadystatechange = function () {
                if (asyncXHR.readyState == 4) {
                    if (asyncXHR.responseText) {
                        var cmd = safeJSONParse(asyncXHR.responseText);

                        if (cmd && cmd.command == 'eval') {
                            doEval(function(aardwolfEval) { return eval(aardwolfEval); }, cmd);
                        }
                        else {
                            processCommand(cmd);
                        }
						setTimeout(listenToServer, 0);
                    }
                }
            };
			listenTimeout = setTimeout(function() {
				dropCommandConnection();
				listenToServer();
			}, 50 * 1000); // Internal XHR timeout fires at 60 secs, we let an adecuate margin to restart the server.

            asyncXHR.send(null);
        } catch (ex) {
            alert('Aardwolf encountered an error while waiting for command: ' + ex.toString());
            listenToServer();
        }
    }

    function dropCommandConnection() {
        if (asyncXHR) {
            asyncXHR.abort();
        }
		if (listenTimeout) {
			clearTimeout(listenTimeout);
		}
        asyncXHR = null;
		listenTimeout = null;
    }

    function sendToServer(path, payload) {
        try {
            var req = new XMLHttpRequest();
            req.open('POST', serverUrl + '/mobile' + path, false);
            req.setRequestHeader('Content-Type', 'application/json');
			if (path === '/breakpoint') {
				req.timeout = 0;
			}
            req.send(JSON.stringify(payload));
			if (!req.responseText) {
				// Timeout, retry
				return sendToServer(path, payload);
			}
            return safeJSONParse(req.responseText);
        } catch (ex) {
            alert('Aardwolf encountered an error while sending data: ' + ex.toString());
            listenToServer();
        }
    }

    function replaceConsole() {
        if (!window.console) {
            window.console = {};
        }

        ['info', 'log', 'warn', 'error'].forEach(function(f) {
            var oldFunc = window.console[f];

            window.console[f] = function() {
                var args = Array.prototype.slice.call(arguments);
                /* Write to local console before writing to the potentially slow remote console.
                   Make sure that the original function actually exists, otherwise this will
                   case an error on WindowsPhone where the console object is not available. */
                oldFunc && oldFunc.apply(window.console, args);
				if (consoleRedirected) {
					sendToServer('/console', {
						command: 'print-message',
						type: f.toUpperCase(),
						message: args.toString()
					});
				}
            };
        });
    }

    function processCommand(cmd) {
        switch (cmd.command) {
			case 'update-redirect-console':
				consoleRedirected = cmd.data;
				return true;
            case 'set-breakpoints':
                breakpoints = {};
                cmd.data.forEach(function(bp) {
                    var file = bp[0];
                    var line = bp[1];
                    if (!breakpoints[file]) {
                        breakpoints[file] = {};
                    }
                    breakpoints[file][line] = true;
                });
                return true;

            case 'breakpoint-continue':
                shouldBreak = function() { return false; };
                return false;

            case 'break-on-next':
            case 'breakpoint-step':
            case 'breakpoint-step-in':
                shouldBreak = function() { return true; };
                return false;

            case 'breakpoint-step-out':
                shouldBreak = (function(oldDepth) {
                    return function(depth) {
                        return depth < oldDepth;
                    };
                })(stackDepth);
                return false;

            case 'breakpoint-step-over':
                shouldBreak = (function(oldDepth) {
                    return function(depth) {
                        return depth <= oldDepth;
                    };
                })(stackDepth);
                return false;
        }
    }

    function doEval(evalScopeFunc, cmd) {
        var evalResult;
        try {
            evalResult = evalScopeFunc(cmd.data);
        } catch (ex) {
            evalResult = 'ERROR: ' + ex.toString();
        }
		sendEvalResult(evalResult, cmd.data);
    }
	function sendEvalResult(result, input) {
		if (result instanceof Object) {
			result = JSON.stringify(result, function(k, v) {
				if (typeof v == "function") {
					return "[function]";
				}
				return v;
			});
		}

		sendToServer('/console', {
			command: 'print-eval-result',
			input: input,
			result: result
		});
	}

    function getStack() {
        var callstack = [];
        var currentFunction = arguments.callee;
		var i = 0;
        while ((i < 10) && currentFunction) {
            var fname = currentFunction.name || '<anonymous>';
            callstack.push(fname);
			currentFunction = currentFunction.caller;
			i++;
        }
        return callstack;
    }

    function safeJSONParse(str) {
        try {
            return JSON.parse(str);
        } catch (ex) {
            return null;
        }
    }

    this.init = function() {
        replaceConsole();
        var cmd = sendToServer('/init', {
            command: 'mobile-connected'
        });
        if (cmd) {
            processCommand(cmd);
        }
        listenToServer();
    };

    this.updatePosition = function(file, line, isDebuggerStatement, evalScopeFunc) {
        /* Webkit's exceptions don't contain any useful file and line data,
           so we keep track of this manually for exception reporting purposes. */
        lastFile = file;
        lastLine = line;

        while (true) {
            var isBreakpoint = (breakpoints[file] && breakpoints[file][line]) || /* explicit breakpoint? */
                               isDebuggerStatement ||                            /* debugger; statement? */
                               shouldBreak(stackDepth);                          /* step (in|over|out) or break-on-next? */

            if (!isBreakpoint) {
                return;
            }

            dropCommandConnection();

            var cmd = sendToServer('/breakpoint', {
                command: 'report-breakpoint',
                file: file,
                line: line,
                stack: getStack().slice(1)
            });
            listenToServer();

            if (!cmd) {
                return;
            }

            if (cmd.command == 'eval') {
                doEval(evalScopeFunc, cmd);
            }
            else {
                var isInternalCommand = processCommand(cmd);
                if (!isInternalCommand) {
                    return;
                }
            }
        }
    };

    this.reportException = function(e) {
        sendToServer('/console', {
            command: 'report-exception',
            message: e.toString(),
            file: lastFile,
            line: lastLine,
            stack: getStack().slice(1)
        });
    };

	this.inspect = function(object) {
		if (object) {
			sendEvalResult(object, 'Aaardwolf.inspect(variable)');
		} else {
			sendEvalResult('ERROR: Undefined variable', 'Aaardwolf.inspect');
		}
	};

    var stack = [];
    var stackDepth = 0;

    this.pushStack = function(functionName, file, line) {
        stack.push([functionName, file, line]);
        ++stackDepth;
    };

    this.popStack = function() {
        var f = stack.pop();
        --stackDepth;
    };

})();

window.Aardwolf.init();



================================================
FILE: package.json
================================================
{
    "name": "aardwolf-plus",
    "description": "Aardwolf++ debugger",
    "url": "http://lexandera.com/aardwolf",
    "author": "Aleksander Kmetec <aleksander.kmetec@gmail.com>. Javier López <jlopez@tid.es>",
    "dependencies": {
        "coffee-script": "1.1.2",
        "optimist": "0.2.6",
	    "node-watch": "0.2.4",
        "prompt": "0.2.9"
    },
    "version": "0.0.0"
}


================================================
FILE: rewriter/coffeerewriter.js
================================================
'use strict';

var fs = require('fs');
var path = require('path');

var debugStatementTemplate = 
    fs.readFileSync(path.join(__dirname, 'templates/debug-template.coffee')).toString().trim();
var exceptionInterceptorTemplate = 
    fs.readFileSync(path.join(__dirname, 'templates/exception-template.coffee')).toString().replace(/\n\r?/g, '').replace(/ {4}/g, ' ');

var exceptionInterceptorParts = exceptionInterceptorTemplate.split('SPLIT');
var exceptionInterceptorStart = exceptionInterceptorParts[0].trim();
var exceptionInterceptorEnd = exceptionInterceptorParts[1].trim();


function addDebugStatements(filePath, text) {
    var coffee = require('coffee-script');
    var lines = text.split('\n');
    var out = [];
    
    var breakpoints = [];
    
    function buildDebugStatement(file, line, isDebuggerStatement) {
        breakpoints.push(line);
        
        return debugStatementTemplate
                    .replace('__FILE__', file)
                    .replace('__LINE__', line)
                    .replace('__DEBUGGER__', isDebuggerStatement ? 'true' : 'false');
    }
    
    
    lines.forEach(function(line, i) {
        var parts;
        var lineNum = i+1;
        
        if (line.match(/^\s*#/)) {
            return;
        }
        
        if (parts = line.match(/^(\s*)[^\s]+/)) {
            var match = line.match(/^(\s*)(debugger.*)$/);
            var isDebuggerStatement = !!match; 
            out.push((parts[1] || '') + '('+ buildDebugStatement(filePath, lineNum, isDebuggerStatement) +');');
            
            if (isDebuggerStatement) {
                /* Comment out the debugger statement to avoid triggering any native debuggers */
                line = match[1] + '#' + match[2];
            }
            
            out.push(line);
        }
    });

    return {
        file: exceptionInterceptorStart + coffee.compile(out.join('\n')) + exceptionInterceptorEnd,
        breakpoints: breakpoints
    };
}


module.exports = {
    addDebugStatements: addDebugStatements
};



================================================
FILE: rewriter/jsrewriter.js
================================================
'use strict';

var fs = require('fs');
var path = require('path');
var jstok = require('./jstokenizer.js');

var debugStatementTemplate =
    fs.readFileSync(path.join(__dirname, 'templates/debug-template.js')).toString().trim();
var exceptionInterceptorTemplate =
    fs.readFileSync(path.join(__dirname, 'templates/exception-template.js')).toString().replace(/\n\r?/g, '').replace(/ {4}/g, ' ');

var exceptionInterceptorParts = exceptionInterceptorTemplate.split('SPLIT');
var exceptionInterceptorStart = exceptionInterceptorParts[0].trim();
var exceptionInterceptorEnd = exceptionInterceptorParts[1].trim();

function buildExceptionInterceptorStart(functionName, file, line) {
    return exceptionInterceptorStart
                .replace('__FUNCTION__', functionName)
                .replace('__FILE__', file)
                .replace('__LINE__', line);
}

function addDebugStatements(filePath, text) {
    var nestingDepth = [0];
    var out = [];
    var line = 1;
    var semicolonOrFunctionBoundryEncountered = true;
    var newlineEncountered = true;
    var functionEncountered = false;
    var wordAfterFunction = null;
	var prevToken = null;
	var openSwitch = false;
	var openCase = false;

	var invalidTokens = ['(', '[', ',', '=', ':', 'return', '|', '?'];

	var breakpoints = [];

	function buildDebugStatement(file, line, isDebuggerStatement) {
		breakpoints.push(line);
		return debugStatementTemplate
			.replace('__FILE__', file)
			.replace('__LINE__', line)
			.replace('__DEBUGGER__', isDebuggerStatement ? 'true' : 'false');
	}

    jstok.tokenize(text, function(token, type) {
        /* drop carriage returns... we don't need them. */
        if (token === '\r') {
            return;
        }

		if (token == 'switch') {
			openSwitch = true;
		}
		if (token == 'case') {
			openCase = true;
		}

        if (token == '"use strict"' || token == "'use strict'") {
            token = '/* Aardwolf cannot work in strict mode. Disabling. '+token.split('').join('_')+' */';
        }

        /*
            Whenever we encounter some code:
            - if it's after a semicolon and a newline, or at the beginning of a function,
              insert a debug statement in front of it
            - it it's anywhere else, reset the semicolon and newline flags because we're not
              anywhere near a place where a debug statement should be inserted

            Yes, this rewriter assumes that you're using semicolons in your code.
        */
        if (['word', 'number', 'string', 'char'].indexOf(type) > -1) {
            if (type != 'char' &&
                token != 'else' &&
                semicolonOrFunctionBoundryEncountered &&
                newlineEncountered)
            {
                var isDebuggerStatement = token === 'debugger';
                out.push(buildDebugStatement(filePath, line, isDebuggerStatement));

                if (isDebuggerStatement) {
                    /* Comment out the debugger statement to avoid triggering any native debuggers */
                    token = '/*' + token + '*/';
                }
            }

            semicolonOrFunctionBoundryEncountered = false;
            newlineEncountered = false;
        }

        if (type == 'word') {
            if (functionEncountered) {
                wordAfterFunction = token;
            }
            functionEncountered = false;
        }

        if (token === 'function') {
            /* keep a separate nesting depth counter for each nested function */
            nestingDepth.push(0);
            out.push(token);

            functionEncountered = true;
            wordAfterFunction = null;
        }
        else if (token === '{') {
            ++nestingDepth[nestingDepth.length-1];

            out.push(token);

            /* we have just entered a function body - insert the first part of the exception interception block */
            if (nestingDepth.length > 1 && nestingDepth[nestingDepth.length-1] === 1) {
                out.push(buildExceptionInterceptorStart(wordAfterFunction || '<anonymous>', filePath, line));
            }

			if (openSwitch) {
				openSwitch = false;
			} else if (invalidTokens.indexOf(prevToken) < 0) {
				semicolonOrFunctionBoundryEncountered = true;
			}
        }
        else if (token === '}') {
            --nestingDepth[nestingDepth.length-1];

            /* we are about to exit a function body - insert the last part of the exception interception block */
            if (nestingDepth.length > 1 && nestingDepth[nestingDepth.length-1] === 0) {
                out.push(exceptionInterceptorEnd);
                nestingDepth.pop();
            }
			semicolonOrFunctionBoundryEncountered = true;

            out.push(token);
        }
		else if (token === ':' && openCase) {
			openCase = false;
			semicolonOrFunctionBoundryEncountered = true;
			out.push(token);
		}
        else {
            if (token === ';') {
                semicolonOrFunctionBoundryEncountered = true;
            }
            else if (token === '\n') {
                ++line;
                newlineEncountered = true;
            }
            else if (type == 'comment') {
                /* comments can span multiple lines so we need to adjust line count accordingly */
                var parts = token.split('\n');
                line += parts.length - 1;
            }
            out.push(token);
        }
		if (type != 'comment' && type != 'whitespace' && type != 'newline') {
			prevToken = token;
		}
    });


	return {
		file: buildExceptionInterceptorStart('<toplevel>', filePath, 0) + out.join('') + exceptionInterceptorEnd,
		breakpoints: breakpoints
	};
}


module.exports = {
    addDebugStatements: addDebugStatements
};



================================================
FILE: rewriter/jstokenizer.js
================================================
'use strict';

/* A simple JS tokenizer. We're really only interested in a couple of keywords, parentheses, 
   brackets and semicolons, so it doesn't need to be complete as long as it correctly handles
   multi-word tokens such as strings and comments.
*/
function tokenize(str, onToken) {
    var len = str.length;
    var pos = 0;
    var validRegexPos = false;
    
    while (pos < len) {
        var c = str[pos];
        
        if (c === '"' || c === "'") {
            extractString(c);
        }
        else if (c === '/' && str[pos+1] === '/') {
            extractSingleLineComment();
        }
        else if (c === '/' && str[pos+1] === '*') {
            extractMultiLineComment();
        }
        else if (c === '/' && validRegexPos) {
            extractRegexLiteral();
        }
        else if (c === ' ' || c === '\t') {
            extractWhitespace();
        }
        else if ('0123456789'.indexOf(c) > -1) {
            extractNumber();
        }
        else if (c.match(/^[a-zA-Z_$]$/) !== null) {
            extractWord();
        }
        else {
            extractChar();
        }
    }
    
    function onTokenInternal(token, type) {
        /* A slash following an assigment operator, a semicolon or an 
           opening paren can be a regex literal delimiter. */
        if (type === 'char' && ':=;({'.indexOf(token) > -1) {
            validRegexPos = true;
        } else if (!((['comment', 'whitespace']).indexOf(type) > -1)) {
            validRegexPos = false;
        }
        
        onToken(token, type);
    }
    
    function extractSingleLineComment() {
        var endPos = str.indexOf("\n", pos);
        if (endPos === -1) {
            endPos = len - 1;
        }
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }
    
    function extractMultiLineComment() {
        var endPos = pos;
        while (!(str[++endPos] === '*' && str[endPos+1] === '/'));
        endPos += 2;
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }
    
    function extractRegexLiteral() {
        var endPos = pos;
        /* regex literal body /.../ */
        while (str[++endPos] != '/') {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        /* flags following the body */
        while ('gimy'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'regex');
        pos = endPos;
    }
    
    function extractString(quoteChar) {
        var endPos = pos;
        while (str[++endPos] != quoteChar) {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        ++endPos;
        onTokenInternal(str.substring(pos, endPos), 'string');
        pos = endPos;
    }
    
    function extractNumber() {
        var endPos = pos;
        while ('0123456789.eE'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'number');
        pos = endPos;
    }
    
    function extractWord() {
        var endPos = pos;
        while (str[++endPos].match(/^[a-zA-Z_$0-9]$/) !== null);
        onTokenInternal(str.substring(pos, endPos), 'word');
        pos = endPos;
    }
    
    function extractWhitespace() {
        var endPos = pos;
        while (' \t'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'whitespace');
        pos = endPos;
    }
    
    function extractChar() {
        var c = str.substr(pos, 1);
        onTokenInternal(c, c === '\n' ? 'newline' : 'char');
        ++pos;
    }
}

module.exports.tokenize = tokenize;

================================================
FILE: rewriter/multirewriter.js
================================================
'use strict';

var path = require('path');
var fs = require('fs');

var config = require('../config/config.defaults.js');

function isRewritable(filePath) {
    var fileServerBaseDir = path.normalize(config.fileServerBaseDir);
    var fullRequestedFilePath = path.join(fileServerBaseDir, filePath);
    
    /* File must exist and must be located inside the fileServerBaseDir */
    if (fs.existsSync(fullRequestedFilePath) &&
        fs.statSync(fullRequestedFilePath).isFile() &&
        fullRequestedFilePath.indexOf(fileServerBaseDir) === 0)
    {
        if (filePath.substr(-3) == '.js' || filePath.substr(-7) == '.coffee') {
            return true;
        }
        
        return false;
    }
}

function getRewrittenContent(filePath) {
    var fileServerBaseDir = path.normalize(config.fileServerBaseDir);
    var fullRequestedFilePath = path.join(fileServerBaseDir, filePath);
    
    var rewriter;
    
    if (filePath.substr(-3) == '.js') {
        rewriter = require('../rewriter/jsrewriter.js');
    }
    else if (filePath.substr(-7) == '.coffee') {
        rewriter = require('../rewriter/coffeerewriter.js');
    }
    
    if (rewriter) {
        var content = fs.readFileSync(fullRequestedFilePath).toString();
        return rewriter.addDebugStatements(filePath, content);
    }

}

module.exports = {
    getRewrittenContent: getRewrittenContent,
    isRewritable: isRewritable
};



================================================
FILE: rewriter/templates/debug-template.coffee
================================================

Aardwolf.updatePosition("__FILE__", __LINE__, __DEBUGGER__, (aardwolfEval) -> eval(aardwolfEval));


================================================
FILE: rewriter/templates/debug-template.js
================================================

Aardwolf.updatePosition("__FILE__", __LINE__, __DEBUGGER__, aardwolfEvalFunc);



================================================
FILE: rewriter/templates/exception-template.coffee
================================================

try {
  SPLIT
} catch (aardwolfEx) {
    if (!aardwolfEx.rethrown) {
        Aardwolf.reportException(aardwolfEx);
    }
    aardwolfEx.rethrown = true;
    throw aardwolfEx;
}


================================================
FILE: rewriter/templates/exception-template.js
================================================

var __this = this;try {
  Aardwolf.pushStack("__FUNCTION__", "__FILE__", '__LINE__');
  var aardwolfEvalFunc = function(aardwolfEval) { return eval(aardwolfEval); };
  SPLIT
} catch (aardwolfEx) {
    if (!aardwolfEx.rethrown) {
        Aardwolf.reportException(aardwolfEx);
    }
    aardwolfEx.rethrown = true;
    throw aardwolfEx;
} finally {
  Aardwolf.popStack();
}


================================================
FILE: samples/calc-coffee.html
================================================
<!DOCTYPE html>
<html>
    <head>
        <title>COFFEESCRIPT Calculator</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
        
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
        
        <script type="text/javascript" src="http://__SERVER_HOST__:__FILE_SERVER_PORT__/aardwolf.js"></script>
        <script type="text/javascript" src="http://__SERVER_HOST__:__FILE_SERVER_PORT__/calc.coffee"></script>
        
        <style></style>
    </head>
    
    <body>
    
    Take <input type="number" id="number1" /> <br/>
    add <input type="number" id="number2" /> to it <br/>
    and multiply by <input type="number" id="number3" />.<br/>
    <br/>
    You get: <span id="result"></span>
    <br/>
    <br/>
    <button id="calculate">Go!</button>
    <button id="reset">reset</button>
    
    </body>
</html>


================================================
FILE: samples/calc.coffee
================================================

$number1 = null
$number2 = null
$number3 = null
$result = null

$ ->
    # Locate number input fields
    $number1 = $ '#number1'
    $number2 = $ '#number2'
    $number3 = $ '#number3'
    $result = $ '#result'
    
    ($ '#calculate').click calculate
    ($ '#reset').click reset
    
# Performs calculation
calculate = ->
    # read entered numbers
    a = Number $number1.val()
    b = Number $number2.val()
    c = Number $number3.val()
    
    performAddition = (n1, n2) -> 
        console.log 'Performing addition of '+n1+' and '+n2+'.'
        n1 + n2
    
    performMultiplication = (n1, n2) ->
        console.log 'Performing multiplication of '+n1+' and '+n2+'.'
        n1 * n2
    
    sum = performAddition a, b
    total = performMultiplication sum, c
    
    # Update result field
    $result.text total
    
# Clears the result and input fields
reset = ->
    debugger # the 'debugger' statement works also...
    $number1.val ''
    $number2.val ''
    $number3.val ''
    $result.text ''


================================================
FILE: samples/calc.html
================================================
<!DOCTYPE html>
<html>
    <head>
        <title>Calculator</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
        
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
        
        <script type="text/javascript" src="http://__SERVER_HOST__:__FILE_SERVER_PORT__/aardwolf.js"></script>
        <script type="text/javascript" src="http://__SERVER_HOST__:__FILE_SERVER_PORT__/calc.js"></script>
        
        <style></style>
    </head>
    
    <body>
    
    Take <input type="number" id="number1" /> <br/>
    add <input type="number" id="number2" /> to it <br/>
    and multiply by <input type="number" id="number3" />.<br/>
    <br/>
    You get: <span id="result"></span>
    <br/>
    <br/>
    <button id="calculate">Go!</button>
    <button id="reset">reset</button>
    
    </body>
</html>


================================================
FILE: samples/calc.js
================================================

var $number1;
var $number2;
var $number3;
var $result;

$(function() {
    /* Locate number input fields */
    $number1 = $('#number1');
    $number2 = $('#number2');
    $number3 = $('#number3');
    $result = $('#result');
    
    $('#calculate').click(calculate);
    $('#reset').click(reset);
});

/**
 * Performs calculation
 */
function calculate() {
    /* Read entered numbers */
    var a = Number($number1.val());
    var b = Number($number2.val());
    var c = Number($number3.val());

    function performAddition(n1, n2) {
        console.log('Performing addition of '+n1+' and '+n2+'.');
        return n1 + n2;
    }
    
    function performMultiplication(n1, n2) {
        console.log('Performing multiplication of '+n1+' and '+n2+'.');
        
        setTimeout(function() {
            /* Intended to demonstrate exception reporting in async calls */
            nonExistingObjectForExceptionReportingDemonstration.bar();
        }, 1000);
        
        return n1 * n2;
    }
    
    var sum = performAddition(a, b);
    var total = performMultiplication(sum, c);
    
    /* Update result field */
    $result.text(total);
}

/**
 * Clears the result and input fields
 */
function reset() {
    debugger; // the 'debugger' statement works also...
    $number1.val('');
    $number2.val('');
    $number3.val('');
    $result.text('');
}



================================================
FILE: server/debug-file-server.js
================================================
'use strict';

/*
 * Serves source files with debug statements inserted.
 */

var http = require('http');
var path = require('path');
var fs = require('fs');
var url = require('url');

var config = require('../config/config.defaults.js');
var util = require('./server-util.js');

var multirewriter = require('../rewriter/multirewriter.js')

function run() {
    if (!fs.existsSync(config.fileServerBaseDir)) {
        console.error('ERROR: Path does not exist: ' + config.fileServerBaseDir);
        process.exit(1);
    }

    http.createServer(DebugFileServer).listen(config.fileServerPort, null, function() {
        console.log('File server listening for requests on port ' + config.fileServerPort + '.');
    });
}


function DebugFileServer(req, res) {
    var requestedFile = url.parse(req.url).pathname;
    var fileServerBaseDir = path.normalize(config.fileServerBaseDir);
    var fullRequestedFilePath = path.join(fileServerBaseDir, requestedFile);

    
    /* alias for serving the debug library */
    if (requestedFile.toLowerCase() == '/aardwolf.js') {
        util.serveStaticFile(res, path.join(__dirname, '../js/aardwolf.js'));
    }
    /* File must exist and must be located inside the fileServerBaseDir */
    else if (fs.existsSync(fullRequestedFilePath) &&
             fs.statSync(fullRequestedFilePath).isFile() &&
             fullRequestedFilePath.indexOf(fileServerBaseDir) === 0)
    {
        if (multirewriter.isRewritable(requestedFile)) {
            var processedFile = multirewriter.getRewrittenContent(requestedFile);
            res.writeHead(200, {'Content-Type': 'application/javascript'});
            res.end(processedFile.file);
        }
        else {
            util.serveStaticFile(res, fullRequestedFilePath);
        }
    }
    else {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end('NOT FOUND');
    }
}


module.exports.run = run;


================================================
FILE: server/rewriter-server.js
================================================
'use strict';

/*
 * Serves source files with debug statements inserted.
 */

var path = require('path');
var fs = require('fs');
var watch = require('node-watch');

var config = require('../config/config.defaults.js');
var util = require('./server-util.js');
var rewriter = require('../rewriter/jsrewriter.js');

var cachePath,
    fileCache = {
        files: {},
        whiteList: [],
        blackList: []
    };


function run() {
    if (!fs.existsSync(config.fileServerBaseDir)) {
        console.error('ERROR: Path does not exist: ' + config.fileServerBaseDir);
        process.exit(1);
    }

    processFiles();

    var serverBaseDir = path.normalize(config.fileServerBaseDir);
    // Watch for file changes
    watch(serverBaseDir, function(fileName) {
        if (fileName) {
            for (var i = 0; i < config.ignoreFiles.length; i++) {
                if (fileName.indexOf(config.ignoreFiles[i]) >= 0) {
                    return;
                }
            }
            var file = fileName.substr(serverBaseDir.length);
            file = file.replace(/\\/g, '/');
            processFile(file, true);
        }
    });
}

function processFiles() {
    var files = util.getAllFiles(),
        destBaseDir = path.normalize(config.outputDir),
        content;

    if (!fs.existsSync(destBaseDir)) {
        fs.mkdirSync(destBaseDir);
    }
    cachePath = path.join(destBaseDir, 'cache.json');

    if (fs.existsSync(cachePath)) {
        fileCache = JSON.parse(fs.readFileSync(cachePath).toString());

        // Flush cache if white or black list has changed
        if (!fileCache.blackList.equals(config.blackList) || !fileCache.whiteList.equals(config.whiteList)) {
            fileCache = {
                files: {},
                whiteList: config.whiteList,
                blackList: config.blackList
            }
        }
    }

    for (var i = 0; i < files.length; i++) {
        processFile(files[i], false);
    }

    // Copy Aardwolf script
    content =  fs.readFileSync(path.join(__dirname, '../js/aardwolf.js'));

    content = content
        .toString()
        .replace(/__SERVER_HOST__/g, config.serverHost)
        .replace(/__SERVER_PORT__/g, config.serverPort)
        .replace(/__FILE_SERVER_PORT__/g, config.fileServerPort);

    fs.writeFileSync(path.join(destBaseDir, 'aardwolf.js'), content);

    fs.writeFileSync(cachePath, JSON.stringify(fileCache));
}

function processFile(fileName, writeCache) {

    var serverBaseDir = path.normalize(config.fileServerBaseDir),
        destBaseDir = path.normalize(config.outputDir),
        origFilePath = path.join(serverBaseDir, fileName),
        destFilePath = path.join(destBaseDir, fileName),
        fileStat;

    if (!fs.existsSync(origFilePath)) {
        return;
    }

    fileStat = fs.statSync(origFilePath);

    if (fileStat.isDirectory()) {
        try {
            fs.mkdirSync(destFilePath);
        } catch(e) {
            // The folder already exists
        }
        return;
    }

    log('Processing ' + fileName + '... ');

    if (fileCache.files[fileName] && (fileStat.mtime.getTime() == fileCache.files[fileName].time) &&
        fs.existsSync(destFilePath)) {
        log('Skipping\n');
        return;
    }

    var breakpoints = [];

    var mustDebug = true;
    for (var i = 0; i < config.blackList.length; i++) {
        if (fileName.indexOf(config.blackList[i]) >= 0) {
            mustDebug = false;
            break;
        }
    }

    if (mustDebug && config.whiteList.length > 0) {
        mustDebug = false;
        for (i = 0; i < config.whiteList.length; i++) {
            if (fileName.indexOf(config.whiteList[i]) >= 0) {
                mustDebug = true;
                break;
            }
        }
    }

    if ((mustDebug && fileName.substr(-3) === '.js') || fileName === config.indexFile) {

        var content = fs.readFileSync(origFilePath).toString();
        if (fileName === config.indexFile) {
            // Inject aardwolf script in index
            var where = content.indexOf(config.whereToInsertAardwolf) + config.whereToInsertAardwolf.length;

            content = [content.slice(0, where), '\n', config.aardwolfScript, '\n', content.slice(where)].join('');
        } else {
            // Instrument JS code
            log('Debugging ');
            var processedFile = rewriter.addDebugStatements(fileName, content);
            content = processedFile.file;
            breakpoints = processedFile.breakpoints;
        }
        fs.writeFileSync(destFilePath, content);
    } else {
        util.copyFileSync(origFilePath, destFilePath);
    }

    fileCache.files[fileName] = {
        time: fileStat.mtime.getTime(),
        breakpoints: breakpoints
    };
    if (writeCache) {
        fs.writeFileSync(cachePath, JSON.stringify(fileCache));
    }

    log('OK\n');
}



function log(m) {
    if (config.verbose) {
        process.stdout.write(m);
    }
}

module.exports.run = run;


================================================
FILE: server/server-util.js
================================================
'use strict';

var fs = require('fs');
var path = require('path');
var config = require('../config/config.defaults.js');

function serveStaticFile(res, filename) {
    var ext = filename.split('.').reverse()[0];
    var ct = ext == 'js'   ? 'application/javascript' :
             ext == 'css'  ? 'text/css' :
             ext == 'html' ? 'text/html' :
             ext == 'png'  ? 'image/png' :
             ext == 'jpg'  ? 'image/jpeg' :
             ext == 'jpeg' ? 'image/jpeg' :
             'text/plain';

    var fdata = fs.readFileSync(filename);

    if (['png', 'jpg', 'jpeg'].indexOf(ext) == -1) {
        fdata = fdata
            .toString()
            .replace(/__SERVER_HOST__/g, config.serverHost)
            .replace(/__SERVER_PORT__/g, config.serverPort)
            .replace(/__FILE_SERVER_PORT__/g, config.fileServerPort);
    }

    res.writeHead(200, { 'Content-Type': ct });
    res.end(fdata);
}


function getFilesList() {
	return getAllFiles(['.js', '.coffee']);
}

function getAllFiles(extensions) {
	var files = [];
    var baseDir = path.normalize(config.fileServerBaseDir);

    function walk(dir) {
        var fileList = fs.readdirSync(dir);
        fileList.forEach(function(f) {
            var fullPath = path.join(dir, f);
            var stat = fs.statSync(fullPath);
			var listForDebug = !!extensions && extensions.length > 0;
			var include = false;
			var extension;
			var i;

			if (!validFile(fullPath)) {
				return;
			}

			if (listForDebug) {
				for (i = 0; i < config.blackList.length; i++) {
					if (fullPath.indexOf(config.blackList[i]) >= 0) {
						return;
					}
				}
				if (stat.isFile() && config.whiteList.length > 0) {
					var valid = false;
					for (i = 0; i < config.whiteList.length; i++) {

						if (fullPath.indexOf(config.whiteList[i]) >= 0) {
							valid = true;
							break;
						}
					}
					if (!valid) {
						return;
					}
				}
			}

            if (stat.isFile()) {
				if (listForDebug) {
					for (i = 0; i < extensions.length; i++) {
						extension = extensions[i];
						if (fullPath.substr(-(extension.length)) == extension) {
							include = true;
							break;
						}
					}
				} else {
					include = true;
				}
                if (include) {
                    files.push(fullPath.substr(baseDir.length));
                }
            }
            else {
				if (!listForDebug) {
					files.push(fullPath.substr(baseDir.length));
				}
                walk(fullPath);
            }
        });
    }

    walk(baseDir);

    /* Unixify paths */
    files = files.map(function(f) { return f.replace(/\\/g, '/'); });

    return files;
}

function validFile(path) {
	for (var i = 0; i < config.ignoreFiles.length; i++) {
		if (path.indexOf(config.ignoreFiles[i]) >= 0) {
			return false;
		}
	}
	return true;
}

function copyFileSync (srcFile, destFile) {
	var BUF_LENGTH = 64*1024;
	var buff = new Buffer(BUF_LENGTH);
	var fdr = fs.openSync(srcFile, 'r');
	var fdw = fs.openSync(destFile, 'w');
	var bytesRead = 1;
	var pos = 0
	while (bytesRead > 0) {
		bytesRead = fs.readSync(fdr, buff, 0, BUF_LENGTH, pos);
		fs.writeSync(fdw, buff, 0, bytesRead);
		pos += bytesRead;
	}
	fs.closeSync(fdr);
	fs.closeSync(fdw);
}

module.exports.serveStaticFile = serveStaticFile;
module.exports.getFilesList = getFilesList;
module.exports.getAllFiles = getAllFiles;
module.exports.copyFileSync = copyFileSync;



================================================
FILE: server/server.js
================================================
'use strict';

/*
 * Server for communication between the UI and the mobile library.
 * Also serves UI-related files.
 */

var http = require('http');
var path = require('path');
var fs = require('fs');

var config = require('../config/config.defaults.js');
var util = require('./server-util.js');

function run() {
    /* Server for web service ports and debugger UI */
    http.createServer(AardwolfServer).listen(config.serverPort, null, function() {
        console.log('Server listening for requests on port ' + config.serverPort + '.');
    });
}

var mobileDispatcher = new Dispatcher();
var desktopDispatcher = new Dispatcher();

function AardwolfServer(req, res) {
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Origin, Content-Type');
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
    res.setHeader('Access-Control-Allow-Credentials', 'true');

    var body = '';

    if (req.method == 'OPTIONS') {
        res.end();
        return;
    }
    else if (req.method == 'POST') {
        req.on('data', function (chunk) { body += chunk; });
        req.on('end', function () { processPostedData(JSON.parse(body)); });
    }
    else {
        processPostedData();
    }

    function processPostedData(data) {
        switch (req.url) {
            case '/mobile/init':
                mobileDispatcher.end();
                mobileDispatcher = new Dispatcher();
                mobileDispatcher.setClient(res);
                desktopDispatcher.clearMessages();
                desktopDispatcher.addMessage(data);
                break;

            case '/mobile/console':
                desktopDispatcher.addMessage(data);
                ok200();
                break;

            case '/mobile/breakpoint':
                desktopDispatcher.addMessage(data);
                mobileDispatcher.setClient(res);
                break;

            case '/mobile/incoming':
                mobileDispatcher.setClient(res);
                break;

            case '/desktop/outgoing':
                mobileDispatcher.addMessage(data);
                ok200();
                break;

            case '/desktop/incoming':
                desktopDispatcher.setClient(res);
                break;

            case '/files/list':
                ok200({ files: util.getFilesList() });
                break;

            case '/':
            case '/ui':
            case '/ui/':
                res.writeHead(302, {'Location': '/ui/index.html'});
                res.end();
                break;

            default:
                /* check if we need to serve a UI file */
                if (req.url.indexOf('/ui/') === 0) {
                    var requestedFile = req.url.substr(4);
                    var uiFilesDir = path.join(__dirname, '../ui/');
                    var fullRequestedFilePath = path.join(uiFilesDir, requestedFile);

                    /* File must exist and must be located inside the uiFilesDir */
                    if (fs.existsSync(fullRequestedFilePath) && fullRequestedFilePath.indexOf(uiFilesDir) === 0) {
                        util.serveStaticFile(res, fullRequestedFilePath);
                        break;
                    }
                }

                /* check if we need to serve a UI file */
                if (req.url.indexOf('/files/data/') === 0) {
                    var requestedFile = req.url.substr(12);
                    var filesDir = path.normalize(config.fileServerBaseDir);
                    var fullRequestedFilePath = path.join(filesDir, requestedFile);

                    /* File must exist and must be located inside the filesDir */
                    if (fs.existsSync(fullRequestedFilePath) && fullRequestedFilePath.indexOf(filesDir) === 0) {
                        ok200({
                            data: fs.readFileSync(fullRequestedFilePath).toString(),
                            breakpoints: require('../rewriter/multirewriter.js').getRewrittenContent(requestedFile).breakpoints || []
                        });
                        break;
                    }
                }

                /* fallback... */
                res.writeHead(404, {'Content-Type': 'text/plain'});
                res.end('NOT FOUND');
        }
    }

    function ok200(data) {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(data || {}));
    }
}


function Dispatcher() {
    var queue = [];
    var client;

    this.setClient = function(c) {
        this.end();
        client = c;
        process();
    };

    this.addMessage = function(m) {
        queue.push(m);
        process();
    };

    this.end = function() {
        if (client) {
            client.end();
        }
    };

    this.clearMessages = function() {
        queue = [];
    };

    function process() {
        if (client && queue.length > 0) {
            client.writeHead(200, { 'Content-Type': 'application/json' });
            var msg = queue.shift();
            client.end(JSON.stringify(msg));
            client = null;
        }
    }
}

module.exports.run = run;


================================================
FILE: ui/css/buttons.css
================================================
/*
 * Just some other awesome CSS3 buttons
 * http://www.red-team-design.com/just-another-awesome-css3-buttons
 */

.button,
.button.silver
{
    display: inline-block;
    white-space: nowrap;
    background-color: #ccc;
    background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
    background-image: -webkit-linear-gradient(top, #eee, #ccc);
    background-image: -moz-linear-gradient(top, #eee, #ccc);
    background-image: -ms-linear-gradient(top, #eee, #ccc);
    background-image: -o-linear-gradient(top, #eee, #ccc);
    background-image: linear-gradient(top, #eee, #ccc);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#cccccc');
    border: 1px solid #777;
    padding: 0 1.5em;
    margin: 0.5em 0.5em 0.5em 0;
    font: bold 1em/1.9em Arial, Helvetica;
    text-decoration: none;
    color: #333;
    text-shadow: 0 1px 0 rgba(255,255,255,.8);
    -moz-border-radius: .3em;
    -webkit-border-radius: .3em;
    border-radius: .3em;
    -moz-box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);
    -webkit-box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);
    box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);
}

.button:hover,
.button.silver:hover
{
    background-color: #ddd;
    background-image: -webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#ddd));
    background-image: -webkit-linear-gradient(top, #fafafa, #ddd);
    background-image: -moz-linear-gradient(top, #fafafa, #ddd);
    background-image: -ms-linear-gradient(top, #fafafa, #ddd);
    background-image: -o-linear-gradient(top, #fafafa, #ddd);
    background-image: linear-gradient(top, #fafafa, #ddd);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa', EndColorStr='#dddddd');
}

.button:active
{
    -moz-box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;
    -webkit-box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;
    box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;
    position: relative;
    top: 1px;
}


.button.small {
    font-size:0.75em;
}



.button.green
{
    background-color: #DDF906;
    background-image: -webkit-linear-gradient(top, #e8ff38, #92be13);
    background-image: -moz-linear-gradient(top,    #e8ff38, #92be13);
    background-image: -ms-linear-gradient(top,     #e8ff38, #92be13);
    background-image: -o-linear-gradient(top,      #e8ff38, #92be13);
    background-image: linear-gradient(top,         #e8ff38, #92be13);
    border: 1px solid #777;
}


.button.green:hover
{
    background-color: #DDF906;
    background-image: -webkit-linear-gradient(top, #e8ff38, #a8db16);
    background-image: -moz-linear-gradient(top,    #e8ff38, #a8db16);
    background-image: -ms-linear-gradient(top,     #e8ff38, #a8db16);
    background-image: -o-linear-gradient(top,      #e8ff38, #a8db16);
    background-image: linear-gradient(top,         #e8ff38, #a8db16);
    border: 1px solid #777;
}





.button.blue
{
    background-color: #DDF906;
    background-image: -webkit-linear-gradient(top, #92e3fe, #4d9cee);
    background-image: -moz-linear-gradient(top,    #92e3fe, #4d9cee);
    background-image: -ms-linear-gradient(top,     #92e3fe, #4d9cee);
    background-image: -o-linear-gradient(top,      #92e3fe, #4d9cee);
    background-image: linear-gradient(top,         #92e3fe, #4d9cee);
    border: 1px solid #777;
}


.button.blue:hover
{
    background-color: #DDF906;
    background-image: -webkit-linear-gradient(top, #92e3fe, #81d1fe);
    background-image: -moz-linear-gradient(top,    #92e3fe, #81d1fe);
    background-image: -ms-linear-gradient(top,     #92e3fe, #81d1fe);
    background-image: -o-linear-gradient(top,      #92e3fe, #81d1fe);
    background-image: linear-gradient(top,         #92e3fe, #81d1fe);
    border: 1px solid #777;
}


/*
.button:focus
{
    outline: 0;
    background: #fafafa;
}
*/

.button:before
{
    background: #ccc;
    background: rgba(0,0,0,.1);
    float: left;
    width: 1em;
    text-align: center;
    font-size: 1.5em;
    margin: 0 1em 0 -1em;
    padding: 0 .2em;
    -moz-box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);
    -webkit-box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);
    box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);
    -moz-border-radius: .20em 0 0 .20em;
    -webkit-border-radius: .20em 0 0 .20em;
    border-radius: .20em 0 0 .20em;
}

/* Buttons and inputs */

button.button, input.button
{
    cursor: pointer;
    overflow: visible; /* removes extra side spacing in IE */
}

/* removes extra inner spacing in Firefox */
button::-moz-focus-inner
{
  border: 0;
  padding: 0;
}

/* If line-height can't be modified, then fix Firefox spacing with padding */
 input::-moz-focus-inner
{
  padding: .4em;
}

/* The disabled styles */
.button[disabled], .button[disabled]:hover, .button.disabled, .button.disabled:hover
{
    background: #eee;
    color: #aaa;
    border-color: #aaa;
    cursor: default;
    text-shadow: none;
    position: static;
    -moz-box-shadow: none;
    -webkit-box-shadow: none;
    box-shadow: none;
}

/* Hexadecimal entities for the icons */

.add:before
{
    content: "\271A";
}

.edit:before
{
    content: "\270E";
}

.delete:before
{
    content: "\2718";
}

.save:before
{
    content: "\2714";
}

.email:before
{
    content: "\2709";
}

.like:before
{
    content: "\2764";
}

.next:before
{
    content: "\279C";
}

.step-over:before
{
    content: "\21B7";
}

.step-in:before
{
    content: "\21B4";
}

.step-out:before
{
    content: "\21B1";
}

.star:before
{
    content: "\2605";
}

.spark:before
{
    content: "\2737";
}

.play:before
{
    content: "\25B6";
}






================================================
FILE: ui/css/ui.css
================================================

body, html {
    height: 100%;
    font-size: 14px;
}

body {
    font-family: Arial, Helvetica, Tahoma, Verdana, sans-serif;
    margin: 0;
    padding: 0px;
}

div, textarea {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

h2 {
    margin: 0;
    margin-bottom: 5px;
    padding: 0;
    clear: both;
    font-weight: bold;
    font-size: 16px;

    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    user-select: none;
    cursor: default;
    color: #333;
}

h2:not(:first-of-type) {
    margin-top: 10px;
}

pre {
    margin: 0;
}

textarea {
    font-family: "Courier New";
    font-size: 14px;
}

#top-toolbar {
    position: absolute;
    height: 60px;
    width: 100%;
    z-index: 500;

    padding: 8px 0 5px 10px;
    border-bottom: 1px solid #aaa;

    background-image: -webkit-linear-gradient(top, #F0F0F0, #ccc);
    background-image: -moz-linear-gradient(top,    #F0F0F0, #ccc);
    background-image: -ms-linear-gradient(top,     #F0F0F0, #ccc);
    background-image: -o-linear-gradient(top,      #F0F0F0, #ccc);
    background-image: linear-gradient(top,         #F0F0F0, #ccc);
}

#options {
    position: absolute;
    top: 10px;
    right: 20px;
}

#center-panels {
    position: relative;
    display: block;
    width: 100%;
    height: 100%;

    padding-top: 60px;
    padding-right: 430px;
    padding-bottom: 250px;

    margin: 0;
    border: 0;

    overflow: hidden;
    z-index: 400;
}

#code-container {
    background: url(../img/code-bg.png);
    background-position: -8px 0px;
    background-repeat: repeat-y;
    height: 100%;
    overflow: auto;
}

#code {
    white-space: pre;
    font-family: "Courier New", monospace;
    margin: 5px 0 50px 0;
}

#code .comment {
    color: green;
}

#code .keyword {
    color: blue;
}

#code .literal {
    color: purple;
}

#code .string {
    color: purple;
}

#code .number {
    color: brown;
}

#code .linenum {
    color: black;
    cursor: pointer;
    font-weight: bold;
}
#code .linenum.no-breakpoint {
    color: grey;
    cursor: default;
    font-weight: normal;
}

#code .linenum.breakpoint {
    font-weight: bold;
    color: white;
    background-color: red;
}

#sidebar {
    display: block;
    width: 430px;
    padding: 5px;

    position: absolute;
    right: 0px;
    top: 60px;
    bottom: 250px;

    overflow: auto;
    border-left: 2px solid #bbb;
    background: #f7f7f7;
}

#output {
    border-top: 2px solid #bbb;
    position: absolute;
    left: 0;
    bottom: 0;
    height: 250px;
    width: 100%;

    background: white;
    font-family: "Courier New";

    z-index: 500;
    overflow: hidden;
}

#output-inner {
    overflow:auto;
    height: 88%;
}
#output-inner div {
    border-bottom: 1px solid #bbb;
    padding: 3px;
}
#output-bar {
    width: 100%;
    border-top: 2px solid #aaaaaa;
    position:absolute;
    bottom: 0px;
    background-color: white;
}

#output-bar.big-bar {
    top: 50px;
}

.big-bar textarea {
    min-height: 180px;
}

#eval {
    position: absolute;
    border: none;
    right: 0px;
    left: 50px;
}
#output-bar span {
    color: #0040D0;
    border: none;
    font-weight: bolder;
    line-height: 29px;
    padding-right: 5px;
}
#showBig {
    padding-left: 10px;
    padding-right: 10px;
    font-size: 16px;
    cursor: pointer;
}

#btn-clear {
    position: absolute;
    right: 15px;
}
#btn-eval {
    position: absolute;
    right: 15px;
    top: 55px;
    z-index: 30;
}

#breakpoints {
    list-style-type: none;
    padding-left: 10px;
}
#breakpoints li {
    border: 1px solid grey;
    padding: 5px 10px;
    border-radius: 6px;
    -moz-border-radius: 6px;
    -webkit-border-radius: 6px;
    margin-bottom: 10px;
    overflow: auto;
}
#breakpoints li span {
    font-style: normal;
    font-weight: normal;
    font-size: 12px;
    padding-left: 20px;
}

#breakpoints li span.breakpoint {
    font-size: 14px;
    font-style: italic;
    font-weight: bold;
    cursor: pointer;
    padding-left: 0px;
}
#breakpoints li span.breakpoint:hover {
    text-decoration: underline;
}

.unselectable {
    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;

    /*
      Introduced in IE 10.
      See http://ie.microsoft.com/testdrive/HTML5/msUserSelect/
    */
    -ms-user-select: none;
    user-select: none;
}








================================================
FILE: ui/index.html
================================================
<!DOCTYPE html>
<html>
    <head>
        <title>Aardwolf UI</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js"></script>
        <script type="text/javascript" src="js/jstokenizer.js"></script>
        <script type="text/javascript" src="js/coffeetokenizer.js"></script>
        <script type="text/javascript" src="js/ui.js"></script>

        <link href="css/ui.css" rel="stylesheet" type="text/css" />
        <link href="css/buttons.css" rel="stylesheet" type="text/css" />
		<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />

    </head>

    <body>
        <div id="top-toolbar">
            <button id="btn-continue" class="button play green" disabled>continue</button>
            <span id="controls-coffeescript">
                <button id="btn-step" class="button next green" disabled>step</button>
            </span>
            <span id="controls-javascript">
                Step:
                <button id="btn-step-over" class="button step-over green" disabled>over</button>
                <button id="btn-step-in" class="button step-in green" disabled>in</button>
                <button id="btn-step-out" class="button step-out green" disabled>out</button>
            </span>

            File:

            <select id="file-switcher">
            </select>

            <div id="options">
                <input type="checkbox" id="clearConsoleConnect" checked>Clear console on restart <br />
                <input type="checkbox" id="redirectConsole">Redirect remote console here
            </div>

        </div>


        <div id="center-panels">
            <div id="code-container">
                <div id="code">&nbsp;</div>
            </div>

            <div id="sidebar" class="">
                <h2>Breakpoints</h2>
                <button id="btn-update-breakpoints" class="button small">update breakpoints</button>
                <button id="btn-clear-breakpoints" class="button small">clear breakpoints</button>
                <button id="btn-breakon-next" class="button small">break on next</button>
                <ul id="breakpoints"></ul>
            </div>
        </div>

        <div id="output">
            <button id="btn-eval" class="button small">Execute</button>
            <button id="btn-clear" class="button small">Clear console</button>
            <div id="output-inner"></div>
            <div id="output-bar"><span id="showBig">&#8679;</span> <span id="prompt">&gt;</span> <textarea id="eval"></textarea></div>
        </div>


    </body>
</html>



================================================
FILE: ui/js/coffeetokenizer.js
================================================
'use strict';

var keywordListCoffeeScript = [
    'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 
    'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf', 'new', 'delete', 'typeof',
    'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch',
    'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'then', 'unless', 'until',
    'loop', 'of', 'by', 'when', 'and', 'or', 'is', 'isnt', 'not', 'this', 'super'
]; 

var literalListCoffeScript = [
    'true', 'false', 'null', 'undefined', 'yes', 'no', 'on', 'off'
];

function tokenizeCoffeeScript(str, onToken) {
    var len = str.length;
    var pos = 0;
    var validRegexPos = false;
    
    while (pos < len) {
        var c = str[pos];
        
        if (c === '"' || c === "'") {
            extractString(c);
        }
        else if (c === '#' && str[pos+1] === '#' && str[pos+2] === '#') {
            extractMultiLineComment();
        }
        else if (c === '#') {
            extractSingleLineComment();
        }
        else if (c === '/' && validRegexPos) {
            extractRegexLiteral();
        }
        else if (c === ' ' || c === '\t') {
            extractWhitespace();
        }
        else if ('0123456789'.indexOf(c) > -1) {
            extractNumber();
        }
        else if (c.match(/^[a-zA-Z_$]$/) !== null) {
            extractWord();
        }
        else {
            extractChar();
        }
    }
    
    function onTokenInternal(token, type) {
        /* A slash following an assigment operator, a semicolon or an 
           opening paren can be a regex literal delimiter. */
        if (type === 'char' && ':=;({'.indexOf(token) > -1) {
            validRegexPos = true;
        } else {
            validRegexPos = false;
        }
        
        onToken(token, type);
    }
    
    function extractSingleLineComment() {
        var endPos = str.indexOf("\n", pos);
        if (endPos === -1) {
            endPos = len - 1;
        }
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }
    
    function extractMultiLineComment() {
        var endPos = pos;
        while (!(str[++endPos] === '#' && str[endPos+1] === '#' && str[endPos+2] === '#'));
        endPos += 3;
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }
    
    function extractRegexLiteral() {
        var endPos = pos;
        /* regex literal body /.../ */
        while (str[++endPos] != '/') {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        /* flags following the body */
        while ('gimy'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'regex');
        pos = endPos;
    }
    
    function extractString(quoteChar) {
        var endPos = pos;
        while (str[++endPos] != quoteChar) {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        ++endPos;
        onTokenInternal(str.substring(pos, endPos), 'string');
        pos = endPos;
    }
    
    function extractNumber() {
        var endPos = pos;
        while ('0123456789.eE'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'number');
        pos = endPos;
    }
    
    function extractWord() {
        var endPos = pos;
        while (str[++endPos].match(/^[a-zA-Z_$0-9]$/) !== null);
        onTokenInternal(str.substring(pos, endPos), 'word');
        pos = endPos;
    }
    
    function extractWhitespace() {
        var endPos = pos;
        while (' \t'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'whitespace');
        pos = endPos;
    }
    
    function extractChar() {
        var c = str.substr(pos, 1);
        onTokenInternal(c, c === '\n' ? 'newline' : 'char');
        ++pos;
    }
}


================================================
FILE: ui/js/jstokenizer.js
================================================
'use strict';

var keywordListJavaScript = [
    'var', 'function', 'if', 'else', 'while', 'for', 'do', 'in', 'break', 'continue',
    'switch', 'return', 'debugger', 'try', 'catch', 'throw', 'true', 'false', 'this'
];


var literalListJavaScript = [
    'true', 'false', 'null', 'undefined'
];

/* A simple JS tokenizer. We're really only interested in a couple of keywords, parentheses,
   brackets and semicolons, so it doesn't need to be complete as long as it correctly handles
   multi-word tokens such as strings and comments.
*/
function tokenizeJavaScript(str, onToken) {
    var len = str.length;
    var pos = 0;
    var validRegexPos = false;

    while (pos < len) {
        var c = str[pos];

        if (c === '"' || c === "'") {
            extractString(c);
        }
        else if (c === '/' && str[pos+1] === '/') {
            extractSingleLineComment();
        }
        else if (c === '/' && str[pos+1] === '*') {
            extractMultiLineComment();
        }
        else if (c === '/' && validRegexPos) {
            extractRegexLiteral();
        }
        else if (c === ' ' || c === '\t') {
            extractWhitespace();
        }
        else if ('0123456789'.indexOf(c) > -1) {
            extractNumber();
        }
        else if (c.match(/^[a-zA-Z_$]$/) !== null) {
            extractWord();
        }
        else {
            extractChar();
        }
    }

    function onTokenInternal(token, type) {
        /* A slash following an assigment operator, a semicolon or an
           opening paren can be a regex literal delimiter. */
        if (type === 'char' && ':=;({'.indexOf(token) > -1) {
            validRegexPos = true;
        } else {
            validRegexPos = false;
        }

        onToken(token, type);
    }

    function extractSingleLineComment() {
        var endPos = str.indexOf("\n", pos);
        if (endPos === -1) {
            endPos = len - 1;
        }
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }

    function extractMultiLineComment() {
        var endPos = pos;
        while (!(str[++endPos] === '*' && str[endPos+1] === '/'));
        endPos += 2;
        onTokenInternal(str.substring(pos, endPos), 'comment');
        pos = endPos;
    }

    function extractRegexLiteral() {
        var endPos = pos;
        /* regex literal body /.../ */
        while (str[++endPos] != '/') {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        /* flags following the body */
        while ('gimy'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'regex');
        pos = endPos;
    }

    function extractString(quoteChar) {
        var endPos = pos;
        while (str[++endPos] != quoteChar) {
            if (str[endPos] == '\\') {
                ++endPos;
            }
        }
        ++endPos;
        onTokenInternal(str.substring(pos, endPos), 'string');
        pos = endPos;
    }

    function extractNumber() {
        var endPos = pos;
        while ('0123456789.eE'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'number');
        pos = endPos;
    }

    function extractWord() {
        var endPos = pos;
        while (str[++endPos].match(/^[a-zA-Z_$0-9]$/) !== null);
        onTokenInternal(str.substring(pos, endPos), 'word');
        pos = endPos;
    }

    function extractWhitespace() {
        var endPos = pos;
        while (' \t'.indexOf(str[++endPos]) !== -1);
        onTokenInternal(str.substring(pos, endPos), 'whitespace');
        pos = endPos;
    }

    function extractChar() {
        var c = str.substr(pos, 1);
        onTokenInternal(c, c === '\n' ? 'newline' : 'char');
        ++pos;
    }
}


================================================
FILE: ui/js/ui.js
================================================
'use strict';

var files = {};
var breakpoints = [];
var $codeContainer;
var $code;

var $continueBtn;
var $stepBtn;
var $stepOverBtn;
var $stepInBtn;
var $stepOutBtn;

var clearOnConnect = true;
var lineNum = 0;
var evalBig = false;

var history = [];
var currentHistoryPos = 0;

$(function() {
	$('#sidebar').resizable({
		handles: 'w',
		start: function() {
			$(document.body).addClass('unselectable');
		},
		stop: function(e, ui) {
			var sidebar = $('#sidebar');
			sidebar.css('width', 'auto');
			sidebar.css('height', 'auto');
			$(document.body).removeClass('unselectable');

			$('#code-container').css('width', sidebar.position().left);
        }
	});

	$('#output').resizable({
		handles: 'n',
		start: function() {
			$(document.body).addClass('unselectable');
		},
		stop: function(e, ui) {
			$(document.body).removeClass('unselectable');
			$('#sidebar').css('bottom', $('#output').css('height'));

			var code = $('#code-container'),
				yCode,
				yOutput,
				codeHeight;

			yCode = code.position().top;
			yOutput = $('#output').position().top;
			codeHeight = yOutput - yCode;
			code.css('height', codeHeight);
		}
	});
    $('#eval').val("");

	$('#eval').keydown(function(e) {
		e.stopPropagation();

		switch (e.keyCode) {
			case 13: // Enter
				if (!evalBig) {
					e.preventDefault();
					if ($('#eval').val().match(/\S/)) {
						evalCodeRemotely();
						$('#eval').val("");
					}
				}
				break;
			case 38: // Up
				if (!evalBig) {
					e.preventDefault();
					if (currentHistoryPos < history.length) {
						$('#eval').val(history[currentHistoryPos++]);

						if (currentHistoryPos === history.length) {
							currentHistoryPos--;
						}
					}
				}
				break;
			case 40: // Down
				if (!evalBig) {
					e.preventDefault();
					if (currentHistoryPos > 0) {
						$('#eval').val(history[--currentHistoryPos]);
					} else {
						if ($('#eval').val() === history[currentHistoryPos]) {
							$('#eval').val('');
						}
					}
				}
		}
	});

	$('#btn-eval').hide();

	$('#btn-eval').click(function() {
		evalCodeRemotely();
		hideBigEval();
	});

	$('#showBig').click(function() {
		if (evalBig) {
			hideBigEval();
		} else {
			showBigEval();
		}
	});

    $('#btn-update-breakpoints').click(updateBreakpoints);
	$('#btn-clear-breakpoints').click(function() {
		breakpoints = [];
		updateBreakpoints();
	})
    $('#btn-breakon-next').click(setBreakOnNext);
    $('#btn-continue').click(breakpointContinue);
    $('#btn-step').click(breakpointStep);
    $('#btn-step-over').click(breakpointStepOver);
    $('#btn-step-in').click(breakpointStepIn);
    $('#btn-step-out').click(breakpointStepOut);
    $('#file-switcher').change(switcherSwitchFile);

	$('#btn-clear').click(clearConsole);

	$('#clearConsoleConnect').change(function(e) {
		clearOnConnect = $(this).attr('checked') !== undefined;
	});

	$('#redirectConsole').change(toggleRedirectConsole);

    $continueBtn = $('#btn-continue');
    $stepBtn = $('#btn-step');
    $stepOverBtn = $('#btn-step-over');
    $stepInBtn = $('#btn-step-in');
    $stepOutBtn = $('#btn-step-out');
    $codeContainer = $('#code-container');
    $code = $('#code');

	$(window).keydown(function(e) {
		switch(e.which) {
			case 80: // P - Continue
				if ($continueBtn.attr('disabled') == null) {
					breakpointContinue();
				}
				break;
			case 79: // O- Over
				if ($stepOverBtn.attr('disabled') == null) {
					breakpointStepOver();
				}
				break;
			case 78: // I- In
				if ($stepInBtn.attr('disabled') == null) {
					breakpointStepIn();
				}
				break;
			case 77: // U- Out
				if ($stepOutBtn.attr('disabled') == null) {
					breakpointStepOut();
				}
				break;
		}
	});

    loadSourceFiles();
    listenToServer();

    showFile({ file: $('#file-switcher').val() });

	if (window.localStorage) {
		var strBreakpoints = window.localStorage.getItem('breakpoints');
		if (strBreakpoints) {
			breakpoints = JSON.parse(strBreakpoints);
			updateBreakpoints();
		}
	}
});

function initDebugger() {
    loadSourceFiles();
    postToServer({ command: 'set-breakpoints', data: breakpoints});
}

function loadSourceFiles() {
	var prevSelected = $('#file-switcher').val();

    var fileList = getFromServer('/files/list');
    files = {};

    $('#file-switcher option').remove();
    addToFileSwitcher('', '<select file>');

	var isAvailable = false;
    fileList && fileList.files.forEach(function(f) {
		if (f === prevSelected) {
			isAvailable = true;
		}
        var fdata = getFromServer('/files/data/' + f);
        files[f] = {
			file: fdata.data,
			breakpoints: fdata.breakpoints
		};
        addToFileSwitcher(f, f);
    });

	if (isAvailable) {
		$('#file-switcher').val(prevSelected);
	}
}

function toggleRedirectConsole() {
	postToServer({command: 'update-redirect-console', data: $('#redirectConsole').attr('checked') !== undefined});
}

function updateBreakpoints() {
	var breakpoint,
		fileBreakpoints;
	for (var i = 0; i < breakpoints.length; i++) {
		breakpoint = breakpoints[i];

		if (!files[breakpoint[0]] || !files[breakpoint[0]].file) {
			breakpoints.splice(i, 1);
			i--;
			continue;
		}

		fileBreakpoints = files[breakpoint[0]].breakpoints;
		if (fileBreakpoints.indexOf(parseInt(breakpoint[1], 10)) < 0) {
			for (var j = 0; j < fileBreakpoints.length; j++) {
				if (fileBreakpoints[j] > parseInt(breakpoint[1], 10)) {
					break;
				}
			}
			if (j < fileBreakpoints.length) {
				breakpoint[1] = fileBreakpoints[j];
			} else {
				breakpoint[1] = fileBreakpoints[fileBreakpoints.length - 1];
			}
		}
	}

	if (window.localStorage) {
		window.localStorage.setItem('breakpoints', JSON.stringify(breakpoints));
	}

    postToServer({ command: 'set-breakpoints', data: breakpoints });
    paintBreakpoints($('#file-switcher').val());

	var ul = $('#breakpoints');
	ul.empty();
	breakpoints = breakpoints.sort(function(a, b) {
		if (a[0] > b[0]) {
			return 1;
		} else if (a[0] < b[0]) {
			return -1;
		} else {
			return (a[1] - b[1]);
		}
	});

	$(breakpoints).each(function(i, breakpoint) {
		ul.append(
			$('<li>')
				.append(
					$('<button>')
						.append('-')
						.click(function() {
							breakpoints.splice(i, 1);
							updateBreakpoints();
						})
				)
				.append(
					$('<span>')
						.attr('class', 'breakpoint')
						.append(breakpoint[0] + ':' + breakpoint[1])
						.click(function() {
							showFile({ file: breakpoint[0], goToLine: breakpoint[1]});
						})
				)
				.append('<br>')
				//.append('&nbsp;&nbsp;&nbsp;&nbsp;')
				.append(
					$('<span>').append(getLine(files[breakpoint[0]].file, breakpoint[1]))
				)
		);
	})
}

function setBreakOnNext() {
    postToServer({ command: 'break-on-next', data: breakpoints });
}

function evalCodeRemotely() {
	var data =  $('#eval').val();

	data = data.replace(/\bthis\b/, '__this');
	history.unshift(data);
	currentHistoryPos = 0;
    postToServer({ command: 'eval', data: data});
}

function breakpointContinue() {
    removeLineHightlight();
    disableContinueAndStep();
    postToServer({ command: 'breakpoint-continue' });
}

function breakpointStepCommand(command) {
    removeLineHightlight();
    disableContinueAndStep();
    postToServer({ command: command });
}

function breakpointStep(command) {
    breakpointStepCommand('breakpoint-step');
}

function breakpointStepOver() {
    breakpointStepCommand('breakpoint-step-over');
}

function breakpointStepIn() {
    breakpointStepCommand('breakpoint-step-in');
}

function breakpointStepOut() {
    breakpointStepCommand('breakpoint-step-out');
}

function addToFileSwitcher(filePath, fileLabel) {
    $('<option></option>').val(filePath).text(fileLabel).appendTo($('#file-switcher'));
}

function switcherSwitchFile() {
    showFile({ file: $('#file-switcher').val() });
}

function postToServer(payload) {
    var req = new XMLHttpRequest();
    req.open('POST', '/desktop/outgoing', false);
    req.setRequestHeader('Content-Type', 'application/json');
    req.send(JSON.stringify(payload));
}

function getFromServer(path) {
    var req = new XMLHttpRequest();
    req.open('GET', path, false);
    req.send();
    return safeJSONParse(req.responseText);
}

function listenToServer() {
    var req = new XMLHttpRequest();
    req.open('GET', '/desktop/incoming', true);
    req.onreadystatechange = function () {
        if (req.readyState == 4) {
            var data = safeJSONParse(req.responseText);
            if (data) {
                processOutput(data);
            }
            listenToServer();
        }
    };
    req.send(null);
}

function showBreakpoint(data) {
    showFile(data);
    $('#file-switcher').val(data.file);
}

function showFile(data) {
    var codeTokens = [];
    var keywordList;
    var literalList;
    var tokenize;

    if (fileExt(data.file) == 'coffee') {
        keywordList = keywordListCoffeeScript;
        literalList = literalListCoffeScript;
        tokenize = tokenizeCoffeeScript;

        $('#controls-coffeescript').show();
        $('#controls-javascript').hide();
    }
    else {
        keywordList = keywordListJavaScript;
        literalList = literalListJavaScript;
        tokenize = tokenizeJavaScript;

        $('#controls-coffeescript').hide();
        $('#controls-javascript').show();
    }
	if (!files[data.file]) {
		return;
	}
    tokenize(files[data.file].file || '', function(token, type) {
        var pre = '';
        var post = '';

        if (type === 'word' && keywordList.indexOf(token) > -1) {
            pre = '<span class="keyword">';
            post = '</span>';
        }
        else if (type === 'word' && literalList.indexOf(token) > -1) {
            pre = '<span class="literal">';
            post = '</span>';
        }
        else if (['string', 'comment', 'number'].indexOf(type) > -1) {
            pre = '<span class="'+type+'">';
            post = '</span>';
        }

        codeTokens.push(pre);
        codeTokens.push(token.replace(/</g, '&lt;'));
        codeTokens.push(post);
    });

    var codeLines = codeTokens
        .join('')
        .split('\n')
        .map(function(x, i) {
            var num = String(i+1);

			var extraClass = '';
			if (files[data.file].breakpoints.indexOf(i + 1) < 0) {
				extraClass = ' no-breakpoint';
			}
            var paddedNum = '<span class="linenum'+ extraClass + '" file="'+data.file+'" line="'+num+'">' +
                            '      '.substr(num.length) + num + ' ' +
                            '</span>';
            return paddedNum + ' ' + x;
        });

    $code.html(codeLines.join('\n'));
    $code.find('.linenum:not(.no-breakpoint)').click(toggleBreakpoint);
    paintBreakpoints(data.file);

    var numLines = codeLines.length;
    var textAreaHeight = $codeContainer.height();
    var textAreaContentHeight = $codeContainer[0].scrollHeight;
    var codeHeight = $code.height();
    var heightPerLine = codeHeight / numLines;

	var line = 0;
    if (data.line) {
		line = data.line;
        highlightLine(data.line, numLines);
        enableContinueAndStep();
    } else if (data.goToLine) {
		line = data.goToLine;
	}

    if (textAreaContentHeight > textAreaHeight) {
        var scrollAmountPerLine = (textAreaContentHeight - textAreaHeight) / numLines;
        var scrollTo = Math.round(line * scrollAmountPerLine);
        $codeContainer.scrollTop(scrollTo);
    }
}

function highlightLine(line, numLines) {
    var codeHeight = $code.height();
    var heightPerLine = codeHeight / numLines;

    $code.css({
        'background-image': 'url("img/breakpoint-arrow.png"), url("img/breakpoint-bg.png")',
        'background-repeat': 'no-repeat, no-repeat, repeat-y',
        'background-size': '9px 7px, 100% '+Math.round(heightPerLine)+'px',
        'background-position': '5px '+Math.round((line - 1) * heightPerLine + ((heightPerLine - 7) / 2))+'px, '+
                               '0px '+Math.round((line - 1) * heightPerLine)+'px'
    });
}

function removeLineHightlight() {
    $code.css({ 'background-image': '' });
}

function paintBreakpoints(file) {
    $code.find('.linenum').each(function(i, elem) {
        if (existsBreakpoint(file, i+1)) {
            $(elem).addClass('breakpoint');
        }
        else {
            $(elem).removeClass('breakpoint');
        }
    });
}

function existsBreakpoint(f, l) {
    return breakpoints.filter(function(b) { return b[0] == f && b[1] == l; }).length > 0;
}

function toggleBreakpoint() {
    if (breakpoints === null) {
        alert('Could not parse the list of breakpoints!');
        return;
    }

    var $marker = $(this);
    var file = $marker.attr('file');
    var line = $marker.attr('line');
    if (existsBreakpoint(file, line)) {
        $(this).removeClass('breakpoint');

        breakpoints = breakpoints.filter(function(b) { return !(b[0] == file && b[1] == line); });
    }
    else {
        $(this).addClass('breakpoint');

        breakpoints.push([file, line]);
    }

    updateBreakpoints();
}

function enableContinueAndStep() {
    $continueBtn.attr('disabled', null);
    $stepBtn.attr('disabled', null);
    $stepOverBtn.attr('disabled', null);
    $stepInBtn.attr('disabled', null);
    $stepOutBtn.attr('disabled', null);
}

function disableContinueAndStep() {
    $continueBtn.attr('disabled', true);
    $stepBtn.attr('disabled', true);
    $stepOverBtn.attr('disabled', true);
    $stepInBtn.attr('disabled', true);
    $stepOutBtn.attr('disabled', true);
}

function processOutput(data) {
    switch (data.command) {
        case 'mobile-connected':
			if (clearOnConnect) {
				clearConsole();
			}
			$('#redirectConsole').attr('checked', false);
            writeToConsole('Remote device connected.');
            initDebugger();
            break;
        case 'print-message':
            writeToConsole('<b>'+data.type + '</b>: ' + data.message);
            break;
        case 'print-eval-result':
			try {
				writeToConsole(
					'<br/>&nbsp;&nbsp;&nbsp; <b>INPUT:</b> ' + data.input.replace(/\b__this\b/, 'this') +
						'<br/>&nbsp;&nbsp;&nbsp; <b>RESULT:</b> ' +
						JSON.stringify(JSON.parse(data.result), null, '\t')
							.replace(/\n/g, '<br>&nbsp;&nbsp;&nbsp;')
							.replace(/\t/g, '&nbsp;&nbsp;'));
			} catch (e) {
           		writeToConsole('<b>INPUT:</b> ' + data.input.replace(/\b__this\b/, 'this') + ' <b>RESULT:</b> ' + data.result);
			}
            break;
        case 'report-exception':
            writeToConsole('<b>EXCEPTION</b>: ' + data.message + ' at ' + data.file + ', line ' + data.line);
            break;
        case 'report-breakpoint':
            showBreakpoint(data);
            break;
    }
}

function safeJSONParse(str) {
    try {
        return JSON.parse(str);
    } catch (ex) {
        return null;
    }
}

function clearConsole() {
	lineNum = 0;
	$('#output-inner').empty();
}

function writeToConsole(msg) {
    $('<div></div>').html((++lineNum) + ': ' + msg).appendTo($('#output-inner'))[0].scrollIntoView();
}


function fileExt(fileName) {
    return fileName.split('.').slice(-1)[0];
}

function showBigEval() {
	evalBig = true;
	$('#showBig').html('&#8681;');
	$('#output-bar').addClass('big-bar');
	$('#btn-eval').show();
}
function hideBigEval() {
	evalBig = false;
	$('#showBig').html('&#8679;');
	$('#output-bar').removeClass('big-bar');
	$('#eval').val('');
	$('#btn-eval').hide();
}

function getLine(string, lineNo) {
	var newString = string;
	for (var i = 0; i < lineNo - 1; i++) {
		newString = newString.slice(newString.indexOf('\n') + 1);
	}
	return newString.substring(0, newString.indexOf('\n'));
}
Download .txt
gitextract_3aaz7ybb/

├── .gitignore
├── LICENSE
├── README.md
├── app.js
├── config/
│   └── config.defaults.js
├── js/
│   └── aardwolf.js
├── package.json
├── rewriter/
│   ├── coffeerewriter.js
│   ├── jsrewriter.js
│   ├── jstokenizer.js
│   ├── multirewriter.js
│   └── templates/
│       ├── debug-template.coffee
│       ├── debug-template.js
│       ├── exception-template.coffee
│       └── exception-template.js
├── samples/
│   ├── calc-coffee.html
│   ├── calc.coffee
│   ├── calc.html
│   └── calc.js
├── server/
│   ├── debug-file-server.js
│   ├── rewriter-server.js
│   ├── server-util.js
│   └── server.js
└── ui/
    ├── css/
    │   ├── buttons.css
    │   └── ui.css
    ├── index.html
    └── js/
        ├── coffeetokenizer.js
        ├── jstokenizer.js
        └── ui.js
Download .txt
SYMBOL INDEX (69 symbols across 14 files)

FILE: app.js
  function scanAvailableIPs (line 68) | function scanAvailableIPs() {
  function startApp (line 82) | function startApp() {

FILE: js/aardwolf.js
  function listenToServer (line 21) | function listenToServer() {
  function dropCommandConnection (line 54) | function dropCommandConnection() {
  function sendToServer (line 65) | function sendToServer(path, payload) {
  function replaceConsole (line 85) | function replaceConsole() {
  function processCommand (line 110) | function processCommand(cmd) {
  function doEval (line 155) | function doEval(evalScopeFunc, cmd) {
  function sendEvalResult (line 164) | function sendEvalResult(result, input) {
  function getStack (line 181) | function getStack() {
  function safeJSONParse (line 194) | function safeJSONParse(str) {

FILE: rewriter/coffeerewriter.js
  function addDebugStatements (line 16) | function addDebugStatements(filePath, text) {

FILE: rewriter/jsrewriter.js
  function buildExceptionInterceptorStart (line 16) | function buildExceptionInterceptorStart(functionName, file, line) {
  function addDebugStatements (line 23) | function addDebugStatements(filePath, text) {

FILE: rewriter/jstokenizer.js
  function tokenize (line 7) | function tokenize(str, onToken) {

FILE: rewriter/multirewriter.js
  function isRewritable (line 8) | function isRewritable(filePath) {
  function getRewrittenContent (line 25) | function getRewrittenContent(filePath) {

FILE: samples/calc.js
  function calculate (line 21) | function calculate() {
  function reset (line 53) | function reset() {

FILE: server/debug-file-server.js
  function run (line 17) | function run() {
  function DebugFileServer (line 29) | function DebugFileServer(req, res) {

FILE: server/rewriter-server.js
  function run (line 23) | function run() {
  function processFiles (line 47) | function processFiles() {
  function processFile (line 88) | function processFile(fileName, writeCache) {
  function log (line 172) | function log(m) {

FILE: server/server-util.js
  function serveStaticFile (line 7) | function serveStaticFile(res, filename) {
  function getFilesList (line 32) | function getFilesList() {
  function getAllFiles (line 36) | function getAllFiles(extensions) {
  function validFile (line 108) | function validFile(path) {
  function copyFileSync (line 117) | function copyFileSync (srcFile, destFile) {

FILE: server/server.js
  function run (line 15) | function run() {
  function AardwolfServer (line 25) | function AardwolfServer(req, res) {
  function Dispatcher (line 133) | function Dispatcher() {

FILE: ui/js/coffeetokenizer.js
  function tokenizeCoffeeScript (line 15) | function tokenizeCoffeeScript(str, onToken) {

FILE: ui/js/jstokenizer.js
  function tokenizeJavaScript (line 17) | function tokenizeJavaScript(str, onToken) {

FILE: ui/js/ui.js
  function initDebugger (line 181) | function initDebugger() {
  function loadSourceFiles (line 186) | function loadSourceFiles() {
  function toggleRedirectConsole (line 213) | function toggleRedirectConsole() {
  function updateBreakpoints (line 217) | function updateBreakpoints() {
  function setBreakOnNext (line 291) | function setBreakOnNext() {
  function evalCodeRemotely (line 295) | function evalCodeRemotely() {
  function breakpointContinue (line 304) | function breakpointContinue() {
  function breakpointStepCommand (line 310) | function breakpointStepCommand(command) {
  function breakpointStep (line 316) | function breakpointStep(command) {
  function breakpointStepOver (line 320) | function breakpointStepOver() {
  function breakpointStepIn (line 324) | function breakpointStepIn() {
  function breakpointStepOut (line 328) | function breakpointStepOut() {
  function addToFileSwitcher (line 332) | function addToFileSwitcher(filePath, fileLabel) {
  function switcherSwitchFile (line 336) | function switcherSwitchFile() {
  function postToServer (line 340) | function postToServer(payload) {
  function getFromServer (line 347) | function getFromServer(path) {
  function listenToServer (line 354) | function listenToServer() {
  function showBreakpoint (line 369) | function showBreakpoint(data) {
  function showFile (line 374) | function showFile(data) {
  function highlightLine (line 463) | function highlightLine(line, numLines) {
  function removeLineHightlight (line 476) | function removeLineHightlight() {
  function paintBreakpoints (line 480) | function paintBreakpoints(file) {
  function existsBreakpoint (line 491) | function existsBreakpoint(f, l) {
  function toggleBreakpoint (line 495) | function toggleBreakpoint() {
  function enableContinueAndStep (line 518) | function enableContinueAndStep() {
  function disableContinueAndStep (line 526) | function disableContinueAndStep() {
  function processOutput (line 534) | function processOutput(data) {
  function safeJSONParse (line 568) | function safeJSONParse(str) {
  function clearConsole (line 576) | function clearConsole() {
  function writeToConsole (line 581) | function writeToConsole(msg) {
  function fileExt (line 586) | function fileExt(fileName) {
  function showBigEval (line 590) | function showBigEval() {
  function hideBigEval (line 596) | function hideBigEval() {
  function getLine (line 604) | function getLine(string, lineNo) {
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (98K chars).
[
  {
    "path": ".gitignore",
    "chars": 115,
    "preview": "/config/config.local.js\r\n/node_modules/\r\n/nbproject/\r\ncache.json\r\nsamples_output\r\n.idea\r\natlassian-ide-plugin.xml\r\n"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "Copyright (C) 2011 by Aleksander Kmetec\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 7232,
    "preview": "Aardwolf\n========\n\nAardwolf is a remote JavaScript debugger for Android / iOS / Windows Phone 7 / BlackBerry OS 6+ and i"
  },
  {
    "path": "app.js",
    "chars": 2577,
    "preview": "'use strict';\n\nvar argv = require('optimist').argv;\nvar fs = require('fs');\nvar config = require('./config/config.defaul"
  },
  {
    "path": "config/config.defaults.js",
    "chars": 1819,
    "preview": "'use strict';\n\n/*\n *\n * To change defaults set in this file create a file called config.local.js\n * in the same director"
  },
  {
    "path": "js/aardwolf.js",
    "chars": 8441,
    "preview": "/*\n * Aardwold mobile runtime library.\n *\n * Do not enable JS strict mode for this file as it\n * will disable some funct"
  },
  {
    "path": "package.json",
    "chars": 383,
    "preview": "{\n    \"name\": \"aardwolf-plus\",\n    \"description\": \"Aardwolf++ debugger\",\n    \"url\": \"http://lexandera.com/aardwolf\",\n   "
  },
  {
    "path": "rewriter/coffeerewriter.js",
    "chars": 2037,
    "preview": "'use strict';\n\nvar fs = require('fs');\nvar path = require('path');\n\nvar debugStatementTemplate = \n    fs.readFileSync(pa"
  },
  {
    "path": "rewriter/jsrewriter.js",
    "chars": 5730,
    "preview": "'use strict';\n\nvar fs = require('fs');\nvar path = require('path');\nvar jstok = require('./jstokenizer.js');\n\nvar debugSt"
  },
  {
    "path": "rewriter/jstokenizer.js",
    "chars": 3636,
    "preview": "'use strict';\n\n/* A simple JS tokenizer. We're really only interested in a couple of keywords, parentheses, \n   brackets"
  },
  {
    "path": "rewriter/multirewriter.js",
    "chars": 1408,
    "preview": "'use strict';\n\nvar path = require('path');\nvar fs = require('fs');\n\nvar config = require('../config/config.defaults.js')"
  },
  {
    "path": "rewriter/templates/debug-template.coffee",
    "chars": 100,
    "preview": "\nAardwolf.updatePosition(\"__FILE__\", __LINE__, __DEBUGGER__, (aardwolfEval) -> eval(aardwolfEval));\n"
  },
  {
    "path": "rewriter/templates/debug-template.js",
    "chars": 81,
    "preview": "\nAardwolf.updatePosition(\"__FILE__\", __LINE__, __DEBUGGER__, aardwolfEvalFunc);\n\n"
  },
  {
    "path": "rewriter/templates/exception-template.coffee",
    "chars": 178,
    "preview": "\ntry {\n  SPLIT\n} catch (aardwolfEx) {\n    if (!aardwolfEx.rethrown) {\n        Aardwolf.reportException(aardwolfEx);\n    "
  },
  {
    "path": "rewriter/templates/exception-template.js",
    "chars": 373,
    "preview": "\nvar __this = this;try {\n  Aardwolf.pushStack(\"__FUNCTION__\", \"__FILE__\", '__LINE__');\n  var aardwolfEvalFunc = function"
  },
  {
    "path": "samples/calc-coffee.html",
    "chars": 1022,
    "preview": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>COFFEESCRIPT Calculator</title>\n        <meta http-equiv=\"Content-Type\""
  },
  {
    "path": "samples/calc.coffee",
    "chars": 1013,
    "preview": "\n$number1 = null\n$number2 = null\n$number3 = null\n$result = null\n\n$ ->\n    # Locate number input fields\n    $number1 = $ "
  },
  {
    "path": "samples/calc.html",
    "chars": 1005,
    "preview": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Calculator</title>\n        <meta http-equiv=\"Content-Type\" content=\"tex"
  },
  {
    "path": "samples/calc.js",
    "chars": 1367,
    "preview": "\nvar $number1;\nvar $number2;\nvar $number3;\nvar $result;\n\n$(function() {\n    /* Locate number input fields */\n    $number"
  },
  {
    "path": "server/debug-file-server.js",
    "chars": 1911,
    "preview": "'use strict';\n\n/*\n * Serves source files with debug statements inserted.\n */\n\nvar http = require('http');\nvar path = req"
  },
  {
    "path": "server/rewriter-server.js",
    "chars": 4967,
    "preview": "'use strict';\n\n/*\n * Serves source files with debug statements inserted.\n */\n\nvar path = require('path');\nvar fs = requi"
  },
  {
    "path": "server/server-util.js",
    "chars": 3401,
    "preview": "'use strict';\n\nvar fs = require('fs');\nvar path = require('path');\nvar config = require('../config/config.defaults.js');"
  },
  {
    "path": "server/server.js",
    "chars": 5234,
    "preview": "'use strict';\n\n/*\n * Server for communication between the UI and the mobile library.\n * Also serves UI-related files.\n *"
  },
  {
    "path": "ui/css/buttons.css",
    "chars": 5741,
    "preview": "/*\n * Just some other awesome CSS3 buttons\n * http://www.red-team-design.com/just-another-awesome-css3-buttons\n */\n\n.but"
  },
  {
    "path": "ui/css/ui.css",
    "chars": 4407,
    "preview": "\nbody, html {\n    height: 100%;\n    font-size: 14px;\n}\n\nbody {\n    font-family: Arial, Helvetica, Tahoma, Verdana, sans-"
  },
  {
    "path": "ui/index.html",
    "chars": 2870,
    "preview": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Aardwolf UI</title>\n        <meta http-equiv=\"Content-Type\" content=\"te"
  },
  {
    "path": "ui/js/coffeetokenizer.js",
    "chars": 3965,
    "preview": "'use strict';\n\nvar keywordListCoffeeScript = [\n    'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let',"
  },
  {
    "path": "ui/js/jstokenizer.js",
    "chars": 3778,
    "preview": "'use strict';\n\nvar keywordListJavaScript = [\n    'var', 'function', 'if', 'else', 'while', 'for', 'do', 'in', 'break', '"
  },
  {
    "path": "ui/js/ui.js",
    "chars": 15546,
    "preview": "'use strict';\n\nvar files = {};\nvar breakpoints = [];\nvar $codeContainer;\nvar $code;\n\nvar $continueBtn;\nvar $stepBtn;\nvar"
  }
]

About this extraction

This page contains the full source code of the lexandera/Aardwolf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (89.3 KB), approximately 23.4k tokens, and a symbol index with 69 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!