<commit_before>$(document).on("pagecreate", "#pageMap", function(e, data){

  var ref = new Firebase("https://halle.firebaseio.com");

// Firebase Facebook login -----------------------------------------
// Get login to be first page you see with choice between google and facebook
  $('#login').on('tap', function(e) {
    e.preventDefault();
    var ref = new Firebase("https://halle.firebaseio.com");
    ref.authWithOAuthPopup("facebook", function(error, authData) {
      if (error) {
        alert("login failed!");
        console.log("Login Failed!", error);
      } else {
        alert("login successful!", authData);
        console.log("Authenticated successfully with payload:", authData);
      }
    });
  });

// Format map  -----------------------------------------
  $(".ui-content", this).css({
     height: $(window).height(),
     width: $(window).width()
  });

  var mapOptions = {
        zoom: 13,
        disableDefaultUI: true,
        zoomControl: true
        };
  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
  navigator.geolocation.getCurrentPosition(function(position) {

    initialLocation = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
    map.setCenter(initialLocation);

      var spaceId //MAKES PARKING SPACE ID AVAILABLE

      var marker = new google.maps.Marker({
        position: initialLocation,
        map: map,
        icon: currentLoc
      });

//Center Map Button ------------------------
      var centerControlDiv = document.createElement('div');
      var centerControl = new CenterControl(centerControlDiv, initialLocation, map);

      centerControlDiv.index = 1;
      map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(centerControlDiv);

//Directions ATTEMPT -------------------------------------------
      var directionsDisplay;
      var directionsService = new google.maps.DirectionsService();

      directionsDisplay = new google.maps.DirectionsRenderer();
      directionsDisplay.setMap(map);

      var calcRoute = function(start, finish) {
        var request = {
            origin:start,
            destination:finish,
            travelMode: google.maps.TravelMode.DRIVING
        };
        directionsService.route(request, function(response, status) {
          if (status == google.maps.DirectionsStatus.OK) {
            // setAllMap(null);
            directionsDisplay.setDirections(response);
          }
          else{alert("Server Error: Directions Unavailable")}
        });
      }

// Add a space to the database -----------------------------------------
// TO DO : Add ability to move marker before saving current location as open space -----------------------------------------
    $('#create-space').on('tap', function(e) {
      e.preventDefault();

      $('#post-space').popup("open", {
          overlayTheme: "a",
          positionTo: "window",
        })

      $('#add-space').on('tap', function(e) {
        var longitude = position.coords.longitude;
        var latitude  = position.coords.latitude;
        var note      = $(this).siblings('div').children().val()
        var data      = {space:{longitude:+longitude,latitude:+latitude,note:note}};
        var headers   = '{"Content-Type":"application/json"}';

        $.ajax({
          url: 'http://calm-island-3256.herokuapp.com/spaces',
          // url: 'http://localhost:3000/spaces',
          type: "POST",
          data: data,
          headers: headers
        }).done(function(response) {
          $('#add-space').remove();
          $('#note').remove();
          $('#popup-par').text('Added √');
          setTimeout(function () {
            $('#post-space').popup('close');
          }, 1500);
          var marker = new google.maps.Marker({
                  position: new google.maps.LatLng(response.latitude,response.longitude),
                  map: map,
                  title:  response.note,
                  icon: markerSelect(response),
                  id: response.id,
                  creation: response.converted_time,
                  animation: google.maps.Animation.DROP,
                  zIndex: google.maps.Marker.MAX_ZINDEX + 1
          });
        }).fail(function(response) {
          console.log(response);
          alert("shits fucked up");
        });
      });
    });

// Show available spaces from database -----------------------------------------
    $.ajax({
      url: 'http://calm-island-3256.herokuapp.com',
      // url: 'http://localhost:3000',
      type: "GET",
    }).done(function(response){
      parkingSpots = response
      for(i = 0; i < parkingSpots.length; i++){
        var marker = new google.maps.Marker({
              position: new google.maps.LatLng(parkingSpots[i].latitude,parkingSpots[i].longitude),
              map: map,
              title:  parkingSpots[i].note,
              icon: markerSelect(parkingSpots[i]), //set marker according to age
              id: parkingSpots[i].id,
              creation: parkingSpots[i].converted_time
        });
        google.maps.event.addListener(marker, 'click', spaceDetails);
      };
    });

    var spaceDetails = function() {
      spaceId = this.id
      $('#space-options').popup("open", {
        overlayTheme: "a",
        positionTo: "window",
      })
      $('p').text(this.title);
    };

// Claim a parking spot  -----------------------------------------
    $('#claim').on('click', function(e){
      e.preventDefault();
      var headers = '{"Content-Type":"application/json"}';
      var button = $(this)
      $.ajax({
        url: 'http://calm-island-3256.herokuapp.com/spaces/'+spaceId,
        // url: 'http://localhost:3000/spaces/'+spaceId,
        type: 'PUT',
        headers: headers,
        data: '' //test without this
      }).done(function(response) {
        $('#space-options p').remove();
        $('#space-options a').remove();
        $('#space-options button').remove();
        $('#space-options h4').text('Claimed √');
        setTimeout(function () {
          $('#space-options').popup('close');
        }, 1500);
        // navigation begins
        calcRoute(initialLocation, response.latitude +","+ response.longitude)
      }).fail(function(response) {
        alert("fuck you guys")
      })
    });
  });
});

// Space markers CSS -----------------------------------------
var currentLoc = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.65,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#263238',
        fillOpacity: 1
    }

var spaceFresh = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.5,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#76FF03',
        fillOpacity: 1
    }
var spaceStale = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.5,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#AD1457',
        fillOpacity: 1
    }

// Differentiate between new and stale spaces -----------------------------------------
var markerSelect = function(spaceObject){
  var creation = spaceObject.converted_time
  if ((Date.now() - creation) <= (5*60000)){
    return spaceFresh;
  }

  else{
    console.log(Date.now() - creation)
    return spaceStale;
  }
}
var CenterControl = function(controlDiv, centerLocation, map) {

  // Set CSS for the control border
  var controlUI = document.createElement('div');
  controlUI.style.backgroundColor = 'red';
  controlUI.style.border = '2px solid #fff';
  controlUI.style.borderRadius = '35px';
  controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
  controlUI.style.cursor = 'pointer';
  controlUI.style.marginBottom = '22px';
  controlUI.style.textAlign = 'center';
  controlUI.title = 'Click to recenter the map';
  controlDiv.appendChild(controlUI);

  // Set CSS for the control interior
  var controlText = document.createElement('div');
  controlText.style.color = 'rgb(25,25,25)';
  controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlText.style.fontSize = '4px';
  controlText.style.lineHeight = '38px';
  controlText.style.paddingLeft = '5px';
  controlText.style.paddingRight = '5px';
  controlText.innerHTML = 'Center';
  controlUI.appendChild(controlText);

  // Setup the click event listeners: simply set the map to
  // Chicago
  google.maps.event.addDomListener(controlUI, 'click', function() {
    map.setCenter(centerLocation)
  });
}

<commit_msg>added ajax call<commit_after>$(document).on("pagecreate", "#pageMap", function(e, data){

  var ref = new Firebase("https://halle.firebaseio.com");

// Firebase Facebook login -----------------------------------------
// Get login to be first page you see with choice between google and facebook
  $('#login').on('tap', function(e) {
    e.preventDefault();
    var ref = new Firebase("https://halle.firebaseio.com");
    ref.authWithOAuthPopup("facebook", function(error, authData) {
      if (error) {
        alert("login failed!");
        console.log("Login Failed!", error);
      } else {
        var user_id = authData.facebook.id
        $.ajax({
          // url: 'http://calm-island-3256.herokuapp.com',
          url: 'http://localhost:3000/users/'+user_id+'/identify',
          type: 'GET',
          data: authData
        }).done(function(response) {
          console.log(response)
        }).fail(function() {
          alert("YOU'RE A FAILURE")
          console.log("FAILURE")
        });
      }
    });
  });

// Format map  -----------------------------------------
  $(".ui-content", this).css({
     height: $(window).height(),
     width: $(window).width()
  });

  var mapOptions = {
        zoom: 13,
        disableDefaultUI: true,
        zoomControl: true
        };
  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
  navigator.geolocation.getCurrentPosition(function(position) {

    initialLocation = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
    map.setCenter(initialLocation);

      var spaceId //MAKES PARKING SPACE ID AVAILABLE

      var marker = new google.maps.Marker({
        position: initialLocation,
        map: map,
        icon: currentLoc
      });

//Center Map Button ------------------------
      var centerControlDiv = document.createElement('div');
      var centerControl = new CenterControl(centerControlDiv, initialLocation, map);

      centerControlDiv.index = 1;
      map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(centerControlDiv);

//Directions ATTEMPT -------------------------------------------
      var directionsDisplay;
      var directionsService = new google.maps.DirectionsService();

      directionsDisplay = new google.maps.DirectionsRenderer();
      directionsDisplay.setMap(map);

      var calcRoute = function(start, finish) {
        var request = {
            origin:start,
            destination:finish,
            travelMode: google.maps.TravelMode.DRIVING
        };
        directionsService.route(request, function(response, status) {
          if (status == google.maps.DirectionsStatus.OK) {
            // setAllMap(null);
            directionsDisplay.setDirections(response);
          }
          else{alert("Server Error: Directions Unavailable")}
        });
      }

// Add a space to the database -----------------------------------------
// TO DO : Add ability to move marker before saving current location as open space -----------------------------------------
    $('#create-space').on('tap', function(e) {
      e.preventDefault();

      $('#post-space').popup("open", {
          overlayTheme: "a",
          positionTo: "window",
        })

      $('#add-space').on('tap', function(e) {
        var longitude = position.coords.longitude;
        var latitude  = position.coords.latitude;
        var note      = $(this).siblings('div').children().val()
        var data      = {space:{longitude:+longitude,latitude:+latitude,note:note}};
        var headers   = '{"Content-Type":"application/json"}';

        $.ajax({
          // url: 'http://calm-island-3256.herokuapp.com/spaces',
          url: 'http://localhost:3000/spaces',
          type: "POST",
          data: data,
          headers: headers
        }).done(function(response) {
          $('#add-space').remove();
          $('#note').remove();
          $('#popup-par').text('Added √');
          setTimeout(function () {
            $('#post-space').popup('close');
          }, 1500);
          var marker = new google.maps.Marker({
                  position: new google.maps.LatLng(response.latitude,response.longitude),
                  map: map,
                  title:  response.note,
                  icon: markerSelect(response),
                  id: response.id,
                  creation: response.converted_time,
                  animation: google.maps.Animation.DROP,
                  zIndex: google.maps.Marker.MAX_ZINDEX + 1
          });
        }).fail(function(response) {
          console.log(response);
          alert("shits fucked up");
        });
      });
    });

// Show available spaces from database -----------------------------------------
    $.ajax({
      // url: 'http://calm-island-3256.herokuapp.com',
      url: 'http://localhost:3000',
      type: "GET",
    }).done(function(response){
      parkingSpots = response
      for(i = 0; i < parkingSpots.length; i++){
        var marker = new google.maps.Marker({
              position: new google.maps.LatLng(parkingSpots[i].latitude,parkingSpots[i].longitude),
              map: map,
              title:  parkingSpots[i].note,
              icon: markerSelect(parkingSpots[i]), //set marker according to age
              id: parkingSpots[i].id,
              creation: parkingSpots[i].converted_time
        });
        google.maps.event.addListener(marker, 'click', spaceDetails);
      };
    });

    var spaceDetails = function() {
      spaceId = this.id
      $('#space-options').popup("open", {
        overlayTheme: "a",
        positionTo: "window",
      })
      $('p').text(this.title);
    };

// Claim a parking spot  -----------------------------------------
    $('#claim').on('click', function(e){
      e.preventDefault();
      var headers = '{"Content-Type":"application/json"}';
      var button = $(this)
      $.ajax({
        // url: 'http://calm-island-3256.herokuapp.com/spaces/'+spaceId,
        url: 'http://localhost:3000/spaces/'+spaceId,
        type: 'PUT',
        headers: headers,
        data: '' //test without this
      }).done(function(response) {
        $('#space-options p').remove();
        $('#space-options a').remove();
        $('#space-options button').remove();
        $('#space-options h4').text('Claimed √');
        setTimeout(function () {
          $('#space-options').popup('close');
        }, 1500);
        // navigation begins
        calcRoute(initialLocation, response.latitude +","+ response.longitude)
      }).fail(function(response) {
        alert("fuck you guys")
      })
    });
  });
});

// Space markers CSS -----------------------------------------
var currentLoc = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.65,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#263238',
        fillOpacity: 1
    }

var spaceFresh = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.5,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#76FF03',
        fillOpacity: 1
    }
var spaceStale = {
        path: fontawesome.markers.EXCLAMATION,
        scale: 0.5,
        strokeWeight: 0.2,
        strokeColor: 'black',
        strokeOpacity: 1,
        fillColor: '#AD1457',
        fillOpacity: 1
    }

// Differentiate between new and stale spaces -----------------------------------------
var markerSelect = function(spaceObject){
  var creation = spaceObject.converted_time
  if ((Date.now() - creation) <= (5*60000)){
    return spaceFresh;
  }

  else{
    console.log(Date.now() - creation)
    return spaceStale;
  }
}
var CenterControl = function(controlDiv, centerLocation, map) {

  // Set CSS for the control border
  var controlUI = document.createElement('div');
  controlUI.style.backgroundColor = 'red';
  controlUI.style.border = '2px solid #fff';
  controlUI.style.borderRadius = '35px';
  controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
  controlUI.style.cursor = 'pointer';
  controlUI.style.marginBottom = '22px';
  controlUI.style.textAlign = 'center';
  controlUI.title = 'Click to recenter the map';
  controlDiv.appendChild(controlUI);

  // Set CSS for the control interior
  var controlText = document.createElement('div');
  controlText.style.color = 'rgb(25,25,25)';
  controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlText.style.fontSize = '4px';
  controlText.style.lineHeight = '38px';
  controlText.style.paddingLeft = '5px';
  controlText.style.paddingRight = '5px';
  controlText.innerHTML = 'Center';
  controlUI.appendChild(controlText);

  // Setup the click event listeners: simply set the map to
  // Chicago
  google.maps.event.addDomListener(controlUI, 'click', function() {
    map.setCenter(centerLocation)
  });
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * 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.
 *
 */

"use strict";

var fs = require("fs"),
    path = require("path"),
    request = require("request-json"),
    temporary = require("temporary"),
    LogfileProcessor = require("./logfileProcessor").LogfileProcessor,
    programArgs = require('commander'),
    Promise = require("bluebird"),
    writeFile = Promise.promisify(require("fs").writeFile);

programArgs
    .version('0.0.1')
    .option('-d, --download', 'Download logfiles from S3')
    .option('-e, --extract', 'Extract Extension download data from downloaded logfiles')
    .option('-t, --tempFolder <path>', 'Path to temp folder (makes it easier to inspect logfiles)')
    .option('-p, --progress [true|false]', 'Print progress information')
    .option('-u, --update <path>', 'Update the extension registry with download data from <path>')
    .option('-v, --verbose [true|false]', 'Increase the level of output')
    .parse(process.argv);

// Workaround: posting to SSL with self signed certificate fails
// https://github.com/mikeal/request/issues/418
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

// read the config. This file must exists. Otherwise follow these setup instructions
// https://github.com/adobe/brackets-registry to create it.
var config = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../config/config.json")));
var httpPort = config.securePort || 4040; // default port for registry webapp
var protocol = config.insecure ? "http" : "https";

// Constants
var DOWNLOAD_STATS_FILENAME = "downloadStats.json",
    RECENT_DOWNLOAD_STATS_FILENAME = "recentDownloadStats.json";

/**
 * This is a helper log function that can be turned on and off by providing `-v``
 * when running this script from the command line.
 */
function log() {
    if (programArgs.verbose) {
        console.log(Array.prototype.slice.apply(arguments).join(' '));
    }
}

// create temp folder for logfiles
var tempFolder = programArgs.tempFolder || config.tempFolder;
if (tempFolder) {
    try {
        fs.mkdirSync(tempFolder);
    } catch (e) {
        // we don't care if the temp directory already exist,
        // since this should only happen during testing.
        // Usually we will use a generated temp dir that is unique
        if (e.code !== "EEXIST") {
            // tell us what went wrong
            console.error(e.toString());
        }
    }
} else {
    tempFolder = new temporary.Dir().path;
    log("Using temp directory:", tempFolder);
}

/**
 * Return a path to the download data file.
 */
function datafilePath() {
    return path.resolve(__dirname, DOWNLOAD_STATS_FILENAME);
}

function downloadLogFiles(progress) {
    var deferred = Promise.defer(),
        logfileProcessor = new LogfileProcessor(config);

    log("Downloading logfiles from S3");

    var promise = logfileProcessor.downloadLogfiles(tempFolder);
    promise.then(function (lastProcessedKey) {
        deferred.resolve();
    });

    if (progress) {
        promise.progressed(function (value) {
            process.stdout.write(value);
        });
    }

    return deferred.promise;
}

function extractExtensionDownloadData(progress) {
    var deferred = Promise.defer(),
        logfileProcessor = new LogfileProcessor(config),
        promise = logfileProcessor.extractDownloadStats(tempFolder);

    log("Extract extension download data from logfiles in", tempFolder);

    promise.then(function (downloadStats) {
        writeFile(datafilePath(), JSON.stringify(downloadStats)).then(function () {
            deferred.resolve(downloadStats);
        });
    });

    if (progress) {
        promise.progressed(function (value) {
            process.stdout.write(value);
        });
    }

    return deferred.promise;
}

function updateExtensionDownloadData(datafile, progress) {
    var deferred = Promise.defer();

    // posting works only from localhost
    var url = protocol + "://localhost:" + httpPort,
        client = request.createClient(url);

    client.sendFile("/stats", path.resolve(datafile), null, function (err, res, body) {
        if (err) {
            console.error(err);
            deferred.reject(err);
        } else {
            log("File uploaded");
            deferred.resolve();
        }
    });

    return deferred.promise;
}

function doItAll(progress) {
    downloadLogFiles(progress).then(function () {
        extractExtensionDownloadData(progress).then(function (downloadStats) {
            // posting works only from localhost
            updateExtensionDownloadData(datafilePath(), progress).then(function () {
                // remove the data file after uploading the download data
                fs.unlinkSync(datafilePath());

                if (!config["debug.keepTempFolder"]) {
                    fs.rmdirSync(tempFolder);
                }
            });
        });
    });
}

// Let's get to work
if (programArgs.download) {
    downloadLogFiles(programArgs.progress);
} else if (programArgs.extract) {
    extractExtensionDownloadData(programArgs.progress);
} else if (programArgs.update) {
    updateExtensionDownloadData(programArgs.update, programArgs.progress);
} else {
    doItAll(programArgs.progress);
}
<commit_msg>Delete Folder working now (#80)<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * 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.
 *
 */

"use strict";

var fs = require("fs"),
    path = require("path"),
    request = require("request-json"),
    temporary = require("temporary"),
    LogfileProcessor = require("./logfileProcessor").LogfileProcessor,
    programArgs = require('commander'),
    Promise = require("bluebird"),
    writeFile = Promise.promisify(require("fs").writeFile);

programArgs
    .version('0.0.1')
    .option('-d, --download', 'Download logfiles from S3')
    .option('-e, --extract', 'Extract Extension download data from downloaded logfiles')
    .option('-t, --tempFolder <path>', 'Path to temp folder (makes it easier to inspect logfiles)')
    .option('-p, --progress [true|false]', 'Print progress information')
    .option('-u, --update <path>', 'Update the extension registry with download data from <path>')
    .option('-v, --verbose [true|false]', 'Increase the level of output')
    .parse(process.argv);

// Workaround: posting to SSL with self signed certificate fails
// https://github.com/mikeal/request/issues/418
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

// read the config. This file must exists. Otherwise follow these setup instructions
// https://github.com/adobe/brackets-registry to create it.
var config = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../config/config.json")));
var httpPort = config.securePort || 4040; // default port for registry webapp
var protocol = config.insecure ? "http" : "https";

// Constants
var DOWNLOAD_STATS_FILENAME = "downloadStats.json",
    RECENT_DOWNLOAD_STATS_FILENAME = "recentDownloadStats.json";

/**
 * This is a helper log function that can be turned on and off by providing `-v``
 * when running this script from the command line.
 */
function log() {
    if (programArgs.verbose) {
        console.log(Array.prototype.slice.apply(arguments).join(' '));
    }
}

// create temp folder for logfiles
var tempFolder = programArgs.tempFolder || config.tempFolder;
if (tempFolder) {
    try {
        fs.mkdirSync(tempFolder);
    } catch (e) {
        // we don't care if the temp directory already exist,
        // since this should only happen during testing.
        // Usually we will use a generated temp dir that is unique
        if (e.code !== "EEXIST") {
            // tell us what went wrong
            console.error(e.toString());
        }
    }
} else {
    tempFolder = new temporary.Dir().path;
    log("Using temp directory:", tempFolder);
}

/**
 * Return a path to the download data file.
 */
function datafilePath() {
    return path.resolve(__dirname, DOWNLOAD_STATS_FILENAME);
}

function downloadLogFiles(progress) {
    var deferred = Promise.defer(),
        logfileProcessor = new LogfileProcessor(config);

    log("Downloading logfiles from S3");

    var promise = logfileProcessor.downloadLogfiles(tempFolder);
    promise.then(function (lastProcessedKey) {
        deferred.resolve();
    });

    if (progress) {
        promise.progressed(function (value) {
            process.stdout.write(value);
        });
    }

    return deferred.promise;
}

function extractExtensionDownloadData(progress) {
    var deferred = Promise.defer(),
        logfileProcessor = new LogfileProcessor(config),
        promise = logfileProcessor.extractDownloadStats(tempFolder);

    log("Extract extension download data from logfiles in", tempFolder);

    promise.then(function (downloadStats) {
        writeFile(datafilePath(), JSON.stringify(downloadStats)).then(function () {
            deferred.resolve(downloadStats);
        });
    });

    if (progress) {
        promise.progressed(function (value) {
            process.stdout.write(value);
        });
    }

    return deferred.promise;
}

function updateExtensionDownloadData(datafile, progress) {
    var deferred = Promise.defer();

    // posting works only from localhost
    var url = protocol + "://localhost:" + httpPort,
        client = request.createClient(url);

    client.sendFile("/stats", path.resolve(datafile), null, function (err, res, body) {
        if (err) {
            console.error(err);
            deferred.reject(err);
        } else {
            log("File uploaded");
            deferred.resolve();
        }
    });

    return deferred.promise;
}

function deleteFolderRecursive(path) {
    if (fs.existsSync(path)) {
        fs.readdirSync(path).forEach(function (file, index) {
            var curPath = path + "/" + file;
            if (fs.lstatSync(curPath).isDirectory()) { // recurse
                deleteFolderRecursive(curPath);
            } else { // delete file
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
}

function doItAll(progress) {
    downloadLogFiles(progress).then(function () {
        extractExtensionDownloadData(progress).then(function (downloadStats) {
            // posting works only from localhost
            updateExtensionDownloadData(datafilePath(), progress).then(function () {
                // remove the data file after uploading the download data
                fs.unlinkSync(datafilePath());

                if (!config["debug.keepTempFolder"]) {
                    deleteFolderRecursive(tempFolder);
                }
            });
        });
    });
}

// Let's get to work
if (programArgs.download) {
    downloadLogFiles(programArgs.progress);
} else if (programArgs.extract) {
    extractExtensionDownloadData(programArgs.progress);
} else if (programArgs.update) {
    updateExtensionDownloadData(programArgs.update, programArgs.progress);
} else {
    doItAll(programArgs.progress);
}
<|endoftext|>


<commit_before>/*
 * stompit.FrameInputStream
 * Copyright (c) 2013 Graham Daws <graham.daws@gmail.com>
 * MIT licensed
 */

var events  = require('events');
var stream  = require('stream');
var util    = require('./util');
var errors  = require('./errors');

var MAX_LINE_LENGTH = 4096;
var CR = '\r'.charCodeAt(0);
var LF = '\n'.charCodeAt(0);

function FrameInputStream(source, options){

    events.EventEmitter.call(this);

    this.TransportError = (options && options.TransportError) || errors.TransportError || Error;
    this.ProtocolError = (options && options.ProtocolError) || errors.ProtocolError || Error;

    if(!this.setVersion('1.1')){
        throw new Error('Could not set to version 1.1 mode');
    }

    this._source = source;

    this._frameQueue = [];

    var self = this;

    // Using nextTick fixes a bug where the stream was set to reading state
    // but the net stream implementor stopped reading. This bus was caused
    // by ignoring recursive readable events triggered by unshifts.
    var parseFrameOnNextTick = process.nextTick.bind(null, this._parseFrame.bind(this));
    source.on('readable', parseFrameOnNextTick);

    var error = processError.bind(this);

    var isEnded = false;
    var end = function(){
        if(!isEnded){
            isEnded = true;
            emptyFrameQueue.apply(self);
            self.emit('end');
        }
    };

    source.on('close', function(hadError){
        if(!hadError){
            end();
        }
    });

    source.on('end', function(){
        if(self._frame && self._frame.readingFrame){
            error(new self.ProtocolError('read frame error'));
        }
        else{
            end();
        }
    });

    source.once('error', function(exception){
        var wrapperException = new self.TransportError(exception.message);
        util.extend(wrapperException, exception);
        error(wrapperException);
    });
}

util.inherits(FrameInputStream, events.EventEmitter);

FrameInputStream.prototype.setVersion = function(version){

    var funcs = supportedVersions[version];

    if(funcs === undefined){
        return false;
    }

    this._version = version;
    this._versionTraits = funcs;

    this._incomingFrameConstructor = function(){
        IncomingFrame.apply(this, arguments);
    };

    util.inherits(this._incomingFrameConstructor, IncomingFrame);

    this._incomingFrameConstructor.prototype._decode = funcs.decode;

    return true;
};

FrameInputStream.prototype.decodeHeaderValue = function(value){
    return version_11.decode(value);
};

FrameInputStream.prototype.getSource = function(){
    return this._source;
};

FrameInputStream.prototype.readFrame = function(callback, errorCallback){

    var IncomingFrameClass = this._incomingFrameConstructor;

    var frame = new IncomingFrameClass(this, callback, errorCallback);

    frame.on('error', function(){});

    if(this._frame === undefined){
        this._frame = frame;
        this._parseFrame();
    }
    else{
        this._frameQueue.push(frame);
    }
};

FrameInputStream.prototype.readFrames = function(callback, errorCallback){

    var self = this;

    var readFrame = function(){
        self.readFrame(onReadFrame, errorCallback);
    };

    var onReadFrame = function(frame){
        readFrame();
        callback(frame);
    };

    readFrame();
};

FrameInputStream.prototype._parseFrame = function(){

    try{
        while(this._frame && this._frame._parse.call(this._frame, this._source));
    }
    catch(exception){

        if(exception instanceof ParseError){
            processError.call(this, exception);
        }
        else{
            throw exception;
        }
    }
};

function emptyFrameQueue(){

    var error = new Error('stream has ended');

    if(this._frame){

        if(this._frame.errorCallback){
            this._frame.errorCallback(this._frame.readingFrame ? this._frame : null);
        }

        if(this._frame.readingFrame){
            this._frame.emit('error', error);
        }

        delete this._frame;
    }

    var frameQueue = this._frameQueue;
    var frameQueueLen = frameQueue.length;

    for(var i = 0; i < frameQueueLen; i++){
        if(frameQueue[i].errorCallback){
            frameQueue[i].errorCallback(null);
        }
        frameQueue[i].emit('error', error);
    }

    this._frameQueue = [];
}

function processError(exception){
    emptyFrameQueue.apply(this);
    this.emit('error', exception);
}

function ParseError(message){
    this.message = message;
}

ParseError.prototype = Error.prototype;

function IncomingFrame(frameInputStream, readerCallback, errorCallback){

    stream.Readable.call(this);

    this._frameInputStream = frameInputStream;

    this.readerCallback = readerCallback;
    this.errorCallback = errorCallback;

    this.readingFrame = false;
    this._lineStash = '';

    this._pushable = true;

    this._parse = readCommandLine;
    this.headers = {};
}

util.inherits(IncomingFrame, stream.Readable);

IncomingFrame.prototype._read = function(){
    this._frameInputStream._frame._pushable = true;
    this._frameInputStream._parseFrame();
};

IncomingFrame.prototype._readLine = function(source){

    var stashLength = this._lineStash.length;

    var data;

    while((data = source.read()) !== null){

        var dataLength = data.length;

        for(var i = 0; i < dataLength; i++){

            if(stashLength + i + 1 > MAX_LINE_LENGTH){
                throw new ParseError('maximum line length exceeded (' + MAX_LINE_LENGTH + ' character limit)');
            }

            if(data[i] === LF){

                var end = i;

                if(i > 0 && data[i - 1] === CR){
                    end = i - 1;
                }

                var line = this._lineStash + data.toString('utf-8', 0, end);

                this._lineStash = '';

                source.unshift(data.slice(i + 1));

                return line;
            }
        }

        this._lineStash += data.toString('utf-8');
    }

    return null;
};

IncomingFrame.prototype.readEmptyBody = function(callback){

    callback = callback || function(){};

    var self = this;

    var onReadable = function(){
        var buffer = self.read();
        if(buffer !== null){
            callback(false);
            callback = function(){};
        }
    };

    this.once('end', function(){
        self.removeListener('readable', onReadable);
        callback(true);
    });

    this.once('readable', onReadable);

    this.read(0);
};

function readCommandLine(source){

    var line = this._readLine(source);

    if(line !== null && line.length > 0){

        this.readingFrame = true;

        this.command = this._decode(line);

        this._parse = readHeaderFieldLine;

        return true;
    }

    return false;
}

function readHeaderFieldLine(source){

    var line = this._readLine(source);

    if(line !== null){

        if(line.length > 0){

            var separator = line.indexOf(':');

            if(separator === -1){
                throw new ParseError('header parse error');
            }

            var header = line.split(':');

            var name = this._decode(line.substring(0, separator));
            var value = this._decode(line.substring(separator + 1));

            if(!this.headers.hasOwnProperty(name)){
                this.headers[name] = value;
            }

            return true;
        }
        else{

            if(this.headers['content-length'] !== undefined){

                var contentLength = parseInt(this.headers['content-length'], 10);

                if(isNaN(contentLength)){
                    throw new ParseError('invalid content-length');
                }

                this.headers['content-length'] = contentLength;

                this._contentLengthRemaining = contentLength;

                this._parse = readFixedLengthBody;
            }
            else{
                this._parse = readBody;
            }

            delete this.errorCallback;

            var self = this;

            process.nextTick(function(){
                self.readerCallback(self);
                delete self.readerCallback;
            });

            // Return false to let the frame user control the reading of the body
        }
    }

    return false;
}

function readBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var terminated = null;

        var chunkLength = chunk.length;
        for(var i = 0; i < chunkLength; i++){
            if(chunk[i] === 0){
                terminated = i;
                break;
            }
        }

        if(terminated === null){
            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readTrailer;

            source.unshift(chunk.slice(terminated + 1));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, terminated));
            this.push(null);

            return true;
        }
    }

    return false;
}

function readFixedLengthBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var lengthRemaining = this._contentLengthRemaining;

        if(chunk.length < lengthRemaining){

            this._contentLengthRemaining -= chunk.length;

            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readNullByte;

            source.unshift(chunk.slice(lengthRemaining));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, lengthRemaining));

            return true;
        }
    }

    return false;
}

function readNullByte(source){

    var nullByte = source.read(1);

    if(nullByte === null){
        return false;
    }

    if(nullByte[0] !== 0){
        throw new ParseError('expected null byte');
    }

    this._parse = readTrailer;

    this.push(null);

    return true;
}

function readTrailer(source){

    // The peer can drop the connection now without triggering an error
    this.readingFrame = false;

    var chunk;

    while((chunk = source.read(1)) !== null){

        if(chunk[0] == CR){
            this._lineStash = '\r';
            continue;
        }

        if(this._lineStash.length > 0 && chunk[0] !== LF){
            throw new ParseError('invalid EOL sequence');
        }

        this._lineStash = '';

        // If not LF then it's the first character of the next frame
        if(chunk[0] !== LF){
            source.unshift(chunk);
            this._frameInputStream._frame = this._frameInputStream._frameQueue.shift();
            return true;
        }
    }

    return false;
}

function createDecodeFunction(escapeSequences){
    return function(value){
        return value.replace(/\\./gi, function(sequence){
            if(escapeSequences.hasOwnProperty(sequence)){
                return escapeSequences[sequence];
            }
            else{
                throw new ParseError('undefined escape sequence');
            }
        });
    };
}

var version_10 = {
    decode: function(value){return value;}
};

var version_11 = {
    decode: createDecodeFunction({'\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var version_12 = {
    decode: createDecodeFunction({'\\r': '\r', '\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var supportedVersions = {
    '1.0': version_10,
    '1.1': version_11,
    '1.2': version_12
};

module.exports = FrameInputStream;
<commit_msg>Check for active frame before trying to modify it<commit_after>/*
 * stompit.FrameInputStream
 * Copyright (c) 2013 Graham Daws <graham.daws@gmail.com>
 * MIT licensed
 */

var events  = require('events');
var stream  = require('stream');
var util    = require('./util');
var errors  = require('./errors');

var MAX_LINE_LENGTH = 4096;
var CR = '\r'.charCodeAt(0);
var LF = '\n'.charCodeAt(0);

function FrameInputStream(source, options){

    events.EventEmitter.call(this);

    this.TransportError = (options && options.TransportError) || errors.TransportError || Error;
    this.ProtocolError = (options && options.ProtocolError) || errors.ProtocolError || Error;

    if(!this.setVersion('1.1')){
        throw new Error('Could not set to version 1.1 mode');
    }

    this._source = source;

    this._frameQueue = [];

    var self = this;

    // Using nextTick fixes a bug where the stream was set to reading state
    // but the net stream implementor stopped reading. This bus was caused
    // by ignoring recursive readable events triggered by unshifts.
    var parseFrameOnNextTick = process.nextTick.bind(null, this._parseFrame.bind(this));
    source.on('readable', parseFrameOnNextTick);

    var error = processError.bind(this);

    var isEnded = false;
    var end = function(){
        if(!isEnded){
            isEnded = true;
            emptyFrameQueue.apply(self);
            self.emit('end');
        }
    };

    source.on('close', function(hadError){
        if(!hadError){
            end();
        }
    });

    source.on('end', function(){
        if(self._frame && self._frame.readingFrame){
            error(new self.ProtocolError('read frame error'));
        }
        else{
            end();
        }
    });

    source.once('error', function(exception){
        var wrapperException = new self.TransportError(exception.message);
        util.extend(wrapperException, exception);
        error(wrapperException);
    });
}

util.inherits(FrameInputStream, events.EventEmitter);

FrameInputStream.prototype.setVersion = function(version){

    var funcs = supportedVersions[version];

    if(funcs === undefined){
        return false;
    }

    this._version = version;
    this._versionTraits = funcs;

    this._incomingFrameConstructor = function(){
        IncomingFrame.apply(this, arguments);
    };

    util.inherits(this._incomingFrameConstructor, IncomingFrame);

    this._incomingFrameConstructor.prototype._decode = funcs.decode;

    return true;
};

FrameInputStream.prototype.decodeHeaderValue = function(value){
    return version_11.decode(value);
};

FrameInputStream.prototype.getSource = function(){
    return this._source;
};

FrameInputStream.prototype.readFrame = function(callback, errorCallback){

    var IncomingFrameClass = this._incomingFrameConstructor;

    var frame = new IncomingFrameClass(this, callback, errorCallback);

    frame.on('error', function(){});

    if(this._frame === undefined){
        this._frame = frame;
        this._parseFrame();
    }
    else{
        this._frameQueue.push(frame);
    }
};

FrameInputStream.prototype.readFrames = function(callback, errorCallback){

    var self = this;

    var readFrame = function(){
        self.readFrame(onReadFrame, errorCallback);
    };

    var onReadFrame = function(frame){
        readFrame();
        callback(frame);
    };

    readFrame();
};

FrameInputStream.prototype._parseFrame = function(){

    try{
        while(this._frame && this._frame._parse.call(this._frame, this._source));
    }
    catch(exception){

        if(exception instanceof ParseError){
            processError.call(this, exception);
        }
        else{
            throw exception;
        }
    }
};

function emptyFrameQueue(){

    var error = new Error('stream has ended');

    if(this._frame){

        if(this._frame.errorCallback){
            this._frame.errorCallback(this._frame.readingFrame ? this._frame : null);
        }

        if(this._frame.readingFrame){
            this._frame.emit('error', error);
        }

        delete this._frame;
    }

    var frameQueue = this._frameQueue;
    var frameQueueLen = frameQueue.length;

    for(var i = 0; i < frameQueueLen; i++){
        if(frameQueue[i].errorCallback){
            frameQueue[i].errorCallback(null);
        }
        frameQueue[i].emit('error', error);
    }

    this._frameQueue = [];
}

function processError(exception){
    emptyFrameQueue.apply(this);
    this.emit('error', exception);
}

function ParseError(message){
    this.message = message;
}

ParseError.prototype = Error.prototype;

function IncomingFrame(frameInputStream, readerCallback, errorCallback){

    stream.Readable.call(this);

    this._frameInputStream = frameInputStream;

    this.readerCallback = readerCallback;
    this.errorCallback = errorCallback;

    this.readingFrame = false;
    this._lineStash = '';

    this._pushable = true;

    this._parse = readCommandLine;
    this.headers = {};
}

util.inherits(IncomingFrame, stream.Readable);

IncomingFrame.prototype._read = function(){
    if(this._frameInputStream._frame){
        this._frameInputStream._frame._pushable = true;
        this._frameInputStream._parseFrame();
    }
};

IncomingFrame.prototype._readLine = function(source){

    var stashLength = this._lineStash.length;

    var data;

    while((data = source.read()) !== null){

        var dataLength = data.length;

        for(var i = 0; i < dataLength; i++){

            if(stashLength + i + 1 > MAX_LINE_LENGTH){
                throw new ParseError('maximum line length exceeded (' + MAX_LINE_LENGTH + ' character limit)');
            }

            if(data[i] === LF){

                var end = i;

                if(i > 0 && data[i - 1] === CR){
                    end = i - 1;
                }

                var line = this._lineStash + data.toString('utf-8', 0, end);

                this._lineStash = '';

                source.unshift(data.slice(i + 1));

                return line;
            }
        }

        this._lineStash += data.toString('utf-8');
    }

    return null;
};

IncomingFrame.prototype.readEmptyBody = function(callback){

    callback = callback || function(){};

    var self = this;

    var onReadable = function(){
        var buffer = self.read();
        if(buffer !== null){
            callback(false);
            callback = function(){};
        }
    };

    this.once('end', function(){
        self.removeListener('readable', onReadable);
        callback(true);
    });

    this.once('readable', onReadable);

    this.read(0);
};

function readCommandLine(source){

    var line = this._readLine(source);

    if(line !== null && line.length > 0){

        this.readingFrame = true;

        this.command = this._decode(line);

        this._parse = readHeaderFieldLine;

        return true;
    }

    return false;
}

function readHeaderFieldLine(source){

    var line = this._readLine(source);

    if(line !== null){

        if(line.length > 0){

            var separator = line.indexOf(':');

            if(separator === -1){
                throw new ParseError('header parse error');
            }

            var header = line.split(':');

            var name = this._decode(line.substring(0, separator));
            var value = this._decode(line.substring(separator + 1));

            if(!this.headers.hasOwnProperty(name)){
                this.headers[name] = value;
            }

            return true;
        }
        else{

            if(this.headers['content-length'] !== undefined){

                var contentLength = parseInt(this.headers['content-length'], 10);

                if(isNaN(contentLength)){
                    throw new ParseError('invalid content-length');
                }

                this.headers['content-length'] = contentLength;

                this._contentLengthRemaining = contentLength;

                this._parse = readFixedLengthBody;
            }
            else{
                this._parse = readBody;
            }

            delete this.errorCallback;

            var self = this;

            process.nextTick(function(){
                self.readerCallback(self);
                delete self.readerCallback;
            });

            // Return false to let the frame user control the reading of the body
        }
    }

    return false;
}

function readBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var terminated = null;

        var chunkLength = chunk.length;
        for(var i = 0; i < chunkLength; i++){
            if(chunk[i] === 0){
                terminated = i;
                break;
            }
        }

        if(terminated === null){
            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readTrailer;

            source.unshift(chunk.slice(terminated + 1));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, terminated));
            this.push(null);

            return true;
        }
    }

    return false;
}

function readFixedLengthBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var lengthRemaining = this._contentLengthRemaining;

        if(chunk.length < lengthRemaining){

            this._contentLengthRemaining -= chunk.length;

            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readNullByte;

            source.unshift(chunk.slice(lengthRemaining));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, lengthRemaining));

            return true;
        }
    }

    return false;
}

function readNullByte(source){

    var nullByte = source.read(1);

    if(nullByte === null){
        return false;
    }

    if(nullByte[0] !== 0){
        throw new ParseError('expected null byte');
    }

    this._parse = readTrailer;

    this.push(null);

    return true;
}

function readTrailer(source){

    // The peer can drop the connection now without triggering an error
    this.readingFrame = false;

    var chunk;

    while((chunk = source.read(1)) !== null){

        if(chunk[0] == CR){
            this._lineStash = '\r';
            continue;
        }

        if(this._lineStash.length > 0 && chunk[0] !== LF){
            throw new ParseError('invalid EOL sequence');
        }

        this._lineStash = '';

        // If not LF then it's the first character of the next frame
        if(chunk[0] !== LF){
            source.unshift(chunk);
            this._frameInputStream._frame = this._frameInputStream._frameQueue.shift();
            return true;
        }
    }

    return false;
}

function createDecodeFunction(escapeSequences){
    return function(value){
        return value.replace(/\\./gi, function(sequence){
            if(escapeSequences.hasOwnProperty(sequence)){
                return escapeSequences[sequence];
            }
            else{
                throw new ParseError('undefined escape sequence');
            }
        });
    };
}

var version_10 = {
    decode: function(value){return value;}
};

var version_11 = {
    decode: createDecodeFunction({'\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var version_12 = {
    decode: createDecodeFunction({'\\r': '\r', '\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var supportedVersions = {
    '1.0': version_10,
    '1.1': version_11,
    '1.2': version_12
};

module.exports = FrameInputStream;
<|endoftext|>


<commit_before>module.exports = function(file) {
  this.file = file;
};

module.exports.prototype.emberImport = function() {
  var found = false;

  this.file.iterateNodesByType(['ImportDeclaration'], function(node) {
    if (!node.source || node.source.value !== 'ember') {
      return;
    }

    var specifiers = node.specifiers;
    if (!specifiers || specifiers.length != 1) {
      return;
    }

    if (specifiers[0].local) {
      found = specifiers[0].local.name;
    }
  });

  return found;
};

module.exports.prototype.findEmberClass = function(className) {
  var nodes = [];
  var _this = this;
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['Identifier'], function(node) {
    if (node.name === className) {
      if (emberImport && node.parentNode.object.name !== emberImport) {
        return;
      }

      nodes.push(node);
    }
  });

  return nodes;
};

module.exports.prototype.findEmberFunction = function(functionName, args) {
  var nodes = [];
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['CallExpression'], function(node) {
    if (!node.callee.property || node.callee.property.name !== functionName) {
      return;
    }

    if (node.callee.object.name !== emberImport) {
      return;
    }

    if (args && node.arguments.length < args) {
      return;
    }

    nodes.push(node);
  });

  return nodes;
};

module.exports.prototype.findExtendBlocks = function() {
  var nodes = [];
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['CallExpression'], function(node) {
    if (node.callee.property.name !== 'extend' || node.arguments.length === 0) {
      return;
    }

    block = node.arguments[node.arguments.length - 1];
    nodes.push(block);
  });

  return nodes;
};

module.exports.prototype.findExtendBlocksProperties = function(key) {
  var properties = [];
  this.findExtendBlocks().forEach(function(block) {
    block.properties.forEach(function(property) {
      if (property.key.name === key) {
        properties.push(property);
      }
    });
  });

  return properties;
};
<commit_msg>Guard against missing properties<commit_after>module.exports = function(file) {
  this.file = file;
};

module.exports.prototype.emberImport = function() {
  var found = false;

  this.file.iterateNodesByType(['ImportDeclaration'], function(node) {
    if (!node.source || node.source.value !== 'ember') {
      return;
    }

    var specifiers = node.specifiers;
    if (!specifiers || specifiers.length != 1) {
      return;
    }

    if (specifiers[0].local) {
      found = specifiers[0].local.name;
    }
  });

  return found;
};

module.exports.prototype.findEmberClass = function(className) {
  var nodes = [];
  var _this = this;
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['Identifier'], function(node) {
    if (node.name === className) {
      if (emberImport && node.parentNode.object.name !== emberImport) {
        return;
      }

      nodes.push(node);
    }
  });

  return nodes;
};

module.exports.prototype.findEmberFunction = function(functionName, args) {
  var nodes = [];
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['CallExpression'], function(node) {
    if (!node.callee.property || node.callee.property.name !== functionName) {
      return;
    }

    if (node.callee.object.name !== emberImport) {
      return;
    }

    if (args && node.arguments.length < args) {
      return;
    }

    nodes.push(node);
  });

  return nodes;
};

module.exports.prototype.findExtendBlocks = function() {
  var nodes = [];
  var emberImport = this.emberImport();

  if (!emberImport) {
    emberImport = 'Ember';
  }

  this.file.iterateNodesByType(['CallExpression'], function(node) {
    if (!node.callee.property || node.callee.property.name !== 'extend' || node.arguments.length === 0) {
      return;
    }

    block = node.arguments[node.arguments.length - 1];
    nodes.push(block);
  });

  return nodes;
};

module.exports.prototype.findExtendBlocksProperties = function(key) {
  var properties = [];
  this.findExtendBlocks().forEach(function(block) {
    if (!block.properties) {
      return;
    }

    block.properties.forEach(function(property) {
      if (property.key.name === key) {
        properties.push(property);
      }
    });
  });

  return properties;
};
<|endoftext|>


<commit_before>/*
Copyright (C) 2011 by Gregory Burlet, Alastair Porter

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.
*/

/**
 * Creates a neume
 * Each neume has: name, root pitch, list of neume elements, each element has difference from root pitch (int)
 *                 modifier (liquescence etc.) - alt, shift, ctrl, ctrl+shift in the Medieval Final Plugin.
 * @class Models a neume-a container for one or more notes.
 * @param {Object} options key {string}, type {Toe.Model.CheironomicNeume.Type}, rootNote.pitch {string},
 *                 rootNote.octave {number}, modifier {Toe.Model.CheironomicNeume.Modifier}, interact {Boolean}
 */
Toe.Model.CheironomicNeume = function(options) {
    // call super constructor
    Toe.Model.Neume.call(this, options);
}

// inherit prototype from generic neume model
Toe.Model.CheironomicNeume.prototype = new Toe.Model.Neume();
Toe.Model.CheironomicNeume.prototype.constructor = Toe.Model.CheironomicNeume;

// Load neume search tree from json so the tree doesn't need to be populated on load
// TODO: get json dump of new search tree for staffless neumes
Toe.Model.CheironomicNeume.SearchTree = new SearchTree();
Toe.Model.CheironomicNeume.SearchTree.populateFromJSON('{"rootNode":{"payload":{"typeid":"punctum","name":"Punctum"},"children":{"0":{"payload":{"typeid":"distropha","name":"Distropha"},"children":{"0":{"payload":{"typeid":"tristropha","name":"Tristropha"},"children":{},"numChildren":0}},"numChildren":1},"1":{"payload":{"typeid":"podatus","name":"Podatus"},"children":{"1":{"payload":{"typeid":"scandicus.1","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.2","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.3","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.4","name":"Scandicus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"scandicus.flexus.3","name":"Scandicus Flexus"},"children":{},"numChildren":0}},"numChildren":2},"-1":{"payload":{"typeid":"scandicus.flexus.2","name":"Scandicus Flexus"},"children":{},"numChildren":0}},"numChildren":2},"-1":{"payload":{"typeid":"scandicus.flexus.1","name":"Scandicus Flexus"},"children":{"-1":{"payload":{"typeid":"scandicus.subpunctis.1","name":"Scandicus Subpunctis"},"children":{"-1":{"payload":{"typeid":"scandicus.subpunctis.2","name":"Scandicus Subpunctis"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":1}},"numChildren":2},"-1":{"payload":{"typeid":"torculus","name":"Torculus"},"children":{"1":{"payload":{"typeid":"torculus.resupinus.1","name":"Torculus Resupinus"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.2","name":"Torculus Resupinus"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.3","name":"Torculus Resupinus"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.4","name":"Torculus Resupinus"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":1}},"numChildren":1},"-1":{"payload":{"typeid":"podatus.subpunctis.1","name":"Podatus Subpunctis"},"children":{"1":{"payload":{"typeid":"podatus.subpunctis.resupinus.1","name":"Podatus Subpunctis Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"podatus.subpunctis.2","name":"Podatus Subpunctis"},"children":{"1":{"payload":{"typeid":"podatus.subpunctis.resupinus.2","name":"Podatus Subpunctis Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"podatus.subpunctis.3","name":"Podatus Subpunctis"},"children":{"1":{"payload":{"typeid":"podatus.subpunctis.resupinus.3","name":"Podatus Subpunctis Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"podatus.subpunctis.4","name":"Podatus Subpunctis"},"children":{},"numChildren":0}},"numChildren":2}},"numChildren":2}},"numChildren":2}},"numChildren":2}},"numChildren":2},"-1":{"payload":{"typeid":"clivis","name":"Clivis"},"children":{"1":{"payload":{"typeid":"porrectus","name":"Porrectus"},"children":{"1":{"payload":{"typeid":"compound.2","name":"Compound"},"children":{"-1":{"payload":{"typeid":"compound.1","name":"Compound"},"children":{},"numChildren":0}},"numChildren":1},"-1":{"payload":{"typeid":"porrectus.flexus","name":"Porrectus Flexus"},"children":{"-1":{"payload":{"typeid":"porrectus.subpunctis.1","name":"Porrectus Subpunctis"},"children":{"1":{"payload":{"typeid":"porrectus.subpunctis.resupinus.1","name":"Porrectus Subpunctis Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"porrectus.subpunctis.2","name":"Porrectus Subpunctis"},"children":{"1":{"payload":{"typeid":"porrectus.subpunctis.resupinus.2","name":"Porrectus Subpunctis Resupinus"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":2}},"numChildren":1}},"numChildren":2},"-1":{"payload":{"typeid":"climacus.1","name":"Climacus"},"children":{"1":{"payload":{"typeid":"climacus.resupinus.1","name":"Climacus Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"climacus.2","name":"Climacus"},"children":{"1":{"payload":{"typeid":"climacus.resupinus.2","name":"Climacus Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"climacus.3","name":"Climacus"},"children":{"1":{"payload":{"typeid":"climacus.resupinus.3","name":"Climacus Resupinus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"climacus.4","name":"Climacus"},"children":{"1":{"payload":{"typeid":"climacus.resupinus.4","name":"Climacus Resupinus"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":2}},"numChildren":2}},"numChildren":2}},"numChildren":2}},"numChildren":3},"numNodes":1}');

/**
 * Fills the neume with data from an MEI neume element
 *
 * @methodOf Toe.Model.CheironomicNeume
 * @param {jQuery wrapped element set} neumeData the MEI neume data
 * @param {jQuery wrapped element set} facs the MEI facs data for the provided neume
 */
Toe.Model.CheironomicNeume.prototype.neumeFromMei = function(neumeData, bb) {
    // check the DOM element is in fact a neume
    if (neumeData.nodeName.toLowerCase() != "neume") {
        throw new Error("neumeFromMei: invalid neume data");
    }

    this.id = $(neumeData).attr("xml:id");
    var nName = $(neumeData).attr("name").toLowerCase();
    // perform neume -> neume & modifier transformations
    // For example, in the current MEI neumes module, cephalicus and epiphonus are their
    // own neumes. In the Medieval Finale plugin they are clivis and podatus neumes, respectively,
    // with an alt (liquescence) modifier.
    if (nName == "epiphonus") {
        nName = "podatus";
        this.props.modifier = "liquescence";
    }
    else if (nName == "cephalicus") {
        nName = "clivis";
        this.props.modifier = "liquescence";
    }

    this.typeid = nName;
    this.name = nName;

    this.setBoundingBox(bb);

    // cache a local copy of this so the handle isn't overidden within the jquery function
    var theNeume = this;
    $(neumeData).find("note").each(function(it, el) {
        var ncType = "punctum";
        if ($(this).parent().attr("quilisma") == "true") {
            ncType = "quilisma";
        }
        else if (nName == "virga" || nName == "bivirga" || nName == "trivirga") {
            ncType = "virga";
        }

        // add note ornaments
        var ornaments = new Array();

        // check for dot
        var dotForm = $("> dot", this).attr("form");
        if (dotForm) {
            ornaments.push(new Toe.Model.Ornament("dot", {form: dotForm}));
        }

        var nc = new Toe.Model.CheironomicNeumeComponent({type: ncType, ornaments: ornaments});

        theNeume.addComponent(nc);
    });

    // for chaining
    return this;
}

/**
 * Derives the name of the neume using the pitch differences
 * and sets the name on the model.
 *
 * @methodOf Toe.Model.CheironomicNeume
 * @returns {string} neume name
 * TODO: search the new tree for deriving the neume typeid
 */
Toe.Model.CheironomicNeume.prototype.deriveName = function(options) {
    var opts = {
        enforceHeadShapes: true
    };

    $.extend(opts, options);

    return this.name;
}

/**
 * Change head shapes of neume components in the model which are changed
 * when neume type changes.
 * TODO
 */
Toe.Model.CheironomicNeume.prototype.enforceHeadShapes = function() {
}
<commit_msg>Staffless neume search tree<commit_after>/*
Copyright (C) 2011 by Gregory Burlet, Alastair Porter

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.
*/

/**
 * Creates a neume
 * Each neume has: name, root pitch, list of neume elements, each element has difference from root pitch (int)
 *                 modifier (liquescence etc.) - alt, shift, ctrl, ctrl+shift in the Medieval Final Plugin.
 * @class Models a neume-a container for one or more notes.
 * @param {Object} options key {string}, type {Toe.Model.CheironomicNeume.Type}, rootNote.pitch {string},
 *                 rootNote.octave {number}, modifier {Toe.Model.CheironomicNeume.Modifier}, interact {Boolean}
 */
Toe.Model.CheironomicNeume = function(options) {
    // call super constructor
    Toe.Model.Neume.call(this, options);

    // relative pitch with respect to previous neume component
    this.melodicMovement = null;
}

// inherit prototype from generic neume model
Toe.Model.CheironomicNeume.prototype = new Toe.Model.Neume();
Toe.Model.CheironomicNeume.prototype.constructor = Toe.Model.CheironomicNeume;

// Load neume search tree from json so the tree doesn't need to be populated on load
Toe.Model.CheironomicNeume.SearchTree = new SearchTree();
Toe.Model.CheironomicNeume.SearchTree.populateFromJSON('{"rootNode":{"payload":{"typeid":"punctum","name":"Punctum"},"children":{"1":{"payload":{"typeid":"pes","name":"Pes"},"children":{"1":{"payload":{"typeid":"scandicus.1","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.2","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.3","name":"Scandicus"},"children":{"1":{"payload":{"typeid":"scandicus.4","name":"Scandicus"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":1}},"numChildren":1},"-1":{"payload":{"typeid":"torculus","name":"Torculus"},"children":{"1":{"payload":{"typeid":"torculus.resupinus.1","name":"Torculus Resupinus"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.2","name":"Torculus Resupinus"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.3","name":"Torculus Resupinus 3"},"children":{"-1":{"payload":{"typeid":"torculus.resupinus.4","name":"Torculus Resupinus 4"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":1}},"numChildren":1}},"numChildren":1}},"numChildren":2},"-1":{"payload":{"typeid":"clivis","name":"Clivis"},"children":{"1":{"payload":{"typeid":"porrectus","name":"Porrectus"},"children":{},"numChildren":0},"-1":{"payload":{"typeid":"climacus.1","name":"Climacus"},"children":{"-1":{"payload":{"typeid":"climacus.2","name":"Climacus"},"children":{"-1":{"payload":{"typeid":"climacus.3","name":"Climacus"},"children":{"-1":{"payload":{"typeid":"climacus.4","name":"Climacus"},"children":{},"numChildren":0}},"numChildren":1}},"numChildren":1}},"numChildren":1}},"numChildren":2}},"numChildren":2},"numNodes":1}');

/**
 * Fills the neume with data from an MEI neume element
 *
 * @methodOf Toe.Model.CheironomicNeume
 * @param {jQuery wrapped element set} neumeData the MEI neume data
 * @param {jQuery wrapped element set} facs the MEI facs data for the provided neume
 */
Toe.Model.CheironomicNeume.prototype.neumeFromMei = function(neumeData, bb) {
    // check the DOM element is in fact a neume
    if (neumeData.nodeName.toLowerCase() != "neume") {
        throw new Error("neumeFromMei: invalid neume data");
    }

    this.id = $(neumeData).attr("xml:id");
    var nName = $(neumeData).attr("name").toLowerCase();
    // perform neume -> neume & modifier transformations
    // For example, in the current MEI neumes module, cephalicus and epiphonus are their
    // own neumes. In the Medieval Finale plugin they are clivis and podatus neumes, respectively,
    // with an alt (liquescence) modifier.
    if (nName == "epiphonus") {
        nName = "podatus";
        this.props.modifier = "liquescence";
    }
    else if (nName == "cephalicus") {
        nName = "clivis";
        this.props.modifier = "liquescence";
    }

    this.typeid = this.name = nName;

    this.setBoundingBox(bb);

    // cache a local copy of this so the handle isn't overidden within the jquery function
    var theNeume = this;
    $(neumeData).find("note").each(function(it, el) {
        var ncType = "punctum";
        if ($(this).parent().attr("quilisma") == "true") {
            ncType = "quilisma";
        }
        else if (nName == "virga" || nName == "bivirga" || nName == "trivirga") {
            ncType = "virga";
        }

        // add note ornaments
        var ornaments = new Array();

        // check for dot
        var dotForm = $("> dot", this).attr("form");
        if (dotForm) {
            ornaments.push(new Toe.Model.Ornament("dot", {form: dotForm}));
        }

        var nc = new Toe.Model.CheironomicNeumeComponent({type: ncType, ornaments: ornaments});

        theNeume.addComponent(nc);
    });

    // for chaining
    return this;
}

/**
 * Derives the name of the neume using the pitch differences
 * and sets the name on the model.
 *
 * @methodOf Toe.Model.CheironomicNeume
 * @returns {string} neume name
 * TODO: search the new tree for deriving the neume typeid
 */
Toe.Model.CheironomicNeume.prototype.deriveName = function(options) {
    var opts = {
        enforceHeadShapes: true
    };

    $.extend(opts, options);

    return this.name;
}

/**
 * Change head shapes of neume components in the model which are changed
 * when neume type changes.
 * TODO
 */
Toe.Model.CheironomicNeume.prototype.enforceHeadShapes = function() {
}
<|endoftext|>


<commit_before>/*global THREE */

var APP  = APP || {};

APP.simulation = (function simulation(THREE) {
	'use strict';

	var make = function make(args, afterValidation) {

		args = args || {};

		var defaults = {
			timeMultiplier: 1000000,
			gravityMultiplier: 1,

			gridSize: 150,
			gridSpacing: 5,	//5 grid lines per grid

			particleCount: 100,

			minParticleSize: 0.1,
			maxParticleSize: 1,

			minParticleMass: 0.5,
			maxParticleMass: 5,

			drawTrails: false,
			trailLength: 100,

			startingVelocity: 1,

			detectCollisions: true,

			seed: Date.now()
		};

		var inputValidationSettings = {
			minGridSize: 10,
			maxGridSize: 10000,

			minParticleCount: 1,
			maxParticleCount: 1000,

			minTrailLength: 0,
			maxTrailLength: 1000
		};

		var state = {
			gravity: 6.673e-11,

			timeMultiplier: args.timeMultiplier || defaults.timeMultiplier,
			gravityMultiplier: args.gravityMultiplier || defaults.gravityMultiplier,

			gridSize: args.gridSize || defaults.gridSize,
			gridSpacing: args.gridSpacing || defaults.gridSpacing,

			particleCount: args.particleCount || defaults.particleCount,

			minParticleSize: args.minParticleSize || defaults.minParticleSize,
			maxParticleSize: args.maxParticleSize || defaults.maxParticleSize,

			minParticleMass: args.minParticleMass || defaults.minParticleMass,
			maxParticlemass: args.maxParticleMass || defaults.maxParticleMass,

			//drawTrails is checked below
			trailLength: args.trailLength || defaults.trailLength,

			startingVelocity: args.startingVelocity || defaults.startingVelocity,

			seed: args.seed || defaults.seed
		};

		//need special checks for booleans
		if (args.drawTrails === undefined) {
			state.drawTrails = defaults.drawTrails;
		} else {
			state.drawTrails = args.drawTrails;
		}
		if (args.detectCollisions === undefined) {
			state.detectCollisions = defaults.detectCollisions;
		} else {
			state.detectCollisions = args.detectCollisions;
		}

		//check for NaN values from textfield inputs
		if (state.minParticleSize !== state.minParticleSize) {
			state.minParticleSize = defaults.minParticleSize;
		}
		if (state.maxParticleSize !== state.maxParticleSize) {
			state.maxParticleSize = defaults.maxParticleSize;
		}
		if (state.minParticleMass !== state.minParticleMass) {
			state.minParticleMass = defaults.minParticleMass;
		}
		if (state.maxParticleMass !== state.maxParticleMass) {
			state.maxParticleMass = defaults.maxParticleMass;
		}
		if (state.startingVelocity !== state.startingVelocity) {
			state.startingVelocity = defaults.startingVelocity;
		}

		if (state.particleCount < 0) {
			state.particleCount = -state.particleCount;
		}
		if (state.trailLength < 0) {
			state.trailLength = -state.trailLength;
		}
		if (state.gridSize < 0) {
			state.gridSize = -state.gridSize;
		}
		if (state.minParticleSize < 0) {
			state.minParticleSize = -state.minParticleSize;
		}
		if (state.maxParticleSize < 0) {
			state.maxParticleSize = -state.maxParticleSize;
		}
		if (state.minParticleMass < 0) {
			state.minParticleMass = -state.minParticleMass;
		}
		if (state.maxParticleMass < 0) {
			state.maxParticleMass = -state.maxParticleMass;
		}
		if (state.startingVelocity < 0) {
			state.startingVelocity = -state.startingVelocity;
		}

		if (state.particleCount < inputValidationSettings.minParticleCount) {
			state.particleCount = inputValidationSettings.minParticleCount;
		} else if (state.particleCount > inputValidationSettings.maxParticleCount) {
			state.particleCount = inputValidationSettings.maxParticleCount;
		}
		if (state.trailLength < inputValidationSettings.minTrailLength) {
			state.trailLength = inputValidationSettings.minTrailLength;
		} else if (state.trailLength > inputValidationSettings.maxTrailLength) {
			state.trailLength = inputValidationSettings.maxTrailLength;
		}
		if (state.gridSize < inputValidationSettings.minGridSize) {
			state.gridSize = inputValidationSettings.minGridSize;
		}
		if (state.minParticleSize > state.maxParticleSize) {
			state.maxParticleSize = state.minParticleSize;
		}
		if (state.minParticleMass > state.maxParticleMass) {
			state.maxParticleMass = state.minParticleMass;
		}

		// ideally this will be used to set input values to their validated values
		afterValidation();

		var init = function init() {
			var i;
			var maxAbsRange = state.gridSize;

			var getRandom = new Math.seedrandom(state.seed);

			var randX;
			var randY;
			var randZ;

			var randVX;
			var randVY;
			var randVZ;

			var randomSizeMassColor;

			var radiusRange = state.maxParticleSize - state.minParticleSize;
			var massRange = state.maxParticleMass - state.minParticleMass;

			var radius;
			var mass;
			var color;
			var velocity;

			state.bodyArray = [];

			for (i = 0; i < state.particleCount; ++i) {

				randX = (getRandom() * maxAbsRange * 2) - maxAbsRange;
				randY = (getRandom() * maxAbsRange * 2) - maxAbsRange;
				randZ = (getRandom() * maxAbsRange * 2) - maxAbsRange;

				randomSizeMassColor = getRandom();

				radius = (randomSizeMassColor * radiusRange) + state.minParticleSize;
				mass   = (randomSizeMassColor * massRange) + state.minParticleMass;
				color = randomSizeMassColor * 0.5; // todo: this will need to be changed

				randVX = (getRandom() * 2) - 1;
				randVY = (getRandom() * 2) - 1;
				randVZ = (getRandom() * 2) - 1;

				velocity = new THREE.Vector3(randVX, randVY, randVZ);
				velocity.normalize();
				velocity.multiplyScalar(sim.startingVelocity / timeMultiplier);

				state.bodyArray[i] = APP.body.make({
					// position is in meters
					position: new THREE.Vector3(randX,randY,randZ),
					// radius is in meters
					radius: radius,
					// mass is in kg
					mass: mass,
					// color: (new THREE.Color()).setHSL(color, 1, 0.5),
					color: (new THREE.Color()).setRGB(color, color, color),
					// velocity is in meters / second
					velocity: velocity,
					isLocked: false,
					drawLines: state.drawLines,
					totalMass: roughTotalMass,
					trailLength: state.trailLength
				});

			}

		};

		var pause = function pause() {
			state.paused = true;
		};

		var play = function play() {
			state.paused = false;
		};

		var getState = function getState() {
			return state;
		};

		var update = function update() {

			updateSingleThreaded();

			updatePositions();

		};

		var updateSingleThreaded = function updateSingleThreaded() {
			var i;
			var j;
			var bodyArray = state.bodyArray;
			var arrayLen = bodyArray.length;

			var thisState;
			var thisPosition;
			var thisVelocity;
			var thisMass;
			var thisRadius;
			var thisMomentum;

			var otherState;
			var otherPosition;
			var otherVelocity;
			var otherMass;
			var otherRadius;
			var otherMomentum;

			var positionDiff;
			var accelerationDirection;
			var adjustedGravity = state.gravity * state.gravityMultiplier;
			var adjustedGravityPerDistanceSquared;

			var distanceSq;
			var collisionDistanceSq;
			var totalMomentum;

			var finalMass;
			var finalVelocity;
			var finalRadius;

			var calculate;

			outerLoop:
			for (i = 0; i < arrayLen; ++i) {

				thisState = bodyArray[i].getState();

				if (thisState.mass === 0) {
					continue outerLoop;
				}

				thisPosition = thisState.position;
				thisMass = thisState.mass;

				innerLoop:
				for (j = i + 1; j < arrayLen; ++j) {

					otherState = bodyArray[j].getState();

					if (otherState.mass === 0) {
						continue innerLoop;
					}

					otherPosition = otherState.position;
					otherMass = otherState.mass;

					positionDiff = cloneVector(otherPosition).sub(thisPosition);
					distanceSq = positionDiff.lengthSq();

					if (state.detectCollisions) {

						thisRadius = thisState.radius;
						otherRadius = otherState.radius;
						collisionDistanceSq = (thisRadius + otherRadius) * (thisRadius + otherRadius);

						if (distanceSq <= collisionDistanceSq ) {

							thisVelocity = thisState.velocity;
							otherVelocity = otherState.velocity;

							//we don't want to modify the velocity vectors here so we must clone
							thisMomentum = cloneVector(thisVelocity).multiplyScalar(thisMass);
							otherMomentum = cloneVector(otherVelocity).multiplyScalar(otherMass);
							totalMomentum = thisMomentum.add(otherMomentum);

							finalMass = thisMass + otherMass;
							finalVelocity = totalMomentum.divideScalar(finalMass);
							finalRadius = Math.pow((thisRadius * thisRadius * thisRadius + otherRadius * otherRadius * otherRadius), 1/3);

							if (thisRadius >= otherRadius) {
								thisState.velocity = cloneVector(finalVelocity); // this may not be necessary
								thisState.mass = finalMass;
								thisState.radius = finalRadius;
								otherState.mass = 0;

								// we should keep calculating for "this" object (bodyArray[i])
								// but we need to skip accel. calculations
								// between the current "this" object and the current "other" object (bodyArray[j])
								// because they will result in a 0 add to accel for "this" object.
								continue innerLoop;
							} else {
								otherState.velocity = cloneVector(finalVelocity); // this may not be necessary
								otherState.mass = finalMass;
								otherState.radius = finalRadius;
								thisState.mass = 0;

								// we should stop calculating for "this" object (bodyArray[i])
								// because this object will soon be removed
								// and all subsequent calculations on it will result in a 0 add to accel.
								continue outerLoop;
							}
						}
					}

					adjustedGravityPerDistanceSquared = adjustedGravity / distanceSq;

					accelerationDirection = positionDiff.normalize(); // for "this" object

					calculate = true;
					if (thisState.isLocked || (thisState.respectOnlyLocked && !otherState.isLocked)) {
						calculate = false;
					}

					if (calculate) {
						//calculate accel Vector for 'this' object.
					}

					calculate = true;
					if (otherState.isLocked || (otherState.respectOnlyLocked && !thisState.isLocked)) {
						calculate = false;
					}

					if (calculate) {
						accelerationDirection.negate();	// for "other" object
						//calculate accel Vector for 'other' object.
					}
				}
			}
		};

		var updatePositions = function updatePositions() {
			var i;
			var bodyArray = state.bodyArray;
			var arrayLen = bodyArray.length;

			for (i = 0; i < arrayLen; ++i) {
				//update positions
			}
		};

		var cloneVector = function cloneVector(v) {
			return new THREE.Vector3(v.x, v.y, v.z);
		};

		return {
			init: init,
			start: start,
			pause: pause,
			getState: getState,
			update: update
		};

	};

	return {
		make: make
	};
}(THREE));<commit_msg>fixed a few bugs, test commit from Visual Studio Code.<commit_after>/*global THREE */

var APP  = APP || {};

APP.simulation = (function simulation(THREE) {
	'use strict';

	var make = function make(args, afterValidation) {

		args = args || {};

		var defaults = {
			timeMultiplier: 1000000,
			gravityMultiplier: 1,

			gridSize: 150,
			gridSpacing: 5,	//5 grid lines per grid

			particleCount: 100,

			minParticleSize: 0.1,
			maxParticleSize: 1,

			minParticleMass: 0.5,
			maxParticleMass: 5,

			drawTrails: false,
			trailLength: 100,

			startingVelocity: 1,

			detectCollisions: true,

			seed: Date.now()
		};

		var inputValidationSettings = {
			minGridSize: 10,
			maxGridSize: 10000,

			minParticleCount: 1,
			maxParticleCount: 1000,

			minTrailLength: 0,
			maxTrailLength: 1000
		};

		var state = {
			gravity: 6.673e-11,

			timeMultiplier: args.timeMultiplier || defaults.timeMultiplier,
			gravityMultiplier: args.gravityMultiplier || defaults.gravityMultiplier,

			gridSize: args.gridSize || defaults.gridSize,
			gridSpacing: args.gridSpacing || defaults.gridSpacing,

			particleCount: args.particleCount || defaults.particleCount,

			minParticleSize: args.minParticleSize || defaults.minParticleSize,
			maxParticleSize: args.maxParticleSize || defaults.maxParticleSize,

			minParticleMass: args.minParticleMass || defaults.minParticleMass,
			maxParticlemass: args.maxParticleMass || defaults.maxParticleMass,

			roughTotalMass: ( state.particleCount * (state.minParticleCount + state.maxParticleCount) / 2),

			//drawTrails is checked below
			trailLength: args.trailLength || defaults.trailLength,

			startingVelocity: args.startingVelocity || defaults.startingVelocity,

			seed: args.seed || defaults.seed
		};

		//need special checks for booleans
		if (args.drawTrails === undefined) {
			state.drawTrails = defaults.drawTrails;
		} else {
			state.drawTrails = args.drawTrails;
		}
		if (args.detectCollisions === undefined) {
			state.detectCollisions = defaults.detectCollisions;
		} else {
			state.detectCollisions = args.detectCollisions;
		}

		//check for NaN values from textfield inputs
		if (state.minParticleSize !== state.minParticleSize) {
			state.minParticleSize = defaults.minParticleSize;
		}
		if (state.maxParticleSize !== state.maxParticleSize) {
			state.maxParticleSize = defaults.maxParticleSize;
		}
		if (state.minParticleMass !== state.minParticleMass) {
			state.minParticleMass = defaults.minParticleMass;
		}
		if (state.maxParticleMass !== state.maxParticleMass) {
			state.maxParticleMass = defaults.maxParticleMass;
		}
		if (state.startingVelocity !== state.startingVelocity) {
			state.startingVelocity = defaults.startingVelocity;
		}

		if (state.particleCount < 0) {
			state.particleCount = -state.particleCount;
		}
		if (state.trailLength < 0) {
			state.trailLength = -state.trailLength;
		}
		if (state.gridSize < 0) {
			state.gridSize = -state.gridSize;
		}
		if (state.minParticleSize < 0) {
			state.minParticleSize = -state.minParticleSize;
		}
		if (state.maxParticleSize < 0) {
			state.maxParticleSize = -state.maxParticleSize;
		}
		if (state.minParticleMass < 0) {
			state.minParticleMass = -state.minParticleMass;
		}
		if (state.maxParticleMass < 0) {
			state.maxParticleMass = -state.maxParticleMass;
		}
		if (state.startingVelocity < 0) {
			state.startingVelocity = -state.startingVelocity;
		}

		if (state.particleCount < inputValidationSettings.minParticleCount) {
			state.particleCount = inputValidationSettings.minParticleCount;
		} else if (state.particleCount > inputValidationSettings.maxParticleCount) {
			state.particleCount = inputValidationSettings.maxParticleCount;
		}
		if (state.trailLength < inputValidationSettings.minTrailLength) {
			state.trailLength = inputValidationSettings.minTrailLength;
		} else if (state.trailLength > inputValidationSettings.maxTrailLength) {
			state.trailLength = inputValidationSettings.maxTrailLength;
		}
		if (state.gridSize < inputValidationSettings.minGridSize) {
			state.gridSize = inputValidationSettings.minGridSize;
		}
		if (state.minParticleSize > state.maxParticleSize) {
			state.maxParticleSize = state.minParticleSize;
		}
		if (state.minParticleMass > state.maxParticleMass) {
			state.maxParticleMass = state.minParticleMass;
		}

		// ideally this will be used to set input values to their validated values
		afterValidation();

		var init = function init() {
			var i;
			var maxAbsRange = state.gridSize;

			var getRandom = new Math.seedrandom(state.seed);

			var randX;
			var randY;
			var randZ;

			var randVX;
			var randVY;
			var randVZ;

			var randomSizeMassColor;

			var radiusRange = state.maxParticleSize - state.minParticleSize;
			var massRange = state.maxParticleMass - state.minParticleMass;

			var radius;
			var mass;
			var color;
			var velocity;

			state.bodyArray = [];

			for (i = 0; i < state.particleCount; ++i) {

				randX = (getRandom() * maxAbsRange * 2) - maxAbsRange;
				randY = (getRandom() * maxAbsRange * 2) - maxAbsRange;
				randZ = (getRandom() * maxAbsRange * 2) - maxAbsRange;

				randomSizeMassColor = getRandom();

				radius = (randomSizeMassColor * radiusRange) + state.minParticleSize;
				mass   = (randomSizeMassColor * massRange) + state.minParticleMass;
				color = randomSizeMassColor * 0.5; // todo: this will need to be changed

				randVX = (getRandom() * 2) - 1;
				randVY = (getRandom() * 2) - 1;
				randVZ = (getRandom() * 2) - 1;

				velocity = new THREE.Vector3(randVX, randVY, randVZ);
				velocity.normalize();
				velocity.multiplyScalar(state.startingVelocity / state.timeMultiplier);

				state.bodyArray[i] = APP.body.make({
					// position is in meters
					position: new THREE.Vector3(randX,randY,randZ),
					// radius is in meters
					radius: radius,
					// mass is in kg
					mass: mass,
					// color: (new THREE.Color()).setHSL(color, 1, 0.5),
					color: (new THREE.Color()).setRGB(color, color, color),
					// velocity is in meters / second
					velocity: velocity,
					isLocked: false,
					drawLines: state.drawLines,
					totalMass: state.roughTotalMass,
					trailLength: state.trailLength
				});

			}

		};

		var pause = function pause() {
			state.paused = true;
		};

		var play = function play() {
			state.paused = false;
		};

		var getState = function getState() {
			return state;
		};

		var update = function update() {

			updateSingleThreaded();

			updatePositions();

		};

		var updateSingleThreaded = function updateSingleThreaded() {
			var i;
			var j;
			var bodyArray = state.bodyArray;
			var arrayLen = bodyArray.length;

			var thisState;
			var thisPosition;
			var thisVelocity;
			var thisMass;
			var thisRadius;
			var thisMomentum;

			var otherState;
			var otherPosition;
			var otherVelocity;
			var otherMass;
			var otherRadius;
			var otherMomentum;

			var positionDiff;
			var accelerationDirection;
			var adjustedGravity = state.gravity * state.gravityMultiplier;
			var adjustedGravityPerDistanceSquared;

			var distanceSq;
			var collisionDistanceSq;
			var totalMomentum;

			var finalMass;
			var finalVelocity;
			var finalRadius;

			var calculate;

			outerLoop:
			for (i = 0; i < arrayLen; ++i) {

				thisState = bodyArray[i].getState();

				if (thisState.mass === 0) {
					continue outerLoop;
				}

				thisPosition = thisState.position;
				thisMass = thisState.mass;

				innerLoop:
				for (j = i + 1; j < arrayLen; ++j) {

					otherState = bodyArray[j].getState();

					if (otherState.mass === 0) {
						continue innerLoop;
					}

					otherPosition = otherState.position;
					otherMass = otherState.mass;

					positionDiff = cloneVector(otherPosition).sub(thisPosition);
					distanceSq = positionDiff.lengthSq();

					if (state.detectCollisions) {

						thisRadius = thisState.radius;
						otherRadius = otherState.radius;
						collisionDistanceSq = (thisRadius + otherRadius) * (thisRadius + otherRadius);

						if (distanceSq <= collisionDistanceSq ) {

							thisVelocity = thisState.velocity;
							otherVelocity = otherState.velocity;

							//we don't want to modify the velocity vectors here so we must clone
							thisMomentum = cloneVector(thisVelocity).multiplyScalar(thisMass);
							otherMomentum = cloneVector(otherVelocity).multiplyScalar(otherMass);
							totalMomentum = thisMomentum.add(otherMomentum);

							finalMass = thisMass + otherMass;
							finalVelocity = totalMomentum.divideScalar(finalMass);
							finalRadius = Math.pow((thisRadius * thisRadius * thisRadius + otherRadius * otherRadius * otherRadius), 1/3);

							if (thisRadius >= otherRadius) {
								thisState.velocity = cloneVector(finalVelocity); // this may not be necessary
								thisState.mass = finalMass;
								thisState.radius = finalRadius;
								otherState.mass = 0;

								// we should keep calculating for "this" object (bodyArray[i])
								// but we need to skip accel. calculations
								// between the current "this" object and the current "other" object (bodyArray[j])
								// because they will result in a 0 add to accel for "this" object.
								continue innerLoop;
							} else {
								otherState.velocity = cloneVector(finalVelocity); // this may not be necessary
								otherState.mass = finalMass;
								otherState.radius = finalRadius;
								thisState.mass = 0;

								// we should stop calculating for "this" object (bodyArray[i])
								// because this object will soon be removed
								// and all subsequent calculations on it will result in a 0 add to accel.
								continue outerLoop;
							}
						}
					}

					adjustedGravityPerDistanceSquared = adjustedGravity / distanceSq;

					accelerationDirection = positionDiff.normalize(); // for "this" object

					calculate = true;
					if (thisState.isLocked || (thisState.respectOnlyLocked && !otherState.isLocked)) {
						calculate = false;
					}

					if (calculate) {
						//calculate accel Vector for 'this' object.
					}

					calculate = true;
					if (otherState.isLocked || (otherState.respectOnlyLocked && !thisState.isLocked)) {
						calculate = false;
					}

					if (calculate) {
						accelerationDirection.negate();	// for "other" object
						//calculate accel Vector for 'other' object.
					}
				}
			}
		};

		var updatePositions = function updatePositions() {
			var i;
			var bodyArray = state.bodyArray;
			var arrayLen = bodyArray.length;

			for (i = 0; i < arrayLen; ++i) {
				//update positions
			}
		};

		var cloneVector = function cloneVector(v) {
			return new THREE.Vector3(v.x, v.y, v.z);
		};

		return {
			init: init,
			play: play,
			pause: pause,
			getState: getState,
			update: update
		};

	};

	return {
		make: make
	};
}(THREE));<|endoftext|>