<commit_before>'use strict';

var util     = require('util')
    ,qs      = require('querystring')
    ,request = require('request');

var API = '/api/json';
var NEWJOB = '%s/createItem/?name=%s';
var DELETE = '%s/job/%s/doDelete';
var BUILD = '%s/job/%s/build' + API;
var BUILD_INFO = '%s/job/%s/%s' + API;
var DISABLE = '%s/job/%s/disable';
var ENABLE = '%s/job/%s/enable';
var BUILDWITHPARAMS = '%s/job/%s/buildWithParameters';
var CONFIG = '%s/job/%s/config.xml';
var JOBINFO = '%s/job/%s' + API;
var LIST = '%s' + API;
var LAST_SUCCESS = '%s/job/%s/lastSuccessfulBuild' + API;
var TEST_REPORT = '%s/job/%s/lastSuccessfulBuild/testReport' + API;
var LAST_BUILD = '%s/job/%s/lastBuild' + API;
var LAST_COMPLETED_BUILD = '%s/job/%s/lastCompletedBuild' + API;
var LAST_REPORT = '%s/job/%s/lastBuild/testReport' + API;
var QUEUE = '%s/queue' + API;
var COMPUTERS = '%s/computer' + API;
var JOB_OUTPUT = '%s/job/%s/consoleText' + API;

var init = exports.init = function(host) {

    //Helper Functions
    var build_url = function(command) {
        /*
        Builds REST url to Jenkins
        */

        // Create the url using the format function
        var url = util.format.call(this, command, host);

        // if command is the only arg, we are done
        if (arguments.length == 1) return url;

        // Grab the arguments except for the first (command)
        var args = Array.prototype.slice.call(arguments).slice(1);

        // Append url to front of args array
        args.unshift(url);

        // Create full url with all the arguments
        url = util.format.apply(this, args);

        return url;

    };
    return {
        build: function(jobname, params, callback) {
            var buildurl;
            /*
            Trigger Jenkins to build.
            */
            if (typeof params === 'function') {
                buildurl = build_url(BUILD, jobname);
                callback = params;
            } else {
                buildurl = build_url(BUILDWITHPARAMS+"?"+qs.stringify(params), jobname)
            }

            request({method: 'POST', url: buildurl }, function(error, response) {
                if ( error || (response.statusCode !== 201 && response.statusCode !== 302) ) {
                    callback(error || true, response);
                    return;
                }
                var data = "job is executed";
                callback(null, data);
            });
        },
        all_jobs: function(callback) {
            /*
            Return a list of object literals containing the name and color of all jobs on the Jenkins server
            */
            request({method: 'GET', url: build_url(LIST)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString()).jobs;
                callback(null, data);
            });
        },
        job_info: function(jobname, callback) {
            /*
            Get all information for a job
            */
            request({method: 'GET', url: build_url(JOBINFO, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_build_info: function(jobname, callback) {
            /*
            Get information for the last build of a job
            */
            request({method: 'GET', url: build_url(LAST_BUILD, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_completed_build_info: function(jobname, callback) {
            /*
            Get information for the last completed build of a job
            */
            request({method: 'GET', url: build_url(LAST_COMPLETED_BUILD, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        build_info: function(jobname, number, callback) {
            /*
            Get information for the build number of a job
            */
            request({method: 'GET', url: build_url(BUILD_INFO, jobname, number)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_build_report: function(jobname, callback) {
            /*
            Get the last build report for a job
            */
            request({method: 'GET', url: build_url(LAST_REPORT, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        get_config_xml: function(jobname, callback) {
            /*
            Get the config xml for a job
            */
            request({method: 'GET', url: build_url(CONFIG, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = body;
                callback(null, data);
            });
        },
        create_job: function(jobname, job_config, callback) {
            /*
           Create a new job based on a job_config string
            */

            request(
                {method: 'POST'
                ,url: build_url(NEWJOB, jobname)
                ,body: job_config
                ,headers: { "content-type": "application/xml"}
                },

                function(error, response, body) {
                    if ( error || response.statusCode !== 200 ) {
                        callback(error || true, response);
                        return;
                    }
                    var data = body;
                    callback(null, data);
                }
            );
        },
        copy_job: function(jobname, new_job, modifyfunction, callback) {
            /*
            Copies a job and allows you to pass in a function to modify the configuration
            of the job you would like to copy
            */

            var self = this;
            self.get_config_xml(jobname, function(error, data) {
                if (error) {
                    callback(error, data);
                    return;
                }
                self.create_job(new_job, modifyfunction(data), function(error, data) {
                    if (error) {
                        callback(error, data);
                        return;
                    }
                    callback(null, data);
                });
            });

        },
        delete_job: function(jobname, callback) {
            /*
            Deletes a job
            */
            request({method: 'POST', url: build_url(DELETE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });

        },
        disable_job: function(jobname, callback) {
            /*
            Disables a job
            */
            request({method: 'POST', url: build_url(DISABLE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });
        },
        enable_job: function(jobname, callback) {
            /*
            Enables a job
            */
            request({method: 'POST', url: build_url(ENABLE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });
        },
        last_success: function(jobname, callback) {
            /*
            Get the last build report for a job
            */
            request({method: 'POST', url: build_url(LAST_SUCCESS, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body);
                callback(null, data);
            });

        },
        last_result: function(jobname, callback) {
            /*
            Get the last result for a job
            */
            var self = this;
            self.job_info(jobname, function(error, data) {
                var last_result_url = data.lastBuild.url;

                request({method: 'GET', url: build_url(last_result_url + API, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                        callback(error || true, response);
                        return;
                    }
                    data = JSON.parse(body);
                    callback(null, data);
                });
            });

        },
        queue: function(callback) {
            /*
             Get all queued items
             */
            request({method: 'GET', url: build_url(QUEUE)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        computers: function(callback) {
            /*
             Get details about all jenkins workers including currently executing jobs
             */
            request({method: 'GET', url: build_url(COMPUTERS + '?depth=1')}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        job_output: function(jobname, buildname, callback) {
            /*
            Get the output for a job's build
            */
            request({method: 'POST', url: build_url(JOB_OUTPUT, jobname + '/' + buildname)}, function(error, response, body) {
                if (response.statusCode !== 200 || error) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.stringify({"output": body});
                data = JSON.parse(data);
                callback(null, data);
            });
        }

    }

};

if (!module.parent) {
}
<commit_msg>last_build_report method fix<commit_after>'use strict';

var util     = require('util')
    ,qs      = require('querystring')
    ,request = require('request');

var API = '/api/json';
var NEWJOB = '%s/createItem/?name=%s';
var DELETE = '%s/job/%s/doDelete';
var BUILD = '%s/job/%s/build' + API;
var BUILD_INFO = '%s/job/%s/%s' + API;
var DISABLE = '%s/job/%s/disable';
var ENABLE = '%s/job/%s/enable';
var BUILDWITHPARAMS = '%s/job/%s/buildWithParameters';
var CONFIG = '%s/job/%s/config.xml';
var JOBINFO = '%s/job/%s' + API;
var LIST = '%s' + API;
var LAST_SUCCESS = '%s/job/%s/lastSuccessfulBuild' + API;
var TEST_REPORT = '%s/job/%s/lastSuccessfulBuild/testReport' + API;
var LAST_BUILD = '%s/job/%s/lastBuild' + API;
var LAST_COMPLETED_BUILD = '%s/job/%s/lastCompletedBuild' + API;
var LAST_REPORT = '%s/job/%s/lastBuild' + API;
var QUEUE = '%s/queue' + API;
var COMPUTERS = '%s/computer' + API;
var JOB_OUTPUT = '%s/job/%s/consoleText' + API;

var init = exports.init = function(host) {

    //Helper Functions
    var build_url = function(command) {
        /*
        Builds REST url to Jenkins
        */

        // Create the url using the format function
        var url = util.format.call(this, command, host);

        // if command is the only arg, we are done
        if (arguments.length == 1) return url;

        // Grab the arguments except for the first (command)
        var args = Array.prototype.slice.call(arguments).slice(1);

        // Append url to front of args array
        args.unshift(url);

        // Create full url with all the arguments
        url = util.format.apply(this, args);

        return url;

    };
    return {
        build: function(jobname, params, callback) {
            var buildurl;
            /*
            Trigger Jenkins to build.
            */
            if (typeof params === 'function') {
                buildurl = build_url(BUILD, jobname);
                callback = params;
            } else {
                buildurl = build_url(BUILDWITHPARAMS+"?"+qs.stringify(params), jobname)
            }

            request({method: 'POST', url: buildurl }, function(error, response) {
                if ( error || (response.statusCode !== 201 && response.statusCode !== 302) ) {
                    callback(error || true, response);
                    return;
                }
                var data = "job is executed";
                callback(null, data);
            });
        },
        all_jobs: function(callback) {
            /*
            Return a list of object literals containing the name and color of all jobs on the Jenkins server
            */
            request({method: 'GET', url: build_url(LIST)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString()).jobs;
                callback(null, data);
            });
        },
        job_info: function(jobname, callback) {
            /*
            Get all information for a job
            */
            request({method: 'GET', url: build_url(JOBINFO, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_build_info: function(jobname, callback) {
            /*
            Get information for the last build of a job
            */
            request({method: 'GET', url: build_url(LAST_BUILD, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_completed_build_info: function(jobname, callback) {
            /*
            Get information for the last completed build of a job
            */
            request({method: 'GET', url: build_url(LAST_COMPLETED_BUILD, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        build_info: function(jobname, number, callback) {
            /*
            Get information for the build number of a job
            */
            request({method: 'GET', url: build_url(BUILD_INFO, jobname, number)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        last_build_report: function(jobname, callback) {
            /*
            Get the last build report for a job
            */
            request({method: 'GET', url: build_url(LAST_REPORT, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        get_config_xml: function(jobname, callback) {
            /*
            Get the config xml for a job
            */
            request({method: 'GET', url: build_url(CONFIG, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = body;
                callback(null, data);
            });
        },
        create_job: function(jobname, job_config, callback) {
            /*
           Create a new job based on a job_config string
            */

            request(
                {method: 'POST'
                ,url: build_url(NEWJOB, jobname)
                ,body: job_config
                ,headers: { "content-type": "application/xml"}
                },

                function(error, response, body) {
                    if ( error || response.statusCode !== 200 ) {
                        callback(error || true, response);
                        return;
                    }
                    var data = body;
                    callback(null, data);
                }
            );
        },
        copy_job: function(jobname, new_job, modifyfunction, callback) {
            /*
            Copies a job and allows you to pass in a function to modify the configuration
            of the job you would like to copy
            */

            var self = this;
            self.get_config_xml(jobname, function(error, data) {
                if (error) {
                    callback(error, data);
                    return;
                }
                self.create_job(new_job, modifyfunction(data), function(error, data) {
                    if (error) {
                        callback(error, data);
                        return;
                    }
                    callback(null, data);
                });
            });

        },
        delete_job: function(jobname, callback) {
            /*
            Deletes a job
            */
            request({method: 'POST', url: build_url(DELETE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });

        },
        disable_job: function(jobname, callback) {
            /*
            Disables a job
            */
            request({method: 'POST', url: build_url(DISABLE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });
        },
        enable_job: function(jobname, callback) {
            /*
            Enables a job
            */
            request({method: 'POST', url: build_url(ENABLE, jobname)}, function(error, response, body) {
                if ( error || response.statusCode === 404 ) {
                    callback(error || true, response);
                    return;
                }
                callback(null, body);
            });
        },
        last_success: function(jobname, callback) {
            /*
            Get the last build report for a job
            */
            request({method: 'POST', url: build_url(LAST_SUCCESS, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body);
                callback(null, data);
            });

        },
        last_result: function(jobname, callback) {
            /*
            Get the last result for a job
            */
            var self = this;
            self.job_info(jobname, function(error, data) {
                var last_result_url = data.lastBuild.url;

                request({method: 'GET', url: build_url(last_result_url + API, jobname)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                        callback(error || true, response);
                        return;
                    }
                    data = JSON.parse(body);
                    callback(null, data);
                });
            });

        },
        queue: function(callback) {
            /*
             Get all queued items
             */
            request({method: 'GET', url: build_url(QUEUE)}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        computers: function(callback) {
            /*
             Get details about all jenkins workers including currently executing jobs
             */
            request({method: 'GET', url: build_url(COMPUTERS + '?depth=1')}, function(error, response, body) {
                if ( error || response.statusCode !== 200 ) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.parse(body.toString());
                callback(null, data);
            });
        },
        job_output: function(jobname, buildname, callback) {
            /*
            Get the output for a job's build
            */
            request({method: 'POST', url: build_url(JOB_OUTPUT, jobname + '/' + buildname)}, function(error, response, body) {
                if (response.statusCode !== 200 || error) {
                    callback(error || true, response);
                    return;
                }
                var data = JSON.stringify({"output": body});
                data = JSON.parse(data);
                callback(null, data);
            });
        }

    }

};

if (!module.parent) {
}
<|endoftext|>


<commit_before>import { Class as AstroClass, Behavior, Module } from 'meteor/jagi:astronomy';
import {uniq, zipObject, each} from 'lodash';
import relateTo from './relateTo';
const hasMeteorMethod = Module.modules.storage.utils.hasMeteorMethod;
const callMeteorMethod = Module.modules.storage.utils.callMeteorMethod;

Behavior.create({
  name: 'relations',
  options: {
    // many or single
    type: 'many',
    // local key to store in foreign doc
    keyField: '_id',
    // Field name in local class that stores keys
    keyStore: 'ThoseThings',
    // Key to store from related class
    foreignKeyField: '_id',
    // What field to store this key in, in related class
    // make sure it's defined in related class
    foreignKeyStore: 'TheseThings',
    // What class to relate to
    foreignClass: 'Those',
  },
  createClassDefinition: function() {
    const behavior = this;
    // console.log(behavior);
    // const field = set({}, behavior.options.keyStore, {
    //   type: behavior.options.type === 'many' ? [String] : String,
    //   optional: true,
    //   default() {
    //     return behavior.options.type === 'many' ? [] : '';
    //   },
    // })
    return {
      // fields: {
        // ...field,
        // [behavior.options.keyStore]: {
        //   type: behavior.options.type === 'many' ? [String] : String,
        //   optional: true,
        //   default() {
        //     return behavior.options.type === 'many' ? [] : '';
        //   },
        // }
      // },
      helpers: {
        getRelated(className, query, options) {
          const RelatedClass = AstroClass.get(className);
          console.log(this.constructor.getBehavior('relations'), this, behavior);
          // get specified doc, if it's related
          if (typeOf query = 'string') {
            return RelatedClass.findOne({
              $and: [
                {_id: query},
                {[behavior.options.foreignKeyStore]: {$in: [this._id]}},
              ],
            });
          }
          // get all related docs
          else {
            return RelatedClass.find({
              [behavior.options.foreignKeyStore]: {$in: [this._id]},
            });
          }
        },
        relateTo(foreignClassName, foreignKey, callback) {
          const {
            keyField,
            keyStore,
            foreignKeyField,
            foreignKeyStore,
            foreignClass,
          } = behavior.options;
          const key = this[keyField];
          const Class = this.constructor;
          const localClassName = Class.className;
          return callMeteorMethod(
            Class,
            '/Astronomy/relateTo',
            [{
              localClassName,
              foreignClassName,
              key,
              keyStore,
              keyField,
              foreignKey,
              foreignKeyStore,
              foreignKeyField,
            }],
            callback,
          );
        },
      },
    };
  },
  apply(Class) {
    const Collection = Class.getCollection();
    //
    // // If it's a remote collection then we register methods on the connection
    // // object of the collection.
    let connection = Collection._connection;
    if (connection) {
      // Prepare meteor methods to be added.
      let meteorMethods = {
        '/Astronomy/relateTo': relateTo,
    //     '/Astronomy/softRestore': meteorSoftRestore
      };
      each(meteorMethods, (meteorMethod, methodName) => {
        if (!hasMeteorMethod(connection, methodName)) {
          // Add meteor method.
          connection.methods(zipObject([methodName], [meteorMethod]));
        }
      });
    }
    Class.extend(
      this.createClassDefinition(), ['fields', 'events', 'helpers', 'methods']
    );
  }
});
<commit_msg>work on getRelated<commit_after>import { Class as AstroClass, Behavior, Module } from 'meteor/jagi:astronomy';
import {uniq, zipObject, each} from 'lodash';
import relateTo from './relateTo';
const hasMeteorMethod = Module.modules.storage.utils.hasMeteorMethod;
const callMeteorMethod = Module.modules.storage.utils.callMeteorMethod;

Behavior.create({
  name: 'relations',
  options: {
    // many or single
    type: 'many',
    // local key to store in foreign doc
    keyField: '_id',
    // Field name in local class that stores keys
    keyStore: 'ThoseThings',
    // Key to store from related class
    foreignKeyField: '_id',
    // What field to store this key in, in related class
    // make sure it's defined in related class
    foreignKeyStore: 'TheseThings',
    // What class to relate to
    foreignClass: 'Those',
  },
  createClassDefinition() {
    const behavior = this;
    // console.log(behavior);
    // const field = set({}, behavior.options.keyStore, {
    //   type: behavior.options.type === 'many' ? [String] : String,
    //   optional: true,
    //   default() {
    //     return behavior.options.type === 'many' ? [] : '';
    //   },
    // })
    return {
      // fields: {
        // ...field,
        // [behavior.options.keyStore]: {
        //   type: behavior.options.type === 'many' ? [String] : String,
        //   optional: true,
        //   default() {
        //     return behavior.options.type === 'many' ? [] : '';
        //   },
        // }
      // },
      // events: {
      //   beforeSave(e) {
      //     const doc = e.target;
      //     const {keyStore} = behavior.options;
      //     e.stopPropagation();
      //     doc.set(keyStore, uniq(doc[keyStore]));
      //     console.log(e)
      //   },
      // },
      helpers: {
        getRelated(className, query, options) {
          const RelatedClass = AstroClass.get(className);
          console.log(this.constructor.getBehavior('relations'), this, behavior);
          // get specified doc, if it's related
          if (typeOf query = 'string') {
            return RelatedClass.find({
              $and: [
                {_id: query},
                {[behavior.options.foreignKeyStore]: {$in: [this._id]}},
              ],
            });
          }
          // get all related docs
          else {
            return RelatedClass.find({
              [behavior.options.foreignKeyStore]: {$in: [this._id]},
            });
          }
        },
        relateTo(foreignClassName, foreignKey, callback) {
          const {
            keyField,
            keyStore,
            foreignKeyField,
            foreignKeyStore,
            foreignClass,
          } = behavior.options;
          const key = this[keyField];
          const Class = this.constructor;
          const localClassName = Class.className;
          return callMeteorMethod(
            Class,
            '/Astronomy/relateTo',
            [{
              localClassName,
              foreignClassName,
              key,
              keyStore,
              keyField,
              foreignKey,
              foreignKeyStore,
              foreignKeyField,
            }],
            callback,
          );
        },
      },
    };
  },
  apply(Class) {
    const Collection = Class.getCollection();
    //
    // // If it's a remote collection then we register methods on the connection
    // // object of the collection.
    let connection = Collection._connection;
    if (connection) {
      // Prepare meteor methods to be added.
      let meteorMethods = {
        '/Astronomy/relateTo': relateTo,
    //     '/Astronomy/softRestore': meteorSoftRestore
      };
      each(meteorMethods, (meteorMethod, methodName) => {
        if (!hasMeteorMethod(connection, methodName)) {
          // Add meteor method.
          connection.methods(zipObject([methodName], [meteorMethod]));
        }
      });
    }
    Class.extend(
      this.createClassDefinition(), ['fields', 'events', 'helpers', 'methods']
    );
  }
});
<|endoftext|>


<commit_before>// this is an artifact of a older version
// will be integrated into the module
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();<commit_msg>updated main.js<commit_after>// this is an artifact of a older version
// will be integrated into the module
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

module.exports = context;<|endoftext|>


<commit_before>module.exports = {
  activate(state) {
    this.commandsDisposable = atom.commands.add("atom-workspace", {
      "theme-switch:next": () => this.switch("next", "profiles"),
      "theme-switch:prev": () => this.switch("prev", "profiles"),
      "theme-switch:next-dark": () => this.switch("next", "darkProfiles"),
      "theme-switch:prev-dark": () => this.switch("prev", "darkProfiles"),
      "theme-switch:next-light": () => this.switch("next", "lightProfiles"),
      "theme-switch:prev-light": () => this.switch("prev", "lightProfiles"),
      "theme-switch:clip-current-theme": () => atom.clipboard.write(this.getCurrentTheme()),
    })
  },

  deactivate() {
    this.commandsDisposable.dispose()
  },

  getCurrentTheme() {
    return atom.config.get("core.themes").join(" ")
  },

  switch(direction, profileName) {
    const profiles = atom.config.get(`theme-switch.${profileName}`).map(profile => profile.replace(/ +/g, " ")) // normalize spaces.
    let index = profiles.indexOf(this.getCurrentTheme())

    // determine next/prev index
    if (index >= 0) {
      if (direction === "next") {
        index++
        if (index >= profiles.length) index = 0
      } else {
        index--
        if (index < 0) index = profiles.length - 1
      }
    } else {
      index = 0
    }

    atom.config.set("core.themes", profiles[index].split(" "))
    if (atom.config.get("theme-switch.logToConsole")) {
      console.info("theme-switch:", this.getCurrentTheme())
    }
  },
}
<commit_msg>minor cleanup avoid using switch because of syntax hl ugly<commit_after>module.exports = {
  activate() {
    this.commandsDisposable = atom.commands.add("atom-workspace", {
      "theme-switch:next": () => this.switchTheme("next", "profiles"),
      "theme-switch:prev": () => this.switchTheme("prev", "profiles"),
      "theme-switch:next-dark": () => this.switchTheme("next", "darkProfiles"),
      "theme-switch:prev-dark": () => this.switchTheme("prev", "darkProfiles"),
      "theme-switch:next-light": () => this.switchTheme("next", "lightProfiles"),
      "theme-switch:prev-light": () => this.switchTheme("prev", "lightProfiles"),
      "theme-switch:clip-current-theme": () => atom.clipboard.write(this.getCurrentTheme()),
    })
  },

  deactivate() {
    this.commandsDisposable.dispose()
  },

  getCurrentTheme() {
    return atom.config.get("core.themes").join(" ")
  },

  switchTheme(direction, profileName) {
    const profiles = atom.config.get(`theme-switch.${profileName}`).map(profile => profile.replace(/ +/g, " ")) // normalize spaces.
    let index = profiles.indexOf(this.getCurrentTheme())

    // determine next/prev index
    if (index >= 0) {
      if (direction === "next") {
        index++
        if (index >= profiles.length) index = 0
      } else {
        index--
        if (index < 0) index = profiles.length - 1
      }
    } else {
      index = 0
    }

    atom.config.set("core.themes", profiles[index].split(" "))
    if (atom.config.get("theme-switch.logToConsole")) {
      console.info("theme-switch:", this.getCurrentTheme())
    }
  },
}
<|endoftext|>


<commit_before>//     node-email-templates
//     Copyright (c) 2012 Nick Baugh <niftylettuce@gmail.com>
//     MIT Licensed

// Node.js module for rendering beautiful emails with [ejs][1] templates
//  and email-friendly inline CSS using [juice][2].

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/node-email-templates>

// # node-email-templates

var path  = require('path')
  , fs    = require('fs')
  , async = require('async')
  , ejs   = require('ejs')
  , juice = require('juice')
  , zlib  = require('zlib')
  , _     = require('underscore')

var validBufferTypes = [ 'deflate', 'deflateRaw', 'gzip' ]

// # Email Template
var EmailTemplate = function(templateDirectory, defaults, done) {

  if (typeof defaults === 'function') done = defaults;

  var that = this;

  this.html = '';
  this.text = '';
  this.stylesheet = '';
  this.bufferType = ''
  this.render = function(locals, html, text, stylesheet, bufferType, callback) {
    // Check if `bufferType` is a function (callback), or string
    var isBuffer = false
    if (typeof bufferType === 'function') {
      callback = bufferType
    } else if (typeof bufferType === 'string') {
      // Ensure that `bufferType` is a valid `zlib` compression type
      if (_.indexOf(validBufferTypes, bufferType) === -1) {
        throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
      } else {
        isBuffer = true
      }
    }

    // Check if `that.bufferType` is not an empty string, so we'll use the global bufferType instead
    if (that.bufferType !== '') {
      isBuffer = true
      bufferType = that.bufferType
    }

    if (!html) html = that.html
    if (!text) text = that.text
    if (!stylesheet) stylesheet = that.stylesheet
    locals = _.defaults(locals, (typeof defaults === 'object') ? defaults : {});
    html = ejs.render(html, locals);
    text = (text) ? ejs.render(text, locals) : '';
    if (stylesheet) html = juice(html, stylesheet);
    if (typeof html === 'function') callback = html;

    // return a compressed buffer
    if (isBuffer) {
      async.map([ new Buffer(html), new Buffer(text) ], zlib[bufferType], function(err, buffers) {
        if (err) return callback(err)
        html = buffers[0]
        text = buffers[1]
        callback(null, html, text)
      })
    } else {
      callback(null, html, text)
    }
  };

  // Ensure the `templateDirectory` is undefined
  if (typeof templateDirectory === 'undefined')
    return done(new Error('templateDirectory is undefined'));

  // Ensure the `templateDirectory` is a valid directory path
  fs.stat(templateDirectory, function(err, stats) {

    if (err) return done(new Error(err));

    if (!stats.isDirectory())
      return done(new Error('templateDirectory is not a valid directory path'));

    done(null, function(templateName, locals, bufferType, callback) {

      // Check if `bufferType` is a function (callback), or string
      var isBuffer = false
      if (typeof bufferType === 'function') {
        callback = bufferType
      } else if (typeof bufferType === 'string') {
        // Ensure that `bufferType` is a valid `zlib` compression type
        if (_.indexOf(validBufferTypes, bufferType) === -1) {
          throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
        } else {
          isBuffer = true
        }
      }

      // Fallback if user doesn't pass all the args
      var batchEmail = false;
      if (typeof locals !== 'undefined' && typeof locals === 'function') {
        callback = locals;
        locals = {};
      } else if (typeof callback !== 'undefined' && typeof callback === 'function') {
        if (typeof locals === 'undefined' || !locals instanceof Object) {
          locals = {};
        } else if (locals === true) {
          // We are trying to load the template into memory, but not render it
          // This lets us send batches of emails using the same template
          //  and we only are loading the assets once
          batchEmail = true;
        }
      }

      // Batch load function

      var batchCheck = function() {
        if (batchEmail) {
          if (isBuffer) that.bufferType = bufferType
          return callback(null, that.render);
        }
        if (isBuffer) {
          that.render(locals, null, null, null, bufferType, callback)
        } else {
          that.render(locals, null, null, null, callback)
        }
        //that.render(locals, callback);
      };

      // Ensure the `templateName` is valid
      if (templateName === 'undefined') return callback('templateName was not defined');

      // Set the full path to the template
      var templatePath = path.join(templateDirectory, templateName);

      // Ensure the `templateName` is a valid directory path
      fs.stat(templatePath, function(err, stats) {

        if (err) return callback(err);

        if (!stats.isDirectory())
          return callback(templatePath + ' is not a valid directory path');

        // Ensure that at least the html.ejs file exists inside
        that.html = path.join(templatePath, 'html.ejs');
        fs.stat(that.html, function(err, stats) {

          if (err) return callback(err);

          if (!stats.isFile()) return callback(that.html + ' is not a valid file path');

          // Read the html file
          fs.readFile(that.html, 'utf8', function(err, data) {

            if (err) return callback(err);

            if (data === '') return callback(that.html + ' was an empty file');
            else that.html = data;

            // Set asset paths
            that.text = path.join(templatePath, 'text.ejs');
            that.stylesheet = path.join(templatePath, 'style.css');

            async.map([that.text, that.stylesheet], checkExists, function(err, results) {

              if (err) return callback(err);

              if (!results[0]) that.text = false
              if (!results[1]) that.stylesheet = false

              if (that.text && that.stylesheet) {
                async.map([that.text, that.stylesheet], fs.readFile, function(err, results) {
                  if (err) return callback(err);
                  results[0] = results[0].toString('utf8');
                  results[1] = results[1].toString('utf8');
                  if (results[0] === '') that.text = false;
                  if (results[1] === '') that.stylesheet = false;
                  that.text = results[0];
                  that.stylesheet = results[1];
                  batchCheck();
                });
              } else if (that.text && !that.stylesheet) {
                fs.readFile(that.text, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.text + ' was empty');
                  that.text = data;
                  batchCheck();
                });
              } else if (!that.text && that.stylesheet) {
                fs.readFile(that.stylesheet, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.stylesheet + ' was empty');
                  batchCheck();
                });
              } else {
                batchCheck();
              }

            });

          });
        });
      });
    });

    function checkExists(item, callback) {
      function cb(exists) {
        return callback(null, exists)
      }
      // node >= v0.8.x
      if (fs.exists) return fs.exists(item, cb)
      // other versions of node fallback to path.exists
      return path.exists(item, cb)
    }

  });
};

module.exports = function(templateDirectory, defaults, done) {
  return new EmailTemplate(templateDirectory, defaults, done);
};
<commit_msg>Fix the batch problem ( has no method slice )<commit_after>//     node-email-templates
//     Copyright (c) 2012 Nick Baugh <niftylettuce@gmail.com>
//     MIT Licensed

// Node.js module for rendering beautiful emails with [ejs][1] templates
//  and email-friendly inline CSS using [juice][2].

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/node-email-templates>

// # node-email-templates

var path  = require('path')
  , fs    = require('fs')
  , async = require('async')
  , ejs   = require('ejs')
  , juice = require('juice')
  , zlib  = require('zlib')
  , _     = require('underscore')

var validBufferTypes = [ 'deflate', 'deflateRaw', 'gzip' ]

// # Email Template
var EmailTemplate = function(templateDirectory, defaults, done) {

  if (typeof defaults === 'function') done = defaults;

  var that = this;

  this.html = '';
  this.text = '';
  this.stylesheet = '';
  this.bufferType = ''
  this.render = function(locals, html, text, stylesheet, bufferType, callback) {
    // Check if `bufferType` is a function (callback), or string
    var isBuffer = false
    if (typeof bufferType === 'function') {
      callback = bufferType
    } else if (typeof bufferType === 'string') {
      // Ensure that `bufferType` is a valid `zlib` compression type
      if (_.indexOf(validBufferTypes, bufferType) === -1) {
        throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
      } else {
        isBuffer = true
      }
    }

    // Check if `that.bufferType` is not an empty string, so we'll use the global bufferType instead
    if (that.bufferType !== '') {
      isBuffer = true
      bufferType = that.bufferType
    }

    if (typeof html === 'function') {
      callback = html;
      html     = that.html;
    }
    if (!html) html = that.html
    if (!text) text = that.text
    if (!stylesheet) stylesheet = that.stylesheet
    locals = _.defaults(locals, (typeof defaults === 'object') ? defaults : {});
    html = ejs.render(html, locals);
    text = (text) ? ejs.render(text, locals) : '';
    if (stylesheet) html = juice(html, stylesheet);

    // return a compressed buffer
    if (isBuffer) {
      async.map([ new Buffer(html), new Buffer(text) ], zlib[bufferType], function(err, buffers) {
        if (err) return callback(err)
        html = buffers[0]
        text = buffers[1]
        callback(null, html, text)
      })
    } else {
      callback(null, html, text)
    }
  };

  // Ensure the `templateDirectory` is undefined
  if (typeof templateDirectory === 'undefined')
    return done(new Error('templateDirectory is undefined'));

  // Ensure the `templateDirectory` is a valid directory path
  fs.stat(templateDirectory, function(err, stats) {

    if (err) return done(new Error(err));

    if (!stats.isDirectory())
      return done(new Error('templateDirectory is not a valid directory path'));

    done(null, function(templateName, locals, bufferType, callback) {

      // Check if `bufferType` is a function (callback), or string
      var isBuffer = false
      if (typeof bufferType === 'function') {
        callback = bufferType
      } else if (typeof bufferType === 'string') {
        // Ensure that `bufferType` is a valid `zlib` compression type
        if (_.indexOf(validBufferTypes, bufferType) === -1) {
          throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
        } else {
          isBuffer = true
        }
      }

      // Fallback if user doesn't pass all the args
      var batchEmail = false;
      if (typeof locals !== 'undefined' && typeof locals === 'function') {
        callback = locals;
        locals = {};
      } else if (typeof callback !== 'undefined' && typeof callback === 'function') {
        if (typeof locals === 'undefined' || !locals instanceof Object) {
          locals = {};
        } else if (locals === true) {
          // We are trying to load the template into memory, but not render it
          // This lets us send batches of emails using the same template
          //  and we only are loading the assets once
          batchEmail = true;
        }
      }

      // Batch load function

      var batchCheck = function() {
        if (batchEmail) {
          if (isBuffer) that.bufferType = bufferType
          return callback(null, that.render);
        }
        if (isBuffer) {
          that.render(locals, null, null, null, bufferType, callback)
        } else {
          that.render(locals, null, null, null, callback)
        }
        //that.render(locals, callback);
      };

      // Ensure the `templateName` is valid
      if (templateName === 'undefined') return callback('templateName was not defined');

      // Set the full path to the template
      var templatePath = path.join(templateDirectory, templateName);

      // Ensure the `templateName` is a valid directory path
      fs.stat(templatePath, function(err, stats) {

        if (err) return callback(err);

        if (!stats.isDirectory())
          return callback(templatePath + ' is not a valid directory path');

        // Ensure that at least the html.ejs file exists inside
        that.html = path.join(templatePath, 'html.ejs');
        fs.stat(that.html, function(err, stats) {

          if (err) return callback(err);

          if (!stats.isFile()) return callback(that.html + ' is not a valid file path');

          // Read the html file
          fs.readFile(that.html, 'utf8', function(err, data) {

            if (err) return callback(err);

            if (data === '') return callback(that.html + ' was an empty file');
            else that.html = data;

            // Set asset paths
            that.text = path.join(templatePath, 'text.ejs');
            that.stylesheet = path.join(templatePath, 'style.css');

            async.map([that.text, that.stylesheet], checkExists, function(err, results) {

              if (err) return callback(err);

              if (!results[0]) that.text = false
              if (!results[1]) that.stylesheet = false

              if (that.text && that.stylesheet) {
                async.map([that.text, that.stylesheet], fs.readFile, function(err, results) {
                  if (err) return callback(err);
                  results[0] = results[0].toString('utf8');
                  results[1] = results[1].toString('utf8');
                  if (results[0] === '') that.text = false;
                  if (results[1] === '') that.stylesheet = false;
                  that.text = results[0];
                  that.stylesheet = results[1];
                  batchCheck();
                });
              } else if (that.text && !that.stylesheet) {
                fs.readFile(that.text, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.text + ' was empty');
                  that.text = data;
                  batchCheck();
                });
              } else if (!that.text && that.stylesheet) {
                fs.readFile(that.stylesheet, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.stylesheet + ' was empty');
                  batchCheck();
                });
              } else {
                batchCheck();
              }

            });

          });
        });
      });
    });

    function checkExists(item, callback) {
      function cb(exists) {
        return callback(null, exists)
      }
      // node >= v0.8.x
      if (fs.exists) return fs.exists(item, cb)
      // other versions of node fallback to path.exists
      return path.exists(item, cb)
    }

  });
};

module.exports = function(templateDirectory, defaults, done) {
  return new EmailTemplate(templateDirectory, defaults, done);
};
<|endoftext|>


<commit_before>'use babel'

import { CompositeDisposable } from 'atom'

export default {
  activate () {
    this.bootstrap()
    this.disposables = new CompositeDisposable()

    this.disposables.add(atom.commands.add('atom-workspace', {
      'latex:build': () => this.composer.build(),
      'latex:clean': () => this.composer.clean(),
      'latex:sync': () => this.composer.sync()
    }))

    this.disposables.add(atom.workspace.observeTextEditors(editor => {
      this.disposables.add(editor.onDidSave(() => {
        // Let's play it safe; only trigger builds for the active editor.
        const activeEditor = atom.workspace.getActiveTextEditor()
        if (editor === activeEditor && atom.config.get('latex.buildOnSave')) {
          this.composer.build()
        }
      }))
    }))
  },

  deactivate () {
    if (this.disposables) {
      this.disposables.dispose()
      delete this.disposables
    }

    if (this.composer) {
      this.composer.destroy()
      delete this.composer
    }

    if (global.latex) {
      delete global.latex
    }
  },

  consumeStatusBar (statusBar) {
    this.bootstrap()
    this.composer.setStatusBar(statusBar)
  },

  bootstrap () {
    if (this.composer && global.latex) { return }

    const Latex = require('./latex')
    const Composer = require('./composer')

    global.latex = new Latex()
    this.composer = new Composer()
  }
}
<commit_msg>:shirt: Remove spaces inside brackets<commit_after>'use babel'

import {CompositeDisposable} from 'atom'

export default {
  activate () {
    this.bootstrap()
    this.disposables = new CompositeDisposable()

    this.disposables.add(atom.commands.add('atom-workspace', {
      'latex:build': () => this.composer.build(),
      'latex:clean': () => this.composer.clean(),
      'latex:sync': () => this.composer.sync()
    }))

    this.disposables.add(atom.workspace.observeTextEditors(editor => {
      this.disposables.add(editor.onDidSave(() => {
        // Let's play it safe; only trigger builds for the active editor.
        const activeEditor = atom.workspace.getActiveTextEditor()
        if (editor === activeEditor && atom.config.get('latex.buildOnSave')) {
          this.composer.build()
        }
      }))
    }))
  },

  deactivate () {
    if (this.disposables) {
      this.disposables.dispose()
      delete this.disposables
    }

    if (this.composer) {
      this.composer.destroy()
      delete this.composer
    }

    if (global.latex) {
      delete global.latex
    }
  },

  consumeStatusBar (statusBar) {
    this.bootstrap()
    this.composer.setStatusBar(statusBar)
  },

  bootstrap () {
    if (this.composer && global.latex) { return }

    const Latex = require('./latex')
    const Composer = require('./composer')

    global.latex = new Latex()
    this.composer = new Composer()
  }
}
<|endoftext|>


<commit_before>'use strict'

const LRU = require('lru-cache')
const FS = require('fs')

class SmartCache {
  constructor(cacheOptions, cacheValidator, cacheFetcher) {
    if (!cacheOptions) {
      cacheOptions = {max: 500, maxAge: 1000 * 60 * 60 * 24}
    }
    this.cache = new LRU(cacheOptions)
    this.cacheStats = new Map()
    this.validate = cacheValidator || SmartCache.DefaultValidator
    this.fetch = cacheFetcher || SmartCache.DefaultFetcher
  }

  get(filePath) {
    const Me = this
    return this.validate(filePath, this.cacheStats.get(filePath))
      .then(function(newValue) {
        if (newValue !== false || !this.cache.has(filePath)) {
          this.cacheStats.set(filePath, newValue)
          return Me.fetch(filePath).then(function(contents) {
            Me.cache.set(filePath, contents)
            return contents
          })
        }
        return Me.cache.get(filePath)
      })
  }

  dispose() {
    this.cache.reset()
    this.cacheStats.clear()
  }

  // Return false to use previous result
  // Return anything else to be saved in oldValue and trigger fetch
  static DefaultValidator(filePath, oldValue) {
    const Deferred = Promise.defer()
    FS.stat(filePath, function(err, stats) {
      if (err) Deferred.resolve(false)
      else {
        const newValue = stats.ctime.toString()
        if (oldValue === newValue) Deferred.resolve(false)
        else Deferred.resolve(stats.ctime.toString())
      }
    })
    return Deferred.promise
  }

  static DefaultFetcher(filePath) {
    const Deferred = Promise.defer()
    FS.readFile(filePath, function(err, data) {
      if (err) Deferred.reject(err)
      else Deferred.resolve(data.toString())
    })
    return Deferred.promise
  }
}

module.exports = SmartCache
<commit_msg>:bug: Fix a typo<commit_after>'use strict'

const LRU = require('lru-cache')
const FS = require('fs')

class SmartCache {
  constructor(cacheOptions, cacheValidator, cacheFetcher) {
    if (!cacheOptions) {
      cacheOptions = {max: 500, maxAge: 1000 * 60 * 60 * 24}
    }
    this.cache = new LRU(cacheOptions)
    this.cacheStats = new Map()
    this.validate = cacheValidator || SmartCache.DefaultValidator
    this.fetch = cacheFetcher || SmartCache.DefaultFetcher
  }

  get(filePath) {
    const Me = this
    return this.validate(filePath, this.cacheStats.get(filePath))
      .then(function(newValue) {
        if (newValue !== false || !this.cache.has(filePath)) {
          Me.cacheStats.set(filePath, newValue)
          return Me.fetch(filePath).then(function(contents) {
            Me.cache.set(filePath, contents)
            return contents
          })
        }
        return Me.cache.get(filePath)
      })
  }

  dispose() {
    this.cache.reset()
    this.cacheStats.clear()
  }

  // Return false to use previous result
  // Return anything else to be saved in oldValue and trigger fetch
  static DefaultValidator(filePath, oldValue) {
    const Deferred = Promise.defer()
    FS.stat(filePath, function(err, stats) {
      if (err) Deferred.resolve(false)
      else {
        const newValue = stats.ctime.toString()
        if (oldValue === newValue) Deferred.resolve(false)
        else Deferred.resolve(stats.ctime.toString())
      }
    })
    return Deferred.promise
  }

  static DefaultFetcher(filePath) {
    const Deferred = Promise.defer()
    FS.readFile(filePath, function(err, data) {
      if (err) Deferred.reject(err)
      else Deferred.resolve(data.toString())
    })
    return Deferred.promise
  }
}

module.exports = SmartCache
<|endoftext|>


<commit_before>var _ = require('lodash');
var astUtils = require('./ast-utils');
var strings = require('./strings');

var buffer
  , print
  , indentationLevel
  ;

var initializeCompiler = function() {
    buffer = [];
    print = _.bind(buffer.push, buffer);
    indentationLevel = 0;
};

var indent = function() {
    indentationLevel++;
};

var outdent = function() {
    indentationLevel--;
    if(indentationLevel < 0) {
        throw new Error('Tried to outdent too far.');
    }
};

var printIndent = function() {
    // TODO create customizable indentation level
    // TODO make this faster?
    _.times(indentationLevel, function() {
        print('    ');
    });
};

// TODO properly translate all binops and unops:
//   ones that GML has that JS doesn't have
//   ones with different behavior that need to be implemented differently
//   DIV, MOD, ^^, bitwise ops
//   how does GML do type coercion (42 + "hello world")?  Do I need to emulate that behavior?
var generateExpression = function(astNode, omitIndentation) {
    switch(astNode.type) {

        case 'identifier':
            var variable = astNode.variable;
            if(variable) {
                if(variable.getAccessType() === 'PROP_ACCESS') {
                    print(variable.getContainingObjectIdentifier() + '.');
                }
                print(variable.getJsIdentifier());
            } else {
                print(astNode.name);
            }
            // TODO will this ever need to be enclosed in parentheses?
            // How should I be handling this in the general case?
            break;

        case 'binop':
            print('(');
            // special-case the dot operator
            switch(astNode.op) {
                case '.':
                    // Special case: if `self` or `other is on the left side of the dot, we don't need to dereference
                    if(astNode.expr1.type === 'identifier' && astNode.expr1.variable && _.contains(['self', 'other'], astNode.expr1.variable.getIdentifier())) {
                        generateExpression(astNode.expr1);
                        print('.');
                        generateExpression(astNode.expr2);
                    } else {
                        print(strings.ANGL_RUNTIME_IDENTIFIER + '.resolveObjectBeforeDot(');
                        generateExpression(astNode.expr1);
                        print(').');
                        generateExpression(astNode.expr2);
                    }
                    break;

                case 'div':
                    print('(');
                    generateExpression(astNode.expr1);
                    print(' / ');
                    generateExpression(astNode.expr2);
                    print(')|0');
                    break;

                case 'mod':
                    generateExpression(astNode.expr1);
                    print(' % ');
                    generateExpression(astNode.expr2);
                    break;

                default:
                    generateExpression(astNode.expr1);
                    print(' ' + astNode.op + ' ');
                    generateExpression(astNode.expr2);
            }
            print(')');
            break;

        case 'unop':
            print('(');
            print(astNode.op);
            generateExpression(astNode.expr);
            print(')');
            break;

        case 'number':
            print('(');
            print(astNode.val.toString());
            // TODO does toString always produce valid Javascript that will create the exact same number?
            print(')');
            break;

        case 'string':
            print('(');
            print(JSON.stringify(astNode.val));
            // TODO this fails in a select few corner cases.  Use something better,
            // perhaps stolen from the Jade source code
            print(')');
            break;

        case 'index':
            // TODO this needs a lot of work
            // What do we do when index values aren't numbers?  Aren't integers?
            // What about when the array isn't initialized or the target isn't an array?
            print('(');
            generateExpression(astNode.expr);
            print(')');
            _.each(astNode.indexes, function (index) {
                print('[');
                generateExpression(index);
                print(']');
            });
            break;

        case 'funccall':
            print('(');
            generateExpression(astNode.expr);
            print(')');
            if(astNode.isMethodCall) {
                // Method calls: `self`/`this` is automatically set to the object to which the method belongs
                // `other` should be set to the local `self` value
                print('(');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('self')
                });
            } else {
                // Function calls: Function's `self` and `other` are the local `self` and `other` values
                print('.call(');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('self')
                });
                print(', ');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('other')
                })
            }
            _.each(astNode.args, function(arg, i, args) {
                print(', ');
                generateExpression(arg);
            });
            print(')');
            break;

        case 'script':
            print('function(');
            print(['other'].concat(astNode.args).join(', '));
            print(') {\n');
            indent();
            generateLocalVariableAllocation(astNode);
            // TODO this part of the AST doesn't seem quite right, suggesting there are
            // possibilities I'm not aware of.
            // These sanity checks will reject anything unexpected.
            /*if(!(_.isObject(astNode.stmts) && _(_.keys(astNode.stmts).sort()).isEqual(['list', 'type']) && astNode.stmts.type === 'statements' && _.isArray(astNode.stmts.list))) {
             throw new Error('Failed sanity checks on stmts!')
             }
             _.each(astNode.stmts.list, generateStatement)*/
            generateStatement(astNode.stmts);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'jsfunccall':
            print('(');
            print(astNode.expr);
            print(')(');
            _.each(astNode.args, function(arg, i, args) {
                if(i) print(', ');
                generateExpression(arg);
            });
            print(')');
            break;

        default:
            throw new Error('Unknown expression type: "' + astNode.type + '"');
    }
};

var generateStatement = function(astNode, omitTerminator, omitIndentation) {
    if(arguments.length < 2) omitTerminator = false;
    switch(astNode.type) {

        case 'var':
            omitIndentation || printIndent();
            print('var ');
            _.each(astNode.list, function (varNode, i, args) {
                print (varNode.name);
                if (varNode.hasOwnProperty('expr')) {
                    print (' = ');
                    generateExpression(varNode.expr);
                }
                if(i < args.length - 1) {
                    print(', ');
                }
            });
            break;

        case 'assign':
            omitIndentation || printIndent();
            generateExpression(astNode.lval);
            print(' = ');
            generateExpression(astNode.rval);
            break;

        case 'scriptdef':
            omitIndentation || printIndent();
            print(strings.ANGL_GLOBALS_IDENTIFIER + '.' + astNode.name);
            print(' = function(');
            print(['other'].concat(astNode.args).join(', '));
            print(') {\n');
            indent();
            generateLocalVariableAllocation(astNode);
            // TODO this part of the AST doesn't seem quite right, suggesting there are
            // possibilities I'm not aware of.
            // These sanity checks will reject anything unexpected.
            /*if(!(_.isObject(astNode.stmts) && _(_.keys(astNode.stmts).sort()).isEqual(['list', 'type']) && astNode.stmts.type === 'statements' && _.isArray(astNode.stmts.list))) {
                throw new Error('Failed sanity checks on stmts!')
            }
            _.each(astNode.stmts.list, generateStatement)*/
            generateStatement(astNode.stmts);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'const':
            omitIndentation || printIndent();
            print(strings.ANGL_GLOBALS_IDENTIFIER + '.' + astNode.name);
            print(' = ');
            generateExpression(astNode.expr);
            break;

        case 'switch':
            omitIndentation || printIndent();
            print('switch(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            _.each(astNode.cases, function(caseNode) {
                generateCase(caseNode);
            });
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'for':
            omitIndentation || printIndent();
            print('for(');
            generateStatement(astNode.initstmt, true, true);
            print('; ');
            generateExpression(astNode.contexpr);
            print('; ');
            generateStatement(astNode.stepstmt, true, true);
            print(') {\n');
            indent();
            // TODO I bet there are some scoping issues I'm not dealing with correctly.
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'cmpassign':
            // Rewrite the cmpassign into a simpler binop and assign combo.
            // E.g. a += 1 becomes a = a + 1
            // TODO this will produce somewhat uglier code.  Maybe pretty it up later.
            // TODO what if the lval contains a function call?  Will it execute twice?
            generateStatement({
                type: 'assign',
                lval: astNode.lval,
                rval: {
                    type: 'binop',
                    op: astNode.op,
                    expr1: astNode.lval,
                    expr2: astNode.rval
                }
            }, omitTerminator, omitIndentation);
            break;

        case 'ifelse':
            omitIndentation || printIndent();
            print('if(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            generateStatement(astNode.stmt1);
            outdent();
            omitIndentation || printIndent();
            print('} else {\n');
            indent();
            generateStatement(astNode.stmt2);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'if':
            // This is a special case of ifelse where the else block is empty.
            generateStatement({
                type: 'ifelse',
                expr: astNode.expr,
                stmt1: astNode.stmt,
                stmt2: {type: 'nop'}
            }, omitTerminator, omitIndentation);
            break;

        case 'while':
            omitIndentation || printIndent();
            print('while(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            generateStatement(astNode.stmt);
            outdent();
            printIndent();
            print('}');
            break;

        case 'dountil':
            omitIndentation || printIndent();
            print('do {\n');
            indent();
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('} while(!(');
            generateExpression(astNode.expr);
            print('))');
            break;

        case 'break':
            omitIndentation || printIndent();
            print('break');
            // TODO are break semantics ever different in Angl than they are in JS?
            break;

        case 'continue':
            omitIndentation || printIndent();
            print('continue');
            // TODO are continue semantics ever different in Angl than they are in JS?
            break;

        case 'statements':
            _.each(astNode.list, function(statement) {
                generateStatement(statement);
            });
            break;

        case 'funccall':
            // Delegate to the expression generator
            omitIndentation || printIndent();
            generateExpression(astNode);
            break;

        case 'with':
            // TODO I DONT WANNA IMPLEMENT THIS WAAAAAH
            // Also it requires some sort of runtime that can find all instances of
            // a given object type to iterate over.
            // For now, I'm emitting a comment that explains code has been omitted.
            var indexIdentifier = {
                type: 'identifier',
                variable: astNode.indexVariable
            };
            var allObjectsIdentifier = {
                type: 'identifier',
                variable: astNode.allObjectsVariable
            };
            var innerSelfIdentifier = {
                type: 'identifier',
                variable: astUtils.getAnglScope(astNode).getVariableByIdentifier('self')
            };
            omitIndentation || printIndent();
            print('for(');
            generateExpression(indexIdentifier);
            print(' = 0; ');
            generateExpression(indexIdentifier);
            print(' < ');
            generateExpression(allObjectsIdentifier);
            print('.length; ');
            generateExpression(indexIdentifier);
            print('++) {\n');
            indent();
            // Assign the value of inner `self`
            omitIndentation || printIndent();
            generateExpression(innerSelfIdentifier);
            print(' = ');
            generateExpression(allObjectsIdentifier);
            print('[');
            generateExpression(indexIdentifier);
            print('];\n');
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'return':
            // TODO is there ever a situation where a Javascript 'return' won't do what we want?
            // For example, inside a _.each() iterator function
            omitIndentation || printIndent();
            print('return (');
            generateExpression(astNode.expr);
            print(')');
            break;

        case 'exit':
            // TODO same caveats as 'return'
            omitIndentation || printIndent();
            print('return');
            break;

        case 'nop':
            // No-ops don't do anything.  I'm assuming they never trigger any behavior by
            // "seperating" adjacent statements.
            break;

        default:
            throw new Error('Unknown statement type: "' + astNode.type + '"');
    }
    // Statements are terminated by a semicolon and a newline
    // except for a few exceptions.
    // Also, in certain contexts we want to omit this termination
    // (e.g., initializer statement of a for loop)
    if(!_.contains(['nop', 'statements'], astNode.type) && !omitTerminator) {
        print(';\n');
    }
};

var generateCase = function(astNode) {
    switch(astNode.type) {

        case 'case':
            printIndent();
            print('case (');
            generateExpression(astNode.expr);
            print('):\n');
            indent();
            generateStatement(astNode.stmts);
            outdent();
            break;

        case 'defaultcase':
            printIndent();
            print('default:\n');
            indent();
            generateStatement(astNode.stmts);
            outdent();
            break;

        default:
            throw new Error('Unknown case type: "' + astNode.type + '"');
    }
};

var generateLocalVariableAllocation = function(astNode) {
    var localVariables = _.filter(astUtils.getAnglScope(astNode).getVariablesArray(), function(variable) {
        return variable.getAllocationType() === 'LOCAL';
    });
    if(localVariables.length) {
        printIndent();
        print('var ');
        print(_.map(localVariables, function(variable) {
            return variable.getJsIdentifier();
        }).join(', '));
        print(';\n');
    }
}

var generateTopNode = function(astNode) {
    switch(astNode.type) {

        case 'file':
            // RequireJS `define()` call
            print('define(function(require) {\n');
            indent();
            printIndent();
            // Something removes "use strict" from the source code unless I split it up like so.  RequireJS perhaps?
            print('"use' + ' strict";\n');
            // require modules
            printIndent();
            print('var ' + strings.ANGL_GLOBALS_IDENTIFIER + ' = require(' + JSON.stringify(strings.ANGL_GLOBALS_MODULE) + ');\n');
            printIndent();
            print('var ' + strings.ANGL_RUNTIME_IDENTIFIER + ' = require(' + JSON.stringify(strings.ANGL_RUNTIME_MODULE) + ');\n');
            // allocate local variables
            generateLocalVariableAllocation(astNode);
            // delegate to the statement generator
            _.each(astNode.stmts, function(node) {
                generateStatement(node);
            });
            outdent();
            print('});');
            break;

        default:
            throw new Error('Unknown root node type: "' + astNode.type + '"');
    }
};

var compile = module.exports = function(ast) {
    initializeCompiler();
    generateTopNode(ast);
    return _.flatten(buffer).join('');
};

<commit_msg>Fixed bug where cmpassign statements would generate an extra unindented semicolon in the JavaScript<commit_after>var _ = require('lodash');
var astUtils = require('./ast-utils');
var strings = require('./strings');

var buffer
  , print
  , indentationLevel
  ;

var initializeCompiler = function() {
    buffer = [];
    print = _.bind(buffer.push, buffer);
    indentationLevel = 0;
};

var indent = function() {
    indentationLevel++;
};

var outdent = function() {
    indentationLevel--;
    if(indentationLevel < 0) {
        throw new Error('Tried to outdent too far.');
    }
};

var printIndent = function() {
    // TODO create customizable indentation level
    // TODO make this faster?
    _.times(indentationLevel, function() {
        print('    ');
    });
};

// TODO properly translate all binops and unops:
//   ones that GML has that JS doesn't have
//   ones with different behavior that need to be implemented differently
//   DIV, MOD, ^^, bitwise ops
//   how does GML do type coercion (42 + "hello world")?  Do I need to emulate that behavior?
var generateExpression = function(astNode, omitIndentation) {
    switch(astNode.type) {

        case 'identifier':
            var variable = astNode.variable;
            if(variable) {
                if(variable.getAccessType() === 'PROP_ACCESS') {
                    print(variable.getContainingObjectIdentifier() + '.');
                }
                print(variable.getJsIdentifier());
            } else {
                print(astNode.name);
            }
            // TODO will this ever need to be enclosed in parentheses?
            // How should I be handling this in the general case?
            break;

        case 'binop':
            print('(');
            // special-case the dot operator
            switch(astNode.op) {
                case '.':
                    // Special case: if `self` or `other is on the left side of the dot, we don't need to dereference
                    if(astNode.expr1.type === 'identifier' && astNode.expr1.variable && _.contains(['self', 'other'], astNode.expr1.variable.getIdentifier())) {
                        generateExpression(astNode.expr1);
                        print('.');
                        generateExpression(astNode.expr2);
                    } else {
                        print(strings.ANGL_RUNTIME_IDENTIFIER + '.resolveObjectBeforeDot(');
                        generateExpression(astNode.expr1);
                        print(').');
                        generateExpression(astNode.expr2);
                    }
                    break;

                case 'div':
                    print('(');
                    generateExpression(astNode.expr1);
                    print(' / ');
                    generateExpression(astNode.expr2);
                    print(')|0');
                    break;

                case 'mod':
                    generateExpression(astNode.expr1);
                    print(' % ');
                    generateExpression(astNode.expr2);
                    break;

                default:
                    generateExpression(astNode.expr1);
                    print(' ' + astNode.op + ' ');
                    generateExpression(astNode.expr2);
            }
            print(')');
            break;

        case 'unop':
            print('(');
            print(astNode.op);
            generateExpression(astNode.expr);
            print(')');
            break;

        case 'number':
            print('(');
            print(astNode.val.toString());
            // TODO does toString always produce valid Javascript that will create the exact same number?
            print(')');
            break;

        case 'string':
            print('(');
            print(JSON.stringify(astNode.val));
            // TODO this fails in a select few corner cases.  Use something better,
            // perhaps stolen from the Jade source code
            print(')');
            break;

        case 'index':
            // TODO this needs a lot of work
            // What do we do when index values aren't numbers?  Aren't integers?
            // What about when the array isn't initialized or the target isn't an array?
            print('(');
            generateExpression(astNode.expr);
            print(')');
            _.each(astNode.indexes, function (index) {
                print('[');
                generateExpression(index);
                print(']');
            });
            break;

        case 'funccall':
            print('(');
            generateExpression(astNode.expr);
            print(')');
            if(astNode.isMethodCall) {
                // Method calls: `self`/`this` is automatically set to the object to which the method belongs
                // `other` should be set to the local `self` value
                print('(');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('self')
                });
            } else {
                // Function calls: Function's `self` and `other` are the local `self` and `other` values
                print('.call(');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('self')
                });
                print(', ');
                generateExpression({
                    type: 'identifier',
                    variable: astUtils.getAnglScope(astNode).getVariableByIdentifierInChain('other')
                })
            }
            _.each(astNode.args, function(arg, i, args) {
                print(', ');
                generateExpression(arg);
            });
            print(')');
            break;

        case 'script':
            print('function(');
            print(['other'].concat(astNode.args).join(', '));
            print(') {\n');
            indent();
            generateLocalVariableAllocation(astNode);
            // TODO this part of the AST doesn't seem quite right, suggesting there are
            // possibilities I'm not aware of.
            // These sanity checks will reject anything unexpected.
            /*if(!(_.isObject(astNode.stmts) && _(_.keys(astNode.stmts).sort()).isEqual(['list', 'type']) && astNode.stmts.type === 'statements' && _.isArray(astNode.stmts.list))) {
             throw new Error('Failed sanity checks on stmts!')
             }
             _.each(astNode.stmts.list, generateStatement)*/
            generateStatement(astNode.stmts);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'jsfunccall':
            print('(');
            print(astNode.expr);
            print(')(');
            _.each(astNode.args, function(arg, i, args) {
                if(i) print(', ');
                generateExpression(arg);
            });
            print(')');
            break;

        default:
            throw new Error('Unknown expression type: "' + astNode.type + '"');
    }
};

var generateStatement = function(astNode, omitTerminator, omitIndentation) {
    if(arguments.length < 2) omitTerminator = false;
    switch(astNode.type) {

        case 'var':
            omitIndentation || printIndent();
            print('var ');
            _.each(astNode.list, function (varNode, i, args) {
                print (varNode.name);
                if (varNode.hasOwnProperty('expr')) {
                    print (' = ');
                    generateExpression(varNode.expr);
                }
                if(i < args.length - 1) {
                    print(', ');
                }
            });
            break;

        case 'assign':
            omitIndentation || printIndent();
            generateExpression(astNode.lval);
            print(' = ');
            generateExpression(astNode.rval);
            break;

        case 'scriptdef':
            omitIndentation || printIndent();
            print(strings.ANGL_GLOBALS_IDENTIFIER + '.' + astNode.name);
            print(' = function(');
            print(['other'].concat(astNode.args).join(', '));
            print(') {\n');
            indent();
            generateLocalVariableAllocation(astNode);
            // TODO this part of the AST doesn't seem quite right, suggesting there are
            // possibilities I'm not aware of.
            // These sanity checks will reject anything unexpected.
            /*if(!(_.isObject(astNode.stmts) && _(_.keys(astNode.stmts).sort()).isEqual(['list', 'type']) && astNode.stmts.type === 'statements' && _.isArray(astNode.stmts.list))) {
                throw new Error('Failed sanity checks on stmts!')
            }
            _.each(astNode.stmts.list, generateStatement)*/
            generateStatement(astNode.stmts);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'const':
            omitIndentation || printIndent();
            print(strings.ANGL_GLOBALS_IDENTIFIER + '.' + astNode.name);
            print(' = ');
            generateExpression(astNode.expr);
            break;

        case 'switch':
            omitIndentation || printIndent();
            print('switch(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            _.each(astNode.cases, function(caseNode) {
                generateCase(caseNode);
            });
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'for':
            omitIndentation || printIndent();
            print('for(');
            generateStatement(astNode.initstmt, true, true);
            print('; ');
            generateExpression(astNode.contexpr);
            print('; ');
            generateStatement(astNode.stepstmt, true, true);
            print(') {\n');
            indent();
            // TODO I bet there are some scoping issues I'm not dealing with correctly.
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'cmpassign':
            // Rewrite the cmpassign into a simpler binop and assign combo.
            // E.g. a += 1 becomes a = a + 1
            // TODO this will produce somewhat uglier code.  Maybe pretty it up later.
            // TODO what if the lval contains a function call?  Will it execute twice?
            generateStatement({
                type: 'assign',
                lval: astNode.lval,
                rval: {
                    type: 'binop',
                    op: astNode.op,
                    expr1: astNode.lval,
                    expr2: astNode.rval
                }
            }, true, omitIndentation);
            break;

        case 'ifelse':
            omitIndentation || printIndent();
            print('if(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            generateStatement(astNode.stmt1);
            outdent();
            omitIndentation || printIndent();
            print('} else {\n');
            indent();
            generateStatement(astNode.stmt2);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'if':
            // This is a special case of ifelse where the else block is empty.
            generateStatement({
                type: 'ifelse',
                expr: astNode.expr,
                stmt1: astNode.stmt,
                stmt2: {type: 'nop'}
            }, omitTerminator, omitIndentation);
            break;

        case 'while':
            omitIndentation || printIndent();
            print('while(');
            generateExpression(astNode.expr);
            print(') {\n');
            indent();
            generateStatement(astNode.stmt);
            outdent();
            printIndent();
            print('}');
            break;

        case 'dountil':
            omitIndentation || printIndent();
            print('do {\n');
            indent();
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('} while(!(');
            generateExpression(astNode.expr);
            print('))');
            break;

        case 'break':
            omitIndentation || printIndent();
            print('break');
            // TODO are break semantics ever different in Angl than they are in JS?
            break;

        case 'continue':
            omitIndentation || printIndent();
            print('continue');
            // TODO are continue semantics ever different in Angl than they are in JS?
            break;

        case 'statements':
            _.each(astNode.list, function(statement) {
                generateStatement(statement);
            });
            break;

        case 'funccall':
            // Delegate to the expression generator
            omitIndentation || printIndent();
            generateExpression(astNode);
            break;

        case 'with':
            // TODO I DONT WANNA IMPLEMENT THIS WAAAAAH
            // Also it requires some sort of runtime that can find all instances of
            // a given object type to iterate over.
            // For now, I'm emitting a comment that explains code has been omitted.
            var indexIdentifier = {
                type: 'identifier',
                variable: astNode.indexVariable
            };
            var allObjectsIdentifier = {
                type: 'identifier',
                variable: astNode.allObjectsVariable
            };
            var innerSelfIdentifier = {
                type: 'identifier',
                variable: astUtils.getAnglScope(astNode).getVariableByIdentifier('self')
            };
            omitIndentation || printIndent();
            print('for(');
            generateExpression(indexIdentifier);
            print(' = 0; ');
            generateExpression(indexIdentifier);
            print(' < ');
            generateExpression(allObjectsIdentifier);
            print('.length; ');
            generateExpression(indexIdentifier);
            print('++) {\n');
            indent();
            // Assign the value of inner `self`
            omitIndentation || printIndent();
            generateExpression(innerSelfIdentifier);
            print(' = ');
            generateExpression(allObjectsIdentifier);
            print('[');
            generateExpression(indexIdentifier);
            print('];\n');
            generateStatement(astNode.stmt);
            outdent();
            omitIndentation || printIndent();
            print('}');
            break;

        case 'return':
            // TODO is there ever a situation where a Javascript 'return' won't do what we want?
            // For example, inside a _.each() iterator function
            omitIndentation || printIndent();
            print('return (');
            generateExpression(astNode.expr);
            print(')');
            break;

        case 'exit':
            // TODO same caveats as 'return'
            omitIndentation || printIndent();
            print('return');
            break;

        case 'nop':
            // No-ops don't do anything.  I'm assuming they never trigger any behavior by
            // "seperating" adjacent statements.
            break;

        default:
            throw new Error('Unknown statement type: "' + astNode.type + '"');
    }
    // Statements are terminated by a semicolon and a newline
    // except for a few exceptions.
    // Also, in certain contexts we want to omit this termination
    // (e.g., initializer statement of a for loop)
    if(!_.contains(['nop', 'statements'], astNode.type) && !omitTerminator) {
        print(';\n');
    }
};

var generateCase = function(astNode) {
    switch(astNode.type) {

        case 'case':
            printIndent();
            print('case (');
            generateExpression(astNode.expr);
            print('):\n');
            indent();
            generateStatement(astNode.stmts);
            outdent();
            break;

        case 'defaultcase':
            printIndent();
            print('default:\n');
            indent();
            generateStatement(astNode.stmts);
            outdent();
            break;

        default:
            throw new Error('Unknown case type: "' + astNode.type + '"');
    }
};

var generateLocalVariableAllocation = function(astNode) {
    var localVariables = _.filter(astUtils.getAnglScope(astNode).getVariablesArray(), function(variable) {
        return variable.getAllocationType() === 'LOCAL';
    });
    if(localVariables.length) {
        printIndent();
        print('var ');
        print(_.map(localVariables, function(variable) {
            return variable.getJsIdentifier();
        }).join(', '));
        print(';\n');
    }
}

var generateTopNode = function(astNode) {
    switch(astNode.type) {

        case 'file':
            // RequireJS `define()` call
            print('define(function(require) {\n');
            indent();
            printIndent();
            // Something removes "use strict" from the source code unless I split it up like so.  RequireJS perhaps?
            print('"use' + ' strict";\n');
            // require modules
            printIndent();
            print('var ' + strings.ANGL_GLOBALS_IDENTIFIER + ' = require(' + JSON.stringify(strings.ANGL_GLOBALS_MODULE) + ');\n');
            printIndent();
            print('var ' + strings.ANGL_RUNTIME_IDENTIFIER + ' = require(' + JSON.stringify(strings.ANGL_RUNTIME_MODULE) + ');\n');
            // allocate local variables
            generateLocalVariableAllocation(astNode);
            // delegate to the statement generator
            _.each(astNode.stmts, function(node) {
                generateStatement(node);
            });
            outdent();
            print('});');
            break;

        default:
            throw new Error('Unknown root node type: "' + astNode.type + '"');
    }
};

var compile = module.exports = function(ast) {
    initializeCompiler();
    generateTopNode(ast);
    return _.flatten(buffer).join('');
};

<|endoftext|>