<commit_before>"use strict";

define(["oauth", "storage"], function(_oauth, Storage) {

    /**
     * Various utility functions
     */
    var Util = {
        /**
         * Returns true when run within a gaia environment
         */
        isFirefoxOS: function() {
            return window.location.protocol === 'app:';
        },
        /**
         * Determines if the connection is metered (pay per use)
         */
        isMeteredConnection: function() {
            var connection = navigator.connection || navigator.mozConnection;
            if (!connection) {
                return false;
            }
            return connection.metered;
        },
        /**
         * Determines if there's a cap on the bandwidth
         */
        isBandwidthCapped: function() {
            var connection = navigator.connection || navigator.mozConnection;
            if (!connection) {
                return false;
            }
            return connection.bandwidth == "Infinity";
        },
        /**
         * Obtains a URL query parameter
         */
        getParameterByName: function(name, params) {
            if (_.isUndefined(params)) {
                params = window.location.search;
            }
            if (params.length && params[0] != "?") {
                params = "?" + params;
            }
            var match = RegExp('[?&]' + name + '=([^&]*)').exec(params);
            return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
        },
        /**
         * Adds a query parameter to the specified url
         */
        appendQueryParam: function(url, name, value) {
            if (url.indexOf("?") == -1) {
                url += "?";
            } else {
                url += "&";
            }
            return url + name + "=" + value;
        },
        /**
         * Formats a number with thousand separators
         * http://stackoverflow.com/a/2901298/3153
         */
        numberWithCommas: function(x) {
            if (_.isUndefined(x)) {
                return 0;
            }
            return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        },
        /**
         * Binds a set of arguments to a function without modifying the bound `this`
         */
        partial: function( fn /*, args...*/) {
          var aps = Array.prototype.slice;
          var args = aps.call(arguments, 1);
          return function() {
              return fn.apply(this, args.concat(aps.call(arguments)));
          }
        },
        // An example generic Mixin that you can add to any component that should react
        // to changes in a Backbone component. The use cases we've identified thus far
        // are for Collections -- since they trigger a change event whenever any of
        // their constituent items are changed there's no need to reconcile for regular
        // models. One caveat: this relies on getBackboneModels() to always return the
        // same model instances throughout the lifecycle of the component. If you're
        // using this mixin correctly (it should be near the top of your component
        // hierarchy) this should not be an issue.
        // https://github.com/facebook/react/blob/1be9a9e/examples/todomvc-backbone/js/app.js#L148-L171
        BackboneMixin : {
          componentDidMount: function() {
            // Whenever there may be a change in the Backbone data, trigger a reconcile.
            this.getBackboneModels().forEach(function(model) {
              model.on('add change remove', this.forceUpdate.bind(this, null), this);
            }, this);
          },

          componentWillUnmount: function() {
            // Ensure that we clean up any dangling references when the component is
            // destroyed.
            this.getBackboneModels().forEach(function(model) {
              model.off(null, null, this);
            }, this);
          }
        }
    };

    /**
     * Client side library for the KA API
     */
    var APIClient = {
        oauth: {
            consumerKey: "",
            consumerSecret: "",
            token: Util.getParameterByName("oauth_token"),
            tokenSecret: Util.getParameterByName("oauth_token_secret"),
            oauthVerifier: Util.getParameterByName("oauth_verifier")
        },
        _loadAuth: function() {
            var oauth = localStorage.getItem("oauth");
            if (oauth) {
                this.oauth = JSON.parse(oauth);
            }
        },
        _loadCompletedAndProgress: function() {
            this.completedEntities = localStorage.getItem("completed");
            if (this.completedEntities) {
                this.completedEntities = JSON.parse(this.completedEntities);
            }
            this.startedEntities = localStorage.getItem("progress");
            if (this.startedEntities) {
                this.startedEntities = JSON.parse(this.startedEntities);
            }
        },
        _saveAuth: function() {
            localStorage.setItem("oauth", JSON.stringify(this.oauth));
        },
        _getSecrets: function() {
            return $.ajax({
                url: "/secrets.json",
                timeout: 15000,
                dataType: "json",
            });
        },
        _getAccessToken: function() {
            return $.oauth($.extend( {}, this.oauth, {
                type: "GET",
                url: this.API_BASE + "/auth/access_token",
                oauthCallback: this._oauthCallback,
                timeout: 15000,
                success: (data) => {
                    this.oauth.token = Util.getParameterByName("oauth_token", data);
                    this.oauth.tokenSecret = Util.getParameterByName("oauth_token_secret", data);
                    this.oauth.oauthVerifier = undefined;
                },
                error: (xhr, status) => {
                    alert("error: " + status);
                    console.log(xhr);
                }
            }));
        },
        isSignedIn: function() {
            return !!(this.oauth.consumerKey &&
                this.oauth.consumerSecret &&
                this.oauth.token &&
                this.oauth.tokenSecret);
        },
        init: function() {
            // If a login is not in progress, then load the auth info
            var oauthVerifier = Util.getParameterByName("oauth_token");
            if (!oauthVerifier) {
                this._loadAuth();
            }
            var d = $.Deferred();
            this._oauthCallback = window.location.href.split("#")[0].split('?')[0];
            this.completedEntities = [];
            this.startedEntities = [];
            this.videosProgress = {};
            if (Util.isFirefoxOS()) {
                this._oauthCallback = "http://firefoxos.non-existent-domain-asdfg.com/authenticated.html"
            }

            // TODO: Only fetch from secrets.json if we don't have local storage values
            this._getSecrets().done((keyData) => {
                this.oauth.consumerKey = keyData.key;
                this.oauth.consumerSecret = keyData.secret;

                // TODO: Only do access token stuff if we don't have local storage values
                if (this.oauth.oauthVerifier) {
                    this._getAccessToken().done(() => {
                        this._saveAuth();
                        d.resolve();
                    });
                } else {
                    d.resolve();
                }
            });
            return d.promise();
        },
        signIn: function() {
            // Start the oauth process by redirecting them to the request_token url
            var url = $.getURL($.extend( {}, this.oauth, {
                url: this.API_BASE + "/auth/request_token",
                oauthCallback: this._oauthCallback
            }));
            window.location = url;
        },
        signOut: function() {
            this.oauth.token = "";
            this.oauth.tokenSecret = "";
            this._saveAuth();
        },
        _basicAPICall: function(url, extraParams, method) {
            if (_.isUndefined(method)) {
                method = "GET";
            }
            if (extraParams) {
                for (var p in extraParams) {
                    url = Util.appendQueryParam(url, p, extraParams[p]);
                }
            }
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: method,
                url: url,
                timeout: 15000,
                dataType: "json",
                success: (data) => {
                    d.resolve(data);
                },
                error: function(xhr, status) {
                    d.reject();
                    console.error("error: " + status);
                    console.error(xhr);
                }
            }));
            return d.promise();
        },
        getUserProgress: function() {
            var extraParams = {
                kind: "Video,Article"
            };
            return this._basicAPICall(this.API_V1_BASE + "/user/progress_summary", extraParams);
        },
        getUserInfo: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user");
        },
        getTopicTree: function() {
            if (!Util.isFirefoxOS()) {
                return this._basicAPICall("/knowledge-map.json");
            }
            var d = $.Deferred();

            var filename = "topictree1.json";
            var topicTreePromise = Storage.readText(filename);
            topicTreePromise.done((data) => {
                d.resolve(JSON.parse(data));
            });
            topicTreePromise.fail(() => {
                var promise = this._basicAPICall(this.API_V1_BASE + "/fxos/topictree");
                promise.done((data) => {
                    Storage.writeText(filename, JSON.stringify(data));
                    d.resolve(data);
                });
            });

            return d.promise();
        },
        getVideoTranscript: function(youTubeId) {
            return this._basicAPICall(this.API_V1_BASE + `/videos/${youTubeId}/transcript`);
        },
        getArticle: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + "/articles/" + articleId);
        },
        reportArticleRead: function(articleId) {
            var d = $.Deferred();
            var promise = this._basicAPICall(this.API_V1_BASE + `/user/article/${articleId}/log`, undefined, "POST");
            promise.done((data) => {
                console.log('reported article complete!');
                console.log(data);
                d.resolve(data);
                this.completedEntities.push(articleId);
            });
            return d.promise();
        },
        getUserVideos: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user/videos");
        },
        reportVideoProgress: function(videoId, youTubeId, duration, secondsWatched, lastSecondWatched) {
            var extraParams = {
                seconds_watched: secondsWatched.toString(),
                last_second_watched: lastSecondWatched.toString()
            };
            return this._basicAPICall(this.API_V1_BASE + `/user/videos/${youTubeId}/log`, extraParams, "POST");
        },
        API_BASE: "https://www.khanacademy.org/api",
        API_V1_BASE: "https://www.khanacademy.org/api/v1",
        //API_BASE: "http://192.168.1.131:8080/api",
        //API_V1_BASE: "http://192.168.1.131:8080/api/v1",
        //API_BASE: "http://stable.ka.local:8080/api",
        //API_V1_BASE: "http://stable.ka.local:8080/api/v1",
    };

    var KA = {
        Util: Util,
        APIClient: APIClient
    };

    return KA;
});
<commit_msg>Fix unlimited bandwidth check<commit_after>"use strict";

define(["oauth", "storage"], function(_oauth, Storage) {

    /**
     * Various utility functions
     */
    var Util = {
        /**
         * Returns true when run within a gaia environment
         */
        isFirefoxOS: function() {
            return window.location.protocol === 'app:';
        },
        /**
         * Determines if the connection is metered (pay per use)
         */
        isMeteredConnection: function() {
            var connection = navigator.connection || navigator.mozConnection;
            if (!connection) {
                return false;
            }
            return connection.metered;
        },
        /**
         * Determines if there's a cap on the bandwidth
         */
        isBandwidthCapped: function() {
            var connection = navigator.connection || navigator.mozConnection;
            if (!connection) {
                return false;
            }
            return connection.bandwidth !== Infinity;
        },
        /**
         * Obtains a URL query parameter
         */
        getParameterByName: function(name, params) {
            if (_.isUndefined(params)) {
                params = window.location.search;
            }
            if (params.length && params[0] != "?") {
                params = "?" + params;
            }
            var match = RegExp('[?&]' + name + '=([^&]*)').exec(params);
            return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
        },
        /**
         * Adds a query parameter to the specified url
         */
        appendQueryParam: function(url, name, value) {
            if (url.indexOf("?") == -1) {
                url += "?";
            } else {
                url += "&";
            }
            return url + name + "=" + value;
        },
        /**
         * Formats a number with thousand separators
         * http://stackoverflow.com/a/2901298/3153
         */
        numberWithCommas: function(x) {
            if (_.isUndefined(x)) {
                return 0;
            }
            return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        },
        /**
         * Binds a set of arguments to a function without modifying the bound `this`
         */
        partial: function( fn /*, args...*/) {
          var aps = Array.prototype.slice;
          var args = aps.call(arguments, 1);
          return function() {
              return fn.apply(this, args.concat(aps.call(arguments)));
          }
        },
        // An example generic Mixin that you can add to any component that should react
        // to changes in a Backbone component. The use cases we've identified thus far
        // are for Collections -- since they trigger a change event whenever any of
        // their constituent items are changed there's no need to reconcile for regular
        // models. One caveat: this relies on getBackboneModels() to always return the
        // same model instances throughout the lifecycle of the component. If you're
        // using this mixin correctly (it should be near the top of your component
        // hierarchy) this should not be an issue.
        // https://github.com/facebook/react/blob/1be9a9e/examples/todomvc-backbone/js/app.js#L148-L171
        BackboneMixin : {
          componentDidMount: function() {
            // Whenever there may be a change in the Backbone data, trigger a reconcile.
            this.getBackboneModels().forEach(function(model) {
              model.on('add change remove', this.forceUpdate.bind(this, null), this);
            }, this);
          },

          componentWillUnmount: function() {
            // Ensure that we clean up any dangling references when the component is
            // destroyed.
            this.getBackboneModels().forEach(function(model) {
              model.off(null, null, this);
            }, this);
          }
        }
    };

    /**
     * Client side library for the KA API
     */
    var APIClient = {
        oauth: {
            consumerKey: "",
            consumerSecret: "",
            token: Util.getParameterByName("oauth_token"),
            tokenSecret: Util.getParameterByName("oauth_token_secret"),
            oauthVerifier: Util.getParameterByName("oauth_verifier")
        },
        _loadAuth: function() {
            var oauth = localStorage.getItem("oauth");
            if (oauth) {
                this.oauth = JSON.parse(oauth);
            }
        },
        _loadCompletedAndProgress: function() {
            this.completedEntities = localStorage.getItem("completed");
            if (this.completedEntities) {
                this.completedEntities = JSON.parse(this.completedEntities);
            }
            this.startedEntities = localStorage.getItem("progress");
            if (this.startedEntities) {
                this.startedEntities = JSON.parse(this.startedEntities);
            }
        },
        _saveAuth: function() {
            localStorage.setItem("oauth", JSON.stringify(this.oauth));
        },
        _getSecrets: function() {
            return $.ajax({
                url: "/secrets.json",
                timeout: 15000,
                dataType: "json",
            });
        },
        _getAccessToken: function() {
            return $.oauth($.extend( {}, this.oauth, {
                type: "GET",
                url: this.API_BASE + "/auth/access_token",
                oauthCallback: this._oauthCallback,
                timeout: 15000,
                success: (data) => {
                    this.oauth.token = Util.getParameterByName("oauth_token", data);
                    this.oauth.tokenSecret = Util.getParameterByName("oauth_token_secret", data);
                    this.oauth.oauthVerifier = undefined;
                },
                error: (xhr, status) => {
                    alert("error: " + status);
                    console.log(xhr);
                }
            }));
        },
        isSignedIn: function() {
            return !!(this.oauth.consumerKey &&
                this.oauth.consumerSecret &&
                this.oauth.token &&
                this.oauth.tokenSecret);
        },
        init: function() {
            // If a login is not in progress, then load the auth info
            var oauthVerifier = Util.getParameterByName("oauth_token");
            if (!oauthVerifier) {
                this._loadAuth();
            }
            var d = $.Deferred();
            this._oauthCallback = window.location.href.split("#")[0].split('?')[0];
            this.completedEntities = [];
            this.startedEntities = [];
            this.videosProgress = {};
            if (Util.isFirefoxOS()) {
                this._oauthCallback = "http://firefoxos.non-existent-domain-asdfg.com/authenticated.html"
            }

            // TODO: Only fetch from secrets.json if we don't have local storage values
            this._getSecrets().done((keyData) => {
                this.oauth.consumerKey = keyData.key;
                this.oauth.consumerSecret = keyData.secret;

                // TODO: Only do access token stuff if we don't have local storage values
                if (this.oauth.oauthVerifier) {
                    this._getAccessToken().done(() => {
                        this._saveAuth();
                        d.resolve();
                    });
                } else {
                    d.resolve();
                }
            });
            return d.promise();
        },
        signIn: function() {
            // Start the oauth process by redirecting them to the request_token url
            var url = $.getURL($.extend( {}, this.oauth, {
                url: this.API_BASE + "/auth/request_token",
                oauthCallback: this._oauthCallback
            }));
            window.location = url;
        },
        signOut: function() {
            this.oauth.token = "";
            this.oauth.tokenSecret = "";
            this._saveAuth();
        },
        _basicAPICall: function(url, extraParams, method) {
            if (_.isUndefined(method)) {
                method = "GET";
            }
            if (extraParams) {
                for (var p in extraParams) {
                    url = Util.appendQueryParam(url, p, extraParams[p]);
                }
            }
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: method,
                url: url,
                timeout: 15000,
                dataType: "json",
                success: (data) => {
                    d.resolve(data);
                },
                error: function(xhr, status) {
                    d.reject();
                    console.error("error: " + status);
                    console.error(xhr);
                }
            }));
            return d.promise();
        },
        getUserProgress: function() {
            var extraParams = {
                kind: "Video,Article"
            };
            return this._basicAPICall(this.API_V1_BASE + "/user/progress_summary", extraParams);
        },
        getUserInfo: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user");
        },
        getTopicTree: function() {
            if (!Util.isFirefoxOS()) {
                return this._basicAPICall("/knowledge-map.json");
            }
            var d = $.Deferred();

            var filename = "topictree1.json";
            var topicTreePromise = Storage.readText(filename);
            topicTreePromise.done((data) => {
                d.resolve(JSON.parse(data));
            });
            topicTreePromise.fail(() => {
                var promise = this._basicAPICall(this.API_V1_BASE + "/fxos/topictree");
                promise.done((data) => {
                    Storage.writeText(filename, JSON.stringify(data));
                    d.resolve(data);
                });
            });

            return d.promise();
        },
        getVideoTranscript: function(youTubeId) {
            return this._basicAPICall(this.API_V1_BASE + `/videos/${youTubeId}/transcript`);
        },
        getArticle: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + "/articles/" + articleId);
        },
        reportArticleRead: function(articleId) {
            var d = $.Deferred();
            var promise = this._basicAPICall(this.API_V1_BASE + `/user/article/${articleId}/log`, undefined, "POST");
            promise.done((data) => {
                console.log('reported article complete!');
                console.log(data);
                d.resolve(data);
                this.completedEntities.push(articleId);
            });
            return d.promise();
        },
        getUserVideos: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user/videos");
        },
        reportVideoProgress: function(videoId, youTubeId, duration, secondsWatched, lastSecondWatched) {
            var extraParams = {
                seconds_watched: secondsWatched.toString(),
                last_second_watched: lastSecondWatched.toString()
            };
            return this._basicAPICall(this.API_V1_BASE + `/user/videos/${youTubeId}/log`, extraParams, "POST");
        },
        API_BASE: "https://www.khanacademy.org/api",
        API_V1_BASE: "https://www.khanacademy.org/api/v1",
        //API_BASE: "http://192.168.1.131:8080/api",
        //API_V1_BASE: "http://192.168.1.131:8080/api/v1",
        //API_BASE: "http://stable.ka.local:8080/api",
        //API_V1_BASE: "http://stable.ka.local:8080/api/v1",
    };

    var KA = {
        Util: Util,
        APIClient: APIClient
    };

    return KA;
});
<|endoftext|>


<commit_before>'use strict';
var app = angular.module('app', []);
app.config(function($httpProvider){
  delete $httpProvider.defaults.headers.common['X-Requested-With'];
});

app.controller('ctrl', function($http) {
  var ctrl = this;
  ctrl.acronyms = [];
  ctrl.shout = function() {
      console.log(ctrl.input.des);
  }

  ctrl.acronize = function() {
    ctrl.acronyms.push(ctrl.input.acronym);
    ctrl.input.acronym='';
    ctrl.input.des='';
  }

  var URL = 'http://acronator.azurewebsites.net/api/test';
  var URL2 = 'http://words.bighugelabs.com/api/2/ff854eb1f0151b1a2d15940fdb5cb1b5/word/json';

  ctrl.sendObject = function() {
    if(!(ctrl.input&&ctrl.input.acronym&&ctrl.input.des)) {
      console.log('NO acronym or description');
    }
    else {
      //start spinner
      var target = document.getElementById('spinner');
      var spinner = new Spinner().spin(target);

      //create object from input fields
      var acronymObject = {'acronym': ctrl.input.acronym, 'description': ctrl.input.des}
      ctrl.input.acronym = '';
      ctrl.input.des = '';
      console.log(acronate(acronymObject.acronym, acronymObject.description));
      console.log(acronymObject);

      $http.post(URL, acronymObject).success(function(data) {
        //TEST
        console.log(data);

        ctrl.acronyms = acronate(acronymObject.acronym, acronymObject.description);

        //stop spinner
        spinner.stop();

        /*
        for(var i = 0; i<datas.length; i++) {
          var tokens = datas[i].replace('"', '').split(" ");
          console.log(tokens);
          ctrl.acronyms.push(tokens);
        }
        */

      }).error(function(){
        console.log('ERROR');
      });
    }
  }
});
<commit_msg>Stop Azure<commit_after>'use strict';
var app = angular.module('app', []);
app.config(function($httpProvider){
  $httpProvider.defaults.useXDomain = true;
  delete $httpProvider.defaults.headers.common['X-Requested-With'];
});

app.controller('ctrl', function($http) {
  var ctrl = this;
  ctrl.acronyms = [];
  ctrl.shout = function() {
      console.log(ctrl.input.des);
  }

  ctrl.acronize = function() {
    ctrl.acronyms.push(ctrl.input.acronym);
    ctrl.input.acronym='';
    ctrl.input.des='';
  }

  ctrl.parse = function(object) {
    console.log(object);
    return JSON.parse(object);
  }

  var URL2 = 'http://words.bighugelabs.com/api/2/ff854eb1f0151b1a2d15940fdb5cb1b5/'
  + word
  + '/json'
  + '?callback=parse';

  ctrl.sendObject = function() {
    if(!(ctrl.input&&ctrl.input.acronym&&ctrl.input.des)) {
      console.log('NO acronym or description');
    }
    else {
      //start spinner
      var target = document.getElementById('spinner');
      var spinner = new Spinner().spin(target);

      //create object from input fields
      var acronymObject = {'acronym': ctrl.input.acronym, 'description': ctrl.input.des}
      ctrl.input.acronym = '';
      ctrl.input.des = '';

      ctrl.acronyms = acronate(acronymObject.acronym, acronymObject.description);

      //stop spinner
      spinner.stop();
    }
  }
});
<|endoftext|>


<commit_before>var spRedirect = function (appname) {
    var hostname = window.location.hostname;
    var pathname = window.location.pathname;
    var protocol = window.location.protocol;

    var reg = new RegExp ('/' + appname);
    var path = pathname.replace (reg, '/' + appname + '/sp');

    var locateurl = protocol + '//' + hostname + path;

    if (navigator.userAgent.indexOf('iPhone') > 0 &&
        navigator.userAgent.indexOf('iPad') == -1 ||
        navigator.userAgent.indexOf('iPod') > 0 ||
        navigator.userAgent.indexOf('Android') > 0)
    {
        location.href = locateurl;
    }
};
<commit_msg>bug fix<commit_after>var spRedirect = function (appname) {
    var hostname = window.location.hostname;
    var pathname = window.location.pathname;
    var protocol = window.location.protocol;

    var reg = new RegExp ('/' + appname);
    var path = pathname.replace (reg, '/' + appname + '/sp');

    var locateurl = protocol + '//' + hostname + path;

    console.log (locateurl);

    if (navigator.userAgent.indexOf('iPhone') > 0 &&
        navigator.userAgent.indexOf('iPad') == -1 ||
        navigator.userAgent.indexOf('iPod') > 0 ||
        navigator.userAgent.indexOf('Android') > 0)
    {
        location.href = locateurl;
    }
};
<|endoftext|>


<commit_before>"use strict";

var JS100 = JS100 || {};

JS100.Instrument = function(audioContext, config) {
  var instrument = {};

  instrument.audioContext = audioContext;

  // VCO
  instrument.type = config.waveform;
  instrument.amplitude = config.amplitude;

  // LFO
  instrument.lfoWaveform  = config.lfoWaveform;
  instrument.lfoFrequency = config.lfoFrequency;
  instrument.lfoAmplitude = config.lfoAmplitude;

  // VCF
  instrument.filterFrequency   = config.filterCutoff;
  instrument.filterResonance   = config.filterResonance;
  instrument.filterLFOWaveform = config.filterLFOWaveform;
  instrument.filterLFOFrequency = config.filterLFOFrequency;
  instrument.filterLFOAmplitude = config.filterLFOAmplitude;

  // Envelope
  instrument.envelopeAttack  = config.envelopeAttack;
  instrument.envelopeDecay   = config.envelopeDecay;
  instrument.envelopeSustain = config.envelopeSustain;
  instrument.envelopeRelease = config.envelopeRelease;

  return instrument;
};

JS100.InstrumentEvent = function(instrument) {
  var instrumentEvent = {};
  var audioContext = instrument.audioContext;
  instrumentEvent.instrument = instrument;

  // Build VCA
  var vca = audioContext.createGain();

  // Build VCF
  var vcf = audioContext.createBiquadFilter();

  // Build VCO
  var vco = audioContext.createOscillator();
  vco.type = instrument.type;

  // Build LFO
  var pitchLfoVco = audioContext.createOscillator();
  pitchLfoVco.type = instrument.lfoWaveform;
  pitchLfoVco.frequency.value = instrument.lfoFrequency;
  var pitchLfoGain = audioContext.createGain();
  pitchLfoGain.gain.value = instrument.lfoAmplitude;

  vcf.frequency.value = instrument.filterFrequency;
  vcf.Q.value = instrument.filterResonance;
  var filterLfoVco = audioContext.createOscillator();
  filterLfoVco.type = instrument.filterLFOWaveform;
  filterLfoVco.frequency.value = instrument.filterLFOFrequency;
  var filterLfoGain = audioContext.createGain();
  filterLfoGain.gain.value = instrument.filterLFOAmplitude;

  pitchLfoVco.connect(pitchLfoGain);
  pitchLfoGain.connect(vco.frequency);
  filterLfoVco.connect(filterLfoGain);
  filterLfoGain.connect(vcf.frequency);
  vco.connect(vcf);
  vcf.connect(vca);
  vca.connect(audioContext.destination);

  instrumentEvent.play = function(note, gateOnTime, gateOffTime) {
    if (note.frequency() > 0.0) {
      vco.frequency.value = note.frequency();

      vco.start(gateOnTime);
      pitchLfoVco.start(gateOnTime);
      filterLfoVco.start(gateOnTime);

      // Envelope
      vca.gain.setValueAtTime(0.0, gateOnTime);
      vca.gain.linearRampToValueAtTime(instrument.amplitude, gateOnTime + instrument.envelopeAttack);
      vca.gain.linearRampToValueAtTime(instrument.envelopeSustain * instrument.amplitude,
                                       gateOnTime + instrument.envelopeAttack + instrument.envelopeDecay);

      vco.stop(gateOffTime + instrument.envelopeRelease);
      vca.gain.linearRampToValueAtTime(0.0, gateOffTime + instrument.envelopeRelease);
      pitchLfoVco.stop(gateOffTime + instrument.envelopeRelease);
      filterLfoVco.stop(gateOffTime + instrument.envelopeRelease);
    }
  };

  return instrumentEvent;
}

JS100.Transport = function(audioContext, instrument, rawNotes, tempo, loop) {
  var transport = {};

  var SCHEDULE_AHEAD_TIME = 0.2;
  var TICK_INTERVAL = 50;  // in milliseconds

  var parseRawNotes = function(rawNotes) {
    var sequence = [];
    var splitNotes = rawNotes.split(" ");

    for (var i = 0; i < splitNotes.length; i++) {
      var noteParts = splitNotes[i].split("-");
      var note = JS100.Note(noteParts[0], noteParts[1], 1);
      sequence[i] = note;
    }

    return sequence;
  };

  transport.updateTempo = function(newTempo) {
    transport.tempo = newTempo;

    var sixteenthsPerMinute = transport.tempo * 4;
    transport.stepTime = 60.0 / sixteenthsPerMinute;
  };

  transport.tick = function() {
    var sequence = transport.sequence;
    var finalTime = audioContext.currentTime + SCHEDULE_AHEAD_TIME;

    while (nextNoteTime < finalTime) {
      for (var i = 0; i < sequence.length; i++) {
        var note = sequence[sequenceIndex];

        var e = new JS100.InstrumentEvent(transport.instrument);
        e.play(note, nextNoteTime, nextNoteTime + transport.stepTime);
      }

      sequenceIndex += 1;
      if (sequenceIndex >= sequence.length) {
        if (transport.loop) {
          sequenceIndex = 0;
        }
        else {
          stop();
        }
      }

      nextNoteTime += transport.stepTime;
    }
  };

  transport.toggle = function() {
    if (playing) {
      stop();
    }
    else {
      start();
    }
  };

  function start() {
    sequenceIndex = 0;
    nextNoteTime = audioContext.currentTime;

    transport.tick();
    timeoutId = window.setInterval(transport.tick, TICK_INTERVAL);
    playing = true;
  };

  function stop() {
    window.clearInterval(timeoutId);
    playing = false;
  };

  var sequenceIndex;
  var nextNoteTime;
  var timeoutId;
  var playing = false;

  transport.sequence = parseRawNotes(rawNotes);
  transport.loop = loop;
  transport.instrument = instrument;

  transport.updateTempo(tempo);

  return transport;
};

JS100.Note = function(noteName, octave, duration) {
  var note = {};

  note.noteName = noteName;
  note.octave = octave;
  note.duration = duration;

  note.frequency = function() {
    if (noteName === "_" || noteName === ".") {
      return 0.0;
    }

    noteName = JS100.MusicTheory.ENHARMONIC_EQUIVALENTS[noteName];
    var octaveMultiplier = Math.pow(2.0, (octave - JS100.MusicTheory.MIDDLE_OCTAVE));
    var frequency = JS100.MusicTheory.NOTE_RATIOS[noteName] * JS100.MusicTheory.MIDDLE_A_FREQUENCY * octaveMultiplier;

    return frequency;
  };

  return note;
};

JS100.MusicTheory = {
  NOTE_RATIOS: {
    "A"  : 1.0,
    "A#" : 16.0 / 15.0,
    "B"  : 9.0 / 8.0,
    "C"  : 6.0 / 5.0,
    "C#" : 5.0 / 4.0,
    "D"  : 4.0 / 3.0,
    "D#" : 45.0 / 32.0,
    "E"  : 3.0 / 2.0,
    "F"  : 8.0 / 5.0,
    "F#" : 5.0 / 3.0,
    "G"  : 9.0 / 5.0,
    "G#" : 15.0 / 8.0
  },

  ENHARMONIC_EQUIVALENTS: {
    "A"   : "A",
    "G##" : "A",
    "Bbb" : "A",

    "A#"  : "A#",
    "Bb"  : "A#",
    "Cbb" : "A#",

    "B"   : "B",
    "A##" : "B",
    "Cb"  : "B",

    "C"   : "C",
    "B#"  : "C",
    "Dbb" : "C",

    "C#"  : "C#",
    "B##" : "C#",
    "Db"  : "C#",

    "D"   : "D",
    "C##" : "D",
    "Ebb" : "D",

    "D#"  : "D#",
    "Eb"  : "D#",
    "Fbb" : "D#",

    "E"   : "E",
    "D##" : "E",
    "Fb"  : "E",

    "F"   : "F",
    "E#"  : "F",
    "Gbb" : "F",

    "F#"  : "F#",
    "E##" : "F#",
    "Gb"  : "F#",

    "G"   : "G",
    "F##" : "G",
    "Abb" : "G",

    "G#"  : "G#",
    "Ab"  : "G#"
  },

  MIDDLE_OCTAVE: 4,
  MIDDLE_A_FREQUENCY: 440.0,
};
<commit_msg>Making method private<commit_after>"use strict";

var JS100 = JS100 || {};

JS100.Instrument = function(audioContext, config) {
  var instrument = {};

  instrument.audioContext = audioContext;

  // VCO
  instrument.type = config.waveform;
  instrument.amplitude = config.amplitude;

  // LFO
  instrument.lfoWaveform  = config.lfoWaveform;
  instrument.lfoFrequency = config.lfoFrequency;
  instrument.lfoAmplitude = config.lfoAmplitude;

  // VCF
  instrument.filterFrequency   = config.filterCutoff;
  instrument.filterResonance   = config.filterResonance;
  instrument.filterLFOWaveform = config.filterLFOWaveform;
  instrument.filterLFOFrequency = config.filterLFOFrequency;
  instrument.filterLFOAmplitude = config.filterLFOAmplitude;

  // Envelope
  instrument.envelopeAttack  = config.envelopeAttack;
  instrument.envelopeDecay   = config.envelopeDecay;
  instrument.envelopeSustain = config.envelopeSustain;
  instrument.envelopeRelease = config.envelopeRelease;

  return instrument;
};

JS100.InstrumentEvent = function(instrument) {
  var instrumentEvent = {};
  var audioContext = instrument.audioContext;
  instrumentEvent.instrument = instrument;

  // Build VCA
  var vca = audioContext.createGain();

  // Build VCF
  var vcf = audioContext.createBiquadFilter();

  // Build VCO
  var vco = audioContext.createOscillator();
  vco.type = instrument.type;

  // Build LFO
  var pitchLfoVco = audioContext.createOscillator();
  pitchLfoVco.type = instrument.lfoWaveform;
  pitchLfoVco.frequency.value = instrument.lfoFrequency;
  var pitchLfoGain = audioContext.createGain();
  pitchLfoGain.gain.value = instrument.lfoAmplitude;

  vcf.frequency.value = instrument.filterFrequency;
  vcf.Q.value = instrument.filterResonance;
  var filterLfoVco = audioContext.createOscillator();
  filterLfoVco.type = instrument.filterLFOWaveform;
  filterLfoVco.frequency.value = instrument.filterLFOFrequency;
  var filterLfoGain = audioContext.createGain();
  filterLfoGain.gain.value = instrument.filterLFOAmplitude;

  pitchLfoVco.connect(pitchLfoGain);
  pitchLfoGain.connect(vco.frequency);
  filterLfoVco.connect(filterLfoGain);
  filterLfoGain.connect(vcf.frequency);
  vco.connect(vcf);
  vcf.connect(vca);
  vca.connect(audioContext.destination);

  instrumentEvent.play = function(note, gateOnTime, gateOffTime) {
    if (note.frequency() > 0.0) {
      vco.frequency.value = note.frequency();

      vco.start(gateOnTime);
      pitchLfoVco.start(gateOnTime);
      filterLfoVco.start(gateOnTime);

      // Envelope
      vca.gain.setValueAtTime(0.0, gateOnTime);
      vca.gain.linearRampToValueAtTime(instrument.amplitude, gateOnTime + instrument.envelopeAttack);
      vca.gain.linearRampToValueAtTime(instrument.envelopeSustain * instrument.amplitude,
                                       gateOnTime + instrument.envelopeAttack + instrument.envelopeDecay);

      vco.stop(gateOffTime + instrument.envelopeRelease);
      vca.gain.linearRampToValueAtTime(0.0, gateOffTime + instrument.envelopeRelease);
      pitchLfoVco.stop(gateOffTime + instrument.envelopeRelease);
      filterLfoVco.stop(gateOffTime + instrument.envelopeRelease);
    }
  };

  return instrumentEvent;
}

JS100.Transport = function(audioContext, instrument, rawNotes, tempo, loop) {
  var transport = {};

  var SCHEDULE_AHEAD_TIME = 0.2;
  var TICK_INTERVAL = 50;  // in milliseconds

  var parseRawNotes = function(rawNotes) {
    var sequence = [];
    var splitNotes = rawNotes.split(" ");

    for (var i = 0; i < splitNotes.length; i++) {
      var noteParts = splitNotes[i].split("-");
      var note = JS100.Note(noteParts[0], noteParts[1], 1);
      sequence[i] = note;
    }

    return sequence;
  };

  transport.updateTempo = function(newTempo) {
    transport.tempo = newTempo;

    var sixteenthsPerMinute = transport.tempo * 4;
    transport.stepTime = 60.0 / sixteenthsPerMinute;
  };

  transport.toggle = function() {
    if (playing) {
      stop();
    }
    else {
      start();
    }
  };

  function tick() {
    var sequence = transport.sequence;
    var finalTime = audioContext.currentTime + SCHEDULE_AHEAD_TIME;

    while (nextNoteTime < finalTime) {
      for (var i = 0; i < sequence.length; i++) {
        var note = sequence[sequenceIndex];

        var e = new JS100.InstrumentEvent(transport.instrument);
        e.play(note, nextNoteTime, nextNoteTime + transport.stepTime);
      }

      sequenceIndex += 1;
      if (sequenceIndex >= sequence.length) {
        if (transport.loop) {
          sequenceIndex = 0;
        }
        else {
          stop();
        }
      }

      nextNoteTime += transport.stepTime;
    }
  };

  function start() {
    sequenceIndex = 0;
    nextNoteTime = audioContext.currentTime;

    tick();
    timeoutId = window.setInterval(tick, TICK_INTERVAL);
    playing = true;
  };

  function stop() {
    window.clearInterval(timeoutId);
    playing = false;
  };

  var sequenceIndex;
  var nextNoteTime;
  var timeoutId;
  var playing = false;

  transport.sequence = parseRawNotes(rawNotes);
  transport.loop = loop;
  transport.instrument = instrument;

  transport.updateTempo(tempo);

  return transport;
};

JS100.Note = function(noteName, octave, duration) {
  var note = {};

  note.noteName = noteName;
  note.octave = octave;
  note.duration = duration;

  note.frequency = function() {
    if (noteName === "_" || noteName === ".") {
      return 0.0;
    }

    noteName = JS100.MusicTheory.ENHARMONIC_EQUIVALENTS[noteName];
    var octaveMultiplier = Math.pow(2.0, (octave - JS100.MusicTheory.MIDDLE_OCTAVE));
    var frequency = JS100.MusicTheory.NOTE_RATIOS[noteName] * JS100.MusicTheory.MIDDLE_A_FREQUENCY * octaveMultiplier;

    return frequency;
  };

  return note;
};

JS100.MusicTheory = {
  NOTE_RATIOS: {
    "A"  : 1.0,
    "A#" : 16.0 / 15.0,
    "B"  : 9.0 / 8.0,
    "C"  : 6.0 / 5.0,
    "C#" : 5.0 / 4.0,
    "D"  : 4.0 / 3.0,
    "D#" : 45.0 / 32.0,
    "E"  : 3.0 / 2.0,
    "F"  : 8.0 / 5.0,
    "F#" : 5.0 / 3.0,
    "G"  : 9.0 / 5.0,
    "G#" : 15.0 / 8.0
  },

  ENHARMONIC_EQUIVALENTS: {
    "A"   : "A",
    "G##" : "A",
    "Bbb" : "A",

    "A#"  : "A#",
    "Bb"  : "A#",
    "Cbb" : "A#",

    "B"   : "B",
    "A##" : "B",
    "Cb"  : "B",

    "C"   : "C",
    "B#"  : "C",
    "Dbb" : "C",

    "C#"  : "C#",
    "B##" : "C#",
    "Db"  : "C#",

    "D"   : "D",
    "C##" : "D",
    "Ebb" : "D",

    "D#"  : "D#",
    "Eb"  : "D#",
    "Fbb" : "D#",

    "E"   : "E",
    "D##" : "E",
    "Fb"  : "E",

    "F"   : "F",
    "E#"  : "F",
    "Gbb" : "F",

    "F#"  : "F#",
    "E##" : "F#",
    "Gb"  : "F#",

    "G"   : "G",
    "F##" : "G",
    "Abb" : "G",

    "G#"  : "G#",
    "Ab"  : "G#"
  },

  MIDDLE_OCTAVE: 4,
  MIDDLE_A_FREQUENCY: 440.0,
};
<|endoftext|>


<commit_before>load("util.js");

var io = {
	print: function(s) {
		return print(s);
	},
	quit: function() {
		return quit();
	},
};

var connector = {
	selector: java.nio.channels.Selector.open(),
	read_buf: java.nio.ByteBuffer.allocate(1024),
	events: [],

	/* Register/unregister descriptors and operations with the selector.
	   These assume that only one ops of each type is registered per
	   descriptor; for example, you can't register two read events, because
	   when one is unregistered it will unregister the other. */
	register: function(sd, ops) {
		var key = sd.keyFor(this.selector);
		var current_ops = 0;
		if (key)
			current_ops = key.interestOps();
		return sd.register(this.selector, current_ops | ops);
	},
	unregister: function(sd, ops) {
		var key = sd.keyFor(this.selector);
		var current_ops = 0;
		if (key)
			current_ops = key.interestOps();
		return sd.register(this.selector, current_ops & ~ops);
	},

	bytebuffer_to_string: function(bb) {
		return String(new java.lang.String(java.util.Arrays.copyOf(bb.array(), bb.position()), "ISO-8859-1"));
	},
	string_to_bytebuffer: function(s) {
		return java.nio.ByteBuffer.wrap(new java.lang.String(s).getBytes("ISO-8859-1"));
	},

	/* Stringify/serialize a socket descriptor. */
	s_sd: function(sd) {
		var s = sd.socket();
		return s["class"] + "\0" + s.getInetAddress().getHostAddress() + "\0" + s.getPort();
	},

	op_accept: function(channel) {
		var c = channel.socket().accept();
		this.events.push({
			type: "accept",
			sd: channel,
			address: c.getInetAddress().getHostAddress(),
			port: c.getPort(),
			client: c.channel,
		});
		/* For accept only, do not unregister the selection key. */
	},
	op_connect: function(channel) {
		if (channel.isConnectionPending())
			channel.finishConnect();
		this.events.push({
			type: "connect",
			sd: channel,
			address: channel.socket().getInetAddress().getHostAddress(),
			port: channel.socket().getPort(),
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_CONNECT);
	},
	op_read: function(channel) {
		this.read_buf.clear();
		var n = channel.read(this.read_buf);
		var data;
		if (n == -1)
			data = undefined;
		else
			data = this.bytebuffer_to_string(this.read_buf);
		this.events.push({
			type: "recv",
			sd: channel,
			data: data,
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_READ);
	},
	op_write: function(channel) {
		this.events.push({
			type: "send",
			sd: channel,
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_WRITE);
	},

	wait_for_event: function() {
		while (true) {
			if (this.events.length > 0)
				return this.events.shift();

			var n;
			do {
				n = this.selector.select();
			} while (n == 0);
			var key = this.selector.selectedKeys().iterator().next();
			var channel = key.channel();
			this.selector.selectedKeys().remove(key);
			if ((key.readyOps() & java.nio.channels.SelectionKey.OP_ACCEPT)
				== java.nio.channels.SelectionKey.OP_ACCEPT) {
				this.op_accept(channel);
			} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_CONNECT)
				== java.nio.channels.SelectionKey.OP_CONNECT) {
				this.op_connect(channel);
			} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_READ)
				== java.nio.channels.SelectionKey.OP_READ) {
				this.op_read(channel);
			} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_WRITE)
				== java.nio.channels.SelectionKey.OP_WRITE) {
				this.op_write(channel);
			} else {
				throw new Error("Unknown selection key op.");
			}
		}
	},
	listen: function(address, port) {
		var sd = java.nio.channels.ServerSocketChannel.open();
		sd.configureBlocking(false);
		sd.socket().bind(java.net.InetSocketAddress(port));
		this.register(sd, java.nio.channels.SelectionKey.OP_ACCEPT);
		return sd;
	},
	connect: function(address, port) {
		var sd = java.nio.channels.SocketChannel.open();
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_CONNECT);
		sd.connect(java.net.InetSocketAddress(address, port));
		return sd;
	},
	recv: function(sd) {
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_READ);
		return sd;
	},
	send: function(sd, data) {
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_WRITE);
		sd.write(this.string_to_bytebuffer(data));
		return sd;
	},
	close: function(sd) {
		sd.close();
		sd.keyFor(this.selector).cancel();
	},
};

accept_pending = [];
connect_pending = [];
recv_pending = [];
send_pending = [];

function add_pending(pending_list, id, data) {
	pending_list.push({ id: id, data: data });
}

function get_pending(pending_list, id, keep) {
	for (var i = 0; i < pending_list.length; i++) {
		if (pending_list[i].id == id) {
			var pending = pending_list[i];
			if (!keep)
				pending_list.splice(i, 1)[0];
			return pending.data;
		}
	}
}

function listen(address, port, callback, userdata) {
	var sd = connector.listen(address, port);
	add_pending(accept_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function connect(address, port, callback, userdata) {
	var sd = connector.connect(address, port);
	add_pending(connect_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function recv(sd, callback, userdata) {
	var sd = connector.recv(sd);
	add_pending(recv_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function send(sd, data, callback, userdata) {
	var sd = connector.send(sd, data);
	add_pending(send_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function close(sd) {
	while (get_pending(accept_pending, sd))
		;
	while (get_pending(connect_pending, sd))
		;
	while (get_pending(recv_pending, sd))
		;
	while (get_pending(send_pending, sd))
		;
	connector.close(sd);
};

function event_loop() {
	while (true) {
		var ev = connector.wait_for_event();
		io.print("ev: " + repr(ev));
		var pending;
		switch (ev.type) {
		case "accept":
			/* accept events are persistent; don't remove pending
			   except on error. */
			pending = this.get_pending(this.accept_pending, ev.sd, true);
			args = [ev.sd, ev.address, ev.port, ev.client, pending.userdata];
			break;
		case "connect":
			pending = this.get_pending(this.connect_pending, ev.sd);
			args = [ev.sd, ev.address, ev.port, pending.userdata];
			break;
		case "recv":
			pending = this.get_pending(this.recv_pending, ev.sd);
			args = [ev.sd, ev.data, pending.userdata];
			break;
		case "send":
			pending = this.get_pending(this.send_pending, ev.sd);
			args = [ev.sd, pending.userdata];
			break;
		default:
			io.print("Unknown event type \"" + ev.type + "\".");
			io.quit();
		}
		if (pending.callback)
			pending.callback.apply(null, args);
	}
}

io.print("jscat starting.")

var LOCAL_ADDRESS = "0.0.0.0";
var LOCAL_PORT = 9998;

var DIRECTORY_ADDRESS = "localhost";
var DIRECTORY_PORT = 9999;

var peers = {};

function accept_handler(sd, address, port, client, userdata) {
	io.print("Connection from " + address + ":" + port + ".");
	/* Pass the client as userdata. */
	connect(DIRECTORY_ADDRESS, DIRECTORY_PORT, connect_handler, client);
}

function connect_handler(sd, address, port, userdata) {
	io.print("Connection to " + address + ":" + port + ".");
	peers[connector.s_sd(sd)] = userdata;
	peers[connector.s_sd(userdata)] = sd;
	/* Queue initial read events. */
	recv(sd, recv_handler, userdata);
	recv(userdata, recv_handler, sd);
}

function recv_handler(sd, data, userdata) {
	if (data == undefined) {
		close(sd);
		var peer = peers[connector.s_sd(sd)];
		if (peer) {
			connector.close(peer);
			delete peers[connector.s_sd(sd)];
			delete peers[connector.s_sd(peer)];
		}
	} else {
		send(userdata, data);
		recv(sd, recv_handler, userdata);
	}
}

listen(LOCAL_ADDRESS, LOCAL_PORT, accept_handler);

event_loop();
<commit_msg>Factor out the selection key handler. Handle all selection keys in each iteration, not just the first.<commit_after>load("util.js");

var io = {
	print: function(s) {
		return print(s);
	},
	quit: function() {
		return quit();
	},
};

var connector = {
	selector: java.nio.channels.Selector.open(),
	read_buf: java.nio.ByteBuffer.allocate(1024),
	events: [],

	/* Register/unregister descriptors and operations with the selector.
	   These assume that only one ops of each type is registered per
	   descriptor; for example, you can't register two read events, because
	   when one is unregistered it will unregister the other. */
	register: function(sd, ops) {
		var key = sd.keyFor(this.selector);
		var current_ops = 0;
		if (key)
			current_ops = key.interestOps();
		return sd.register(this.selector, current_ops | ops);
	},
	unregister: function(sd, ops) {
		var key = sd.keyFor(this.selector);
		var current_ops = 0;
		if (key)
			current_ops = key.interestOps();
		return sd.register(this.selector, current_ops & ~ops);
	},

	bytebuffer_to_string: function(bb) {
		return String(new java.lang.String(java.util.Arrays.copyOf(bb.array(), bb.position()), "ISO-8859-1"));
	},
	string_to_bytebuffer: function(s) {
		return java.nio.ByteBuffer.wrap(new java.lang.String(s).getBytes("ISO-8859-1"));
	},

	/* Stringify/serialize a socket descriptor. */
	s_sd: function(sd) {
		var s = sd.socket();
		return s["class"] + "\0" + s.getInetAddress().getHostAddress() + "\0" + s.getPort();
	},

	handle_selection_key: function(key) {
		var channel = key.channel();
		if ((key.readyOps() & java.nio.channels.SelectionKey.OP_ACCEPT)
			== java.nio.channels.SelectionKey.OP_ACCEPT) {
			this.op_accept(channel);
		} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_CONNECT)
			== java.nio.channels.SelectionKey.OP_CONNECT) {
			this.op_connect(channel);
		} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_READ)
			== java.nio.channels.SelectionKey.OP_READ) {
			this.op_read(channel);
		} else if ((key.readyOps() & java.nio.channels.SelectionKey.OP_WRITE)
			== java.nio.channels.SelectionKey.OP_WRITE) {
			this.op_write(channel);
		} else {
			throw new Error("Unknown selection key op.");
		}
	},
	op_accept: function(channel) {
		var c = channel.socket().accept();
		this.events.push({
			type: "accept",
			sd: channel,
			address: c.getInetAddress().getHostAddress(),
			port: c.getPort(),
			client: c.channel,
		});
		/* For accept only, do not unregister the selection key. */
	},
	op_connect: function(channel) {
		if (channel.isConnectionPending())
			channel.finishConnect();
		this.events.push({
			type: "connect",
			sd: channel,
			address: channel.socket().getInetAddress().getHostAddress(),
			port: channel.socket().getPort(),
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_CONNECT);
	},
	op_read: function(channel) {
		this.read_buf.clear();
		var n = channel.read(this.read_buf);
		var data;
		if (n == -1)
			data = undefined;
		else
			data = this.bytebuffer_to_string(this.read_buf);
		this.events.push({
			type: "recv",
			sd: channel,
			data: data,
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_READ);
	},
	op_write: function(channel) {
		this.events.push({
			type: "send",
			sd: channel,
		});
		this.unregister(channel, java.nio.channels.SelectionKey.OP_WRITE);
	},

	wait_for_event: function() {
		while (true) {
			if (this.events.length > 0)
				return this.events.shift();

			var n;
			do {
				n = this.selector.select();
			} while (n == 0);

			var iter = this.selector.selectedKeys().iterator();
			while (iter.hasNext())
				this.handle_selection_key(iter.next());
			this.selector.selectedKeys().clear();
		}
	},
	listen: function(address, port) {
		var sd = java.nio.channels.ServerSocketChannel.open();
		sd.configureBlocking(false);
		sd.socket().bind(java.net.InetSocketAddress(port));
		this.register(sd, java.nio.channels.SelectionKey.OP_ACCEPT);
		return sd;
	},
	connect: function(address, port) {
		var sd = java.nio.channels.SocketChannel.open();
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_CONNECT);
		sd.connect(java.net.InetSocketAddress(address, port));
		return sd;
	},
	recv: function(sd) {
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_READ);
		return sd;
	},
	send: function(sd, data) {
		sd.configureBlocking(false);
		this.register(sd, java.nio.channels.SelectionKey.OP_WRITE);
		sd.write(this.string_to_bytebuffer(data));
		return sd;
	},
	close: function(sd) {
		sd.close();
		sd.keyFor(this.selector).cancel();
	},
};

accept_pending = [];
connect_pending = [];
recv_pending = [];
send_pending = [];

function add_pending(pending_list, id, data) {
	pending_list.push({ id: id, data: data });
}

function get_pending(pending_list, id, keep) {
	for (var i = 0; i < pending_list.length; i++) {
		if (pending_list[i].id == id) {
			var pending = pending_list[i];
			if (!keep)
				pending_list.splice(i, 1)[0];
			return pending.data;
		}
	}
}

function listen(address, port, callback, userdata) {
	var sd = connector.listen(address, port);
	add_pending(accept_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function connect(address, port, callback, userdata) {
	var sd = connector.connect(address, port);
	add_pending(connect_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function recv(sd, callback, userdata) {
	var sd = connector.recv(sd);
	add_pending(recv_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function send(sd, data, callback, userdata) {
	var sd = connector.send(sd, data);
	add_pending(send_pending, sd, { callback: callback, userdata: userdata });
	return sd;
}

function close(sd) {
	while (get_pending(accept_pending, sd))
		;
	while (get_pending(connect_pending, sd))
		;
	while (get_pending(recv_pending, sd))
		;
	while (get_pending(send_pending, sd))
		;
	connector.close(sd);
};

function event_loop() {
	while (true) {
		var ev = connector.wait_for_event();
		io.print("ev: " + repr(ev));
		var pending;
		switch (ev.type) {
		case "accept":
			/* accept events are persistent; don't remove pending
			   except on error. */
			pending = this.get_pending(this.accept_pending, ev.sd, true);
			args = [ev.sd, ev.address, ev.port, ev.client, pending.userdata];
			break;
		case "connect":
			pending = this.get_pending(this.connect_pending, ev.sd);
			args = [ev.sd, ev.address, ev.port, pending.userdata];
			break;
		case "recv":
			pending = this.get_pending(this.recv_pending, ev.sd);
			args = [ev.sd, ev.data, pending.userdata];
			break;
		case "send":
			pending = this.get_pending(this.send_pending, ev.sd);
			args = [ev.sd, pending.userdata];
			break;
		default:
			io.print("Unknown event type \"" + ev.type + "\".");
			io.quit();
		}
		if (pending.callback)
			pending.callback.apply(null, args);
	}
}

io.print("jscat starting.")

var LOCAL_ADDRESS = "0.0.0.0";
var LOCAL_PORT = 9998;

var DIRECTORY_ADDRESS = "localhost";
var DIRECTORY_PORT = 9999;

var peers = {};

function accept_handler(sd, address, port, client, userdata) {
	io.print("Connection from " + address + ":" + port + ".");
	/* Pass the client as userdata. */
	connect(DIRECTORY_ADDRESS, DIRECTORY_PORT, connect_handler, client);
}

function connect_handler(sd, address, port, userdata) {
	io.print("Connection to " + address + ":" + port + ".");
	peers[connector.s_sd(sd)] = userdata;
	peers[connector.s_sd(userdata)] = sd;
	/* Queue initial read events. */
	recv(sd, recv_handler, userdata);
	recv(userdata, recv_handler, sd);
}

function recv_handler(sd, data, userdata) {
	if (data == undefined) {
		close(sd);
		var peer = peers[connector.s_sd(sd)];
		if (peer) {
			connector.close(peer);
			delete peers[connector.s_sd(sd)];
			delete peers[connector.s_sd(peer)];
		}
	} else {
		send(userdata, data);
		recv(sd, recv_handler, userdata);
	}
}

listen(LOCAL_ADDRESS, LOCAL_PORT, accept_handler);

event_loop();
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Pascal Belloncle

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

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

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

var
  util = require('util'),
  fs   = require('fs'),
  path = require('path'),
  q    = require('q'),
  argv = require('optimist')
    .options('output', {
     alias: 'o',
     'default':'output'
    })
    .options('config',{
     alias: 'c'
    })
    .options('version',{
     alias: 'v'
    })
    .options('help',{
     alias: 'h'
    })
    .boolean('A', 'd')
    .options('A',{
     alias: 'All'
    })
    .options('d',{
     alias: 'debug'
    })
    .options('templateDir', {
      alias: 't'
    })
    .argv,
  packageJson = require('./package.json'),
  jsdocParser = require('jsdoc3-parser'),
  Mustache = require('mustache');

/**
 * Transforms the AST into a form that represents a
 * single file with modules and their functions.
 *
 * @param {Object} ast
 * @returns {Object}
 *
 * @example
 * { functions:
 *    [ { name: 'testNamed',
 *        params: [ { name: 'file', type: 'String', value: 'filename to parse' } ],
 *        returns: '',
 *        version: '',
 *        description: 'This is a test function\nwith a description on multiple lines' },
 *      { name: 'testAnonynous',
 *        params: [],
 *        returns: 'the result',
 *        version: '',
 *        description: 'function without name',
 *        type: 'String' } ],
 *   methods: [],
 *   classes: [],
 *   modules:
 *    [ { name: 'test_module',
 *        functions:
 *         [ { name: 'testAnonynous',
 *             params: [],
 *             returns: 'the result',
 *             version: '',
 *             description: 'function without name',
 *             type: 'String' } ],
 *        classes: [],
 *        description: '' } ],
 *   global_functions:
 *    [ { name: 'testNamed',
 *        params: [ { name: 'file', type: 'String', value: 'filename to parse' } ],
 *        returns: '',
 *        version: '',
 *        description: 'This is a test function\nwith a description on multiple lines' } ],
 *   globalVariables: [],
 *   description: 'Some extra text\nSome more extra text',
 *   overview: 'This is the overview',
 *   copyright: '2012 Blah Blah Blah',
 *   license: 'MIT',
 *   author: 'Joe Schmo',
 *   version: ''
 * }
 */
function analyze(ast) {
  var result = {
    functions: [],
    methods: [],
    classes: [],
    modules: [],
    members: [],
    globalModule: null,
    globalVariables: [],
    description: '',
    overview: '',
    copyright: '',
    license: '',
    author: '',
    version: '',
    hasMembers: false
  },
  currentModule   = null,
  currentClass    = null,
  currentFunction = null;

  function initGlobalModule() {
    var global = {};
    global.name      = 'Global';
    global.functions = [];
    global.classes   = [];

    result.modules.push(global);
    result.globalModule = global;
  }

  if (!ast) {
    return null;
  }

  ast.forEach(function (tag) {
    switch (tag.kind) {
      case 'file':
        result.license   = tag.license;
        result.author    = tag.author;
        result.copyright = tag.copyright;
        result.overview  = tag.description;

        (currentFunction || result).version = tag.version;
        (currentFunction || result).deprecated = tag.deprecated || true;
        break;
      case 'function':
        if (tag.undocumented) { break; }

        var fn = tag;
        fn.params       = tag.params || [];
        fn.hasParams    = !!fn.params.length;
        // For the function signature
        fn.paramsString = fn.params.map(function(p) {
          return p.name;
        }).join(', ');

        // For param details
        fn.params.forEach(setPipedTypesString);
        fn.returns      = tag.returns || [];
        fn.returns.forEach(setPipedTypesString);
        // To avoid reaching to the parent for these fields
        fn.version      = tag.version || false;
        fn.fires        = tag.fires || [];
        fn.description  = tag.description;
        fn.deprecated   = tag.deprecated || false;
        fn.internal     = isInternal(fn.name);
        fn.moduleName   = currentModule ? currentModule.name : '';
        currentFunction = fn;
        if (currentClass) {
          currentClass.methods.push(fn);
          fn.className = currentClass ? currentClass.name : '';
        }
        else if (currentModule) {
          currentModule.functions.push(fn);
        } else {
          if (!result.globalModule) {
            initGlobalModule();
          }
          result.globalModule.functions.push(fn);
        }
        result.functions.push(fn);
        break;
      case 'emits':
      case 'fires':
        fn.fires.push(tag.name);
        break;
      case 'member':
        if (currentClass && tag.undocumented !== true) {
          currentClass.members.push(tag);
        } else if (tag.scope === 'inner' && tag.undocumented !== true) {
          result.members.push({member: tag.name});
          result.hasMembers = true;
        }
        break;
      case 'return':
      case 'returns':
        if (currentFunction) {
          currentFunction.returns = tag.text;
          currentFunction.type = tag.type;
        }
        break;
      case 'module':
        var module = {};
        module.name = tag.name;
        module.functions = [];
        module.classes = [];
        module.description = tag.description;
        module.requires = tag.requires || [];
        module.hasRequires = !!module.requires.length;
        module.requires.forEach(function(r, i) {
          if (!r) { return ''; }
          module.requires[i] = {req: r};
        });
        result.modules.push(module);
        currentModule = module;
        break;
      case 'class':
        var klass = {};
        klass.name = tag.name;
        klass.methods = [];
        klass.members = [];
        klass.description = tag.description;
        result.classes.push(klass);
        if (currentModule) {
          currentModule.classes.push(klass);
        } else {
          if (!result.globalModule) {
            initGlobalModule();
          }
          result.globalModule.classes.push(klass);
        }
        currentClass = klass;
        break;
    }
  });

  return result;
}

/**
 * Attaches a 'typesString' pipe-separated attribute
 * containing the node's types
 * @param {AST} node - May or may not contain a type attribute
 */
function setPipedTypesString(node) {
  if (! node.type) { return ''; }

  node.typesString = node.type.names.join(' | ');
}

function isInternal(name){
  return name.lastIndexOf('_', 0) === 0;
}

function inspect(text) {
  return util.inspect(text, false, 20);
}

/**
 * Renders markdown from the given analyzed AST
 * @param  {Object} ast - output from analyze()
 * @param  {String} templateDir - templates directory (optional)
 * @return {String} Markdown output
 */
function generateMD(ast, templateDir) {
  if (!ast) { return 'no analyzed ast to generate markdown from'; }
  if (!templateDir) {
    templateDir = __dirname + '/templates/';
  } else {
    templateDir = templateDir.replace(/\\/g, '/') + '/';
  }

  var templates = {
    file: fs.readFileSync(templateDir + 'file.mustache').toString(),
    class: fs.readFileSync(templateDir + 'class.mustache').toString(),
    function: fs.readFileSync(templateDir + 'function.mustache').toString()
  };

  return Mustache.render(templates.file, ast, templates);
}

function generateForDir(filename, destination, templateDir, cb, fileCb) {
  var waiting = 0;
  var touched = 0;
  var error = null;

  function oneFile(directory, file, cb) {
    var fullpath = path.join(destination, file);
    fullpath = fullpath.replace(/\.js$/, '.md');
    if (argv.debug) {
      console.log('Generating', fullpath);
    }
    waiting++;
    jsdocParser(path.join(directory, file), function(err, result) {
      if (err) {
        console.error('Error generating docs for file', file, err);
        waiting--;
        if (!waiting) {
          return cb(err);
        } else {
          error = err;
        }
      }
      if (argv.debug) {
        console.log(file + ' AST: ', util.inspect(result, false, 20));
        console.log(file + ' Analyzed: ', util.inspect(analyze(result), false, 20));
      }

      var data = analyze(result),
          output = generateMD(data, templateDir);

      if (output) {
        fileCb && fileCb(file, data);
        fs.writeFile(fullpath, output, function(err) {
          waiting--;
          if (err) {
            console.error('Error generating docs for file', file, err);
            error = err;
          }
          if (!waiting) {
            return cb(error);
          }
        });
      } else {
        waiting--;
        if (!waiting) {
          return cb(error);
        }
      }
    });
  }

  if (filename.match(/\.js$/)) {
    oneFile(path.dirname(filename), path.basename(filename), cb);
  } else {
    fs.stat(filename, function (err, s) {
      if (!err && s.isDirectory()) {
        fs.readdir(filename, function(err, files) {
          if (err) {
            console.error('Error generating docs for files', filename, err);
            return cb(err);
          }
          files.forEach(function(file) {
            if (file.match(/\.js$/)) {
              oneFile(filename, file, cb), touched++;
            }
          });
          if(!touched) {
            cb();
          }
        });
      } else {
        cb();
      }
    });
  }
}

function loadConfigFile(file, callback){
  var config;

  //check to see if file exists
  fs.exists(file, function(exists) {
    if (exists) {
      try {
        config = require(file);
      } catch(err) {
        console.error('Error loading config file: ', err);
        process.exit();
      }
      for(var key in config){
        if (key !== 'input'){
        argv[key] = config[key];
        } else {
        argv._[0] = config[key];
        }
      }
      callback();
    } else {
      console.error('Error loading config file: ', file);
      process.exit();
    }
  });
}

function printHelp(){
  console.log('Usage:\tjsdox [options] <file | directory>');
  console.log('\tjsdox --All --output docs folder\n');
  console.log('Options:');
  console.log('  -c, --config \t<file>\t Configuration JSON file.');
  console.log('  -A, --All\t\t Generates documentation for all available elements including internal methods.');
  console.log('  -d, --debug\t\t Prints debugging information to the console.');
  console.log('  -H, --help\t\t Prints this message and quits.');
  console.log('  -v, --version\t\t Prints the current version and quits.');
  console.log('  -o, --output\t\t Output directory.');
  console.log('  -t, --templateDir\t\t Template directory to use intead of built-in ones.');
  process.exit();
}

function printVersion(){
  console.log('Version: ' + packageJson.version);
  process.exit();
}

function jsdox() {
  //Handle options
  if(argv.help){
   printHelp();
  }

  if(argv.version){
   printVersion();
  }

  if(argv.config){
    loadConfigFile(argv.config, main);
  } else {
    main();
  }

  function main(){
    if(typeof argv._[0] !== 'undefined'){
      fs.mkdir(argv.output, function() {
        q.all(argv._.map(function(file) {
          var deferred = q.defer();

          generateForDir(file, argv.output, argv.templateDir, function(err) {
            if (err) {
              console.error(err);
              throw err;
            }

            deferred.resolve();
          });

          return deferred.promise;
        }))
        .then(function () {
          console.log('jsdox completed');
        });
      });
    } else {
      console.error('Error missing input file or directory.');
      printHelp();
    }
  }
}

exports.analyze = analyze;
exports.generateMD = generateMD;
exports.generateForDir = generateForDir;
exports.jsdox = jsdox;

if (require.main === module) {
  jsdox();
}
<commit_msg>remove extra tab<commit_after>/*
Copyright (c) 2012 Pascal Belloncle

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

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

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

var
  util = require('util'),
  fs   = require('fs'),
  path = require('path'),
  q    = require('q'),
  argv = require('optimist')
    .options('output', {
     alias: 'o',
     'default':'output'
    })
    .options('config',{
     alias: 'c'
    })
    .options('version',{
     alias: 'v'
    })
    .options('help',{
     alias: 'h'
    })
    .boolean('A', 'd')
    .options('A',{
     alias: 'All'
    })
    .options('d',{
     alias: 'debug'
    })
    .options('templateDir', {
      alias: 't'
    })
    .argv,
  packageJson = require('./package.json'),
  jsdocParser = require('jsdoc3-parser'),
  Mustache = require('mustache');

/**
 * Transforms the AST into a form that represents a
 * single file with modules and their functions.
 *
 * @param {Object} ast
 * @returns {Object}
 *
 * @example
 * { functions:
 *    [ { name: 'testNamed',
 *        params: [ { name: 'file', type: 'String', value: 'filename to parse' } ],
 *        returns: '',
 *        version: '',
 *        description: 'This is a test function\nwith a description on multiple lines' },
 *      { name: 'testAnonynous',
 *        params: [],
 *        returns: 'the result',
 *        version: '',
 *        description: 'function without name',
 *        type: 'String' } ],
 *   methods: [],
 *   classes: [],
 *   modules:
 *    [ { name: 'test_module',
 *        functions:
 *         [ { name: 'testAnonynous',
 *             params: [],
 *             returns: 'the result',
 *             version: '',
 *             description: 'function without name',
 *             type: 'String' } ],
 *        classes: [],
 *        description: '' } ],
 *   global_functions:
 *    [ { name: 'testNamed',
 *        params: [ { name: 'file', type: 'String', value: 'filename to parse' } ],
 *        returns: '',
 *        version: '',
 *        description: 'This is a test function\nwith a description on multiple lines' } ],
 *   globalVariables: [],
 *   description: 'Some extra text\nSome more extra text',
 *   overview: 'This is the overview',
 *   copyright: '2012 Blah Blah Blah',
 *   license: 'MIT',
 *   author: 'Joe Schmo',
 *   version: ''
 * }
 */
function analyze(ast) {
  var result = {
    functions: [],
    methods: [],
    classes: [],
    modules: [],
    members: [],
    globalModule: null,
    globalVariables: [],
    description: '',
    overview: '',
    copyright: '',
    license: '',
    author: '',
    version: '',
    hasMembers: false
  },
  currentModule   = null,
  currentClass    = null,
  currentFunction = null;

  function initGlobalModule() {
    var global = {};
    global.name      = 'Global';
    global.functions = [];
    global.classes   = [];

    result.modules.push(global);
    result.globalModule = global;
  }

  if (!ast) {
    return null;
  }

  ast.forEach(function (tag) {
    switch (tag.kind) {
      case 'file':
        result.license   = tag.license;
        result.author    = tag.author;
        result.copyright = tag.copyright;
        result.overview  = tag.description;

        (currentFunction || result).version = tag.version;
        (currentFunction || result).deprecated = tag.deprecated || true;
        break;
      case 'function':
        if (tag.undocumented) { break; }

        var fn = tag;
        fn.params       = tag.params || [];
        fn.hasParams    = !!fn.params.length;
        // For the function signature
        fn.paramsString = fn.params.map(function(p) {
          return p.name;
        }).join(', ');

        // For param details
        fn.params.forEach(setPipedTypesString);
        fn.returns      = tag.returns || [];
        fn.returns.forEach(setPipedTypesString);
        // To avoid reaching to the parent for these fields
        fn.version      = tag.version || false;
        fn.fires        = tag.fires || [];
        fn.description  = tag.description;
        fn.deprecated   = tag.deprecated || false;
        fn.internal     = isInternal(fn.name);
        fn.moduleName   = currentModule ? currentModule.name : '';
        currentFunction = fn;
        if (currentClass) {
          currentClass.methods.push(fn);
          fn.className = currentClass ? currentClass.name : '';
        }
        else if (currentModule) {
          currentModule.functions.push(fn);
        } else {
          if (!result.globalModule) {
            initGlobalModule();
          }
          result.globalModule.functions.push(fn);
        }
        result.functions.push(fn);
        break;
      case 'emits':
      case 'fires':
        fn.fires.push(tag.name);
        break;
      case 'member':
        if (currentClass && tag.undocumented !== true) {
          currentClass.members.push(tag);
        } else if (tag.scope === 'inner' && tag.undocumented !== true) {
          result.members.push({member: tag.name});
          result.hasMembers = true;
        }
        break;
      case 'return':
      case 'returns':
        if (currentFunction) {
          currentFunction.returns = tag.text;
          currentFunction.type = tag.type;
        }
        break;
      case 'module':
        var module = {};
        module.name = tag.name;
        module.functions = [];
        module.classes = [];
        module.description = tag.description;
        module.requires = tag.requires || [];
        module.hasRequires = !!module.requires.length;
        module.requires.forEach(function(r, i) {
          if (!r) { return ''; }
          module.requires[i] = {req: r};
        });
        result.modules.push(module);
        currentModule = module;
        break;
      case 'class':
        var klass = {};
        klass.name = tag.name;
        klass.methods = [];
        klass.members = [];
        klass.description = tag.description;
        result.classes.push(klass);
        if (currentModule) {
          currentModule.classes.push(klass);
        } else {
          if (!result.globalModule) {
            initGlobalModule();
          }
          result.globalModule.classes.push(klass);
        }
        currentClass = klass;
        break;
    }
  });

  return result;
}

/**
 * Attaches a 'typesString' pipe-separated attribute
 * containing the node's types
 * @param {AST} node - May or may not contain a type attribute
 */
function setPipedTypesString(node) {
  if (! node.type) { return ''; }

  node.typesString = node.type.names.join(' | ');
}

function isInternal(name){
  return name.lastIndexOf('_', 0) === 0;
}

function inspect(text) {
  return util.inspect(text, false, 20);
}

/**
 * Renders markdown from the given analyzed AST
 * @param  {Object} ast - output from analyze()
 * @param  {String} templateDir - templates directory (optional)
 * @return {String} Markdown output
 */
function generateMD(ast, templateDir) {
  if (!ast) { return 'no analyzed ast to generate markdown from'; }
  if (!templateDir) {
    templateDir = __dirname + '/templates/';
  } else {
    templateDir = templateDir.replace(/\\/g, '/') + '/';
  }

  var templates = {
    file: fs.readFileSync(templateDir + 'file.mustache').toString(),
    class: fs.readFileSync(templateDir + 'class.mustache').toString(),
    function: fs.readFileSync(templateDir + 'function.mustache').toString()
  };

  return Mustache.render(templates.file, ast, templates);
}

function generateForDir(filename, destination, templateDir, cb, fileCb) {
  var waiting = 0;
  var touched = 0;
  var error = null;

  function oneFile(directory, file, cb) {
    var fullpath = path.join(destination, file);
    fullpath = fullpath.replace(/\.js$/, '.md');
    if (argv.debug) {
      console.log('Generating', fullpath);
    }
    waiting++;
    jsdocParser(path.join(directory, file), function(err, result) {
      if (err) {
        console.error('Error generating docs for file', file, err);
        waiting--;
        if (!waiting) {
          return cb(err);
        } else {
          error = err;
        }
      }
      if (argv.debug) {
        console.log(file + ' AST: ', util.inspect(result, false, 20));
        console.log(file + ' Analyzed: ', util.inspect(analyze(result), false, 20));
      }

      var data = analyze(result),
          output = generateMD(data, templateDir);

      if (output) {
        fileCb && fileCb(file, data);
        fs.writeFile(fullpath, output, function(err) {
          waiting--;
          if (err) {
            console.error('Error generating docs for file', file, err);
            error = err;
          }
          if (!waiting) {
            return cb(error);
          }
        });
      } else {
        waiting--;
        if (!waiting) {
          return cb(error);
        }
      }
    });
  }

  if (filename.match(/\.js$/)) {
    oneFile(path.dirname(filename), path.basename(filename), cb);
  } else {
    fs.stat(filename, function (err, s) {
      if (!err && s.isDirectory()) {
        fs.readdir(filename, function(err, files) {
          if (err) {
            console.error('Error generating docs for files', filename, err);
            return cb(err);
          }
          files.forEach(function(file) {
            if (file.match(/\.js$/)) {
              oneFile(filename, file, cb), touched++;
            }
          });
          if(!touched) {
            cb();
          }
        });
      } else {
        cb();
      }
    });
  }
}

function loadConfigFile(file, callback){
  var config;

  //check to see if file exists
  fs.exists(file, function(exists) {
    if (exists) {
      try {
        config = require(file);
      } catch(err) {
        console.error('Error loading config file: ', err);
        process.exit();
      }
      for(var key in config){
        if (key !== 'input'){
        argv[key] = config[key];
        } else {
        argv._[0] = config[key];
        }
      }
      callback();
    } else {
      console.error('Error loading config file: ', file);
      process.exit();
    }
  });
}

function printHelp(){
  console.log('Usage:\tjsdox [options] <file | directory>');
  console.log('\tjsdox --All --output docs folder\n');
  console.log('Options:');
  console.log('  -c, --config \t<file>\t Configuration JSON file.');
  console.log('  -A, --All\t\t Generates documentation for all available elements including internal methods.');
  console.log('  -d, --debug\t\t Prints debugging information to the console.');
  console.log('  -H, --help\t\t Prints this message and quits.');
  console.log('  -v, --version\t\t Prints the current version and quits.');
  console.log('  -o, --output\t\t Output directory.');
  console.log('  -t, --templateDir\t Template directory to use intead of built-in ones.');
  process.exit();
}

function printVersion(){
  console.log('Version: ' + packageJson.version);
  process.exit();
}

function jsdox() {
  //Handle options
  if(argv.help){
   printHelp();
  }

  if(argv.version){
   printVersion();
  }

  if(argv.config){
    loadConfigFile(argv.config, main);
  } else {
    main();
  }

  function main(){
    if(typeof argv._[0] !== 'undefined'){
      fs.mkdir(argv.output, function() {
        q.all(argv._.map(function(file) {
          var deferred = q.defer();

          generateForDir(file, argv.output, argv.templateDir, function(err) {
            if (err) {
              console.error(err);
              throw err;
            }

            deferred.resolve();
          });

          return deferred.promise;
        }))
        .then(function () {
          console.log('jsdox completed');
        });
      });
    } else {
      console.error('Error missing input file or directory.');
      printHelp();
    }
  }
}

exports.analyze = analyze;
exports.generateMD = generateMD;
exports.generateForDir = generateForDir;
exports.jsdox = jsdox;

if (require.main === module) {
  jsdox();
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Pascal Belloncle

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

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

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

var
  util = require('util'),
  fs   = require('fs'),
  path = require('path'),
  q    = require('q'),
  argv = require('optimist')
    .options('output', {
     alias: 'o',
     'default':'output'
    })
    .options('config',{
     alias: 'c'
    })
    .options('version',{
     alias: 'v'
    })
    .options('help',{
     alias: 'h'
    })
    .boolean('A', 'd')
    .options('A',{
     alias: 'All'
    })
    .options('d',{
     alias: 'debug'
    })
    .options('templateDir', {
      alias: 't'
    })
    .argv,
  packageJson = require('./package.json'),
  jsdocParser = require('jsdoc3-parser'),
  analyze = require('./lib/analyze'),
  generateMD = require('./lib/generateMD');

function inspect(text) {
  return util.inspect(text, false, 20);
}

function generateForDir(filename, destination, templateDir, cb, fileCb) {
  var waiting = 0;
  var touched = 0;
  var error = null;

  function oneFile(directory, file, cb) {
    var fullpath = path.join(destination, file);
    fullpath = fullpath.replace(/\.js$/, '.md');
    if (argv.debug) {
      console.log('Generating', fullpath);
    }
    waiting++;
    jsdocParser(path.join(directory, file), function(err, result) {
      if (err) {
        console.error('Error generating docs for file', file, err);
        waiting--;
        if (!waiting) {
          return cb(err);
        } else {
          error = err;
        }
      }
      if (argv.debug) {
        console.log(file + ' AST: ', util.inspect(result, false, 20));
        console.log(file + ' Analyzed: ', util.inspect(analyze(result), false, 20));
      }

      var data = analyze(result),
          output = generateMD(data, templateDir);

      if (output) {
        fileCb && fileCb(file, data);
        fs.writeFile(fullpath, output, function(err) {
          waiting--;
          if (err) {
            console.error('Error generating docs for file', file, err);
            error = err;
          }
          if (!waiting) {
            return cb(error);
          }
        });
      } else {
        waiting--;
        if (!waiting) {
          return cb(error);
        }
      }
    });
  }

  if (filename.match(/\.js$/)) {
    oneFile(path.dirname(filename), path.basename(filename), cb);
  } else {
    fs.stat(filename, function (err, s) {
      if (!err && s.isDirectory()) {
        fs.readdir(filename, function(err, files) {
          if (err) {
            console.error('Error generating docs for files', filename, err);
            return cb(err);
          }
          files.forEach(function(file) {
            if (file.match(/\.js$/)) {
              oneFile(filename, file, cb), touched++;
            }
          });
          if(!touched) {
            cb();
          }
        });
      } else {
        cb();
      }
    });
  }
}

function loadConfigFile(file, callback){
  var config;

  //check to see if file exists
  fs.exists(file, function(exists) {
    if (exists) {
      try {
        config = require(file);
      } catch(err) {
        console.error('Error loading config file: ', err);
        process.exit();
      }
      for(var key in config){
        if (key !== 'input'){
        argv[key] = config[key];
        } else {
        argv._[0] = config[key];
        }
      }
      callback();
    } else {
      console.error('Error loading config file: ', file);
      process.exit();
    }
  });
}

function printHelp(){
  console.log('Usage:\tjsdox [options] <file | directory>');
  console.log('\tjsdox --All --output docs folder\n');
  console.log('Options:');
  console.log('  -c, --config \t<file>\t Configuration JSON file.');
  console.log('  -A, --All\t\t Generates documentation for all available elements including internal methods.');
  console.log('  -d, --debug\t\t Prints debugging information to the console.');
  console.log('  -H, --help\t\t Prints this message and quits.');
  console.log('  -v, --version\t\t Prints the current version and quits.');
  console.log('  -o, --output\t\t Output directory.');
  console.log('  -t, --templateDir\t Template directory to use instead of built-in ones.');
  process.exit();
}

function printVersion(){
  console.log('Version: ' + packageJson.version);
  process.exit();
}

function jsdox() {
  //Handle options
  if(argv.help){
   printHelp();
  }

  if(argv.version){
   printVersion();
  }

  if(argv.config){
    loadConfigFile(argv.config, main);
  } else {
    main();
  }

  function main(){
    if(typeof argv._[0] !== 'undefined'){
      fs.mkdir(argv.output, function() {
        q.all(argv._.map(function(file) {
          var deferred = q.defer();

          generateForDir(file, argv.output, argv.templateDir, function(err) {
            if (err) {
              console.error(err);
              throw err;
            }

            deferred.resolve();
          });

          return deferred.promise;
        }))
        .then(function () {
          console.log('jsdox completed');
        });
      });
    } else {
      console.error('Error missing input file or directory.');
      printHelp();
    }
  }
}

exports.analyze = analyze;
exports.generateMD = generateMD;
exports.generateForDir = generateForDir;
exports.jsdox = jsdox;

if (require.main === module) {
  jsdox();
}
<commit_msg>Readability cleanup<commit_after>/*
Copyright (c) 2012 Pascal Belloncle

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

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

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

var
  util = require('util'),
  fs   = require('fs'),
  path = require('path'),
  q    = require('q'),
  argv = require('optimist')
    .options('output', {
     alias: 'o',
     'default':'output'
    })
    .options('config',{
     alias: 'c'
    })
    .options('version',{
     alias: 'v'
    })
    .options('help',{
     alias: 'h'
    })
    .boolean('A', 'd')
    .options('A',{
     alias: 'All'
    })
    .options('d',{
     alias: 'debug'
    })
    .options('templateDir', {
      alias: 't'
    })
    .argv,
  packageJson = require('./package.json'),
  jsdocParser = require('jsdoc3-parser'),
  analyze = require('./lib/analyze'),
  generateMD = require('./lib/generateMD');

function inspect(text) {
  return util.inspect(text, false, 20);
}

function printHelp(){
  console.log('Usage:\tjsdox [options] <file | directory>');
  console.log('\tjsdox --All --output docs folder\n');
  console.log('Options:');
  console.log('  -c, --config \t<file>\t Configuration JSON file.');
  console.log('  -A, --All\t\t Generates documentation for all available elements including internal methods.');
  console.log('  -d, --debug\t\t Prints debugging information to the console.');
  console.log('  -H, --help\t\t Prints this message and quits.');
  console.log('  -v, --version\t\t Prints the current version and quits.');
  console.log('  -o, --output\t\t Output directory.');
  console.log('  -t, --templateDir\t Template directory to use instead of built-in ones.');
  process.exit();
}

function printVersion(){
  console.log('Version: ' + packageJson.version);
  process.exit();
}

/**
 * @param  {String}   filename
 * @param  {String}   destination
 * @param  {String}   templateDir
 * @param  {Function} cb
 * @param  {Function} fileCb
 */
function generateForDir(filename, destination, templateDir, cb, fileCb) {
  var waiting = 0,
      touched = 0,
      error = null;

  function oneFile(directory, file, cb) {
    var fullpath = path.join(destination, file);
    fullpath = fullpath.replace(/\.js$/, '.md');

    if (argv.debug) {
      console.log('Generating', fullpath);
    }

    waiting++;

    jsdocParser(path.join(directory, file), function(err, result) {
      if (err) {
        console.error('Error generating docs for file', file, err);
        waiting--;
        if (!waiting) {
          return cb(err);
        } else {
          error = err;
        }
      }

      if (argv.debug) {
        console.log(file + ' AST: ', util.inspect(result, false, 20));
        console.log(file + ' Analyzed: ', util.inspect(analyze(result), false, 20));
      }

      var data = analyze(result),
          output = generateMD(data, templateDir);

      if (output) {
        fileCb && fileCb(file, data);
        fs.writeFile(fullpath, output, function(err) {
          waiting--;
          if (err) {
            console.error('Error generating docs for file', file, err);
            error = err;
          }
          if (!waiting) {
            return cb(error);
          }
        });

      } else {
        waiting--;
        if (!waiting) {
          return cb(error);
        }
      }
    });
  }

  if (filename.match(/\.js$/)) {
    oneFile(path.dirname(filename), path.basename(filename), cb);

  } else {
    fs.stat(filename, function (err, s) {
      if (!err && s.isDirectory()) {
        fs.readdir(filename, function(err, files) {
          if (err) {
            console.error('Error generating docs for files', filename, err);
            return cb(err);
          }
          files.forEach(function(file) {
            if (file.match(/\.js$/)) {
              oneFile(filename, file, cb), touched++;
            }
          });
          if(!touched) {
            cb();
          }
        });
      } else {
        cb();
      }
    });
  }
}

/**
 * @param  {String}   file
 * @param  {Function} callback
 */
function loadConfigFile(file, callback){
  var config;

  //check to see if file exists
  fs.exists(file, function(exists) {
    if (exists) {
      try {
        config = require(file);
      } catch(err) {
        console.error('Error loading config file: ', err);
        process.exit();
      }

      for(var key in config){
        if (key !== 'input'){
          argv[key] = config[key];
        } else {
          argv._[0] = config[key];
        }
      }
      callback();

    } else {
      console.error('Error loading config file: ', file);
      process.exit();
    }
  });
}

function main(){
  if(typeof argv._[0] !== 'undefined'){
    fs.mkdir(argv.output, function() {
      q.all(argv._.map(function(file) {
        var deferred = q.defer();

        generateForDir(file, argv.output, argv.templateDir, function(err) {
          if (err) {
            console.error(err);
            throw err;
          }

          deferred.resolve();
        });

        return deferred.promise;
      }))
      .then(function () {
        console.log('jsdox completed');
      });
    });
  } else {
    console.error('Error missing input file or directory.');
    printHelp();
  }
}

function jsdox() {
  if(argv.help){
    printHelp();
  }

  if(argv.version){
    printVersion();
  }

  if(argv.config){
    loadConfigFile(argv.config, main);

  } else {
    main();
  }
}

exports.analyze = analyze;
exports.generateMD = generateMD;
exports.generateForDir = generateForDir;
exports.jsdox = jsdox;

if (require.main === module) {
  jsdox();
}
<|endoftext|>


<commit_before>require('karma/lib/cli').run();<commit_msg>should not be here<commit_after><|endoftext|>