<commit_before>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file user.js
 * @description USER API specification
 * @module API
 * @author Chris Bates-Keegan
 *
 */

var Route = require('../route');
var Model = require('../../model');
var Logging = require('../../logging');
var Helpers = require('../../helpers');

var routes = [];

/**
 * @class GetUserList
 */
class GetUserList extends Route {
  constructor() {
    super('user', 'GET USER LIST');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.LIST;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.getAll()
    .then(users => users.map(u => u.details));
  }
}
routes.push(GetUserList);

/**
 * @class GetUser
 */
class GetUser extends Route {
  constructor() {
    super('user/:id', 'GET USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).populate('_person').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user.details);
  }
}
routes.push(GetUser);

/**
 * @class FindUser
 */
class FindUser extends Route {
  constructor() {
    super('user/:app(twitter|facebook|google)/:id', 'FIND USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
    this._userAuthToken = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.getByAppId(this.req.params.app, this.req.params.id).then(user => {
        Logging.logDebug(`FindUser: ${user !== null}`);
        this._user = user;
        if (this._user) {
          Model.Token.findUserAuthToken(this._user.id, this.req.authApp._id)
          .then(token => {
            Logging.logDebug(`FindUserToken: ${token !== null}`);
            this._userAuthToken = token ? token.value : false;
            this._user.updateApps(this.req.authApp)
              .then(resolve, reject);
          });
        } else {
          resolve(true);
        }
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user ? {
      id: this._user.id,
      authToken: this._userAuthToken
    } : false);
  }
}
routes.push(FindUser);

/**
 * @class CreateUserAuthToken
 */
class CreateUserAuthToken extends Route {
  constructor() {
    super('user/:id/token', 'CREATE USER AUTH TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.WRITE;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body.auth ||
        !this.req.body.auth.authLevel ||
        !this.req.body.auth.permissions ||
        !this.req.body.auth.domains) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      this.req.body.auth.type = Model.Constants.Token.Type.USER;
      this.req.body.auth.app = this.req.authApp;

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return Model.Token.add(Object.assign(this.req.body.auth, {user: this._user}))
      .then(t => Object.assign(t.details, {value: t.value}));
  }
}
routes.push(CreateUserAuthToken);

/**
 * @class UpdateUserAppToken
 */
class UpdateUserAppToken extends Route {
  constructor() {
    super('user/:id/:app(twitter|facebook|google)/token', 'UPDATE USER APP TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body ||
        !this.req.body.token) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return this._user.updateToken(this.req.params.app, this.req.body);
  }
}
routes.push(UpdateUserAppToken);

/**
 * @class AddUser
 */
class AddUser extends Route {
  constructor() {
    super('user/:app?', 'ADD USER');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Logging.log(this.req.body.user, Logging.Constants.LogLevel.DEBUG);
      var app = this.req.body.user.app ? this.req.body.user.app : this.req.params.app;

      if (!app ||
          !this.req.body.user.id ||
          !this.req.body.user.token ||
          !this.req.body.user.profileUrl ||
          !this.req.body.user.profileImgUrl) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      if (this.req.body.auth) {
        this.log(this.req.body.auth);
        this.log('User Auth Token Reqested');
        if (!this.req.body.auth.authLevel ||
            !this.req.body.auth.permissions ||
            !this.req.body.auth.domains) {
          this.log('ERROR: Missing required field', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this.req.body.auth.type = Model.Constants.Token.Type.USER;
        this.req.body.auth.app = this.req.authApp;
      }

      Model.Person.findByDetails(this.req.body.user)
        .then(person => {
          Logging.logDebug(`Found Person: ${person !== null}`);
          if (person === null) {
            Model.Person.add(this.req.body.user, this.req.authApp)
            .then(p => {
              Logging.log(p, Logging.Constants.LogLevel.SILLY);
              this._person = p;
              resolve(true);
            });
          } else {
            this._person = person.details;
            resolve(true);
          }
        }, reject);
    });
  }

  _exec() {
    return Model.User
    .add(this.req.body.user, this._person, this.req.body.auth)
    .then(res => Object.assign(res[0], {authToken: res[1] ? res[1].value : false}));
  }
}
routes.push(AddUser);

/**
 * @class DeleteAllUsers
 */
class DeleteAllUsers extends Route {
  constructor() {
    super('user', 'DELETE ALL USERS');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.SUPER;
    this.permissions = Route.Constants.Permissions.DELETE;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.rmAll().then(() => true);
  }
}
routes.push(DeleteAllUsers);

/**
 * @class DeleteUser
 */
class DeleteUser extends Route {
  constructor() {
    super('user/:id', 'DELETE USER');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.DELETE;
    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Model.User.rm(this._user).then(() => true);
  }
}
routes.push(DeleteUser);

class AttachToPerson extends Route {
  constructor() {
    super('user/:id/person', 'ATTACH USER TO PERSON');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
    this._person = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User
        .findById(this.req.params.id).select('-metadata').then(user => {
          if (!user) {
            this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }
          this._user = user;

          if (this._user._person) {
            this.log('ERROR: Already attached to a person', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          if (!this.req.body.name || !this.req.body.email) {
            this.log('ERROR: Missing required field', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          Model.Person
            .findByDetails(this.req.body)
            .then(person => {
              this._person = person;
              if (person) {
                return Model.User.findOne({_person: person});
              }
              return Promise.resolve(null);
            })
            .then(user => {
              if (user && user._id !== this._user._id) {
                this.log('ERROR: Person attached to a different user', Route.LogLevel.ERR);
                reject({statusCode: 400});
                return;
              }
              resolve(true);
            })
            .catch(err => {
              this.log(`ERROR: ${err.message}`, Route.LogLevel.ERR);
              reject({statusCode: 400});
            });
        })
        .catch(Logging.Promise.logError());
    });
  }

  _exec() {
    return this._user.attachToPerson(this._person, this.req.body)
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AttachToPerson);

/**
 * @class AddUserMetadata
 */
class AddUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'ADD USER METADATA');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          this.log(this.req.body.value, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(AddUserMetadata);

/**
 * @class UpdateUserMetadata
 */
class UpdateUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'UPDATE USER METADATA');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._app = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid App ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        if (user.findMetadata(this.req.params.key) === false) {
          this.log('ERROR: Metadata does not exist', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(UpdateUserMetadata);

/**
 * @class GetUserMetadata
 */
class GetUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'GET USER METADATA');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.GET;

    this._metadata = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._metadata = user.findMetadata(this.req.params.key);
        if (this._metadata === false) {
          this.log('WARN: App Metadata Not Found', Route.LogLevel.ERR);
          reject({statusCode: 404});
          return;
        }

        resolve(true);
      });
    });
  }

  _exec() {
    return this._metadata.value;
  }
}
routes.push(GetUserMetadata);

/**
 * @type {*[]}
 */
module.exports = routes;
<commit_msg>- REMOVED: requirement for profileUrl (some google accounts don't have them, and not strictly needed for authentication)<commit_after>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file user.js
 * @description USER API specification
 * @module API
 * @author Chris Bates-Keegan
 *
 */

var Route = require('../route');
var Model = require('../../model');
var Logging = require('../../logging');
var Helpers = require('../../helpers');

var routes = [];

/**
 * @class GetUserList
 */
class GetUserList extends Route {
  constructor() {
    super('user', 'GET USER LIST');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.LIST;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.getAll()
    .then(users => users.map(u => u.details));
  }
}
routes.push(GetUserList);

/**
 * @class GetUser
 */
class GetUser extends Route {
  constructor() {
    super('user/:id', 'GET USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).populate('_person').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user.details);
  }
}
routes.push(GetUser);

/**
 * @class FindUser
 */
class FindUser extends Route {
  constructor() {
    super('user/:app(twitter|facebook|google)/:id', 'FIND USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
    this._userAuthToken = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.getByAppId(this.req.params.app, this.req.params.id).then(user => {
        Logging.logDebug(`FindUser: ${user !== null}`);
        this._user = user;
        if (this._user) {
          Model.Token.findUserAuthToken(this._user.id, this.req.authApp._id)
          .then(token => {
            Logging.logDebug(`FindUserToken: ${token !== null}`);
            this._userAuthToken = token ? token.value : false;
            this._user.updateApps(this.req.authApp)
              .then(resolve, reject);
          });
        } else {
          resolve(true);
        }
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user ? {
      id: this._user.id,
      authToken: this._userAuthToken
    } : false);
  }
}
routes.push(FindUser);

/**
 * @class CreateUserAuthToken
 */
class CreateUserAuthToken extends Route {
  constructor() {
    super('user/:id/token', 'CREATE USER AUTH TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.WRITE;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body.auth ||
        !this.req.body.auth.authLevel ||
        !this.req.body.auth.permissions ||
        !this.req.body.auth.domains) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      this.req.body.auth.type = Model.Constants.Token.Type.USER;
      this.req.body.auth.app = this.req.authApp;

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return Model.Token.add(Object.assign(this.req.body.auth, {user: this._user}))
      .then(t => Object.assign(t.details, {value: t.value}));
  }
}
routes.push(CreateUserAuthToken);

/**
 * @class UpdateUserAppToken
 */
class UpdateUserAppToken extends Route {
  constructor() {
    super('user/:id/:app(twitter|facebook|google)/token', 'UPDATE USER APP TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body ||
        !this.req.body.token) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return this._user.updateToken(this.req.params.app, this.req.body);
  }
}
routes.push(UpdateUserAppToken);

/**
 * @class AddUser
 */
class AddUser extends Route {
  constructor() {
    super('user/:app?', 'ADD USER');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Logging.log(this.req.body.user, Logging.Constants.LogLevel.DEBUG);
      var app = this.req.body.user.app ? this.req.body.user.app : this.req.params.app;

      if (!app ||
          !this.req.body.user.id ||
          !this.req.body.user.token ||
          !this.req.body.user.profileImgUrl) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      if (this.req.body.auth) {
        this.log(this.req.body.auth);
        this.log('User Auth Token Reqested');
        if (!this.req.body.auth.authLevel ||
            !this.req.body.auth.permissions ||
            !this.req.body.auth.domains) {
          this.log('ERROR: Missing required field', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this.req.body.auth.type = Model.Constants.Token.Type.USER;
        this.req.body.auth.app = this.req.authApp;
      }

      Model.Person.findByDetails(this.req.body.user)
        .then(person => {
          Logging.logDebug(`Found Person: ${person !== null}`);
          if (person === null) {
            Model.Person.add(this.req.body.user, this.req.authApp)
            .then(p => {
              Logging.log(p, Logging.Constants.LogLevel.SILLY);
              this._person = p;
              resolve(true);
            });
          } else {
            this._person = person.details;
            resolve(true);
          }
        }, reject);
    });
  }

  _exec() {
    return Model.User
    .add(this.req.body.user, this._person, this.req.body.auth)
    .then(res => Object.assign(res[0], {authToken: res[1] ? res[1].value : false}));
  }
}
routes.push(AddUser);

/**
 * @class DeleteAllUsers
 */
class DeleteAllUsers extends Route {
  constructor() {
    super('user', 'DELETE ALL USERS');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.SUPER;
    this.permissions = Route.Constants.Permissions.DELETE;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.rmAll().then(() => true);
  }
}
routes.push(DeleteAllUsers);

/**
 * @class DeleteUser
 */
class DeleteUser extends Route {
  constructor() {
    super('user/:id', 'DELETE USER');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.DELETE;
    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Model.User.rm(this._user).then(() => true);
  }
}
routes.push(DeleteUser);

class AttachToPerson extends Route {
  constructor() {
    super('user/:id/person', 'ATTACH USER TO PERSON');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
    this._person = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User
        .findById(this.req.params.id).select('-metadata').then(user => {
          if (!user) {
            this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }
          this._user = user;

          if (this._user._person) {
            this.log('ERROR: Already attached to a person', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          if (!this.req.body.name || !this.req.body.email) {
            this.log('ERROR: Missing required field', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          Model.Person
            .findByDetails(this.req.body)
            .then(person => {
              this._person = person;
              if (person) {
                return Model.User.findOne({_person: person});
              }
              return Promise.resolve(null);
            })
            .then(user => {
              if (user && user._id !== this._user._id) {
                this.log('ERROR: Person attached to a different user', Route.LogLevel.ERR);
                reject({statusCode: 400});
                return;
              }
              resolve(true);
            })
            .catch(err => {
              this.log(`ERROR: ${err.message}`, Route.LogLevel.ERR);
              reject({statusCode: 400});
            });
        })
        .catch(Logging.Promise.logError());
    });
  }

  _exec() {
    return this._user.attachToPerson(this._person, this.req.body)
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AttachToPerson);

/**
 * @class AddUserMetadata
 */
class AddUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'ADD USER METADATA');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          this.log(this.req.body.value, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(AddUserMetadata);

/**
 * @class UpdateUserMetadata
 */
class UpdateUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'UPDATE USER METADATA');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._app = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid App ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        if (user.findMetadata(this.req.params.key) === false) {
          this.log('ERROR: Metadata does not exist', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(UpdateUserMetadata);

/**
 * @class GetUserMetadata
 */
class GetUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'GET USER METADATA');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.GET;

    this._metadata = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._metadata = user.findMetadata(this.req.params.key);
        if (this._metadata === false) {
          this.log('WARN: App Metadata Not Found', Route.LogLevel.ERR);
          reject({statusCode: 404});
          return;
        }

        resolve(true);
      });
    });
  }

  _exec() {
    return this._metadata.value;
  }
}
routes.push(GetUserMetadata);

/**
 * @type {*[]}
 */
module.exports = routes;
<|endoftext|>


<commit_before>'use strict';

module.exports = ['$q', '$log', '$http', 'authService', answerService];

function answerService($q, $log, $http, authService) {
  $log.debug('answerService');

  let service = {};
  service.answers = [];

  // MAY NOT NEED SINCE WE DONT DISPLAY A LIST OF ANSWERS SEPERATE FROM THE QUESTIONS
  service.getAnswers = function() {
    $log.debug('inside of service.getAnswers()');

    let url = `${__API_URL__}/api/answers`;
    let config = {
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    };

    return $http.get(url, config)
    .then( res => {
      $log.log('answers retrieved', res);
      service.answers = res.data;
      return service.answers;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });

  };
  // END OF getAnswers function

  service.createAnswer = function(questionID, answer) {
    $log.debug('inside of service.createAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers`;
      let config = {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
          Authorization: `Bearer ${token}`
        }
      };

      return $http.post(url, answer, config);
    })
    .then( res => {
      $log.log('answer created');
      let answer = res.data;
      service.answers.unshift(answer);
      return answer;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.updateAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.updateAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {
          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.deleteAnswer = function(questionID, answerID) {
    $log.debug('answerService.deleteAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}`;
      let config = {
        headers: {
          Authorization: `Bearer ${token}`
        }
      };

      return $http.delete(url, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {
          service.answers.splice(i, 1);
          break;
        }
      }
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.upvoteAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.upvoteAnswer()');
    return authService.getToken()
    .then( token => {
      console.log(token);
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}/upvote`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {

          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.downvoteAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.downvoteAnswer()');
    return authService.getToken()
    .then( token => {
      console.log(token);
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}/downvote`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {

          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };
  return service;
}
<commit_msg>auth for Answer DELETE now working<commit_after>'use strict';

module.exports = ['$q', '$log', '$http', 'authService', answerService];

function answerService($q, $log, $http, authService) {
  $log.debug('answerService');

  let service = {};
  service.answers = [];

  // MAY NOT NEED SINCE WE DONT DISPLAY A LIST OF ANSWERS SEPERATE FROM THE QUESTIONS
  service.getAnswers = function() {
    $log.debug('inside of service.getAnswers()');

    let url = `${__API_URL__}/api/answers`;
    let config = {
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    };

    return $http.get(url, config)
    .then( res => {
      $log.log('answers retrieved', res);
      service.answers = res.data;
      return service.answers;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });

  };
  // END OF getAnswers function

  service.createAnswer = function(questionID, answer) {
    $log.debug('inside of service.createAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers`;
      let config = {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
          Authorization: `Bearer ${token}`
        }
      };

      return $http.post(url, answer, config);
    })
    .then( res => {
      $log.log('answer created');
      let answer = res.data;
      service.answers.unshift(answer);
      return answer;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.updateAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.updateAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {
          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.deleteAnswer = function(questionID, answerID) {
    $log.debug('answerService.deleteAnswer()');

    return authService.getToken()
    .then( token => {
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}`;
      let config = {
        headers: {
          Authorization: `Bearer ${token}`
        }
      };

      return $http.delete(url, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {
          service.answers.splice(i, 1);
          break;
        }
      }
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.upvoteAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.upvoteAnswer()');
    return authService.getToken()
    .then( token => {
      console.log(token);
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}/upvote`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {

          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };

  service.downvoteAnswer = function(questionID, answerID, answerData) {
    $log.debug('answerService.downvoteAnswer()');
    return authService.getToken()
    .then( token => {
      console.log(token);
      let url = `${__API_URL__}/api/questions/${questionID}/answers/${answerID}/downvote`;
      let config = {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      };

      return $http.put(url, answerData, config);
    })
    .then( res => {
      for (let i = 0; i < service.answers.length; i++) {
        let current = service.answers[i];
        if (current._id === answerID) {

          service.answers[i] = res.data;
          break;
        }
      }

      return res.data;
    })
    .catch( err => {
      $log.error(err.message);
      return $q.reject(err);
    });
  };
  return service;
}
<|endoftext|>


<commit_before>"use strict";

var config = require("../../../core/config"),
    paths = config.paths,
    logger = require("connect").logger("short"),
    staticFileHandler = require("./middleware/staticFileHandler.js");

var middleware = [
        staticFileHandler
    ];

function onStaticRequest() {

    if(config.isDev) {
        middleware.unshift(logger);
    }

    return middleware;
}

module.exports = onStaticRequest();<commit_msg>- improved static file router<commit_after>"use strict";

var config = require("../../../core/config"),
    paths = config.paths,
    logger = require("connect").logger(":method :url :status - :response-time ms"),
    staticFileHandler = require("./middleware/staticFileHandler.js");

var middleware = [
        staticFileHandler
    ];

function onStaticRequest() {

    if(config.isDev) {
        middleware.unshift(logger);
    }

    return middleware;
}

module.exports = onStaticRequest();<|endoftext|>


<commit_before>var transformData = undefined;
var svg = undefined;
var pt = undefined;

var category = "Total";
var year = "1950";
var transitionTime = "1s";
var colors = {
  "Thermoelectric": "#FCBA04",
  "Public_Supply": "#BA3228",
  "Industrial": "#8A716A",
  "Irrigation": "#9BC53D",
  "Total": "#2E86AB"
};

$(document).ready(function(){
  get_data();
  svg = document.querySelector("svg");
  pt = svg.createSVGPoint();
  for (var cat in colors) {
    var catButton = $("#" + cat + '-button');
    catButton.css({'fill': colors[cat]});
  }

  var set_slider_click = function(x) {
    $("#bar-" + x).on("click", function(){
      $("#slider")[0].noUiSlider.set(x);
    });
  };

  var yr = 1950;
  while (yr <= 2015) {
    set_slider_click(yr);
    yr += 5;
  }
});

/* depends on jquery */
var animate_resize_map = function(data) {
  var color = colors[category];
  $("#category-area-legend").css(
    {
    "fill": color,
    "stroke":"none",
    "transition": "all " + transitionTime + " ease-in-out"
  });
  $.each(data, function(index, val) {
    var scale = Math.sqrt(val.scaleFactor);
    var style = {
      "fill": color,
      "transform": "scale3d(" + scale + "," + scale + ",1)",
      "stroke":"none",
      "transition": "all " + transitionTime + " ease-in-out"
    };
    var state = $("#" + val.state_name);
    if (state !== undefined) {
      if (isNaN(scale)){ // doesn't seem to work?
        style = {
          "fill":"url(#nodata)",
          "transform": "scale3d(1,1,1)",
          "stroke":"#f1f1f1",
          "transition": "all 0s"
        };
      }
      state.css(style);
    }
  });
  document.getElementById('category-area-text').firstChild.data = transformData['catVals'][category].toLocaleString() + ' mgd water withdrawal';
};

var animate_bars = function(data) {

  $.each(data, function(prop, val) {
    var myYear = prop;
    var color = colors[category];
    var scale = val[category][0]["barScale"];
    var value = val[category][0]["value"];
    var style = {
      "background": color,
      "transform": "scale3d(1," + scale + ",1)",
      "transform-origin": "100% 100%",
      "transition": "all " + transitionTime + " ease-in-out"
    };
    var bar = $("#bar-" + myYear);
    if (bar !== undefined) {
      bar.css(style);
      if (myYear !== year) {
        bar.css('opacity','0.25');
      } else {
        bar.css('opacity','1.0');
      }
      value = value.toLocaleString() + ' mgd';
      bar.attr("title", value);
    }
  });
  update_bar_tips();
};

var get_state_value = (function() {
  var prevState = "";
  var prevCat = "";
  var prevYear = "";
  var prevVal = "";
  var sameHover = function(state) {
    return (prevState === state &&
            prevCat === category &&
            prevYear == year);
  }
  return function(state) {
    if (transformData !== undefined && !sameHover(state)) {
        prevState = state;
        prevCat = category;
        prevYear = year;

        var stateData = transformData["totState"][year][category];
        prevVal = function(allData) {
          for (var i = 0; i < allData.length; i++) {
            if (allData[i]['state_name'] === state){
              return allData[i]['value'];
            }
          }
        }(stateData);
    }
    return prevVal;
  }
})();

var get_data = function() {
  $.get( "js/scaleFactors.json", function( data ) {
    transformData = data;

    var slider = document.getElementById('slider');
    slider.noUiSlider.on('update', function( values, handle ) {
  	  var year = "" + Math.round(values[handle]);
    	setYear(year);
    });

    setCategory(category);
  });
};

var animate = function() {
  var statesTransform = transformData["totState"][year][category];
  var barsTransform = transformData["totNat"];
  animate_resize_map(statesTransform);
  animate_bars(barsTransform);
};

var setCategory = function(cat) {
  category = cat;
  $('.cat-button').css("fill-opacity", '0.7');
  $('.cat-button').css("stroke-opacity","0.0");
  $('#' + cat).css("fill-opacity", "0.0");
  $('#' + cat).css("stroke-opacity","1.0");
  animate();
};

var setYear = function(yr) {
  year = yr;
  animate();
};

var update_bar_tips = function() {
  $.each($(".dataBar"), function(prop, val){
    $(val).off("mouseenter mouseleave");

    if ($(val).data("tooltipsy") !== undefined) {
      $(val).data("tooltipsy").destroy()
    }
  });
  $('.hastip').tooltipsy({
    delay: 50,
    offset: [0, -10]
  });
}

function hovertext(text, evt){
  var tooltip = document.getElementById("tooltip-text");
  var tooltip_bg = document.getElementById("tooltip-box");
  var tool_pt = document.getElementById("tooltip-point");
  if (evt === undefined){
    tooltip.firstChild.data = ' ';
    tooltip_bg.setAttribute("class","hidden");
    tooltip_bg.setAttribute("x",0);
    tool_pt.setAttribute("class","hidden");
    stateVal = " ";
  } else {
    var ref = evt.target.getAttribute('xlink:href').split('-')[0];
    var stateName = ref.replace(/#/g, '');
    var displayNum = Math.round(get_state_value(stateName));
    if (isNaN(displayNum)){
      displayNum = 'no data';
    } else {
      displayNum = displayNum.toLocaleString() + ' mgd';
    }
    text = text + ': ' + displayNum;
    pt = cursorPoint(evt);
    pt.x = Math.round(pt.x);
    pt.y = Math.round(pt.y);
    svgWidth = Number(svg.getAttribute("viewBox").split(" ")[2]);
    tooltip.setAttribute("x",pt.x);
    tooltip.setAttribute("y",pt.y);
    tooltip.firstChild.data = text;
    var length = Math.round(tooltip.getComputedTextLength());
    if (pt.x - length/2 - 6 < 0){
      tooltip.setAttribute("x",length/2+6);
    } else if (pt.x + length/2 + 6 > svgWidth) {
      tooltip.setAttribute("x", svgWidth-length/2-6);
    }
    tool_pt.setAttribute("transform","translate("+pt.x+","+pt.y+")");
    tooltip_bg.setAttribute("x",tooltip.getAttribute("x")-length/2-6);
    tooltip_bg.setAttribute("y",pt.y-35);
    tooltip.setAttribute("class","shown");
    tooltip_bg.setAttribute("class","tooltip-box");
    tool_pt.setAttribute("class","tooltip-box");
    tooltip_bg.setAttribute("width", length+12);
  }
}

function cursorPoint(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}
function changeOpacity(id, val){
  document.getElementById(id).setAttribute("opacity", val);
}
<commit_msg>IE Fixes<commit_after>var transformData = undefined;
var svg = undefined;
var pt = undefined;

var category = "Total";
var year = "1950";
var transitionTime = "1s";
var colors = {
  "Thermoelectric": "#FCBA04",
  "Public_Supply": "#BA3228",
  "Industrial": "#8A716A",
  "Irrigation": "#9BC53D",
  "Total": "#2E86AB"
};

$(document).ready(function(){
  //IE Fix
  var ua = window.navigator.userAgent;
  //IE10 and Below
  var msie = ua.indexOf("MSIE ");
  //IE11
  var trident = ua.indexOf('Trident/');
  //IE Edge
  var edge = ua.indexOf('Edge/');
  if(msie > 0 || trident > 0 || edge > 0){
    alert('Internet Explorer');
  }else{
    alert('Other Browser');
  }

  get_data();
  svg = document.querySelector("svg");
  pt = svg.createSVGPoint();
  for (var cat in colors) {
    var catButton = $("#" + cat + '-button');
    catButton.css({'fill': colors[cat]});
  }

  var set_slider_click = function(x) {
    $("#bar-" + x).on("click", function(){
      $("#slider")[0].noUiSlider.set(x);
    });
  };

  var yr = 1950;
  while (yr <= 2015) {
    set_slider_click(yr);
    yr += 5;
  }
});

/* depends on jquery */
var animate_resize_map = function(data) {
  var color = colors[category];
  $("#category-area-legend").css(
    {
    "fill": color,
    "stroke":"none",
    "transition": "all " + transitionTime + " ease-in-out"
  });
  $.each(data, function(index, val) {
    var scale = Math.sqrt(val.scaleFactor);
    var style = {
      "fill": color,
      "transform": "scale3d(" + scale + "," + scale + ",1)",
      "stroke":"none",
      "transition": "all " + transitionTime + " ease-in-out"
    };
    var state = $("#" + val.state_name);
    if (state !== undefined) {
      if (isNaN(scale)){ // doesn't seem to work?
        style = {
          "fill":"url(#nodata)",
          "transform": "scale3d(1,1,1)",
          "stroke":"#f1f1f1",
          "transition": "all 0s"
        };
      }
      state.css(style);
    }
  });
  document.getElementById('category-area-text').firstChild.data = transformData['catVals'][category].toLocaleString() + ' mgd water withdrawal';
};

var animate_bars = function(data) {

  $.each(data, function(prop, val) {
    var myYear = prop;
    var color = colors[category];
    var scale = val[category][0]["barScale"];
    var value = val[category][0]["value"];
    var style = {
      "background": color,
      "transform": "scale3d(1," + scale + ",1)",
      "transform-origin": "100% 100%",
      "transition": "all " + transitionTime + " ease-in-out"
    };
    var bar = $("#bar-" + myYear);
    if (bar !== undefined) {
      bar.css(style);
      if (myYear !== year) {
        bar.css('opacity','0.25');
      } else {
        bar.css('opacity','1.0');
      }
      value = value.toLocaleString() + ' mgd';
      bar.attr("title", value);
    }
  });
  update_bar_tips();
};

var get_state_value = (function() {
  var prevState = "";
  var prevCat = "";
  var prevYear = "";
  var prevVal = "";
  var sameHover = function(state) {
    return (prevState === state &&
            prevCat === category &&
            prevYear == year);
  }
  return function(state) {
    if (transformData !== undefined && !sameHover(state)) {
        prevState = state;
        prevCat = category;
        prevYear = year;

        var stateData = transformData["totState"][year][category];
        prevVal = function(allData) {
          for (var i = 0; i < allData.length; i++) {
            if (allData[i]['state_name'] === state){
              return allData[i]['value'];
            }
          }
        }(stateData);
    }
    return prevVal;
  }
})();

var get_data = function() {
  $.get( "js/scaleFactors.json", function( data ) {
    transformData = data;

    var slider = document.getElementById('slider');
    slider.noUiSlider.on('update', function( values, handle ) {
  	  var year = "" + Math.round(values[handle]);
    	setYear(year);
    });

    setCategory(category);
  });
};

var animate = function() {
  var statesTransform = transformData["totState"][year][category];
  var barsTransform = transformData["totNat"];
  animate_resize_map(statesTransform);
  animate_bars(barsTransform);
};

var setCategory = function(cat) {
  category = cat;
  $('.cat-button').css("fill-opacity", '0.7');
  $('.cat-button').css("stroke-opacity","0.0");
  $('#' + cat).css("fill-opacity", "0.0");
  $('#' + cat).css("stroke-opacity","1.0");
  animate();
};

var setYear = function(yr) {
  year = yr;
  animate();
};

var update_bar_tips = function() {
  $.each($(".dataBar"), function(prop, val){
    $(val).off("mouseenter mouseleave");

    if ($(val).data("tooltipsy") !== undefined) {
      $(val).data("tooltipsy").destroy()
    }
  });
  $('.hastip').tooltipsy({
    delay: 50,
    offset: [0, -10]
  });
}

function hovertext(text, evt){
  var tooltip = document.getElementById("tooltip-text");
  var tooltip_bg = document.getElementById("tooltip-box");
  var tool_pt = document.getElementById("tooltip-point");
  if (evt === undefined){
    tooltip.firstChild.data = ' ';
    tooltip_bg.setAttribute("class","hidden");
    tooltip_bg.setAttribute("x",0);
    tool_pt.setAttribute("class","hidden");
    stateVal = " ";
  } else {
    var ref = evt.target.getAttribute('xlink:href').split('-')[0];
    var stateName = ref.replace(/#/g, '');
    var displayNum = Math.round(get_state_value(stateName));
    if (isNaN(displayNum)){
      displayNum = 'no data';
    } else {
      displayNum = displayNum.toLocaleString() + ' mgd';
    }
    text = text + ': ' + displayNum;
    pt = cursorPoint(evt);
    pt.x = Math.round(pt.x);
    pt.y = Math.round(pt.y);
    svgWidth = Number(svg.getAttribute("viewBox").split(" ")[2]);
    tooltip.setAttribute("x",pt.x);
    tooltip.setAttribute("y",pt.y);
    tooltip.firstChild.data = text;
    var length = Math.round(tooltip.getComputedTextLength());
    if (pt.x - length/2 - 6 < 0){
      tooltip.setAttribute("x",length/2+6);
    } else if (pt.x + length/2 + 6 > svgWidth) {
      tooltip.setAttribute("x", svgWidth-length/2-6);
    }
    tool_pt.setAttribute("transform","translate("+pt.x+","+pt.y+")");
    tooltip_bg.setAttribute("x",tooltip.getAttribute("x")-length/2-6);
    tooltip_bg.setAttribute("y",pt.y-35);
    tooltip.setAttribute("class","shown");
    tooltip_bg.setAttribute("class","tooltip-box");
    tool_pt.setAttribute("class","tooltip-box");
    tooltip_bg.setAttribute("width", length+12);
  }
}

function cursorPoint(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}
function changeOpacity(id, val){
  document.getElementById(id).setAttribute("opacity", val);
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { Box, Row, Rule, Text } from '@tlon/indigo-react';
import index from '~/logic/lib/omnibox';
import Mousetrap from 'mousetrap';
import OmniboxInput from './OmniboxInput';
import OmniboxResult from './OmniboxResult';

import defaultApps from '~/logic/lib/default-apps';

export class Omnibox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      index: new Map([]),
      query: '',
      results: this.initialResults(),
      selected: []
    };
    this.handleClickOutside = this.handleClickOutside.bind(this);
    this.search = this.search.bind(this);
    this.navigate = this.navigate.bind(this);
    this.control - this.control.bind(this);
    this.setPreviousSelected = this.setPreviousSelected.bind(this);
    this.setNextSelected = this.setNextSelected.bind(this);
    this.renderResults = this.renderResults.bind(this);
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps !== this.props) {
      this.setState({ index: index(this.props.associations, this.props.apps.tiles) });
    }

    if (prevProps && (prevProps.apps !== this.props.apps) && (this.state.query === '')) {
      this.setState({ results: this.initialResults() });
    }

    if (prevProps && this.props.show && prevProps.show !== this.props.show) {
      Mousetrap.bind('escape', () => this.props.api.local.setOmnibox());
      document.addEventListener('mousedown', this.handleClickOutside);
      const touchstart = new Event('touchstart');
      this.omniInput.input.dispatchEvent(touchstart);
      this.omniInput.input.focus();
    }
  }

  componentWillUpdate(prevProps) {
    if (this.props.show && prevProps.show !== this.props.show) {
      Mousetrap.unbind('escape');
      document.removeEventListener('mousedown', this.handleClickOutside);
    }
  }

  getSearchedCategories() {
    return ['apps', 'commands', 'groups', 'subscriptions', 'other'];
  }

  control(evt) {
    if (evt.key === 'Escape') {
      if (this.state.query.length > 0) {
        this.setState({ query: '', results: this.initialResults() });
      } else if (this.props.show) {
        this.props.api.local.setOmnibox();
      }
    };

    if (
      evt.key === 'ArrowUp' ||
      (evt.shiftKey && evt.key === 'Tab')) {
        evt.preventDefault();
      return this.setPreviousSelected();
    }

    if (evt.key === 'ArrowDown' || evt.key === 'Tab') {
      evt.preventDefault();
      this.setNextSelected();
    }

    if (evt.key === 'Enter') {
      evt.preventDefault();
      if (this.state.selected !== []) {
        this.navigate(this.state.selected[0], this.state.selected[1]);
      } else {
        this.navigate(
          Array.from(this.state.results.values()).flat()[0].app,
          Array.from(this.state.results.values()).flat()[0].link);
      }
    }
  }

  handleClickOutside(evt) {
    if (this.props.show && !this.omniBox.contains(evt.target)) {
      this.setState({ results: this.initialResults(), query: '' }, () => {
        this.props.api.local.setOmnibox();
      });
    }
  }

  initialResults() {
    return new Map(this.getSearchedCategories().map((category) => {
      if (!this.state) {
        return [category, []];
      }
      if (category === 'apps') {
        return ['apps', this.state.index.get('apps')];
      }
      if (category === 'other') {
        return ['other', this.state.index.get('other')];
      }
      return [category, []];
    }));
  }

  navigate(app, link) {
    const { props } = this;
    this.setState({ results: this.initialResults(), query: '' }, () => {
      props.api.local.setOmnibox();
      if (defaultApps.includes(app.toLowerCase()) || app === 'profile') {
        props.history.push(link);
      } else {
        window.location.href = link;
      }
    });
  }

  search(event) {
    const { state } = this;
    let query = event.target.value;
    const results = this.initialResults();
    let selected = state.selected;

    this.setState({ query });

    // wipe results if backspacing
    if (query.length === 0) {
      this.setState({ results: results, selected: [] });
      return;
    }

    // don't search for single characters
    if (query.length === 1) {
      return;
    }

    query = query.toLowerCase();

    this.getSearchedCategories().map((category) => {
      const categoryIndex = state.index.get(category);
      results.set(category,
        categoryIndex.filter((result) => {
          return (
            result.title.toLowerCase().includes(query) ||
            result.link.toLowerCase().includes(query) ||
            result.app.toLowerCase().includes(query) ||
            (result.host !== null ? result.host.includes(query) : false)
          );
        })
      );
    });

    const flattenedResultLinks = Array.from(results.values()).flat().map(result => result.link);
    if (!flattenedResultLinks.includes(selected)) {
      selected = flattenedResultLinks[0];
    }

    this.setState({ results, selected });
  }

  setPreviousSelected() {
    const current = this.state.selected;
    const flattenedResults = Array.from(this.state.results.values()).flat();
    const totalLength = flattenedResults.length;
    if (current !== []) {
      const currentIndex = flattenedResults.indexOf(
        ...flattenedResults.filter((e) => {
          return e.link === current[1];
        })
      );
      if (currentIndex > 0) {
        const nextApp = flattenedResults[currentIndex - 1].app;
        const nextLink = flattenedResults[currentIndex - 1].link;
        this.setState({ selected: [nextApp, nextLink] });
      } else {
        const nextApp = flattenedResults[totalLength - 1].app;
        const nextLink = flattenedResults[totalLength - 1].link;
        this.setState({ selected: [nextApp, nextLink] });
      }
    } else {
      const nextApp = flattenedResults[totalLength - 1].app;
      const nextLink = flattenedResults[totalLength - 1].link;
      this.setState({ selected: [nextApp, nextLink] });
    }
  }

  setNextSelected() {
    const current = this.state.selected;
    const flattenedResults = Array.from(this.state.results.values()).flat();
    if (current !== []) {
      const currentIndex = flattenedResults.indexOf(
        ...flattenedResults.filter((e) => {
          return e.link === current[1];
        })
      );
      if (currentIndex < flattenedResults.length - 1) {
      const nextApp = flattenedResults[currentIndex + 1].app;
      const nextLink = flattenedResults[currentIndex + 1].link;
      this.setState({ selected: [nextApp, nextLink] });
      } else {
      const nextApp = flattenedResults[0].app;
      const nextLink = flattenedResults[0].link;
      this.setState({ selected: [nextApp, nextLink] });
      }
    } else {
      const nextApp = flattenedResults[0].app;
      const nextLink = flattenedResults[0].link;
      this.setState({ selected: [nextApp, nextLink] });
    }
  }

  renderResults() {
    const { props, state } = this;
    return <Box
            maxHeight="400px"
            overflowY="auto"
            overflowX="hidden"
            borderBottomLeftRadius='2'
            borderBottomRightRadius='2'
           >
      {this.getSearchedCategories()
        .map(category => Object({ category, categoryResults: state.results.get(category) }))
        .filter(category => category.categoryResults.length > 0)
        .map(({ category, categoryResults }, i) => {
          const categoryTitle = (category === 'other')
            ? null : <Text gray ml={2}>{category.charAt(0).toUpperCase() + category.slice(1)}</Text>;
          const selected = this.state.selected?.length ? this.state.selected[1] : '';
          return (<Box key={i} width='max(50vw, 300px)' maxWidth='600px'>
            <Rule borderTopWidth="0.5px" color="washedGray" />
            {categoryTitle}
            {categoryResults.map((result, i2) => (
              <OmniboxResult
                key={i2}
                icon={result.app}
                text={result.title}
                subtext={result.host}
                link={result.link}
                navigate={() => this.navigate(result.app, result.link)}
                selected={selected}
                dark={props.dark} />
            ))}
          </Box>
        );
      })
      }
    </Box>;
  }

  render() {
    const { props, state } = this;
    if (state?.selected?.length === 0 && Array.from(this.state.results.values()).flat().length) {
      this.setNextSelected();
    }
    return (
        <Box
          backgroundColor='scales.black30'
          width='100vw'
          height='100vh'
          position='absolute'
          top='0'
          right='0'
          zIndex='9'
          display={props.show ? 'block' : 'none'}>
          <Row justifyContent='center'>
            <Box
              mt='20vh'
              width='max(50vw, 300px)'
              maxWidth='600px'
              borderRadius='2'
              backgroundColor='white'
              ref={(el) => {
                this.omniBox = el;
              }}>
              <OmniboxInput
                ref={(el) => {
                  this.omniInput = el;
                }}
                control={e => this.control(e)}
                search={this.search}
                query={state.query}
              />
              {this.renderResults()}
            </Box>
          </Row>
        </Box>
    );
  }
}

export default withRouter(Omnibox);
<commit_msg>leap: ensure selected is always an array<commit_after>import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { Box, Row, Rule, Text } from '@tlon/indigo-react';
import index from '~/logic/lib/omnibox';
import Mousetrap from 'mousetrap';
import OmniboxInput from './OmniboxInput';
import OmniboxResult from './OmniboxResult';

import defaultApps from '~/logic/lib/default-apps';

export class Omnibox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      index: new Map([]),
      query: '',
      results: this.initialResults(),
      selected: []
    };
    this.handleClickOutside = this.handleClickOutside.bind(this);
    this.search = this.search.bind(this);
    this.navigate = this.navigate.bind(this);
    this.control - this.control.bind(this);
    this.setPreviousSelected = this.setPreviousSelected.bind(this);
    this.setNextSelected = this.setNextSelected.bind(this);
    this.renderResults = this.renderResults.bind(this);
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps !== this.props) {
      this.setState({ index: index(this.props.associations, this.props.apps.tiles) });
    }

    if (prevProps && (prevProps.apps !== this.props.apps) && (this.state.query === '')) {
      this.setState({ results: this.initialResults() });
    }

    if (prevProps && this.props.show && prevProps.show !== this.props.show) {
      Mousetrap.bind('escape', () => this.props.api.local.setOmnibox());
      document.addEventListener('mousedown', this.handleClickOutside);
      const touchstart = new Event('touchstart');
      this.omniInput.input.dispatchEvent(touchstart);
      this.omniInput.input.focus();
    }
  }

  componentWillUpdate(prevProps) {
    if (this.props.show && prevProps.show !== this.props.show) {
      Mousetrap.unbind('escape');
      document.removeEventListener('mousedown', this.handleClickOutside);
    }
  }

  getSearchedCategories() {
    return ['apps', 'commands', 'groups', 'subscriptions', 'other'];
  }

  control(evt) {
    if (evt.key === 'Escape') {
      if (this.state.query.length > 0) {
        this.setState({ query: '', results: this.initialResults(), selected: [] });
      } else if (this.props.show) {
        this.props.api.local.setOmnibox();
      }
    };

    if (
      evt.key === 'ArrowUp' ||
      (evt.shiftKey && evt.key === 'Tab')) {
        evt.preventDefault();
      return this.setPreviousSelected();
    }

    if (evt.key === 'ArrowDown' || evt.key === 'Tab') {
      evt.preventDefault();
      this.setNextSelected();
    }

    if (evt.key === 'Enter') {
      evt.preventDefault();
      if (this.state.selected.length > 0) {
        this.navigate(this.state.selected[0], this.state.selected[1]);
      } else if (Array.from(this.state.results.values()).flat().length === 0) {
        return;
      } else {
        this.navigate(
          Array.from(this.state.results.values()).flat()[0].app,
          Array.from(this.state.results.values()).flat()[0].link);
      }
    }
  }

  handleClickOutside(evt) {
    if (this.props.show && !this.omniBox.contains(evt.target)) {
      this.setState({ results: this.initialResults(), query: '', selected: [] }, () => {
        this.props.api.local.setOmnibox();
      });
    }
  }

  initialResults() {
    return new Map(this.getSearchedCategories().map((category) => {
      if (!this.state) {
        return [category, []];
      }
      if (category === 'apps') {
        return ['apps', this.state.index.get('apps')];
      }
      if (category === 'other') {
        return ['other', this.state.index.get('other')];
      }
      return [category, []];
    }));
  }

  navigate(app, link) {
    const { props } = this;
    this.setState({ results: this.initialResults(), query: '' }, () => {
      props.api.local.setOmnibox();
      if (defaultApps.includes(app.toLowerCase()) || app === 'profile') {
        props.history.push(link);
      } else {
        window.location.href = link;
      }
    });
  }

  search(event) {
    const { state } = this;
    let query = event.target.value;
    const results = this.initialResults();
    let selected = state.selected;

    this.setState({ query });

    // wipe results if backspacing
    if (query.length === 0) {
      this.setState({ results: results, selected: [] });
      return;
    }

    // don't search for single characters
    if (query.length === 1) {
      return;
    }

    query = query.toLowerCase();

    this.getSearchedCategories().map((category) => {
      const categoryIndex = state.index.get(category);
      results.set(category,
        categoryIndex.filter((result) => {
          return (
            result.title.toLowerCase().includes(query) ||
            result.link.toLowerCase().includes(query) ||
            result.app.toLowerCase().includes(query) ||
            (result.host !== null ? result.host.includes(query) : false)
          );
        })
      );
    });

    const flattenedResultLinks = Array.from(results.values()).flat().map(result => [result.app, result.link]);
    if (!flattenedResultLinks.includes(selected)) {
      selected = flattenedResultLinks[0] || [];
    }

    this.setState({ results, selected });
  }

  setPreviousSelected() {
    const current = this.state.selected;
    const flattenedResults = Array.from(this.state.results.values()).flat();
    const totalLength = flattenedResults.length;
    if (current !== []) {
      const currentIndex = flattenedResults.indexOf(
        ...flattenedResults.filter((e) => {
          return e.link === current[1];
        })
      );
      if (currentIndex > 0) {
        const nextApp = flattenedResults[currentIndex - 1].app;
        const nextLink = flattenedResults[currentIndex - 1].link;
        this.setState({ selected: [nextApp, nextLink] });
      } else {
        const nextApp = flattenedResults[totalLength - 1].app;
        const nextLink = flattenedResults[totalLength - 1].link;
        this.setState({ selected: [nextApp, nextLink] });
      }
    } else {
      const nextApp = flattenedResults[totalLength - 1].app;
      const nextLink = flattenedResults[totalLength - 1].link;
      this.setState({ selected: [nextApp, nextLink] });
    }
  }

  setNextSelected() {
    const current = this.state.selected;
    const flattenedResults = Array.from(this.state.results.values()).flat();
    if (current !== []) {
      const currentIndex = flattenedResults.indexOf(
        ...flattenedResults.filter((e) => {
          return e.link === current[1];
        })
      );
      if (currentIndex < flattenedResults.length - 1) {
      const nextApp = flattenedResults[currentIndex + 1].app;
      const nextLink = flattenedResults[currentIndex + 1].link;
      this.setState({ selected: [nextApp, nextLink] });
      } else {
      const nextApp = flattenedResults[0].app;
      const nextLink = flattenedResults[0].link;
      this.setState({ selected: [nextApp, nextLink] });
      }
    } else {
      const nextApp = flattenedResults[0].app;
      const nextLink = flattenedResults[0].link;
      this.setState({ selected: [nextApp, nextLink] });
    }
  }

  renderResults() {
    const { props, state } = this;
    return <Box
            maxHeight="400px"
            overflowY="auto"
            overflowX="hidden"
            borderBottomLeftRadius='2'
            borderBottomRightRadius='2'
           >
      {this.getSearchedCategories()
        .map(category => Object({ category, categoryResults: state.results.get(category) }))
        .filter(category => category.categoryResults.length > 0)
        .map(({ category, categoryResults }, i) => {
          const categoryTitle = (category === 'other')
            ? null : <Text gray ml={2}>{category.charAt(0).toUpperCase() + category.slice(1)}</Text>;
          const selected = this.state.selected?.length ? this.state.selected[1] : '';
          return (<Box key={i} width='max(50vw, 300px)' maxWidth='600px'>
            <Rule borderTopWidth="0.5px" color="washedGray" />
            {categoryTitle}
            {categoryResults.map((result, i2) => (
              <OmniboxResult
                key={i2}
                icon={result.app}
                text={result.title}
                subtext={result.host}
                link={result.link}
                navigate={() => this.navigate(result.app, result.link)}
                selected={selected}
                dark={props.dark} />
            ))}
          </Box>
        );
      })
      }
    </Box>;
  }

  render() {
    const { props, state } = this;
    if (state?.selected?.length === 0 && Array.from(this.state.results.values()).flat().length) {
      this.setNextSelected();
    }
    return (
        <Box
          backgroundColor='scales.black30'
          width='100vw'
          height='100vh'
          position='absolute'
          top='0'
          right='0'
          zIndex='9'
          display={props.show ? 'block' : 'none'}>
          <Row justifyContent='center'>
            <Box
              mt='20vh'
              width='max(50vw, 300px)'
              maxWidth='600px'
              borderRadius='2'
              backgroundColor='white'
              ref={(el) => {
                this.omniBox = el;
              }}>
              <OmniboxInput
                ref={(el) => {
                  this.omniInput = el;
                }}
                control={e => this.control(e)}
                search={this.search}
                query={state.query}
              />
              {this.renderResults()}
            </Box>
          </Row>
        </Box>
    );
  }
}

export default withRouter(Omnibox);
<|endoftext|>


<commit_before>import { BoundingBox } from '../../core/shape/bounding-box.js';
import { BoundingSphere } from '../../core/shape/bounding-sphere.js';
import { LIGHTTYPE_DIRECTIONAL } from '../../scene/constants.js';

const tempSphere = new BoundingSphere();

// helper class to store all lights including their original state
class BakeLight {
    constructor(scene, light) {

        this.scene = scene;

        // light of type Light
        this.light = light;

        // original light properties
        this.store();

        // don't use cascades
        light.numCascades = 1;

        // bounds for non-directional light
        if (light.type !== LIGHTTYPE_DIRECTIONAL) {

            // world sphere
            light._node.getWorldTransform();
            light.getBoundingSphere(tempSphere);

            // world aabb
            this.lightBounds = new BoundingBox();
            this.lightBounds.center.copy(tempSphere.center);
            this.lightBounds.halfExtents.set(tempSphere.radius, tempSphere.radius, tempSphere.radius);
        }
    }

    store() {
        this.mask = this.light.mask;
        this.shadowUpdateMode = this.light.shadowUpdateMode;
        this.enabled = this.light.enabled;
        this.intensity = this.light.intensity;
        this.rotation = this.light._node.getLocalRotation().clone();
        this.numCascades = this.light.numCascades;
    }

    restore() {
        const light = this.light;
        light.mask = this.mask;
        light.shadowUpdateMode = this.shadowUpdateMode;
        light.enabled = this.enabled;
        light.intensity = this.intensity;
        light._node.setLocalRotation(this.rotation);
        light.numCascades = this.numCascades;
    }

    startBake() {
        this.light.enabled = true;

        // destroy shadow map the light might have
        this.light._destroyShadowMap();
    }

    endBake(shadowMapCache) {
        const light = this.light;
        light.enabled = false;

        if (light.shadowMap) {
            // return shadow map to the cache
            if (light.shadowMap.cached)
                shadowMapCache.add(light, light.shadowMap);

            light.shadowMap = null;
        }
    }
}

export { BakeLight };
<commit_msg>[Fix] Fix issue with baking clustered lights (#4810)<commit_after>import { BoundingBox } from '../../core/shape/bounding-box.js';
import { BoundingSphere } from '../../core/shape/bounding-sphere.js';
import { LIGHTTYPE_DIRECTIONAL } from '../../scene/constants.js';

const tempSphere = new BoundingSphere();

// helper class to store all lights including their original state
class BakeLight {
    constructor(scene, light) {

        this.scene = scene;

        // light of type Light
        this.light = light;

        // original light properties
        this.store();

        // don't use cascades
        light.numCascades = 1;

        // bounds for non-directional light
        if (light.type !== LIGHTTYPE_DIRECTIONAL) {

            // world sphere
            light._node.getWorldTransform();
            light.getBoundingSphere(tempSphere);

            // world aabb
            this.lightBounds = new BoundingBox();
            this.lightBounds.center.copy(tempSphere.center);
            this.lightBounds.halfExtents.set(tempSphere.radius, tempSphere.radius, tempSphere.radius);
        }
    }

    store() {
        this.mask = this.light.mask;
        this.shadowUpdateMode = this.light.shadowUpdateMode;
        this.enabled = this.light.enabled;
        this.intensity = this.light.intensity;
        this.rotation = this.light._node.getLocalRotation().clone();
        this.numCascades = this.light.numCascades;
    }

    restore() {
        const light = this.light;
        light.mask = this.mask;
        light.shadowUpdateMode = this.shadowUpdateMode;
        light.enabled = this.enabled;
        light.intensity = this.intensity;
        light._node.setLocalRotation(this.rotation);
        light.numCascades = this.numCascades;
    }

    startBake() {
        this.light.enabled = true;

        // destroy shadow map the light might have
        this.light._destroyShadowMap();

        this.light.beginFrame();
    }

    endBake(shadowMapCache) {
        const light = this.light;
        light.enabled = false;

        if (light.shadowMap) {
            // return shadow map to the cache
            if (light.shadowMap.cached)
                shadowMapCache.add(light, light.shadowMap);

            light.shadowMap = null;
        }
    }
}

export { BakeLight };
<|endoftext|>


<commit_before>// @flow
import { observable, action } from 'mobx';
import Store from './lib/Store';
import Request from './lib/LocalizedRequest';
import environment from '../environment';
import type { NextUpdateResponse, PostponeUpdateResponse, ApplyUpdateResponse } from '../api';

export default class NodeUpdateStore extends Store {

  NODE_UPDATE_POLL_INTERVAL = 5000;

  @observable isUpdateAvailable = false;
  @observable isUpdatePostponed = false;
  @observable isNotificationExpanded = false;
  @observable isUpdateInstalled = false;
  @observable updateVersion = null;
  @observable nextUpdateRequest: Request<NextUpdateResponse> = new Request(this.api.nextUpdate);
  @observable postponeUpdateRequest: Request<PostponeUpdateResponse> = new Request(this.api.postponeUpdate);
  @observable applyUpdateRequest: Request<ApplyUpdateResponse> = new Request(this.api.applyUpdate);

  setup() {
    const actions = this.actions.nodeUpdate;
    actions.acceptNodeUpdate.listen(this._acceptNodeUpdate);
    actions.postponeNodeUpdate.listen(this._postponeNodeUpdate);
    actions.toggleNodeUpdateNotificationExpanded.listen(this._toggleNotificationExpanded);
    if (environment.CARDANO_API) {
      setInterval(this.refreshNextUpdate, this.NODE_UPDATE_POLL_INTERVAL);
    }
  }

  @action refreshNextUpdate = () => {
    if (this.stores.networkStatus.isSynced) {
      this.nextUpdateRequest.execute();
      if (this.nextUpdateRequest.result && !this.isUpdateAvailable &&
        !this.isUpdatePostponed && !this.isUpdateInstalled) {
        this.isUpdateAvailable = true;
        this.isNotificationExpanded = true;
        this.updateVersion = this.nextUpdateRequest.result.version;
      }
    }
  };

  @action _postponeNodeUpdate = () => {
    this.postponeUpdateRequest.execute();
    this.isUpdatePostponed = true;
  };

  @action _acceptNodeUpdate = () => {
    this.applyUpdateRequest.execute();
    this.isUpdateAvailable = false;
    this.isUpdateInstalled = true;
  };

  @action _toggleNotificationExpanded = () => {
    this.isNotificationExpanded = !this.isNotificationExpanded;
  };

}
<commit_msg>[CSM-458] Fix eslint warnings<commit_after>// @flow
import { observable, action } from 'mobx';
import Store from './lib/Store';
import Request from './lib/LocalizedRequest';
import environment from '../environment';
import type {
  NextUpdateResponse,
  PostponeUpdateResponse,
  ApplyUpdateResponse,
} from '../api';

export default class NodeUpdateStore extends Store {

  NODE_UPDATE_POLL_INTERVAL = 5000;

  @observable isUpdateAvailable = false;
  @observable isUpdatePostponed = false;
  @observable isNotificationExpanded = false;
  @observable isUpdateInstalled = false;
  @observable updateVersion = null;

  // REQUESTS
  /* eslint-disable max-len */
  @observable nextUpdateRequest: Request<NextUpdateResponse> = new Request(this.api.nextUpdate);
  @observable postponeUpdateRequest: Request<PostponeUpdateResponse> = new Request(this.api.postponeUpdate);
  @observable applyUpdateRequest: Request<ApplyUpdateResponse> = new Request(this.api.applyUpdate);
  /* eslint-disable max-len */

  setup() {
    const actions = this.actions.nodeUpdate;
    actions.acceptNodeUpdate.listen(this._acceptNodeUpdate);
    actions.postponeNodeUpdate.listen(this._postponeNodeUpdate);
    actions.toggleNodeUpdateNotificationExpanded.listen(this._toggleNotificationExpanded);
    if (environment.CARDANO_API) {
      setInterval(this.refreshNextUpdate, this.NODE_UPDATE_POLL_INTERVAL);
    }
  }

  @action refreshNextUpdate = () => {
    if (this.stores.networkStatus.isSynced) {
      this.nextUpdateRequest.execute();
      if (this.nextUpdateRequest.result && !this.isUpdateAvailable &&
        !this.isUpdatePostponed && !this.isUpdateInstalled) {
        this.isUpdateAvailable = true;
        this.isNotificationExpanded = true;
        this.updateVersion = this.nextUpdateRequest.result.version;
      }
    }
  };

  @action _postponeNodeUpdate = () => {
    this.postponeUpdateRequest.execute();
    this.isUpdatePostponed = true;
  };

  @action _acceptNodeUpdate = () => {
    this.applyUpdateRequest.execute();
    this.isUpdateAvailable = false;
    this.isUpdateInstalled = true;
  };

  @action _toggleNotificationExpanded = () => {
    this.isNotificationExpanded = !this.isNotificationExpanded;
  };

}
<|endoftext|>


<commit_before>// Poor man's uglifyjs --define ... intended for use with --nocompress

var _ = require('underscore'),
    uglifyJs = require('../').JavaScript.uglifyJs,
    uglifyAst = require('../').JavaScript.uglifyAst;

function isLeftHandSideOfAssignment(stack) {
    for (var i = stack.length - 2 ; i >= 0 ; i -= 1) {
        if (stack[i] instanceof uglifyJs.AST_Assign) {
            if (stack[i + 1] === stack[i].left) {
                return true;
            } else {
                break;
            }
        }
    }
    return false;
}

function astObjectIndexOfProperty(ast, key) {
    if (ast.properties && ast.properties.length) {
        var index = -1;
        ast.properties.some(function (prop, propIndex) {
            if (prop.key === key) {
                index = propIndex;
                return true;
            }
            return false;
        });
        return index;
    }
    return -1;
}

module.exports = function (queryObj, replacementAstBySymbolName) {
    replacementAstBySymbolName = _.extend({}, replacementAstBySymbolName);
    Object.keys(replacementAstBySymbolName).forEach(function (symbolName) {
        var replacementAst = replacementAstBySymbolName[symbolName];
        if (typeof replacementAst === 'string') {
            replacementAstBySymbolName[symbolName] = uglifyAst.parseExpression(replacementAst);
        } else if (!(replacementAst instanceof uglifyJs.AST_Node)) {
            replacementAstBySymbolName[symbolName] = uglifyAst.objToAst(replacementAst);
        }
    });

    return function replaceSymbolsInJavaScript(assetGraph) {
        assetGraph.findAssets(_.extend({type: 'JavaScript'}, queryObj)).forEach(function (javaScript) {
            var replacementPerformed = false;
            javaScript.parseTree.walk(new uglifyJs.TreeWalker(function (node) {
                if (node instanceof uglifyJs.AST_SymbolRef && Object.prototype.hasOwnProperty.call(replacementAstBySymbolName, node.name) && !isLeftHandSideOfAssignment(this.stack)) {

                    var stack = this.stack.reverse();
                    var replacement = node;
                    var originalNode = node;
                    var dontReplace = false;
                    var nonExistentKey = '';
                    var replacedUntil = -1;

                    stack.some(function (node, stackIndex) {
                        var astIndex;
                        replacedUntil = stackIndex;
                        if (originalNode === node) {
                            replacement = replacementAstBySymbolName[node.name].clone();
                            return false;
                        } else if (node instanceof uglifyJs.AST_Dot && replacement !== node) {
                            astIndex = astObjectIndexOfProperty(replacement, node.property);
                            if (astIndex !== -1) {
                                replacement = replacement.properties[astIndex].value;
                                return false; // continue up the stack
                            } else {
                                dontReplace = true;
                                nonExistentKey = node.property;
                            }
                        } else if (node instanceof uglifyJs.AST_Sub && replacement !== node) {
                            astIndex = astObjectIndexOfProperty(replacement, node.property.value);
                            if (astIndex !== -1) {
                                replacement = replacement.properties[astIndex].value;
                                return false; // continue up the stack
                            } else {
                                dontReplace = true;
                                nonExistentKey = node.property.value;
                            }
                        }
                        return true; // stop traversing the stack
                    });

                    if (dontReplace) {
                        assetGraph.emit('warn', new Error(
                            'Trying to replace with non-existent key "' +
                            nonExistentKey + '" on ' + node.print_to_string()
                        ));
                    }

                    if (!dontReplace && replacement !== node) {
                        uglifyAst.replaceDescendantNode(stack[replacedUntil], stack[replacedUntil - 1], replacement);
                        replacementPerformed = true;
                    }
                }
            }));
            if (replacementPerformed) {
                javaScript.markDirty();
            }
        });
    };
};
<commit_msg>replaceSymbolsInJavaScript: Support primitive values besides strings.<commit_after>// Poor man's uglifyjs --define ... intended for use with --nocompress

var _ = require('underscore'),
    uglifyJs = require('../').JavaScript.uglifyJs,
    uglifyAst = require('../').JavaScript.uglifyAst;

function isLeftHandSideOfAssignment(stack) {
    for (var i = stack.length - 2 ; i >= 0 ; i -= 1) {
        if (stack[i] instanceof uglifyJs.AST_Assign) {
            if (stack[i + 1] === stack[i].left) {
                return true;
            } else {
                break;
            }
        }
    }
    return false;
}

function astObjectIndexOfProperty(ast, key) {
    if (ast.properties && ast.properties.length) {
        var index = -1;
        ast.properties.some(function (prop, propIndex) {
            if (prop.key === key) {
                index = propIndex;
                return true;
            }
            return false;
        });
        return index;
    }
    return -1;
}

module.exports = function (queryObj, replacementAstBySymbolName) {
    replacementAstBySymbolName = _.extend({}, replacementAstBySymbolName);
    Object.keys(replacementAstBySymbolName).forEach(function (symbolName) {
        var replacementAst = replacementAstBySymbolName[symbolName];
        if (!(replacementAst instanceof uglifyJs.AST_Node)) {
            if (replacementAst && typeof replacementAst === 'object') {
                replacementAstBySymbolName[symbolName] = uglifyAst.objToAst(replacementAst);
            } else {
                replacementAstBySymbolName[symbolName] = uglifyAst.parseExpression(String(replacementAst));
            }
        }
    });

    return function replaceSymbolsInJavaScript(assetGraph) {
        assetGraph.findAssets(_.extend({type: 'JavaScript'}, queryObj)).forEach(function (javaScript) {
            var replacementPerformed = false;
            javaScript.parseTree.walk(new uglifyJs.TreeWalker(function (node) {
                if (node instanceof uglifyJs.AST_SymbolRef && Object.prototype.hasOwnProperty.call(replacementAstBySymbolName, node.name) && !isLeftHandSideOfAssignment(this.stack)) {

                    var stack = this.stack.reverse();
                    var replacement = node;
                    var originalNode = node;
                    var dontReplace = false;
                    var nonExistentKey = '';
                    var replacedUntil = -1;

                    stack.some(function (node, stackIndex) {
                        var astIndex;
                        replacedUntil = stackIndex;
                        if (originalNode === node) {
                            replacement = replacementAstBySymbolName[node.name].clone();
                            return false;
                        } else if (node instanceof uglifyJs.AST_Dot && replacement !== node) {
                            astIndex = astObjectIndexOfProperty(replacement, node.property);
                            if (astIndex !== -1) {
                                replacement = replacement.properties[astIndex].value;
                                return false; // continue up the stack
                            } else {
                                dontReplace = true;
                                nonExistentKey = node.property;
                            }
                        } else if (node instanceof uglifyJs.AST_Sub && replacement !== node) {
                            astIndex = astObjectIndexOfProperty(replacement, node.property.value);
                            if (astIndex !== -1) {
                                replacement = replacement.properties[astIndex].value;
                                return false; // continue up the stack
                            } else {
                                dontReplace = true;
                                nonExistentKey = node.property.value;
                            }
                        }
                        return true; // stop traversing the stack
                    });

                    if (dontReplace) {
                        assetGraph.emit('warn', new Error(
                            'Trying to replace with non-existent key "' +
                            nonExistentKey + '" on ' + node.print_to_string()
                        ));
                    }

                    if (!dontReplace && replacement !== node) {
                        uglifyAst.replaceDescendantNode(stack[replacedUntil], stack[replacedUntil - 1], replacement);
                        replacementPerformed = true;
                    }
                }
            }));
            if (replacementPerformed) {
                javaScript.markDirty();
            }
        });
    };
};
<|endoftext|>