<commit_before>'use strict';

var convertToType = require('./type-util').convertToType;

/**
 * @class
 * the variable manager is responsible for managing access to variables.
 *
 * Variable Datatype
 *
 * A variable has the following properties:
 *
 *   name: the name of the variable
 *
 *   type: the type of the variable. The type is a "backend type"
 *
 *
 */
function VariableManager() {

  /** @member object containing the form fields. Initially empty. */
  this.variables = { };

  /** @member boolean indicating whether the variables are fetched */
  this.isVariablesFetched = false;

}

VariableManager.prototype.fetchVariable = function(variable) {
  if(this.isVariablesFetched) {
    throw new Error('Illegal State: cannot call fetchVariable(), variables already fetched.');
  }
  this.createVariable({ name: variable });
};

VariableManager.prototype.createVariable = function(variable) {
  if(!this.variables[variable.name]) {
    this.variables[variable.name] = variable;
  } else {
    throw new Error('Cannot add variable with name '+variable.name+': already exists.');
  }
};

VariableManager.prototype.destroyVariable = function(variableName) {
  if(this.variables[variableName]) {
    delete this.variables[variableName];
  } else {
    throw new Error('Cannot remove variable with name '+variableName+': variable does not exist.');
  }
};

VariableManager.prototype.setOriginalValue = function(variableName, value) {
  if(this.variables[variableName]) {
    this.variables[variableName].originalValue = value;
  } else {
    throw new Error('Cannot set original value of variable with name '+variableName+': variable does not exist.');
  }

};

VariableManager.prototype.variable = function(variableName) {
  return this.variables[variableName];
};

VariableManager.prototype.variableValue = function(variableName, value) {

  var variable = this.variable(variableName);

  if(typeof value === 'undefined' || value === null) {
    value = null;

  } else if(value === '' && variable.type !== 'String') {
    // convert empty string to null for all types except String
    value = null;

  } else if(typeof value === 'string' && variable.type !== 'String') {
    // convert string value into model value
    value = convertToType(value, variable.type);

  }

  if(arguments.length === 2) {
    variable.value = value;
  }

  return variable.value;
};

VariableManager.prototype.isDirty = function(name) {
  var variable = this.variable(name);
  if(this.isJsonVariable(name)) {
    return variable.originalValue !== JSON.stringify(variable.value);
  } else {
    return variable.originalValue !== variable.value || variable.type === 'Object';
  }
};

VariableManager.prototype.isJsonVariable = function(name) {
  var variable = this.variable(name);
  var type = variable.type;

  var supportedTypes = [ 'Object', 'json', 'Json' ];
  var idx = supportedTypes.indexOf(type);

  if (idx === 0) {
    return variable.valueInfo.serializationDataFormat.indexOf('application/json') !== -1;
  }

  return idx !== -1;
};

VariableManager.prototype.isDateVariable = function(name) {
  var variable = this.variable(name);
  return variable.type === 'Date';
};

VariableManager.prototype.variableNames = function() {
  // since we support IE 8+ (http://kangax.github.io/compat-table/es5/)
  return Object.keys(this.variables);
};

module.exports = VariableManager;
<commit_msg>fix(camunda-form): remove exception when adding a variable that already exists<commit_after>'use strict';

var convertToType = require('./type-util').convertToType;

/**
 * @class
 * the variable manager is responsible for managing access to variables.
 *
 * Variable Datatype
 *
 * A variable has the following properties:
 *
 *   name: the name of the variable
 *
 *   type: the type of the variable. The type is a "backend type"
 *
 *
 */
function VariableManager() {

  /** @member object containing the form fields. Initially empty. */
  this.variables = { };

  /** @member boolean indicating whether the variables are fetched */
  this.isVariablesFetched = false;

}

VariableManager.prototype.fetchVariable = function(variable) {
  if(this.isVariablesFetched) {
    throw new Error('Illegal State: cannot call fetchVariable(), variables already fetched.');
  }
  this.createVariable({ name: variable });
};

VariableManager.prototype.createVariable = function(variable) {
  this.variables[variable.name] = variable;
};

VariableManager.prototype.destroyVariable = function(variableName) {
  if(this.variables[variableName]) {
    delete this.variables[variableName];
  } else {
    throw new Error('Cannot remove variable with name '+variableName+': variable does not exist.');
  }
};

VariableManager.prototype.setOriginalValue = function(variableName, value) {
  if(this.variables[variableName]) {
    this.variables[variableName].originalValue = value;
  } else {
    throw new Error('Cannot set original value of variable with name '+variableName+': variable does not exist.');
  }

};

VariableManager.prototype.variable = function(variableName) {
  return this.variables[variableName];
};

VariableManager.prototype.variableValue = function(variableName, value) {

  var variable = this.variable(variableName);

  if(typeof value === 'undefined' || value === null) {
    value = null;

  } else if(value === '' && variable.type !== 'String') {
    // convert empty string to null for all types except String
    value = null;

  } else if(typeof value === 'string' && variable.type !== 'String') {
    // convert string value into model value
    value = convertToType(value, variable.type);

  }

  if(arguments.length === 2) {
    variable.value = value;
  }

  return variable.value;
};

VariableManager.prototype.isDirty = function(name) {
  var variable = this.variable(name);
  if(this.isJsonVariable(name)) {
    return variable.originalValue !== JSON.stringify(variable.value);
  } else {
    return variable.originalValue !== variable.value || variable.type === 'Object';
  }
};

VariableManager.prototype.isJsonVariable = function(name) {
  var variable = this.variable(name);
  var type = variable.type;

  var supportedTypes = [ 'Object', 'json', 'Json' ];
  var idx = supportedTypes.indexOf(type);

  if (idx === 0) {
    return variable.valueInfo.serializationDataFormat.indexOf('application/json') !== -1;
  }

  return idx !== -1;
};

VariableManager.prototype.isDateVariable = function(name) {
  var variable = this.variable(name);
  return variable.type === 'Date';
};

VariableManager.prototype.variableNames = function() {
  // since we support IE 8+ (http://kangax.github.io/compat-table/es5/)
  return Object.keys(this.variables);
};

module.exports = VariableManager;
<|endoftext|>


<commit_before>/* global angular:false */
'use strict';

/**
 * Define our ndslabs module here. All other files will
 * use the single-argument notation for angular.module()
 */
angular.module('ndslabs', [ 'navbar', 'footer', 'ndslabs-services', 'ndslabs-filters', 'ndslabs-directives',  'ndslabs-api', 'ngWizard', 'ngGrid', 'ngAlert', 'ngTagsInput', 'cgBusy',
    'ngRoute', 'ngResource', 'ngCookies', 'ngAnimate', 'ngMessages', 'ui.bootstrap', 'ngPasswordStrength', 'angular-clipboard', 'ui.pwgen', 'frapontillo.gage', 'chart.js', 'ui.gravatar' ])

/**
 * If true, display verbose debug data as JSON
 */
.constant('DEBUG', false)

/**
 *TODO: Whether or not to use mock data (false if talking to live etcd)
 */
//.constant('MOCKDATA', false)

/**
 * Make lodash available for injection into controllers
 */
.constant('_', window._)

/**
 * The route to our "Login" View
 */
.constant('LoginRoute', '/login')

/**
 * The route to our "Request Access" View
 */
.constant('SignUpRoute', '/register')

/**
 * The route to our "Verify Account" View
 */
.constant('VerifyAccountRoute', '/register/verify')

/**
 * The route to our "Recover Password" View
 */
.constant('ResetPasswordRoute', '/recover')

/**
 * The route to the "AppStore" view
 */
.constant('AppStoreRoute', '/store')

/**
 * The route to the "Add Application Spec" view
 */
.constant('AddSpecRoute', '/store/add')

/**
 * The route to the "Edit Application Spec" view
 */
.constant('EditSpecRoute', '/store/edit/:specKey')

/**
 * The route to our "Dashboard" View
 */
.constant('HomeRoute', '/home')

/**
 * The route to the "Add Application Service" view
 */
.constant('AddServiceRoute', '/home/:stackId/add/:service')

/**
 * The route to the "Edit Application Service" view
 */
.constant('EditServiceRoute', '/home/:stackId/edit/:service')

/**
 * The route to the "Application Service Console" view
 */
.constant('ConsoleRoute', '/home/:stackId/console/:service')

/**
 * The version/revision of this GUI
 */
.constant('BuildVersion', '1.0.2-devel')
.constant('BuildDate', '')

/**
 * Hostname / Port for communicating with etcd
 *
 * This must be the external IP and nodePort (when running in k8)
 *
 * TODO: We assume this is running on the same machine as the apiserver.
 */
.constant('ApiHost', 'localhost')
.constant('ApiPort', '')
.constant('ApiPath', '/api')
.constant('ApiSecure', false)

.constant('WebsocketPath', '/console')

/** Store our built ApiUri here */
.value('ApiUri', { api: '', ws: '' })

/**
 * Logic for communicating with etcd (powered by swagger-js-codegen)
 * @param {string} ApiHost - the hostname defined above
 * @param {string} ApiPort - the port defined above
 * @param {string} ApiPath - the path defined above
 * @param {Object} ApiServer - the REST API client generated by swagger; see 'app/shared/NdsLabsRestApi.js'
 */
.factory('NdsLabsApi', [ 'ApiHost', 'ApiPort', 'ApiPath', 'ApiSecure', 'WebsocketPath', 'ApiUri', 'ApiServer',
    function(ApiHost, ApiPort, ApiPath, ApiSecure, WebsocketPath, ApiUri, ApiServer) {
  // TODO: Investigate options / caching
  // XXX: Caching may not be possible due to the unique token sent with every request

  // Start with the protocol
  if (ApiSecure) {
    ApiUri.api = 'https://' + ApiHost
    ApiUri.ws = 'wss://' + ApiHost
  } else {
    ApiUri.api = 'http://' + ApiHost
    ApiUri.ws = 'ws://' + ApiHost
  }

  // Add on the port suffix, if applicable
  if (ApiPort) {
    var portSuffix = ':' + ApiPort

    ApiUri.api += portSuffix
    ApiUri.ws += portSuffix
  }

  // Add on the path suffix, if applicable
   ApiUri.api += ApiPath
   ApiUri.ws += ApiPath + WebsocketPath

  // Instantiate a new client for the ApiServer using our newly built uri
  return new ApiServer(ApiUri.api);
}])

/**
 * A shared store for our AuthInfo, done as a provider so that we
 * can easily inject it into the .config() block below
 */
.provider('AuthInfo', function() {
  this.authInfo = {
    namespace: '',
    password: '',
    saveCookie: false,
    project: null,
    token: null
  };

  this.$get = function() {
    var authInfo = this.authInfo;
    return {
      get: function() { return authInfo; },
      purge: function() {
        // We overwrite this stub function with "terminateSession" inside of the ".run()" handler below
        return true;
      }
    }
  };
})

/**
 * Configure routes / HTTP for our app using the services defined above
 */
.config([ '$routeProvider', '$httpProvider', '$logProvider', 'DEBUG', 'AuthInfoProvider', 'LoginRoute', 'AppStoreRoute', 'HomeRoute', 'ConsoleRoute', 'AddServiceRoute', 'EditServiceRoute', 'AddSpecRoute', 'EditSpecRoute', 'VerifyAccountRoute', 'ResetPasswordRoute', 'SignUpRoute',
    function($routeProvider, $httpProvider, $logProvider, DEBUG, authInfo, LoginRoute, AppStoreRoute, HomeRoute, ConsoleRoute, AddServiceRoute, EditServiceRoute, AddSpecRoute, EditSpecRoute, VerifyAccountRoute, ResetPasswordRoute, SignUpRoute) {
  // Squelch debug-level log messages
  $logProvider.debugEnabled(DEBUG);

  // Setup default behaviors for encountering HTTP errors
  $httpProvider.interceptors.push(['$rootScope', '$cookies', '$q', '$location', '$log', '_', 'DEBUG', 'ApiUri', 'AuthInfo',
      function (scope, $cookies, $q, $location, $log, _, DEBUG, ApiUri, AuthInfo) {
    return {
      // Attach our auth token to each outgoing request (to the api server)
      'request': function(config) {
        // If this is a request for our API server
        if (_.includes(config.url, ApiUri.api)) {
          // If this was *not* an attempt to authenticate
          if (!_.includes(config.url, '/authenticate')) {
            // We need to attach our token to this request
            config.headers['Authorization'] = 'Bearer ' + $cookies.get('token');
          }
        }
        return config;
      },
      'requestError': function(rejection) {
        if (_.includes(rejection.config.url, ApiUri.api)) {
          $log.error("Request error encountered: " + rejection.config.url);
        }
        return $q.reject(rejection);
      },
      'response': function(response) {
        // If this is a response from our API server
        if (_.includes(response.config.url, ApiUri.api)) {
          // If this was in response to an /authenticate or /refresh_token request
          if ((_.includes(response.config.url, '/authenticate') && response.config.method === 'POST')
              || (_.includes(response.config.url, '/refresh_token') && response.config.method === 'GET')) {
            // This response should contain a new token, so save it as a cookie
            $cookies.put('token', response.data.token);
          }
        }

        return response;
      },
      'responseError': function(rejection) {
        // If this is a response from our API server
        if (_.includes(rejection.config.url, ApiUri.api)) {
          $log.error("Response error encountered: " + rejection.config.url);

          // Read out the HTTP error code
          var status = rejection.status;

          // Handle HTTP 401: Not Authorized - User needs to provide credentials
          if (status == 401) {
            // TODO: If we want to intercept the route to redirect them after a successful login
            //window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);

            // Purge current session data
            authInfo.authInfo.token = null;
            $cookies.remove('token');
            $cookies.remove('namespace');

            // Route to Login Page to prompt for credentials
            if ($location.path() !== LoginRoute) {
              $location.path(LoginRoute);
            }
            return $q.reject(rejection);
          }
        }

        // otherwise
        return $q.reject(rejection);
      }
    };
  }]);

  // Setup routes to our different pages
  $routeProvider
  .when(LoginRoute, {
    title: 'Sign In to NDS Labs',
    controller: 'LoginController',
    templateUrl: 'app/login/login.html'
  })
  .when(SignUpRoute, {
    title: 'Sign Up for NDS Labs',
    controller: 'SignUpController',
    templateUrl: 'app/login/signUp/signUp.html'
  })
  .when(VerifyAccountRoute, {
    title: 'E-mail verified!',
    controller: 'VerifyAccountController',
    templateUrl: 'app/login/verify/verify.html'
  })
  .when(ResetPasswordRoute, {
    title: 'Reset Password',
    controller: 'ResetPasswordController',
    templateUrl: 'app/login/reset/reset.html'
  })
  .when(AppStoreRoute, {
    title: 'NDS Labs Catalog',
    controller: 'CatalogController',
    templateUrl: 'app/appStore/catalog.html'
  })
  .when(AddSpecRoute, {
    title: 'Add Application',
    controller: 'AddOrEditSpecController',
    templateUrl: 'app/appStore/addOrEdit/addOrEditSpec.html'
  })
  .when(EditSpecRoute, {
    title: 'Edit Application',
    controller: 'AddOrEditSpecController',
    templateUrl: 'app/appStore/addOrEdit/addOrEditSpec.html'
  })
  .when(HomeRoute, {
    title: 'NDS Labs Dashboard',
    controller: 'DashboardController',
    templateUrl: 'app/dashboard/dashboard.html'
  })
  .when(AddServiceRoute, {
    title: 'Add Application Service',
    controller: 'AddOrEditServiceController',
    templateUrl: 'app/dashboard/service/addOrEditService.html'
  })
  .when(EditServiceRoute, {
    title: 'Edit Application Service',
    controller: 'AddOrEditServiceController',
    templateUrl: 'app/dashboard/service/addOrEditService.html'
  })
  .when(ConsoleRoute, {
    title: 'Application Service Console',
    controller: 'ConsoleController',
    templateUrl: 'app/dashboard/console/console.html'
  })
  .otherwise({ redirectTo: LoginRoute });
}])

/**
 * Once configured, run this section of code to finish bootstrapping our app
 */
.run([ '$rootScope', '$window', '$location', '$log', '$interval', '$cookies', '$uibModalStack', 'Stacks', '_', 'AuthInfo', 'LoginRoute', 'AppStoreRoute', 'HomeRoute', 'NdsLabsApi', 'AutoRefresh', 'ServerData', 'Loading',
    function($rootScope, $window, $location, $log, $interval, $cookies, $uibModalStack, Stacks, _, authInfo, LoginRoute, AppStoreRoute, HomeRoute, NdsLabsApi, AutoRefresh, ServerData, Loading) {

  // Grab saved auth data from cookies and attempt to use the leftover session
  var token = $cookies.get('token');
  var namespace = $cookies.get('namespace');
  if (token && namespace) {
    // Pull our token / namespace from cookies
    authInfo.get().token = token;
    authInfo.get().namespace = namespace;
  }

  // Make _ bindable in partial views
  // TODO: Investigate performance concerns here...
  $rootScope._ = window._;

  // Change the tab/window title when we change routes
  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
    if (current.$$route) {
      $window.document.title = current.$$route.title;
    } else {
      $log.error('Encountered undefined route...');
    }
  });

  // When user changes routes, check that they are still authed
  $rootScope.$on( "$routeChangeStart", function(event, next, current) {
    // Skip token checking for the "Verify Account" View
    if (next.$$route.templateUrl === 'app/login/verify/verify.html') {
      return;
    }

    // Define the logic for ending a user's session in the browser
    var authInterval = null;
    var terminateSession = authInfo.purge = function() {
      // Cancel the auth check interval
      if (authInterval) {
        $interval.cancel(authInterval);
        authInfo.tokenInterval = authInterval = null;
      }

      if (authInfo.get().token) {

        // Purge current session data
        authInfo.get().token = null;
        $cookies.remove('token');
        $cookies.remove('namespace');

        // Close any open modals
        $uibModalStack.dismissAll();

        // Stop any running auto-refresh interval
        AutoRefresh.stop();

        // Purge any server data
        ServerData.purgeAll();

        // user needs to log in, redirect to /login
        if (!_.includes(next.templateUrl, "app/login/login.html")
            && !_.includes(next.templateUrl, "app/login/signUp/signUp.html")
            && !_.includes(next.templateUrl, "app/login/verify/verify.html")) {
          $location.path(LoginRoute);
        }
      }
    };

    // Check our token every 60s
    var tokenCheckMs = 60000;

    // Every so often, check that our token is still valid
    var checkToken = function() {
      NdsLabsApi.getCheckToken().then(function() { $log.debug('Token is still valid.'); }, function() {
        $log.error('Token expired, redirecting to login.');
        terminateSession();
        authInfo.purge();
      });
    };

    // Check if the token is still valid on route changes
    var token = $cookies.get('token');
    if (token) {
      authInfo.get().token = token;
      authInfo.get().namespace = $cookies.get('namespace');
      NdsLabsApi.getRefreshToken().then(function() {
        $log.debug('Token refreshed: ' + authInfo.get().token);

        Loading.set(ServerData.populateAll(authInfo.get().namespace).finally(function() {
          /*if (InitialRedirect) {
            InitialRedirect = false;
            var dest = Stacks.all.length > 0 ? HomeRoute : AppStoreRoute;
            debugger;
            $location.path(dest);
          }*/
        }));

        // Restart our token check interval
        if (authInterval) {
          $interval.cancel(authInterval);
          authInterval = null;
        }
        authInfo.tokenInterval = authInterval = $interval(checkToken, tokenCheckMs);

      }, function() {
        $log.debug('Failed to refresh token!');

        // TODO: Allow login page to reroute user to destination?
        // XXX: This would matter more if there are multiple views
        //authInfo.returnRoute = next.$$route.originalPath;

        terminateSession();
      });
    } else {
      terminateSession();
    }
  });
}]);
<commit_msg>Updated version to 1.0.3<commit_after>/* global angular:false */
'use strict';

/**
 * Define our ndslabs module here. All other files will
 * use the single-argument notation for angular.module()
 */
angular.module('ndslabs', [ 'navbar', 'footer', 'ndslabs-services', 'ndslabs-filters', 'ndslabs-directives',  'ndslabs-api', 'ngWizard', 'ngGrid', 'ngAlert', 'ngTagsInput', 'cgBusy',
    'ngRoute', 'ngResource', 'ngCookies', 'ngAnimate', 'ngMessages', 'ui.bootstrap', 'ngPasswordStrength', 'angular-clipboard', 'ui.pwgen', 'frapontillo.gage', 'chart.js', 'ui.gravatar' ])

/**
 * If true, display verbose debug data as JSON
 */
.constant('DEBUG', false)

/**
 *TODO: Whether or not to use mock data (false if talking to live etcd)
 */
//.constant('MOCKDATA', false)

/**
 * Make lodash available for injection into controllers
 */
.constant('_', window._)

/**
 * The route to our "Login" View
 */
.constant('LoginRoute', '/login')

/**
 * The route to our "Request Access" View
 */
.constant('SignUpRoute', '/register')

/**
 * The route to our "Verify Account" View
 */
.constant('VerifyAccountRoute', '/register/verify')

/**
 * The route to our "Recover Password" View
 */
.constant('ResetPasswordRoute', '/recover')

/**
 * The route to the "AppStore" view
 */
.constant('AppStoreRoute', '/store')

/**
 * The route to the "Add Application Spec" view
 */
.constant('AddSpecRoute', '/store/add')

/**
 * The route to the "Edit Application Spec" view
 */
.constant('EditSpecRoute', '/store/edit/:specKey')

/**
 * The route to our "Dashboard" View
 */
.constant('HomeRoute', '/home')

/**
 * The route to the "Add Application Service" view
 */
.constant('AddServiceRoute', '/home/:stackId/add/:service')

/**
 * The route to the "Edit Application Service" view
 */
.constant('EditServiceRoute', '/home/:stackId/edit/:service')

/**
 * The route to the "Application Service Console" view
 */
.constant('ConsoleRoute', '/home/:stackId/console/:service')

/**
 * The version/revision of this GUI
 */
.constant('BuildVersion', '1.0.3-devel')
.constant('BuildDate', '')

/**
 * Hostname / Port for communicating with etcd
 *
 * This must be the external IP and nodePort (when running in k8)
 *
 * TODO: We assume this is running on the same machine as the apiserver.
 */
.constant('ApiHost', 'localhost')
.constant('ApiPort', '')
.constant('ApiPath', '/api')
.constant('ApiSecure', false)

.constant('WebsocketPath', '/console')

/** Store our built ApiUri here */
.value('ApiUri', { api: '', ws: '' })

/**
 * Logic for communicating with etcd (powered by swagger-js-codegen)
 * @param {string} ApiHost - the hostname defined above
 * @param {string} ApiPort - the port defined above
 * @param {string} ApiPath - the path defined above
 * @param {Object} ApiServer - the REST API client generated by swagger; see 'app/shared/NdsLabsRestApi.js'
 */
.factory('NdsLabsApi', [ 'ApiHost', 'ApiPort', 'ApiPath', 'ApiSecure', 'WebsocketPath', 'ApiUri', 'ApiServer',
    function(ApiHost, ApiPort, ApiPath, ApiSecure, WebsocketPath, ApiUri, ApiServer) {
  // TODO: Investigate options / caching
  // XXX: Caching may not be possible due to the unique token sent with every request

  // Start with the protocol
  if (ApiSecure) {
    ApiUri.api = 'https://' + ApiHost
    ApiUri.ws = 'wss://' + ApiHost
  } else {
    ApiUri.api = 'http://' + ApiHost
    ApiUri.ws = 'ws://' + ApiHost
  }

  // Add on the port suffix, if applicable
  if (ApiPort) {
    var portSuffix = ':' + ApiPort

    ApiUri.api += portSuffix
    ApiUri.ws += portSuffix
  }

  // Add on the path suffix, if applicable
   ApiUri.api += ApiPath
   ApiUri.ws += ApiPath + WebsocketPath

  // Instantiate a new client for the ApiServer using our newly built uri
  return new ApiServer(ApiUri.api);
}])

/**
 * A shared store for our AuthInfo, done as a provider so that we
 * can easily inject it into the .config() block below
 */
.provider('AuthInfo', function() {
  this.authInfo = {
    namespace: '',
    password: '',
    saveCookie: false,
    project: null,
    token: null
  };

  this.$get = function() {
    var authInfo = this.authInfo;
    return {
      get: function() { return authInfo; },
      purge: function() {
        // We overwrite this stub function with "terminateSession" inside of the ".run()" handler below
        return true;
      }
    }
  };
})

/**
 * Configure routes / HTTP for our app using the services defined above
 */
.config([ '$routeProvider', '$httpProvider', '$logProvider', 'DEBUG', 'AuthInfoProvider', 'LoginRoute', 'AppStoreRoute', 'HomeRoute', 'ConsoleRoute', 'AddServiceRoute', 'EditServiceRoute', 'AddSpecRoute', 'EditSpecRoute', 'VerifyAccountRoute', 'ResetPasswordRoute', 'SignUpRoute',
    function($routeProvider, $httpProvider, $logProvider, DEBUG, authInfo, LoginRoute, AppStoreRoute, HomeRoute, ConsoleRoute, AddServiceRoute, EditServiceRoute, AddSpecRoute, EditSpecRoute, VerifyAccountRoute, ResetPasswordRoute, SignUpRoute) {
  // Squelch debug-level log messages
  $logProvider.debugEnabled(DEBUG);

  // Setup default behaviors for encountering HTTP errors
  $httpProvider.interceptors.push(['$rootScope', '$cookies', '$q', '$location', '$log', '_', 'DEBUG', 'ApiUri', 'AuthInfo',
      function (scope, $cookies, $q, $location, $log, _, DEBUG, ApiUri, AuthInfo) {
    return {
      // Attach our auth token to each outgoing request (to the api server)
      'request': function(config) {
        // If this is a request for our API server
        if (_.includes(config.url, ApiUri.api)) {
          // If this was *not* an attempt to authenticate
          if (!_.includes(config.url, '/authenticate')) {
            // We need to attach our token to this request
            config.headers['Authorization'] = 'Bearer ' + $cookies.get('token');
          }
        }
        return config;
      },
      'requestError': function(rejection) {
        if (_.includes(rejection.config.url, ApiUri.api)) {
          $log.error("Request error encountered: " + rejection.config.url);
        }
        return $q.reject(rejection);
      },
      'response': function(response) {
        // If this is a response from our API server
        if (_.includes(response.config.url, ApiUri.api)) {
          // If this was in response to an /authenticate or /refresh_token request
          if ((_.includes(response.config.url, '/authenticate') && response.config.method === 'POST')
              || (_.includes(response.config.url, '/refresh_token') && response.config.method === 'GET')) {
            // This response should contain a new token, so save it as a cookie
            $cookies.put('token', response.data.token);
          }
        }

        return response;
      },
      'responseError': function(rejection) {
        // If this is a response from our API server
        if (_.includes(rejection.config.url, ApiUri.api)) {
          $log.error("Response error encountered: " + rejection.config.url);

          // Read out the HTTP error code
          var status = rejection.status;

          // Handle HTTP 401: Not Authorized - User needs to provide credentials
          if (status == 401) {
            // TODO: If we want to intercept the route to redirect them after a successful login
            //window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);

            // Purge current session data
            authInfo.authInfo.token = null;
            $cookies.remove('token');
            $cookies.remove('namespace');

            // Route to Login Page to prompt for credentials
            if ($location.path() !== LoginRoute) {
              $location.path(LoginRoute);
            }
            return $q.reject(rejection);
          }
        }

        // otherwise
        return $q.reject(rejection);
      }
    };
  }]);

  // Setup routes to our different pages
  $routeProvider
  .when(LoginRoute, {
    title: 'Sign In to NDS Labs',
    controller: 'LoginController',
    templateUrl: 'app/login/login.html'
  })
  .when(SignUpRoute, {
    title: 'Sign Up for NDS Labs',
    controller: 'SignUpController',
    templateUrl: 'app/login/signUp/signUp.html'
  })
  .when(VerifyAccountRoute, {
    title: 'E-mail verified!',
    controller: 'VerifyAccountController',
    templateUrl: 'app/login/verify/verify.html'
  })
  .when(ResetPasswordRoute, {
    title: 'Reset Password',
    controller: 'ResetPasswordController',
    templateUrl: 'app/login/reset/reset.html'
  })
  .when(AppStoreRoute, {
    title: 'NDS Labs Catalog',
    controller: 'CatalogController',
    templateUrl: 'app/appStore/catalog.html'
  })
  .when(AddSpecRoute, {
    title: 'Add Application',
    controller: 'AddOrEditSpecController',
    templateUrl: 'app/appStore/addOrEdit/addOrEditSpec.html'
  })
  .when(EditSpecRoute, {
    title: 'Edit Application',
    controller: 'AddOrEditSpecController',
    templateUrl: 'app/appStore/addOrEdit/addOrEditSpec.html'
  })
  .when(HomeRoute, {
    title: 'NDS Labs Dashboard',
    controller: 'DashboardController',
    templateUrl: 'app/dashboard/dashboard.html'
  })
  .when(AddServiceRoute, {
    title: 'Add Application Service',
    controller: 'AddOrEditServiceController',
    templateUrl: 'app/dashboard/service/addOrEditService.html'
  })
  .when(EditServiceRoute, {
    title: 'Edit Application Service',
    controller: 'AddOrEditServiceController',
    templateUrl: 'app/dashboard/service/addOrEditService.html'
  })
  .when(ConsoleRoute, {
    title: 'Application Service Console',
    controller: 'ConsoleController',
    templateUrl: 'app/dashboard/console/console.html'
  })
  .otherwise({ redirectTo: LoginRoute });
}])

/**
 * Once configured, run this section of code to finish bootstrapping our app
 */
.run([ '$rootScope', '$window', '$location', '$log', '$interval', '$cookies', '$uibModalStack', 'Stacks', '_', 'AuthInfo', 'LoginRoute', 'AppStoreRoute', 'HomeRoute', 'NdsLabsApi', 'AutoRefresh', 'ServerData', 'Loading',
    function($rootScope, $window, $location, $log, $interval, $cookies, $uibModalStack, Stacks, _, authInfo, LoginRoute, AppStoreRoute, HomeRoute, NdsLabsApi, AutoRefresh, ServerData, Loading) {

  // Grab saved auth data from cookies and attempt to use the leftover session
  var token = $cookies.get('token');
  var namespace = $cookies.get('namespace');
  if (token && namespace) {
    // Pull our token / namespace from cookies
    authInfo.get().token = token;
    authInfo.get().namespace = namespace;
  }

  // Make _ bindable in partial views
  // TODO: Investigate performance concerns here...
  $rootScope._ = window._;

  // Change the tab/window title when we change routes
  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
    if (current.$$route) {
      $window.document.title = current.$$route.title;
    } else {
      $log.error('Encountered undefined route...');
    }
  });

  // When user changes routes, check that they are still authed
  $rootScope.$on( "$routeChangeStart", function(event, next, current) {
    // Skip token checking for the "Verify Account" View
    if (next.$$route.templateUrl === 'app/login/verify/verify.html') {
      return;
    }

    // Define the logic for ending a user's session in the browser
    var authInterval = null;
    var terminateSession = authInfo.purge = function() {
      // Cancel the auth check interval
      if (authInterval) {
        $interval.cancel(authInterval);
        authInfo.tokenInterval = authInterval = null;
      }

      if (authInfo.get().token) {

        // Purge current session data
        authInfo.get().token = null;
        $cookies.remove('token');
        $cookies.remove('namespace');

        // Close any open modals
        $uibModalStack.dismissAll();

        // Stop any running auto-refresh interval
        AutoRefresh.stop();

        // Purge any server data
        ServerData.purgeAll();

        // user needs to log in, redirect to /login
        if (!_.includes(next.templateUrl, "app/login/login.html")
            && !_.includes(next.templateUrl, "app/login/signUp/signUp.html")
            && !_.includes(next.templateUrl, "app/login/verify/verify.html")) {
          $location.path(LoginRoute);
        }
      }
    };

    // Check our token every 60s
    var tokenCheckMs = 60000;

    // Every so often, check that our token is still valid
    var checkToken = function() {
      NdsLabsApi.getCheckToken().then(function() { $log.debug('Token is still valid.'); }, function() {
        $log.error('Token expired, redirecting to login.');
        terminateSession();
        authInfo.purge();
      });
    };

    // Check if the token is still valid on route changes
    var token = $cookies.get('token');
    if (token) {
      authInfo.get().token = token;
      authInfo.get().namespace = $cookies.get('namespace');
      NdsLabsApi.getRefreshToken().then(function() {
        $log.debug('Token refreshed: ' + authInfo.get().token);

        Loading.set(ServerData.populateAll(authInfo.get().namespace).finally(function() {
          /*if (InitialRedirect) {
            InitialRedirect = false;
            var dest = Stacks.all.length > 0 ? HomeRoute : AppStoreRoute;
            debugger;
            $location.path(dest);
          }*/
        }));

        // Restart our token check interval
        if (authInterval) {
          $interval.cancel(authInterval);
          authInterval = null;
        }
        authInfo.tokenInterval = authInterval = $interval(checkToken, tokenCheckMs);

      }, function() {
        $log.debug('Failed to refresh token!');

        // TODO: Allow login page to reroute user to destination?
        // XXX: This would matter more if there are multiple views
        //authInfo.returnRoute = next.$$route.originalPath;

        terminateSession();
      });
    } else {
      terminateSession();
    }
  });
}]);
<|endoftext|>


<commit_before>module.exports = function(sonicAnnotatorPath, audioFilesDir, featuresFilesDir) {
	var absp = require("abs");

	var PATH_AUDIO_FILES 	= absp(audioFilesDir || './temp/audio');
	var PATH_FEATURES_FILES = absp(featuresFilesDir || './temp/feats');
	var PATH_BINARY 		= absp(sonicAnnotatorPath || '/home/echo66/tese/features-extractors/sonic-annotator/sonic-annotator');
	var DEFAULT_PLUGINS = [
		'vamp:nnls-chroma:nnls-chroma:chroma',
		'vamp:qm-vamp-plugins:qm-mfcc:coefficients',
		'vamp:qm-vamp-plugins:qm-tempotracker:beats',
	];
	var STATS = new Set(['min', 'max', 'mean', 'std', 'var', 'median', 'sum']);

	var extractionWorker = require('child_process').fork('extraction-worker.js');
	var log4js = require('log4js');
	var router = require('express').Router();
	var misc = require('./misc')(PATH_AUDIO_FILES, PATH_FEATURES_FILES, PATH_BINARY);
	var logger = log4js.getLogger();
	var uuid = require('node-uuid');
	require('shelljs/global');

	mkdir('-p', PATH_AUDIO_FILES);
	mkdir('-p', PATH_FEATURES_FILES);

	extractionWorker.send({
		op : 'init',
		sonicAnnotatorPath : PATH_BINARY,
		audioFilesDir : PATH_AUDIO_FILES,
		featuresFilesDir : PATH_FEATURES_FILES
	});

	function error1(err, res) {
		logger.error(err);
		res.status(500).send();
	}

	function error2(err, reject) {
		logger.error(err);
		reject(err);
	}

	router.get('/', (req, res) => {
		var url = req.query.url;
		var features = req.query.features || [];
		if (features.length == 0)
			res.status(500).json({message: 'You need to specify, at least, one feature.'});

		var tempFilepath = absp('./temp/feats/' + uuid.v1());
		var promises = [];
		featuresToExtract = [];

		features.forEach((featureID) => {
			promises.push(new Promise((resolve, reject) => {
				misc.get_entry('extractions', {url: url, feature_id: featureID})
						.then((entry) => {
							if (entry) {

								if (req.query.stats && req.query.intervals && entry.data) {
									var stats = req.query.stats;
									var intervals = req.query.intervals;

									misc.intervals_pre_processing(req.query.intervals)
											.then((O1) => {
												misc.group_interval_data(entry.data, O1.intervals, O1.intervalsMap, O1.minStart, O1.maxEnd)
													.then((O2) => {
														misc.summary(O2.intervals, O2.intervalsMap, stats)
																.then((summary) => {
																	entry.data = summary;
																	resolve(entry);
																}).catch((err) => error2(err, reject))
													}).catch((err) => error2(err, reject));
											}).catch((err) => error2(err, reject));
								} else {
									logger.info('found');
									resolve(entry);
								}

							} else {
								var obj = {
									url: url,
									feature_id: featureID,
									status: 'pending',
									tempFile: tempFilepath,
									settings: null,
									data: null
								};
								misc.add_entry('extractions', obj)
										.then((entry) => {
											featuresToExtract.push(entry.feature_id)
											resolve(entry);
										}).catch((err) => error2(err, reject));
							}
						}).catch((err) => error2(err, reject))
			}));
		});

		Promise.all(promises).then(function(values) {
			if (featuresToExtract.length > 0) {
				var instructions = {
					op: 'extract',
					url: url,
					plugins: featuresToExtract,
					tempFile: tempFilepath
				};
				extractionWorker.send(instructions);
			}
			res.json(values);
		}, (err) => error1(err, res));
	});

	// TODO: DEPRECATE THIS
	router.get('/summary', (req, res) => {
		var url = req.query.url;
		var feature = req.query.feature;
		var stats = req.query.stats || [];
		if (stats == 'all') {
			stats = new Array();
			STATS.forEach((stat) => stats.push(stat));
		}

		misc.intervals_pre_processing(req.query.intervals)
				.then((O1) => {
					misc.get_entry('extractions', { url: url })
							.then((entry) => {
								misc.group_interval_data(entry.data, O1.intervals, O1.intervalsMap, O1.minStart, O1.maxEnd)
										.then((O2) => {
											misc.summary(O2.intervals, O2.intervalsMap, stats)
													.then((summary) => {
														res.send(summary);
													}).catch((err) => error1(err, res));
										}).catch((err) => error1(err, res));
							}).catch((err) => error1(err, res));

				}).catch((err) => error1(err, res));
	});

	// TODO: DEPRECATE THIS
	/* Retrieve features within a time interval. */
	router.get('/interval', (req, res) => {
		var id = req.query.id;
		var feature = req.query.feature;
		var start = new Number(req.query.start);
		var end = new Number(req.query.end);
		interval_data(id, feature, start, end).then((intervalData) => {
			res.send(intervalData);
		}).catch((error) => error1(err, res));
	});

	return {
		router: router
	};
}<commit_msg>Added endpoint to list available features in the server.<commit_after>module.exports = function(sonicAnnotatorPath, audioFilesDir, featuresFilesDir) {
	var absp = require("abs");

	var PATH_AUDIO_FILES 	= absp(audioFilesDir || './temp/audio');
	var PATH_FEATURES_FILES = absp(featuresFilesDir || './temp/feats');
	var PATH_BINARY 		= absp(sonicAnnotatorPath || '/home/echo66/tese/features-extractors/sonic-annotator/sonic-annotator');
	var DEFAULT_PLUGINS = [
		'vamp:nnls-chroma:nnls-chroma:chroma',
		'vamp:qm-vamp-plugins:qm-mfcc:coefficients',
		'vamp:qm-vamp-plugins:qm-tempotracker:beats',
	];
	var STATS = new Set(['min', 'max', 'mean', 'std', 'var', 'median', 'sum']);

	var extractionWorker = require('child_process').fork('extraction-worker.js');
	var log4js = require('log4js');
	var router = require('express').Router();
	var misc = require('./misc')(PATH_AUDIO_FILES, PATH_FEATURES_FILES, PATH_BINARY);
	var logger = log4js.getLogger();
	var uuid = require('node-uuid');
	require('shelljs/global');

	mkdir('-p', PATH_AUDIO_FILES);
	mkdir('-p', PATH_FEATURES_FILES);

	extractionWorker.send({
		op : 'init',
		sonicAnnotatorPath : PATH_BINARY,
		audioFilesDir : PATH_AUDIO_FILES,
		featuresFilesDir : PATH_FEATURES_FILES
	});

	function error1(err, res) {
		logger.error(err);
		res.status(500).send();
	}

	function error2(err, reject) {
		logger.error(err);
		reject(err);
	}

	router.get('/', (req, res) => {
		var url = req.query.url;
		var features = req.query.features || [];
		if (features.length == 0)
			res.status(500).json({message: 'You need to specify, at least, one feature.'});

		var tempFilepath = absp('./temp/feats/' + uuid.v1());
		var promises = [];
		featuresToExtract = [];

		features.forEach((featureID) => {
			promises.push(new Promise((resolve, reject) => {
				misc.get_entry('extractions', {url: url, feature_id: featureID})
						.then((entry) => {
							if (entry) {

								if (req.query.stats && req.query.intervals && entry.data) {
									var stats = req.query.stats;
									var intervals = req.query.intervals;

									misc.intervals_pre_processing(req.query.intervals)
											.then((O1) => {
												misc.group_interval_data(entry.data, O1.intervals, O1.intervalsMap, O1.minStart, O1.maxEnd)
													.then((O2) => {
														misc.summary(O2.intervals, O2.intervalsMap, stats)
																.then((summary) => {
																	entry.data = summary;
																	resolve(entry);
																}).catch((err) => error2(err, reject))
													}).catch((err) => error2(err, reject));
											}).catch((err) => error2(err, reject));
								} else {
									logger.info('found');
									resolve(entry);
								}

							} else {
								var obj = {
									url: url,
									feature_id: featureID,
									status: 'pending',
									tempFile: tempFilepath,
									settings: null,
									data: null
								};
								misc.add_entry('extractions', obj)
										.then((entry) => {
											featuresToExtract.push(entry.feature_id)
											resolve(entry);
										}).catch((err) => error2(err, reject));
							}
						}).catch((err) => error2(err, reject))
			}));
		});

		Promise.all(promises).then(function(values) {
			if (featuresToExtract.length > 0) {
				var instructions = {
					op: 'extract',
					url: url,
					plugins: featuresToExtract,
					tempFile: tempFilepath
				};
				extractionWorker.send(instructions);
			}
			res.json(values);
		}, (err) => error1(err, res));
	});

	router.get('/features', (req, res) => {
		misc.get_plugins_list().then((plugins) => res.json(plugins)).catch((err) => error1(err, res));
	});

	// TODO: DEPRECATE THIS
	router.get('/summary', (req, res) => {
		var url = req.query.url;
		var feature = req.query.feature;
		var stats = req.query.stats || [];
		if (stats == 'all') {
			stats = new Array();
			STATS.forEach((stat) => stats.push(stat));
		}

		misc.intervals_pre_processing(req.query.intervals)
				.then((O1) => {
					misc.get_entry('extractions', { url: url })
							.then((entry) => {
								misc.group_interval_data(entry.data, O1.intervals, O1.intervalsMap, O1.minStart, O1.maxEnd)
										.then((O2) => {
											misc.summary(O2.intervals, O2.intervalsMap, stats)
													.then((summary) => {
														res.send(summary);
													}).catch((err) => error1(err, res));
										}).catch((err) => error1(err, res));
							}).catch((err) => error1(err, res));

				}).catch((err) => error1(err, res));
	});

	// TODO: DEPRECATE THIS
	/* Retrieve features within a time interval. */
	router.get('/interval', (req, res) => {
		var id = req.query.id;
		var feature = req.query.feature;
		var start = new Number(req.query.start);
		var end = new Number(req.query.end);
		interval_data(id, feature, start, end).then((intervalData) => {
			res.send(intervalData);
		}).catch((error) => error1(err, res));
	});

	return {
		router: router
	};
}<|endoftext|>


<commit_before>var socket;
var g;

var LIONID = 0;
var ANTILOPEID = 1;

var canvas;
var canvasContext;
var canvasStage;

//SpriteSheets
var lionSpriteSheetImage;
var antilopeSpriteSheetImage;
var treeImage;
var grassImage;

var lionSpriteSheet;
var antilopeSpriteSheet;

var playerSprite;

var bmpLionAnimation;
var bmpAntilopeAnimation;

//SpriteSheet controlls.
var imageSize = 128;
var scale = 0.5;

//Game variables.
var animalID;
var x;
var y;
var ID;
var vx;
var vy;

var players = {};

//Rendering
var background;
var playerContainer;
var mapContainer;

$(document).ready(function(){
	var connectTo = 'http://'+location.host.substring(0,location.host.indexOf(':'));
	console.log('Connecting to ' + connectTo);
	socket = io.connect(connectTo);

	socket.on("setup", function(data) {
	  	console.log('Data: ' + data);
		animalID = data.aid;
		ID = data.id;
		x = data.x;
		y = data.y;
		vx = data.vx;
		vy = data.vy;
	}.bind(this));

	socket.on("die", function(data) {
	  	console.log('Data: ' + data);
		die(data);
	}.bind(this));

	socket.on("u", function(data) {
		update(data);
	}.bind(this));

	canvas = document.getElementById('gamescreen');
	canvasContext = canvas.getContext("2d");
	canvasStage = new createjs.Stage(document.getElementById("gamescreen"));

	setup();

});

function sendDataToServer(trigger, data){
	socket.emit(trigger, data);
}

function setup(){
	g = new createjs.Graphics();
	loadImages();
	createBackground();
	createSpriteSheets(animalID);
	setupAnimations();

	canvas.addEventListener('mousemove', function(evt) {
        var mousePos = getMousePos(canvas, evt);

		var deltaX = mousePos.x - canvas.width/2;
		var deltaY = mousePos.y - canvas.height/2;

		var len = Math.sqrt(deltaX*deltaX + deltaY*deltaY);

		deltaX = deltaX/len;
		deltaY = deltaY/len;

		var speedVect = {};

		speedVect.vx = -deltaX;
		speedVect.vy = -deltaY;

		sendDataToServer("v", speedVect);

    }, false);

	//createPlayer();
}

function getMousePos(canvas, evt) {
	var rect = canvas.getBoundingClientRect();
	return {
	  x: evt.clientX - rect.left,
	  y: evt.clientY - rect.top
	};
}

function setupAnimations(){

	createjs.Ticker.addListener(window);
	createjs.Ticker.useRAF = true;
	// Best Framerate targeted (60 FPS)
	createjs.Ticker.setFPS(30);
}

function tick() {
	//render();
	//renderPlayer();
	updateLocal();
    // update the stage:
    canvasStage.update();
}

function loadImages(){
	lionSpriteSheetImage = new Image();
	lionSpriteSheetImage.src = 'lion.png';
	antilopeSpriteSheetImage = new Image();
	antilopeSpriteSheetImage.src = 'antilope.png';
	//lionSpriteSheet.image.onload = function(){
    //	console.log('Loaded lionSpriteSheet');
  	//}
}

function createSpriteSheets(id){
	createLionSpriteSheet();
	createAntilopeSpriteSheet();

}

function createLionSpriteSheet(){
	//example code:
	lionSpriteSheet = new createjs.SpriteSheet({
      // image to use
      images: [lionSpriteSheetImage],
      // width, height & registration point of each sprite
      frames: {width: 128, height: 128, regX: 64, regY: 64},
      animations: {
          left: [0, 3, "left", 3],
          right: [4, 7, "right", 3],
          up: [8, 11, "up", 3],
          down: [12, 15, "down", 3]
          //water: [32, 33, "water", 30]
      }
  });

  //lionSpriteSheet.img.gotoAndPlay("attack");

}

function createAntilopeSpriteSheet(){
	//example code:
	antilopeSpriteSheet = new createjs.SpriteSheet({
      // image to use
      images: [antilopeSpriteSheetImage],
      // width, height & registration point of each sprite
      frames: {width: 128, height: 128, regX: 64, regY: 64},
      animations: {
          run: [0, 0, "run", 10]
          //attack: [8, 15, "attack", 5],
          //idle: [16, 17, "idle", 50],
          //die: [24, 31, "die", 10],
          //water: [32, 33, "water", 30]
      }
  });
  //antilopeSpriteSheet.img.gotoAndPlay("attack");

}

function createSprite(spriteSheet){

  return new createjs.BitmapAnimation(spriteSheet);

}

function createBackground(){
	//g.beginFill(createjs.Graphics.getRGB(224,195,29));

	var shape = new createjs.Shape();
	shape.graphics.beginFill("#E0C31D").drawRect(0, 0, canvas.width, canvas.height);

	//background = new createjs.Rectangle(0, 0, canvas.width, canvas.height);
	canvasStage.addChild(shape);
}

function createPlayer(player){
	//var x = canvas.width/2;
	//var y = canvas.height/2;

	var sprite;

	if (player.animal == LIONID) {
		sprite = createSprite(lionSpriteSheet);
	}else{
		sprite = createSprite(antilopeSpriteSheet);
	}

	if (player.id == ID) {

		var x = canvas.width/2;
		var y = canvas.height/2;

		sprite.x = x;
		sprite.y = y;

		playerSprite = sprite;
	}
	else{
		sprite.x = player.x;
		sprite.y = player.y;
	}
	sprite.scaleX = scale;
	sprite.scaleY = scale;

	sprite.gotoAndPlay("run")

	player.sprite = sprite;

	canvasStage.addChild(sprite);
}

function die(data){
	var player = players[data.id];
	if(player){
		player.vx = 0;
		player.vy = 0;
	}
}

function updatePlayer(data){
	var id = data.id;

	if (!players[id]){

		var player = {
			id:data.id,
			animal:data.animal,
			x:relativeX(data.x),
			y:relativeY(data.y)
		}
		players[player.id] = player;
		createPlayer(player);
	}

	players[id].sprite.x = relativeX(data.x);
	players[id].sprite.y = relativeY(data.y);
	players[id].vx = data.vx;
	players[id].vy = data.vy;

	if(id == ID){
		x = data.x;
		y = data.y;
		players[id].sprite.x = 400;//canvas.width/2;
		players[id].sprite.y = 240;//canvas.heigth/2;
	}

}

function update(data){
	for(var i = 0; i < data.length;i++){
		var player = data[i];
		if (player) {
			updatePlayer(player);
		}
	}
}

function updateLocal(){
	var myplayer = players[ID];
	if (myplayer) {
		var vx = myplayer.vx;
		var vy = myplayer.vy;
		if(Math.abs(vx) > Math.abs(vy)){
			if(vx > 0){
				if(myplayer.sprite.currentAnimation !== 'left'){
					myplayer.sprite.gotoAndPlay('left');
				}
			}else{
				if(myplayer.sprite.currentAnimation !== 'right'){
					myplayer.sprite.gotoAndPlay('right');
				}
			}
		}else{
			if(vy > 0){
				if(myplayer.sprite.currentAnimation !== 'up'){
					myplayer.sprite.gotoAndPlay('up');
				}

			}else{
				if(myplayer.sprite.currentAnimation !== 'down'){
					myplayer.sprite.gotoAndPlay('down');
				}

			}
		}
		//x = x + myplayer.vx*10;
		//y = y + myplayer.vy*10;

		//myplayer.sprite.x = 400;//canvas.width/2;
		//myplayer.sprite.y = 240;//canvas.heigth/2;
	}
	//console.log('player speed ' + myplayer.vx + ' ' + myplayer.vy);
	for(var key in players){
		var player = players[key];
		if (player) {
			if(player.id != ID){
				/*
				var absoluteX = (x + player.sprite.x) + player.vx*10;
				var absoluteY = (y + player.sprite.y) + player.vy*10;
				player.sprite.x = relativeX(absoluteX);
				player.sprite.y = relativeY(absoluteY);
				*/

				player.sprite.x += myplayer.vx*10;
				player.sprite.y += myplayer.vy*10;
			}
		}
	}
}

function relativeX(otherX){
	return canvas.width/2 + (x - otherX);
}

function relativeY(otherY){
	return canvas.height/2 + (y - otherY);
}<commit_msg>added logo<commit_after>var socket;
var g;

var LIONID = 0;
var ANTILOPEID = 1;

var canvas;
var canvasContext;
var canvasStage;

//SpriteSheets
var lionSpriteSheetImage;
var antilopeSpriteSheetImage;
var treeImage;
var grassImage;
var logoImage;
var pressanyImage;

var lionSpriteSheet;
var antilopeSpriteSheet;

var playerSprite;

var bmpLionAnimation;
var bmpAntilopeAnimation;

//SpriteSheet controlls.
var imageSize = 128;
var scale = 0.5;

//Game variables.
var animalID;
var x;
var y;
var ID;
var vx;
var vy;

var players = {};

//Rendering
var background;
var playerContainer;
var mapContainer;

$(document).ready(function(){
	var connectTo = 'http://'+location.host.substring(0,location.host.indexOf(':'));
	console.log('Connecting to ' + connectTo);
	socket = io.connect(connectTo);

	socket.on("setup", function(data) {
	  	console.log('Data: ' + data);
		animalID = data.aid;
		ID = data.id;
		x = data.x;
		y = data.y;
		vx = data.vx;
		vy = data.vy;
	}.bind(this));

	socket.on("die", function(data) {
	  	console.log('Data: ' + data);
		die(data);
	}.bind(this));

	socket.on("u", function(data) {
		update(data);
	}.bind(this));

	canvas = document.getElementById('gamescreen');
	canvasContext = canvas.getContext("2d");
	canvasStage = new createjs.Stage(document.getElementById("gamescreen"));

	setup();

});

function sendDataToServer(trigger, data){
	socket.emit(trigger, data);
}

function setup(){
	g = new createjs.Graphics();
	loadImages();
	createBackground();
	createLogo();
	createSpriteSheets(animalID);
	setupAnimations();

	canvas.addEventListener('mousemove', function(evt) {
        var mousePos = getMousePos(canvas, evt);

		var deltaX = mousePos.x - canvas.width/2;
		var deltaY = mousePos.y - canvas.height/2;

		var len = Math.sqrt(deltaX*deltaX + deltaY*deltaY);

		deltaX = deltaX/len;
		deltaY = deltaY/len;

		var speedVect = {};

		speedVect.vx = -deltaX;
		speedVect.vy = -deltaY;

		sendDataToServer("v", speedVect);

    }, false);

	//createPlayer();
}

function getMousePos(canvas, evt) {
	var rect = canvas.getBoundingClientRect();
	return {
	  x: evt.clientX - rect.left,
	  y: evt.clientY - rect.top
	};
}

function setupAnimations(){

	createjs.Ticker.addListener(window);
	createjs.Ticker.useRAF = true;
	// Best Framerate targeted (60 FPS)
	createjs.Ticker.setFPS(30);
}

function tick() {
	//render();
	//renderPlayer();
	updateLocal();
    // update the stage:
    canvasStage.update();
}

function loadImages(){
	lionSpriteSheetImage = new Image();
	lionSpriteSheetImage.src = 'lion.png';
	antilopeSpriteSheetImage = new Image();
	antilopeSpriteSheetImage.src = 'antilope.png';

	logoImage = new Image();
	logoImage.src = 'logo.png';
	pressanyImage = new Image();
	pressanyImage.src = 'pressanykey.png';
	//lionSpriteSheet.image.onload = function(){
    //	console.log('Loaded lionSpriteSheet');
  	//}
}

function createSpriteSheets(id){
	createLionSpriteSheet();
	createAntilopeSpriteSheet();

}

function createLionSpriteSheet(){
	//example code:
	lionSpriteSheet = new createjs.SpriteSheet({
      // image to use
      images: [lionSpriteSheetImage],
      // width, height & registration point of each sprite
      frames: {width: 128, height: 128, regX: 64, regY: 64},
      animations: {
          left: [0, 3, "left", 3],
          right: [4, 7, "right", 3],
          up: [8, 11, "up", 3],
          down: [12, 15, "down", 3]
          //water: [32, 33, "water", 30]
      }
  });

  //lionSpriteSheet.img.gotoAndPlay("attack");

}

function createAntilopeSpriteSheet(){
	//example code:
	antilopeSpriteSheet = new createjs.SpriteSheet({
      // image to use
      images: [antilopeSpriteSheetImage],
      // width, height & registration point of each sprite
      frames: {width: 128, height: 128, regX: 64, regY: 64},
      animations: {
          run: [0, 0, "run", 10]
          //attack: [8, 15, "attack", 5],
          //idle: [16, 17, "idle", 50],
          //die: [24, 31, "die", 10],
          //water: [32, 33, "water", 30]
      }
  });
  //antilopeSpriteSheet.img.gotoAndPlay("attack");

}

function createSprite(spriteSheet){

  return new createjs.BitmapAnimation(spriteSheet);

}

function createBackground(){
	//g.beginFill(createjs.Graphics.getRGB(224,195,29));

	var shape = new createjs.Shape();
	shape.graphics.beginFill("#E0C31D").drawRect(0, 0, canvas.width, canvas.height);

	//background = new createjs.Rectangle(0, 0, canvas.width, canvas.height);
	canvasStage.addChild(shape);
}

function createLogo(){
	var bitmap = new createjs.Bitmap(logoImage);
	bitmap.x = 400 - 512/2;
	bitmap.y = 240 - 512/2;
	canvasStage.addChild(bitmap);
	var pressany = new createjs.Bitmap(pressanyImage);
	pressany.x = 400 - 256/2;
	pressany.y = 240 - 256/2;
	canvasStage.addChild(pressany);
}

function createPlayer(player){
	//var x = canvas.width/2;
	//var y = canvas.height/2;

	var sprite;

	if (player.animal == LIONID) {
		sprite = createSprite(lionSpriteSheet);
	}else{
		sprite = createSprite(antilopeSpriteSheet);
	}

	if (player.id == ID) {

		var x = canvas.width/2;
		var y = canvas.height/2;

		sprite.x = x;
		sprite.y = y;

		playerSprite = sprite;
	}
	else{
		sprite.x = player.x;
		sprite.y = player.y;
	}
	sprite.scaleX = scale;
	sprite.scaleY = scale;

	sprite.gotoAndPlay("run")

	player.sprite = sprite;

	canvasStage.addChild(sprite);
}

function die(data){
	var player = players[data.id];
	if(player){
		player.vx = 0;
		player.vy = 0;
	}
}

function updatePlayer(data){
	var id = data.id;

	if (!players[id]){

		var player = {
			id:data.id,
			animal:data.animal,
			x:relativeX(data.x),
			y:relativeY(data.y)
		}
		players[player.id] = player;
		createPlayer(player);
	}

	players[id].sprite.x = relativeX(data.x);
	players[id].sprite.y = relativeY(data.y);
	players[id].vx = data.vx;
	players[id].vy = data.vy;

	if(id == ID){
		x = data.x;
		y = data.y;
		players[id].sprite.x = 400;//canvas.width/2;
		players[id].sprite.y = 240;//canvas.heigth/2;
	}

}

function update(data){
	for(var i = 0; i < data.length;i++){
		var player = data[i];
		if (player) {
			updatePlayer(player);
		}
	}
}

function updateLocal(){
	var myplayer = players[ID];
	if (myplayer) {
		var vx = myplayer.vx;
		var vy = myplayer.vy;
		if(Math.abs(vx) > Math.abs(vy)){
			if(vx > 0){
				if(myplayer.sprite.currentAnimation !== 'left'){
					myplayer.sprite.gotoAndPlay('left');
				}
			}else{
				if(myplayer.sprite.currentAnimation !== 'right'){
					myplayer.sprite.gotoAndPlay('right');
				}
			}
		}else{
			if(vy > 0){
				if(myplayer.sprite.currentAnimation !== 'up'){
					myplayer.sprite.gotoAndPlay('up');
				}

			}else{
				if(myplayer.sprite.currentAnimation !== 'down'){
					myplayer.sprite.gotoAndPlay('down');
				}

			}
		}
		//x = x + myplayer.vx*10;
		//y = y + myplayer.vy*10;

		//myplayer.sprite.x = 400;//canvas.width/2;
		//myplayer.sprite.y = 240;//canvas.heigth/2;
	}
	//console.log('player speed ' + myplayer.vx + ' ' + myplayer.vy);
	for(var key in players){
		var player = players[key];
		if (player) {
			if(player.id != ID){
				/*
				var absoluteX = (x + player.sprite.x) + player.vx*10;
				var absoluteY = (y + player.sprite.y) + player.vy*10;
				player.sprite.x = relativeX(absoluteX);
				player.sprite.y = relativeY(absoluteY);
				*/

				player.sprite.x += myplayer.vx*10;
				player.sprite.y += myplayer.vy*10;
			}
		}
	}
}

function relativeX(otherX){
	return canvas.width/2 + (x - otherX);
}

function relativeY(otherY){
	return canvas.height/2 + (y - otherY);
}<|endoftext|>


<commit_before>var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var open = require('open');
var constants = require('../constants.js');
var handleError = require('./handleError.js');

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {object} options Extra options.
 * @param {callback} callback The callback to call with the authorized client.
 */
function authenticate(options, callback) {
    var clientSecret = constants.CLIENT_SECRET;
    var clientId = constants.CLIENT_ID;
    var redirectUrl = constants.REDIRECT_URL;
    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    if (options.force) {
        fs.unlink(constants.APP_DIR + '/' + constants.TOKEN_FILE, function() {});
    }

    // Check if we have previously stored a token.
    fs.readFile(constants.APP_DIR + '/' + constants.TOKEN_FILE, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            var ttl = oauth2Client.credentials['expiry_date'] - (new Date).getTime();

            if (ttl < 10 * 1000) {
                oauth2Client.refreshAccessToken(function(err, token) {
                    if (err) {
                        callback(err, oauth2Client);
                        return;
                    } else {
                        oauth2Client.credentials = token;
                        storeToken(token, function() {
                            callback(err, oauth2Client);
                            return;
                        });
                    }
                });

            } else {
                callback(err, oauth2Client);
                return;
            }
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */
function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: constants.SCOPES
    });
    open(authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    console.log('A webbrowser should have opened, to allow \'gas\' to:');
    console.log('    \'View and manage the files in your Google Drive\'');
    console.log('    \'Modify your Google Apps Script scripts\' behavior\'');
    console.log('');
    console.log('These permissions are necessary for pulling and pushing code from/to your Google Drive.');
    rl.question('Please accept and enter the code from the follow up page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                handleError(err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token, function() {
                callback(err, oauth2Client);
                return;
            });
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 * @param {getEventsCallback} callback
 */
function storeToken(token, callback) {
    try {
        fs.mkdirSync(constants.APP_DIR, function() {});
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(constants.APP_DIR + '/' + constants.TOKEN_FILE, JSON.stringify(token), function() {});
    callback();
    return;
}

module.exports = authenticate;
<commit_msg>improving auth error handling<commit_after>var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var open = require('open');
var constants = require('../constants.js');
var handleError = require('./handleError.js');

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {object} options Extra options.
 * @param {callback} callback The callback to call with the authorized client.
 */
function authenticate(options, callback) {
    var clientSecret = constants.CLIENT_SECRET;
    var clientId = constants.CLIENT_ID;
    var redirectUrl = constants.REDIRECT_URL;
    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    if (options.force) {
        fs.unlink(constants.APP_DIR + '/' + constants.TOKEN_FILE, function() {});
    }

    // Check if we have previously stored a token.
    fs.readFile(constants.APP_DIR + '/' + constants.TOKEN_FILE, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            var ttl = oauth2Client.credentials['expiry_date'] - (new Date).getTime();

            if (ttl < 10 * 1000) {
                oauth2Client.refreshAccessToken(function(err, token) {
                    if (err) {
                        callback(err, oauth2Client);
                        return;
                    } else {
                        oauth2Client.credentials = token;
                        storeToken(token, function() {
                            callback(err, oauth2Client);
                            return;
                        });
                    }
                });

            } else {
                callback(err, oauth2Client);
                return;
            }
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */
function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: constants.SCOPES
    });
    open(authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    console.log('A webbrowser should have opened, to allow \'gas\' to:');
    console.log('    \'View and manage the files in your Google Drive\'');
    console.log('    \'Modify your Google Apps Script scripts\' behavior\'');
    console.log('');
    console.log('These permissions are necessary for pulling and pushing code from/to your Google Drive.');
    rl.question('Please accept and enter the code from the follow up page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                callback(err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token, function(err) {
                callback(err, oauth2Client);
                return;
            });
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 * @param {getEventsCallback} callback
 */
function storeToken(token, callback) {
    try {
        fs.mkdirSync(constants.APP_DIR, function() {});
    } catch (err) {
        callback(err);
    }
    fs.writeFile(constants.APP_DIR + '/' + constants.TOKEN_FILE, JSON.stringify(token), function() {});
    callback();
    return;
}

module.exports = authenticate;
<|endoftext|>


<commit_before>// Copyright 2014-2018, University of Colorado Boulder

/**
 * Deprecated.
 * Button for selecting a game level.
 * Also depicts the progress made on each level.
 *
 * @author John Blanco
 * @author Chris Malley
 * @author Andrea Lin
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var GameTimer = require( 'VEGAS/GameTimer' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var ScoreDisplayDiscreteStars = require( 'VEGAS/ScoreDisplayDiscreteStars' );
  var ScoreDisplayNumberAndStar = require( 'VEGAS/ScoreDisplayNumberAndStar' );
  var ScoreDisplayTextAndNumber = require( 'VEGAS/ScoreDisplayTextAndNumber' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var vegas = require( 'VEGAS/vegas' );

  // constants
  var SCALING_TOLERANCE = 1E-4; // Empirically chosen as something the human eye is unlikely to notice.

  /**
   * @param {Node} icon Scenery node that appears on the button above the progress indicator, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property.<number>} scoreProperty
   * @param {number} perfectScore
   * @param {Object} [options]
   * @constructor
   */
  function LevelSelectionItemNodeDeprecated( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {
    // TODO: numStars and perfectScore not necessarily necessary

    assert && assert( icon instanceof Node );
    assert && assert( typeof numStars === 'number' );

    options = _.extend( {

      // score display type
      scoreDisplayType: 'discreteStars', // or 'numberAndStar' or 'textAndNumber'

      // button size and appearance
      buttonWidth: 150,
      buttonHeight: 150,
      cornerRadius: 10,
      baseColor: 'rgb( 242, 255, 204 )',
      buttonXMargin: 10,
      buttonYMargin: 10,
      // progress indicator (stars)
      progressIndicatorProportion: 0.2, // percentage of the button height occupied by the progress indicator, (0,0.5]
      progressIndicatorMinXMargin: 10,
      progressIndicatorMinYMargin: 5,
      iconToProgressIndicatorYSpace: 10,
      // best time (optional)
      bestTimeProperty: null, // null if no best time || {Property.<number>} best time in seconds
      bestTimeVisibleProperty: null, // null || Property.<boolean>} controls visibility of best time
      bestTimeFill: 'black',
      bestTimeFont: new PhetFont( 24 ),
      bestTimeYSpacing: 10,  // vertical space between drop shadow and best time

      // Tandem
      tandem: Tandem.required
    }, options );

    Node.call( this );

    assert && assert(
      options.progressIndicatorProportion > 0 && options.progressIndicatorProportion <= 0.5,
      'progressIndicatorProportion value out of range'
    );

    var maxContentWidth = options.buttonWidth - 2 * options.buttonXMargin;

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, maxContentWidth,
      options.buttonHeight * options.progressIndicatorProportion, options.cornerRadius, options.cornerRadius, {
        fill: 'white',
        stroke: 'black',
        lineWidth: 1,
        pickable: false
      } );

    var scoreDisplayOptions = { pickable: false };

    // TODO: assert options.scoreDisplayType provided is right
    //TODO memory leak, progressIndicator links to scoreProperty and is not disposed
    if ( options.scoreDisplayType === 'discreteStars' ) {
      var progressIndicator = new ScoreDisplayDiscreteStars( scoreProperty, _.extend( {}, scoreDisplayOptions, {
        numStars: numStars,
        perfectScore: perfectScore
      } ) );
    }
    else if ( options.scoreDisplayType === 'numberAndStar' ) {
      progressIndicator = new ScoreDisplayNumberAndStar( scoreProperty, scoreDisplayOptions );
    }
    else {
      progressIndicator = new ScoreDisplayTextAndNumber( scoreProperty, scoreDisplayOptions );
    }

    progressIndicator.scale( Math.min(
      ( progressIndicatorBackground.width - 2 * options.progressIndicatorMinXMargin ) / progressIndicator.width,
      ( progressIndicatorBackground.height - 2 * options.progressIndicatorMinYMargin ) / progressIndicator.height ) );

    // Icon, scaled and padded to fit and to make the button size correct.
    var iconSize = new Dimension2( maxContentWidth, options.buttonHeight - progressIndicatorBackground.height -
                                                    2 * options.buttonYMargin - options.iconToProgressIndicatorYSpace );
    var adjustedIcon = LevelSelectionItemNodeDeprecated.createSizedImageNode( icon, iconSize );
    adjustedIcon.pickable = false; // TODO: is this needed?

    // Assemble the content.
    var contentNode = new Node();
    if ( progressIndicatorBackground.width > adjustedIcon.width ) {
      adjustedIcon.centerX = progressIndicatorBackground.centerX;
    }
    else {
      progressIndicatorBackground.centerX = adjustedIcon.centerX;
    }
    progressIndicatorBackground.top = adjustedIcon.bottom + options.iconToProgressIndicatorYSpace;
    progressIndicator.center = progressIndicatorBackground.center;
    contentNode.addChild( adjustedIcon );
    contentNode.addChild( progressIndicatorBackground );
    contentNode.addChild( progressIndicator );

    // Create the button
    var buttonOptions = {
      content: contentNode,
      xMargin: options.buttonXMargin,
      yMargin: options.buttonYMargin,
      baseColor: options.baseColor,
      cornerRadius: options.cornerRadius,
      listener: fireFunction,

      // TODO: if LevelSelectionItemNodeDeprecated changes to inheritance, this will have to change,
      // see https://github.com/phetsims/vegas/issues/56
      tandem: options.tandem.createTandem( 'button' )
    };

    var button = new RectangularPushButton( buttonOptions );
    this.addChild( button );

    // Best time (optional), centered below the button, does not move when button is pressed
    if ( options.bestTimeProperty ) {
      var bestTimeNode = new Text( '', { font: options.bestTimeFont, fill: options.bestTimeFill } );
      this.addChild( bestTimeNode );

      //TODO memory leak, missing unlink
      options.bestTimeProperty.link( function( bestTime ) {
        bestTimeNode.text = ( bestTime ? GameTimer.formatTime( bestTime ) : '' );
        bestTimeNode.centerX = button.centerX;
        bestTimeNode.top = button.bottom + options.bestTimeYSpacing;
      } );

      if ( options.bestTimeVisibleProperty ) {
        // TODO memory leak, missing unlinkAttribute
        options.bestTimeVisibleProperty.linkAttribute( bestTimeNode, 'visible' );
      }
    }

    // Pass options to parent class
    this.mutate( options );
  }

  vegas.register( 'LevelSelectionItemNodeDeprecated', LevelSelectionItemNodeDeprecated );

  return inherit( Node, LevelSelectionItemNodeDeprecated, {}, {
    /**
     * Creates a new the same dimensions as size with the specified icon. The icon will be scaled to fit, and a
     * background with the specified size may be added to ensure the bounds of the returned node are correct.
     * @public
     *
     * @param {Node} icon
     * @param {Dimension2} size
     * @returns {Node}
     */
    createSizedImageNode: function( icon, size ) {
      icon.scale( Math.min( size.width / icon.bounds.width, size.height / icon.bounds.height ) );
      if ( Math.abs( icon.bounds.width - size.width ) < SCALING_TOLERANCE &&
           Math.abs( icon.bounds.height - size.height ) < SCALING_TOLERANCE ) {
        // The aspect ratio of the icon matched that of the specified size, so no padding is necessary.
        return icon;
      }
      // else padding is needed in either the horizontal or vertical direction.
      var background = Rectangle.dimension( size, { fill: null } );
      icon.center = background.center;
      background.addChild( icon );
      return background;
    }
  } );
} );<commit_msg>make LevelSelectionItemNodeDeprecated as deprecated<commit_after>// Copyright 2014-2018, University of Colorado Boulder

/**
 * Button for selecting a game level.
 * Also depicts the progress made on each level.
 *
 * @author John Blanco
 * @author Chris Malley
 * @author Andrea Lin
 * @deprecated use LevelSelectionButton
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var GameTimer = require( 'VEGAS/GameTimer' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var ScoreDisplayDiscreteStars = require( 'VEGAS/ScoreDisplayDiscreteStars' );
  var ScoreDisplayNumberAndStar = require( 'VEGAS/ScoreDisplayNumberAndStar' );
  var ScoreDisplayTextAndNumber = require( 'VEGAS/ScoreDisplayTextAndNumber' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var vegas = require( 'VEGAS/vegas' );

  // constants
  var SCALING_TOLERANCE = 1E-4; // Empirically chosen as something the human eye is unlikely to notice.

  /**
   * @param {Node} icon Scenery node that appears on the button above the progress indicator, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property.<number>} scoreProperty
   * @param {number} perfectScore
   * @param {Object} [options]
   * @constructor
   */
  function LevelSelectionItemNodeDeprecated( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {
    // TODO: numStars and perfectScore not necessarily necessary

    assert && assert( icon instanceof Node );
    assert && assert( typeof numStars === 'number' );

    options = _.extend( {

      // score display type
      scoreDisplayType: 'discreteStars', // or 'numberAndStar' or 'textAndNumber'

      // button size and appearance
      buttonWidth: 150,
      buttonHeight: 150,
      cornerRadius: 10,
      baseColor: 'rgb( 242, 255, 204 )',
      buttonXMargin: 10,
      buttonYMargin: 10,
      // progress indicator (stars)
      progressIndicatorProportion: 0.2, // percentage of the button height occupied by the progress indicator, (0,0.5]
      progressIndicatorMinXMargin: 10,
      progressIndicatorMinYMargin: 5,
      iconToProgressIndicatorYSpace: 10,
      // best time (optional)
      bestTimeProperty: null, // null if no best time || {Property.<number>} best time in seconds
      bestTimeVisibleProperty: null, // null || Property.<boolean>} controls visibility of best time
      bestTimeFill: 'black',
      bestTimeFont: new PhetFont( 24 ),
      bestTimeYSpacing: 10,  // vertical space between drop shadow and best time

      // Tandem
      tandem: Tandem.required
    }, options );

    Node.call( this );

    assert && assert(
      options.progressIndicatorProportion > 0 && options.progressIndicatorProportion <= 0.5,
      'progressIndicatorProportion value out of range'
    );

    var maxContentWidth = options.buttonWidth - 2 * options.buttonXMargin;

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, maxContentWidth,
      options.buttonHeight * options.progressIndicatorProportion, options.cornerRadius, options.cornerRadius, {
        fill: 'white',
        stroke: 'black',
        lineWidth: 1,
        pickable: false
      } );

    var scoreDisplayOptions = { pickable: false };

    // TODO: assert options.scoreDisplayType provided is right
    //TODO memory leak, progressIndicator links to scoreProperty and is not disposed
    if ( options.scoreDisplayType === 'discreteStars' ) {
      var progressIndicator = new ScoreDisplayDiscreteStars( scoreProperty, _.extend( {}, scoreDisplayOptions, {
        numStars: numStars,
        perfectScore: perfectScore
      } ) );
    }
    else if ( options.scoreDisplayType === 'numberAndStar' ) {
      progressIndicator = new ScoreDisplayNumberAndStar( scoreProperty, scoreDisplayOptions );
    }
    else {
      progressIndicator = new ScoreDisplayTextAndNumber( scoreProperty, scoreDisplayOptions );
    }

    progressIndicator.scale( Math.min(
      ( progressIndicatorBackground.width - 2 * options.progressIndicatorMinXMargin ) / progressIndicator.width,
      ( progressIndicatorBackground.height - 2 * options.progressIndicatorMinYMargin ) / progressIndicator.height ) );

    // Icon, scaled and padded to fit and to make the button size correct.
    var iconSize = new Dimension2( maxContentWidth, options.buttonHeight - progressIndicatorBackground.height -
                                                    2 * options.buttonYMargin - options.iconToProgressIndicatorYSpace );
    var adjustedIcon = LevelSelectionItemNodeDeprecated.createSizedImageNode( icon, iconSize );
    adjustedIcon.pickable = false; // TODO: is this needed?

    // Assemble the content.
    var contentNode = new Node();
    if ( progressIndicatorBackground.width > adjustedIcon.width ) {
      adjustedIcon.centerX = progressIndicatorBackground.centerX;
    }
    else {
      progressIndicatorBackground.centerX = adjustedIcon.centerX;
    }
    progressIndicatorBackground.top = adjustedIcon.bottom + options.iconToProgressIndicatorYSpace;
    progressIndicator.center = progressIndicatorBackground.center;
    contentNode.addChild( adjustedIcon );
    contentNode.addChild( progressIndicatorBackground );
    contentNode.addChild( progressIndicator );

    // Create the button
    var buttonOptions = {
      content: contentNode,
      xMargin: options.buttonXMargin,
      yMargin: options.buttonYMargin,
      baseColor: options.baseColor,
      cornerRadius: options.cornerRadius,
      listener: fireFunction,

      // TODO: if LevelSelectionItemNodeDeprecated changes to inheritance, this will have to change,
      // see https://github.com/phetsims/vegas/issues/56
      tandem: options.tandem.createTandem( 'button' )
    };

    var button = new RectangularPushButton( buttonOptions );
    this.addChild( button );

    // Best time (optional), centered below the button, does not move when button is pressed
    if ( options.bestTimeProperty ) {
      var bestTimeNode = new Text( '', { font: options.bestTimeFont, fill: options.bestTimeFill } );
      this.addChild( bestTimeNode );

      //TODO memory leak, missing unlink
      options.bestTimeProperty.link( function( bestTime ) {
        bestTimeNode.text = ( bestTime ? GameTimer.formatTime( bestTime ) : '' );
        bestTimeNode.centerX = button.centerX;
        bestTimeNode.top = button.bottom + options.bestTimeYSpacing;
      } );

      if ( options.bestTimeVisibleProperty ) {
        // TODO memory leak, missing unlinkAttribute
        options.bestTimeVisibleProperty.linkAttribute( bestTimeNode, 'visible' );
      }
    }

    // Pass options to parent class
    this.mutate( options );
  }

  vegas.register( 'LevelSelectionItemNodeDeprecated', LevelSelectionItemNodeDeprecated );

  return inherit( Node, LevelSelectionItemNodeDeprecated, {}, {
    /**
     * Creates a new the same dimensions as size with the specified icon. The icon will be scaled to fit, and a
     * background with the specified size may be added to ensure the bounds of the returned node are correct.
     * @public
     *
     * @param {Node} icon
     * @param {Dimension2} size
     * @returns {Node}
     */
    createSizedImageNode: function( icon, size ) {
      icon.scale( Math.min( size.width / icon.bounds.width, size.height / icon.bounds.height ) );
      if ( Math.abs( icon.bounds.width - size.width ) < SCALING_TOLERANCE &&
           Math.abs( icon.bounds.height - size.height ) < SCALING_TOLERANCE ) {
        // The aspect ratio of the icon matched that of the specified size, so no padding is necessary.
        return icon;
      }
      // else padding is needed in either the horizontal or vertical direction.
      var background = Rectangle.dimension( size, { fill: null } );
      icon.center = background.center;
      background.addChild( icon );
      return background;
    }
  } );
} );<|endoftext|>