<commit_before>module.exports = function (app) {
  "use strict";
  var ConnectRoles = require('connect-roles');

  var defaultRoles = {
    users: {
      name: 'users',
      description: 'default role, no access',
      groups: ['users'],
      users: []
    },
    super: {
      name: 'super',
      description: 'super user role',
      groups: ['super'],
      users: ['jcreasy']
    }
  };

  var nconf = require('nconf');
  var fs = require('fs');

  if (app.locals.roles) {
    nconf.defaults({
      'roles': app.locals.roles
    });
    nconf.set('roles', app.locals.roles);
  } else {
    // Check for a config file or the default location.
    var configPath = nconf.get('conf') || app.get('base_config_dir') + '/roleManagerConfig.json';

    if (configPath) {
      nconf.file({file: configPath});
    }

    nconf.defaults({
      'roles': defaultRoles
    });
  }

  var roles = nconf.get('roles');

  var registerDefaultAction = function (module, action, description, defaultRoles) {
    var roles = defaultRoles || ['super'];
    app.locals.logger.log('info', 'registered action: "' + action + '" for module "' + module + '" allowing ' + JSON.stringify(roles) + ' to access: "' + description + '"', {module: module, action: action, roles: roles, description: description});
    registerAction(module, action, description);
    authorizeRoles(action, roles);
  };

  var registerAction = function (module, action, description) {
    app.locals.logger.log('info', 'registered action: "' + action + '" for module "' + module, {module: module, action: action});
    nconf.set('actions:' + action, {module: module, action: action, description: description});
    //saveConfig(function() {});
  };

  var registerRole = function (name, description, groups, users) {
    if (arguments.length <= 1) {
      description = 'No Description Given: ' + name;
    }
    if (arguments.length <= 2) {
      groups = [];
    }
    if (arguments.length <= 3) {
      users = [];
    }
    nconf.set('roles:' + name, { name: name, description: description, groups: groups, users: users});
    //saveConfig(function () {});
  };

  var authorizeRoles = function (action, roles) {
    nconf.set('authorizations:' + action, roles);
    /*saveConfig(function (err, results) {
     if (err) {
     app.locals.logger.log('error', 'error', { err: JSON.stringify(err)});
     } else {
     app.locals.logger.log('debug', 'saved', {results: JSON.stringify(results)});
     }
     });*/
  };

  var saveConfig = function (done) {
    nconf.save(function (err) {
      if (err) {
        done(err);
      } else {
        try {
          fs.readFile(configPath, function (err, configJSON) {
            if (err) {
              done(err);
            } else {
              var config = JSON.parse(configJSON);
              done(null, config);
            }
          });
        } catch (ex) {
          done(ex);
        }
      }
    });
  };

  function initializeRoles() {
    if (!roles.hasOwnProperty('users')) {
      throw new Error('Required role users is not defined');
    }
    if (!roles.hasOwnProperty('guest')) {
      registerRole('guest', 'Built-In Role for unauthenticated Users');
    }
    if (!roles.hasOwnProperty('sales')) {
      registerRole('sales', 'Provides access to lead and account management functionality. Also provides read-only access to monitoring.');
    }
    if (!roles.hasOwnProperty('helpdesk')) {
      registerRole('helpdesk', 'Provides access to helpdesk functionality. Also provides read-only access to monitoring.');
    }
    if (!roles.hasOwnProperty('monitoring')) {
      registerRole('monitoring', 'Provides additional access to monitoring systems.');
    }
    if (!roles.hasOwnProperty('admin')) {
      registerRole('admin', 'Provides full access to sales, monitoring and helpdesk functionality.');
    }
    if (!roles.hasOwnProperty('super')) {
      registerRole('super', 'Provides access to administrative functions.');
    }
  }

  var authFailureHandler = function (req, res, action) {
    var accept = req.headers.accept || '';
    if (req.currentUser) {
      app.locals.logger.log('debug', 'user not allowed', {action: action, groups: req.currentUser.groups});
    } else {
      app.locals.logger.log('debug', 'user is not authenticated', {action: action, groups: []});
    }
    if (~accept.indexOf('html')) {
      if (req.session) {
        req.session.returnTo = req.originalUrl || req.url;
      }
      res.redirect('/account/login');
    } else {
      res.send(403);
    }
  };

  var roleHandler = new ConnectRoles({
    failureHandler: authFailureHandler
  });

  function hasRoleGroups(userGroups, role) {
    var _ = require('underscore');
    var roleGroups = role.groups;

    for (var i = 0; i < userGroups.length; i++) {
      var userGroup = userGroups[i];

      app.locals.logger.log('silly', 'hasRoleGroups: checking userGroup', { roleGroups: roleGroups, userGroup: userGroup});

      if (_.contains(roleGroups, userGroup)) {
        app.locals.logger.log('silly', 'hasRoleGroups: returning true', {roleName: role.name, userGroups: userGroups, roleGroups: roleGroups});
        return true;
      }
    }
    app.locals.logger.log('silly', 'hasRoleGroups: returning false', {roleName: role.name, userGroups: userGroups, roleGroups: roleGroups});
    return false;
  }

  var hasAccess = function (user, role) {
    var _ = require('underscore');
    app.locals.logger.log('silly', 'hasAccess: checking user', { role: role.name, user: user.username, roleUsers: role.users});
    if (_.contains(role.users, user.username)) {
      return true;
    } else {
      return hasRoleGroups(user.groups, role);
    }
  };

  /* Placeholder for When I make this Async */
  var cachedUserRoles = function (user, requiredRoles, join) {
    return authorizeUserRoles(user, requiredRoles, join);
  };

  var authorizeUserRoles = function (user, requiredRoles, join) {
    var accessGranted = false;
    app.locals.logger.log('silly', 'authorizeUserRoles: checking user', {user: user, requiredRoles: requiredRoles});
    for (var i = 0; i < requiredRoles.length; i++) {
      var roleName = requiredRoles[i];
      if (roles.hasOwnProperty(roleName)) {
        var role = roles[roleName];
        var roleGranted = hasAccess(user, role);
        if (roleGranted === true && join === 'OR') {
          app.locals.logger.log('silly', 'returning true role granted with join OR', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          return true;
        } else if (roleGranted === true && join === 'AND') {
          app.locals.logger.log('silly', 'returning true, role granted', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          accessGranted = true;
        } else if (roleGranted === false && join === 'AND') {
          app.locals.logger.log('silly', 'returning false role not granted with join AND', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          return false;
        }
      } else {
        app.locals.logger.log('silly', 'returning false (no property)', {roleName: roleName});
        return false;
      }
    }

    return accessGranted;
  };

  var hasRequiredRoles = function (user, requiredRoles, join) {
    app.locals.logger.log('debug', 'Authorization Request', {requiredRoles: requiredRoles, username: user});

    if (arguments.length <= 1) {
      requiredRoles = ['users'];
    }

    if (arguments.length <= 2) {
      join = 'AND';
    }

    if (join !== 'AND' && join !== 'OR') {
      throw new Error('Invalid Join type ' + join);
    }

    var accessGranted = false;
    var _ = require('underscore');
    var message = '';

    if (user) {
      accessGranted = cachedUserRoles(user, requiredRoles, join);
      if (accessGranted === true) {
        message = 'Authorization Granted';
      } else if (cachedUserRoles(user, 'super'), join) {
        accessGranted = true;
        message = 'Granted with SUPER role';
      } else {
        message = 'Authorization Denied'
      }
      app.locals.logger.log('audit', message, {requiredRoles: requiredRoles, username: user.username, userGroups: user.groups, join: join, accessGranted: accessGranted});
    } else if (_.contains(requiredRoles, 'guest')) {
      accessGranted = true;
      app.locals.logger.log('audit', 'Authorization Granted', {requiredRoles: requiredRoles, accessGranted: accessGranted});
    } else {
      accessGranted = false;
      app.locals.logger.log('audit', 'Authorization Denied', {requiredRoles: requiredRoles, accessGranted: accessGranted});
    }

    return accessGranted;
  };

  var getCachedRoles = function (user, getCachedRolesCallback) {
    /*
     memoryCache.wrap('roleManager::getCachedRoles::' + user.username, function (cb) {
     getRoles(user, cb);
     }, function (err, userRoles) {
     getCachedRolesCallback(err, userRoles);
     });
     */
    getRoles(user, getCachedRolesCallback);
  };

  var getRoles = function (user, getRolesCallback) {
    var userRoles = [];
    for (var roleName in roles) {
      if (roles.hasOwnProperty(roleName)) {
        var role = roles[roleName];
        if (hasAccess(user, role)) {
          if (role.hasOwnProperty('name')) {
            userRoles.push(role.name);
          }
        }
      }
    }
    getRolesCallback(null, userRoles);
  };

  var handle = function (req, res, next) {
    if (req.hasOwnProperty('currentUser')) {
      if (process.env.NODE_ENV === 'test') {
        var _ = require('underscore');
        req.currentUser.roles = _.keys(roles);
        next();
      } else {
        getCachedRoles(req.currentUser, function (err, userRoles) {
          app.locals.logger.log('silly', 'roles added to user', {username: req.currentUser.username, userRoles: userRoles});
          req.currentUser.roles = userRoles;
          next();
        });
      }
    } else {
      app.locals.logger.log('silly', 'no roles added, no user');
      next();
    }
  };

  var isUsers = function (req) {
    return hasRequiredRoles(req.currentUser, ['users']);
  };

  var isSales = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'sales']);
  };

  var isHelpdesk = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'helpdesk']);
  };

  var isMonitoring = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'monitoring']);
  };

  var isAdmin = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'admin']);
  };

  var isSuper = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'super']);
  };

  var isGuest = function (req) {
    if (req.hasOwnProperty('currentUser')) {
      return hasRequiredRoles(req.currentUser, ['guest']);
    } else {
      return hasRequiredRoles(null, ['guest']);
    }

  };

  registerDefaultAction('internal', 'user', 'Minimal Authenticated Access Level', ['users']);
  registerDefaultAction('internal', 'guest', 'Minimal Access Level', ['guests']);

  registerDefaultAction('internal', 'use api', 'Minimal API Access Level', ['users']);
  registerDefaultAction('internal', 'view devices', 'Ability to view customer equipment information', ['helpdesk']);

  roleHandler.use('guest', function (req) {
    return isGuest(req);
  });

  roleHandler.use('user', function (req) {
    return isUsers(req);
  });

  roleHandler.use('view devices', function (req) {
    return isHelpdesk(req);
  });

  roleHandler.use('use api', function (req) {
    return isUsers(req);
  });

  roleHandler.use('view pipeline', function (req) {
    return isSales(req) || isAdmin(req) || isSuper(req);
  });

  roleHandler.use('view accounts', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view tickets', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view monitoring', function (req) {
    return isSales(req) || isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('deactivate customers', isSuper);
  roleHandler.use('decommission device', function (req) {
    return isAdmin(req) || isSuper(req);
  });
  roleHandler.use('issue credit', isSuper);

  roleHandler.use('view pipeline detail', function (req) {
    return isSales(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view monitoring events', function (req) {
    return isSales(req) || isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk tickets', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk devices', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk device detail', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk clients', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk', isUsers);
  roleHandler.use('view helpdesk client detail', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('silence monitoring events', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('delete monitoring events', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });

  initializeRoles();

  //module.use = roleHandler.use;
  //module.can = roleHandler.can;
  //module.is = roleHandler.is;
  //module.userCan = roleHandler.userCan;
  //module.userIs = roleHandler.userIs;
  //module.test = roleHandler.test;
  //module.functionList = roleHandler.functionList;
  //module.failureHandler = roleHandler.failureHandler;
  module.roleHandler = roleHandler;
  module.authorizeUserRoles = authorizeUserRoles;
  module.hasRequiredRoles = hasRequiredRoles;
  module.authFailureHandler = authFailureHandler;
  module.handle = handle;
  module.isUsers = isUsers;
  module.isSales = isSales;
  module.isGuest = isGuest;
  module.isHelpdesk = isHelpdesk;
  module.isMonitoring = isMonitoring;
  module.isAdmin = isAdmin;
  module.isSuper = isSuper;
  module.registerAction = registerAction;
  module.registerDefaultAction = registerDefaultAction;
  module.registerRole = registerRole;
  module.authorizeRoles = authorizeRoles;
  module.saveConfig = saveConfig;

  return module;
};
<commit_msg>adjusting the view devices permission<commit_after>module.exports = function (app) {
  "use strict";
  var ConnectRoles = require('connect-roles');

  var defaultRoles = {
    users: {
      name: 'users',
      description: 'default role, no access',
      groups: ['users'],
      users: []
    },
    super: {
      name: 'super',
      description: 'super user role',
      groups: ['super'],
      users: []
    }
  };

  var nconf = require('nconf');
  var fs = require('fs');

  if (app.locals.roles) {
    nconf.defaults({
      'roles': app.locals.roles
    });
    nconf.set('roles', app.locals.roles);
  } else {
    // Check for a config file or the default location.
    var configPath = nconf.get('conf') || app.get('base_config_dir') + '/roleManagerConfig.json';

    if (configPath) {
      nconf.file({file: configPath});
    }

    nconf.defaults({
      'roles': defaultRoles
    });
  }

  var roles = nconf.get('roles');

  var registerDefaultAction = function (module, action, description, defaultRoles) {
    var roles = defaultRoles || ['super'];
    app.locals.logger.log('info', 'registered action: "' + action + '" for module "' + module + '" allowing ' + JSON.stringify(roles) + ' to access: "' + description + '"', {module: module, action: action, roles: roles, description: description});
    registerAction(module, action, description);
    authorizeRoles(action, roles);
  };

  var registerAction = function (module, action, description) {
    app.locals.logger.log('info', 'registered action: "' + action + '" for module "' + module, {module: module, action: action});
    nconf.set('actions:' + action, {module: module, action: action, description: description});
    //saveConfig(function() {});
  };

  var registerRole = function (name, description, groups, users) {
    if (arguments.length <= 1) {
      description = 'No Description Given: ' + name;
    }
    if (arguments.length <= 2) {
      groups = [];
    }
    if (arguments.length <= 3) {
      users = [];
    }
    nconf.set('roles:' + name, { name: name, description: description, groups: groups, users: users});
    //saveConfig(function () {});
  };

  var authorizeRoles = function (action, roles) {
    nconf.set('authorizations:' + action, roles);
    /*saveConfig(function (err, results) {
     if (err) {
     app.locals.logger.log('error', 'error', { err: JSON.stringify(err)});
     } else {
     app.locals.logger.log('debug', 'saved', {results: JSON.stringify(results)});
     }
     });*/
  };

  var saveConfig = function (done) {
    nconf.save(function (err) {
      if (err) {
        done(err);
      } else {
        try {
          fs.readFile(configPath, function (err, configJSON) {
            if (err) {
              done(err);
            } else {
              var config = JSON.parse(configJSON);
              done(null, config);
            }
          });
        } catch (ex) {
          done(ex);
        }
      }
    });
  };

  function initializeRoles() {
    if (!roles.hasOwnProperty('users')) {
      throw new Error('Required role users is not defined');
    }
    if (!roles.hasOwnProperty('guest')) {
      registerRole('guest', 'Built-In Role for unauthenticated Users');
    }
    if (!roles.hasOwnProperty('sales')) {
      registerRole('sales', 'Provides access to lead and account management functionality. Also provides read-only access to monitoring.');
    }
    if (!roles.hasOwnProperty('helpdesk')) {
      registerRole('helpdesk', 'Provides access to helpdesk functionality. Also provides read-only access to monitoring.');
    }
    if (!roles.hasOwnProperty('monitoring')) {
      registerRole('monitoring', 'Provides additional access to monitoring systems.');
    }
    if (!roles.hasOwnProperty('admin')) {
      registerRole('admin', 'Provides full access to sales, monitoring and helpdesk functionality.');
    }
    if (!roles.hasOwnProperty('super')) {
      registerRole('super', 'Provides access to administrative functions.');
    }
  }

  var authFailureHandler = function (req, res, action) {
    var accept = req.headers.accept || '';
    if (req.currentUser) {
      app.locals.logger.log('debug', 'user not allowed', {action: action, groups: req.currentUser.groups});
    } else {
      app.locals.logger.log('debug', 'user is not authenticated', {action: action, groups: []});
    }
    if (~accept.indexOf('html')) {
      if (req.session) {
        req.session.returnTo = req.originalUrl || req.url;
      }
      res.redirect('/account/login');
    } else {
      res.send(403);
    }
  };

  var roleHandler = new ConnectRoles({
    failureHandler: authFailureHandler
  });

  function hasRoleGroups(userGroups, role) {
    var _ = require('underscore');
    var roleGroups = role.groups;

    for (var i = 0; i < userGroups.length; i++) {
      var userGroup = userGroups[i];

      app.locals.logger.log('silly', 'hasRoleGroups: checking userGroup', { roleGroups: roleGroups, userGroup: userGroup});

      if (_.contains(roleGroups, userGroup)) {
        app.locals.logger.log('silly', 'hasRoleGroups: returning true', {roleName: role.name, userGroups: userGroups, roleGroups: roleGroups});
        return true;
      }
    }
    app.locals.logger.log('silly', 'hasRoleGroups: returning false', {roleName: role.name, userGroups: userGroups, roleGroups: roleGroups});
    return false;
  }

  var hasAccess = function (user, role) {
    var _ = require('underscore');
    app.locals.logger.log('silly', 'hasAccess: checking user', { role: role.name, user: user.username, roleUsers: role.users});
    if (_.contains(role.users, user.username)) {
      return true;
    } else {
      return hasRoleGroups(user.groups, role);
    }
  };

  /* Placeholder for When I make this Async */
  var cachedUserRoles = function (user, requiredRoles, join) {
    return authorizeUserRoles(user, requiredRoles, join);
  };

  var authorizeUserRoles = function (user, requiredRoles, join) {
    var accessGranted = false;
    app.locals.logger.log('silly', 'authorizeUserRoles: checking user', {user: user, requiredRoles: requiredRoles});
    for (var i = 0; i < requiredRoles.length; i++) {
      var roleName = requiredRoles[i];
      if (roles.hasOwnProperty(roleName)) {
        var role = roles[roleName];
        var roleGranted = hasAccess(user, role);
        if (roleGranted === true && join === 'OR') {
          app.locals.logger.log('silly', 'returning true role granted with join OR', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          return true;
        } else if (roleGranted === true && join === 'AND') {
          app.locals.logger.log('silly', 'returning true, role granted', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          accessGranted = true;
        } else if (roleGranted === false && join === 'AND') {
          app.locals.logger.log('silly', 'returning false role not granted with join AND', {roleName: roleName, username: user.username, roleGroups: role.groups, roleUsers: role.users});
          return false;
        }
      } else {
        app.locals.logger.log('silly', 'returning false (no property)', {roleName: roleName});
        return false;
      }
    }

    return accessGranted;
  };

  var hasRequiredRoles = function (user, requiredRoles, join) {
    app.locals.logger.log('debug', 'Authorization Request', {requiredRoles: requiredRoles, username: user});

    if (arguments.length <= 1) {
      requiredRoles = ['users'];
    }

    if (arguments.length <= 2) {
      join = 'AND';
    }

    if (join !== 'AND' && join !== 'OR') {
      throw new Error('Invalid Join type ' + join);
    }

    var accessGranted = false;
    var _ = require('underscore');
    var message = '';

    if (user) {
      accessGranted = cachedUserRoles(user, requiredRoles, join);
      if (accessGranted === true) {
        message = 'Authorization Granted';
      //} else if (cachedUserRoles(user, 'super'), join) {
      //  accessGranted = true;
      //  message = 'Granted with SUPER role';
      } else {
        message = 'Authorization Denied'
      }
      app.locals.logger.log('audit', message, {requiredRoles: requiredRoles, username: user.username, userGroups: user.groups, join: join, accessGranted: accessGranted});
    } else if (_.contains(requiredRoles, 'guest')) {
      accessGranted = true;
      app.locals.logger.log('audit', 'Authorization Granted', {requiredRoles: requiredRoles, accessGranted: accessGranted});
    } else {
      accessGranted = false;
      app.locals.logger.log('audit', 'Authorization Denied', {requiredRoles: requiredRoles, accessGranted: accessGranted});
    }

    return accessGranted;
  };

  var getCachedRoles = function (user, getCachedRolesCallback) {
    /*
     memoryCache.wrap('roleManager::getCachedRoles::' + user.username, function (cb) {
     getRoles(user, cb);
     }, function (err, userRoles) {
     getCachedRolesCallback(err, userRoles);
     });
     */
    getRoles(user, getCachedRolesCallback);
  };

  var getRoles = function (user, getRolesCallback) {
    var userRoles = [];
    for (var roleName in roles) {
      if (roles.hasOwnProperty(roleName)) {
        var role = roles[roleName];
        if (hasAccess(user, role)) {
          if (role.hasOwnProperty('name')) {
            userRoles.push(role.name);
          }
        }
      }
    }
    getRolesCallback(null, userRoles);
  };

  var handle = function (req, res, next) {
    if (req.hasOwnProperty('currentUser')) {
      if (process.env.NODE_ENV === 'test') {
        var _ = require('underscore');
        req.currentUser.roles = _.keys(roles);
        next();
      } else {
        getCachedRoles(req.currentUser, function (err, userRoles) {
          app.locals.logger.log('silly', 'roles added to user', {username: req.currentUser.username, userRoles: userRoles});
          req.currentUser.roles = userRoles;
          next();
        });
      }
    } else {
      app.locals.logger.log('silly', 'no roles added, no user');
      next();
    }
  };

  var isUsers = function (req) {
    return hasRequiredRoles(req.currentUser, ['users']);
  };

  var isSales = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'sales']);
  };

  var isHelpdesk = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'helpdesk']);
  };

  var isMonitoring = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'monitoring']);
  };

  var isAdmin = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'admin']);
  };

  var isSuper = function (req) {
    return hasRequiredRoles(req.currentUser, ['users', 'super']);
  };

  var isGuest = function (req) {
    if (req.hasOwnProperty('currentUser')) {
      return hasRequiredRoles(req.currentUser, ['guest']);
    } else {
      return hasRequiredRoles(null, ['guest']);
    }

  };

  registerDefaultAction('internal', 'user', 'Minimal Authenticated Access Level', ['users']);
  registerDefaultAction('internal', 'guest', 'Minimal Access Level', ['guests']);

  registerDefaultAction('internal', 'use api', 'Minimal API Access Level', ['users']);
  registerDefaultAction('internal', 'view devices', 'Ability to view customer equipment information', ['helpdesk']);

  roleHandler.use('guest', function (req) {
    return isGuest(req);
  });

  roleHandler.use('user', function (req) {
    return isUsers(req);
  });

  roleHandler.use('use api', function (req) {
    return isUsers(req);
  });

  roleHandler.use('view pipeline', function (req) {
    return isSales(req) || isAdmin(req) || isSuper(req);
  });

  roleHandler.use('view accounts', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view tickets', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view monitoring', function (req) {
    return isSales(req) || isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('deactivate customers', isSuper);
  roleHandler.use('decommission device', function (req) {
    return isAdmin(req) || isSuper(req);
  });
  roleHandler.use('issue credit', isSuper);

  roleHandler.use('view pipeline detail', function (req) {
    return isSales(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view monitoring events', function (req) {
    return isSales(req) || isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view devices', function (req) {
    return isSales(req) || isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk tickets', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk devices', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk device detail', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk clients', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('view helpdesk', isUsers);
  roleHandler.use('view helpdesk client detail', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('silence monitoring events', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });
  roleHandler.use('delete monitoring events', function (req) {
    return isHelpdesk(req) || isAdmin(req) || isSuper(req);
  });

  initializeRoles();

  //module.use = roleHandler.use;
  //module.can = roleHandler.can;
  //module.is = roleHandler.is;
  //module.userCan = roleHandler.userCan;
  //module.userIs = roleHandler.userIs;
  //module.test = roleHandler.test;
  //module.functionList = roleHandler.functionList;
  //module.failureHandler = roleHandler.failureHandler;
  module.roleHandler = roleHandler;
  module.authorizeUserRoles = authorizeUserRoles;
  module.hasRequiredRoles = hasRequiredRoles;
  module.authFailureHandler = authFailureHandler;
  module.handle = handle;
  module.isUsers = isUsers;
  module.isSales = isSales;
  module.isGuest = isGuest;
  module.isHelpdesk = isHelpdesk;
  module.isMonitoring = isMonitoring;
  module.isAdmin = isAdmin;
  module.isSuper = isSuper;
  module.registerAction = registerAction;
  module.registerDefaultAction = registerDefaultAction;
  module.registerRole = registerRole;
  module.authorizeRoles = authorizeRoles;
  module.saveConfig = saveConfig;

  return module;
};
<|endoftext|>


<commit_before>
module.exports = readJson
readJson.processJson = processJson

var fs = require("fs")
  , semver = require("./semver")
  , log = require("../utils/log")

function readJson (jsonFile, tag, cb) {
  log(jsonFile, "readJson")
  fs.readFile(jsonFile, processJson(tag, cb))
}
function processJson (tag, cb) {
  if (!cb) cb = tag, tag = null
  if (typeof cb !== "function") {
    var thing = cb, cb = null
    return P(null, thing)
  } else return P

  function P (er, thing) {
    if (er) {
      if (cb) return cb(er, thing)
      throw er
    }
    if (Object.prototype.toString.call(thing) === "[object Object]") {
      return processObject(tag, cb)(er, thing)
    } else {
      return processJsonString(tag, cb)(er, thing)
    }
  }
}
function processJsonString (tag, cb) { return function (er, jsonString) {
  jsonString += ""
  if (er) return cb(er, jsonString)
  var json
  try {
    json = JSON.parse(jsonString)
  } catch (ex) {
    var e = new Error(
      "Failed to parse json\n"+ex.message+"\n"+jsonString)
    if (cb) return cb(e)
    throw e
  }
  return processObject(tag, cb)(er, json)
}}
function processObject (tag, cb) { return function (er, json) {
  if (json.overlay) {
    ;["node", "npm"].forEach(function (k) {
      if (!json.overlay[k]) return undefined
      for (var i in json.overlay[k]) json[i] = json.overlay[k][i]
    })
  }

  // slashes would be a security risk.
  // anything else will just fail harmlessly.
  if (!json.name) {
    var e = new Error("No 'name' field found in package.json")
    if (cb) return cb(e)
    throw e
  }
  json.name = json.name.replace(/\//g, '-')
  if (tag) json.version += tag
  if (!(semver.valid(json.version))) {
    var e = new Error("Invalid version: "+json.version)
    if (cb) return cb(e)
    throw e
  }

  if (json.main) json.main = json.main.replace(/(\.js|\.node)$/, '')

  json._id = json.name+"-"+json.version
  json = testEngine(json)
  if (cb) cb(null,json)
  return json
}}

function testEngine (json) {
  if (!json.engines) json.engines = { "node" : "*" }

  var nodeVer = process.version
    , ok = false
  if (Array.isArray(json.engines)) {
    // Packages/1.0 commonjs style, with an array.
    // hack it to just hang a "node" member with the version range,
    // then do the npm-style check below.
    for (var i = 0, l = json.engines.length; i < l; i ++) {
      var e = json.engines[i].trim()
      if (e.substr(0, 4) === "node") {
        json.engines.node = e.substr(4)
        break
      }
    }
  }
  json._nodeSupported = semver.satisfies(nodeVer, json.engines.node || "undefined")
  return json
}
<commit_msg>Handle node versions ending with +. fix #96<commit_after>
module.exports = readJson
readJson.processJson = processJson

var fs = require("fs")
  , semver = require("./semver")
  , log = require("../utils/log")

function readJson (jsonFile, tag, cb) {
  log(jsonFile, "readJson")
  fs.readFile(jsonFile, processJson(tag, cb))
}
function processJson (tag, cb) {
  if (!cb) cb = tag, tag = null
  if (typeof cb !== "function") {
    var thing = cb, cb = null
    return P(null, thing)
  } else return P

  function P (er, thing) {
    if (er) {
      if (cb) return cb(er, thing)
      throw er
    }
    if (Object.prototype.toString.call(thing) === "[object Object]") {
      return processObject(tag, cb)(er, thing)
    } else {
      return processJsonString(tag, cb)(er, thing)
    }
  }
}
function processJsonString (tag, cb) { return function (er, jsonString) {
  jsonString += ""
  if (er) return cb(er, jsonString)
  var json
  try {
    json = JSON.parse(jsonString)
  } catch (ex) {
    var e = new Error(
      "Failed to parse json\n"+ex.message+"\n"+jsonString)
    if (cb) return cb(e)
    throw e
  }
  return processObject(tag, cb)(er, json)
}}
function processObject (tag, cb) { return function (er, json) {
  if (json.overlay) {
    ;["node", "npm"].forEach(function (k) {
      if (!json.overlay[k]) return undefined
      for (var i in json.overlay[k]) json[i] = json.overlay[k][i]
    })
  }

  // slashes would be a security risk.
  // anything else will just fail harmlessly.
  if (!json.name) {
    var e = new Error("No 'name' field found in package.json")
    if (cb) return cb(e)
    throw e
  }
  json.name = json.name.replace(/\//g, '-')
  if (tag) json.version += tag
  if (!(semver.valid(json.version))) {
    var e = new Error("Invalid version: "+json.version)
    if (cb) return cb(e)
    throw e
  }

  if (json.main) json.main = json.main.replace(/(\.js|\.node)$/, '')

  json._id = json.name+"-"+json.version
  json = testEngine(json)
  if (cb) cb(null,json)
  return json
}}

function testEngine (json) {
  if (!json.engines) json.engines = { "node" : "*" }

  var nodeVer = process.version.replace(/\+$/, '')
    , ok = false
  if (Array.isArray(json.engines)) {
    // Packages/1.0 commonjs style, with an array.
    // hack it to just hang a "node" member with the version range,
    // then do the npm-style check below.
    for (var i = 0, l = json.engines.length; i < l; i ++) {
      var e = json.engines[i].trim()
      if (e.substr(0, 4) === "node") {
        json.engines.node = e.substr(4)
        break
      }
    }
  }
  json._nodeSupported = semver.satisfies(nodeVer, json.engines.node || "undefined")
  return json
}
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import classnames from 'classnames'
import {Scrollbars} from 'react-custom-scrollbars'

class FancyScrollbar extends Component {
  constructor(props) {
    super(props)
  }

  static defaultProps = {
    autoHide: true,
    autoHeight: false,
  }

  render() {
    const {autoHide, autoHeight, children, className, maxHeight} = this.props

    return (
      <Scrollbars
        className={classnames('fancy-scroll--container', {
          [className]: className,
        })}
        autoHide={autoHide}
        autoHideTimeout={1000}
        autoHideDuration={250}
        autoHeight={autoHeight}
        autoHeightMax={maxHeight}
        renderTrackHorizontal={props =>
          <div {...props} className="fancy-scroll--track-h" />}
        renderTrackVertical={props =>
          <div {...props} className="fancy-scroll--track-v" />}
        renderThumbHorizontal={props =>
          <div {...props} className="fancy-scroll--thumb-h" />}
        renderThumbVertical={props =>
          <div {...props} className="fancy-scroll--thumb-v" />}
      >
        {children}
      </Scrollbars>
    )
  }
}

const {bool, node, number, string} = PropTypes

FancyScrollbar.propTypes = {
  children: node.isRequired,
  className: string,
  autoHide: bool,
  autoHeight: bool,
  maxHeight: number,
}

export default FancyScrollbar
<commit_msg>Undoing undo<commit_after>import React, {Component, PropTypes} from 'react'
import classnames from 'classnames'
import {Scrollbars} from 'react-custom-scrollbars'

class FancyScrollbar extends Component {
  constructor(props) {
    super(props)
  }

  static defaultProps = {
    autoHide: true,
    autoHeight: false,
  }

  render() {
    const {autoHide, autoHeight, children, className, maxHeight} = this.props

    return (
      <Scrollbars
        className={classnames('fancy-scroll--container', {
          [className]: className,
        })}
        autoHide={autoHide}
        autoHideTimeout={1000}
        autoHideDuration={250}
        autoHeight={autoHeight}
        autoHeightMax={maxHeight}
        renderTrackHorizontal={props =>
          <div {...props} className="fancy-scroll--track-h" />}
        renderTrackVertical={props =>
          <div {...props} className="fancy-scroll--track-v" />}
        renderThumbHorizontal={props =>
          <div {...props} className="fancy-scroll--thumb-h" />}
        renderThumbVertical={props =>
          <div {...props} className="fancy-scroll--thumb-v" />}
        renderView={props => <div {...props} className="fancy-scroll--view" />}
      >
        {children}
      </Scrollbars>
    )
  }
}

const {bool, node, number, string} = PropTypes

FancyScrollbar.propTypes = {
  children: node.isRequired,
  className: string,
  autoHide: bool,
  autoHeight: bool,
  maxHeight: number,
}

export default FancyScrollbar
<|endoftext|>


<commit_before>console.log(
    '%c lefoy.net - Have fun hacking!   |   Tip: try the Konami code ',
    'background: #000; color: #fff; padding: 10px 3px; line-height: 70px;'
);

$(window).konami({
    cheat: function() {
        $('body').append('

');

        var terminal = $('body').find('#terminal');

        $('#terminal').terminal(function(command, term) {
            switch (command) {
                case 'help':
                    term.echo(['',
                        '    The most commonly used commands are:',
                        '        github         Open the website Github repository',
                        '        summary        Display information about the website',
                        '        contact        Send email to website author',
                        ''
                    ].join('\n'));
                    break;
                case 'github':
                    term.echo('github command...\n');
                    break;
                case 'summary':
                    term.echo('summary command...\n');
                    break;
                case 'contact':
                    term.echo('contact command...\n');
                    break;
                case 'exit':
                    $('#terminal').remove();
                    break;
                default:
                    term.echo('command not found: ' + command + '\n');
            }
        }, {
            greetings: '',
            name: 'terminal',
            prompt: '> ',
            exit: false
        });

        terminal.css({
            position: 'fixed',
            top: '0',
            left: '0',
            width: '100%',
            height: '100%',
            color: '#fff',
            zIndex: '1000000',
            backgroundColor: 'rgba(0, 0, 0, 0.9)'
        });
    }
});

jQuery(document).ready(function($) {

    layout.init();

});
<commit_msg>Terminal tests<commit_after>console.log(
    '%c lefoy.net - Have fun hacking!   |   Tip: try the Konami code ',
    'background: #000; color: #fff; padding: 10px 3px; line-height: 70px;'
);

function terminal() {
    $('body').append('

');

    var terminal = $('body').find('#terminal');

    $('#terminal').terminal(function(command, term) {
        switch (command) {
            case 'help':
                term.echo(['',
                    '    The most commonly used commands are:',
                    '        github         Open the website Github repository',
                    '        summary        Display information about the website',
                    '        contact        Send email to website author',
                    ''
                ].join('\n'));
                break;
            case 'github':
                term.echo('github command...\n');
                break;
            case 'summary':
                term.echo('summary command...\n');
                break;
            case 'contact':
                term.echo('contact command...\n');
                break;
            case 'exit':
                $('#terminal').remove();
                break;
            default:
                term.echo('command not found: ' + command + '\n');
        }
    }, {
        greetings: '',
        name: 'terminal',
        prompt: '> ',
        exit: false
    });

    terminal.css({
        position: 'fixed',
        top: '0',
        left: '0',
        width: '100%',
        height: '100%',
        color: '#fff',
        zIndex: '1000000',
        backgroundColor: 'rgba(0, 0, 0, 0.9)'
    });
}

jQuery(document).ready(function($) {

    layout.init();

});
<|endoftext|>


<commit_before>var Hurricane = (function() {
    Hurricane = function() {
        this.cursor = null;
        this.backoff = 500;
        this.url = '/comet/';
        this.ajax_request();
        this.callbacks = {};
    };

    Hurricane.prototype = {
        on_success: function(response) {
            var messages = JSON.parse(response).messages;
            this.cursor = messages[messages.length-1].uuid;
            for (var msg_idx in messages) {
                var msg = messages[msg_idx];
                var callback = this.callbacks[msg.kind];
                if(callback) {
                    callback(msg);
                }
            }
            this.backoff = 500;
            this.ajax_request();
        },

        on_error: function(response) {
            var hurricane = this;
            setTimeout(function() {
                hurricane.ajax_request();
            }, hurricane.backoff);
            this.backoff *= 2;
        },

        ajax_request: function() {
            var args = $.param({'cursor': this.cursor});
            var hurricane = this;
            $.ajax({
                url: this.url,
                type: 'GET',
                dataType: 'text',
                data: args,
                success: function(response) {
                    hurricane.on_success(response);
                },
                error: function(response) {
                    hurricane.on_error(response);
                }
            });
        },

        add_callback: function(kind, callback) {
            this.callbacks[kind] = callback;
        }
    };

    return new Hurricane();
})();<commit_msg>Respect a BEGINNING_CURSOR variable if it exists<commit_after>var Hurricane = (function() {
    Hurricane = function() {
        this.cursor = BEGINNING_CURSOR || null;
        this.backoff = 500;
        this.url = '/comet/';
        this.ajax_request();
        this.callbacks = {};
    };

    Hurricane.prototype = {
        on_success: function(response) {
            var messages = JSON.parse(response).messages;
            this.cursor = messages[messages.length-1].uuid;
            for (var msg_idx in messages) {
                var msg = messages[msg_idx];
                var callback = this.callbacks[msg.kind];
                if(callback) {
                    callback(msg);
                }
            }
            this.backoff = 500;
            this.ajax_request();
        },

        on_error: function(response) {
            var hurricane = this;
            setTimeout(function() {
                hurricane.ajax_request();
            }, hurricane.backoff);
            this.backoff *= 2;
        },

        ajax_request: function() {
            var args = $.param({'cursor': this.cursor});
            var hurricane = this;
            $.ajax({
                url: this.url,
                type: 'GET',
                dataType: 'text',
                data: args,
                success: function(response) {
                    hurricane.on_success(response);
                },
                error: function(response) {
                    hurricane.on_error(response);
                }
            });
        },

        add_callback: function(kind, callback) {
            this.callbacks[kind] = callback;
        }
    };

    return new Hurricane();
})();<|endoftext|>


<commit_before>Grocy.Components.ProductPicker = { };

Grocy.Components.ProductPicker.GetPicker = function()
{
	return $('#product_id');
}

Grocy.Components.ProductPicker.GetInputElement = function()
{
	return $('#product_id_text_input');
}

Grocy.Components.ProductPicker.GetValue = function()
{
	return $('#product_id').val();
}

Grocy.Components.ProductPicker.SetValue = function(value)
{
	Grocy.Components.ProductPicker.GetInputElement().val(value);
	Grocy.Components.ProductPicker.GetInputElement().trigger('change');
}

Grocy.Components.ProductPicker.SetId = function(value)
{
	Grocy.Components.ProductPicker.GetPicker().val(value);
	Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
	Grocy.Components.ProductPicker.GetInputElement().trigger('change');
}

Grocy.Components.ProductPicker.Clear = function()
{
	Grocy.Components.ProductPicker.SetValue('');
	Grocy.Components.ProductPicker.SetId(null);
}

Grocy.Components.ProductPicker.InProductAddWorkflow = function()
{
	return typeof GetUriParam('createdproduct') !== "undefined" || typeof GetUriParam('product') !== "undefined";
}

Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
{
	return typeof GetUriParam('addbarcodetoselection') !== "undefined";
}

Grocy.Components.ProductPicker.ShowCustomError = function(text)
{
	var element = $("#custom-productpicker-error");
	element.text(text);
	element.removeClass("d-none");
}

Grocy.Components.ProductPicker.HideCustomError = function()
{
	$("#custom-productpicker-error").addClass("d-none");
}

Grocy.Components.ProductPicker.Disable = function()
{
	Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
	$("#barcodescanner-start-button").attr("disabled", "");
	$("#barcodescanner-start-button").addClass("disabled");
}

Grocy.Components.ProductPicker.Enable = function()
{
	Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled");
	$("#barcodescanner-start-button").removeAttr("disabled");
	$("#barcodescanner-start-button").removeClass("disabled");
}

$('.product-combobox').combobox({
	appendId: '_text_input',
	bsVersion: '4',
	clearIfNoMatch: false
});

var prefillProduct = GetUriParam('createdproduct');
var prefillProduct2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-name').toString();
if (!prefillProduct2.isEmpty())
{
	prefillProduct = prefillProduct2;
}
if (typeof prefillProduct !== "undefined")
{
	var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
	if (possibleOptionElement.length === 0)
	{
		possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first();
	}

	if (possibleOptionElement.length > 0)
	{
		$('#product_id').val(possibleOptionElement.val());
		$('#product_id').data('combobox').refresh();
		$('#product_id').trigger('change');

		var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
		nextInputElement.focus();
	}
}

var prefillProductId = GetUriParam("product");
var prefillProductId2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-id').toString();
if (!prefillProductId2.isEmpty())
{
	prefillProductId = prefillProductId2;
}
if (typeof prefillProductId !== "undefined")
{
	$('#product_id').val(prefillProductId);
	$('#product_id').data('combobox').refresh();
	$('#product_id').trigger('change');

	var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
	nextInputElement.focus();
}

var addBarcode = GetUriParam('addbarcodetoselection');
if (addBarcode !== undefined)
{
	$('#addbarcodetoselection').text(addBarcode);
	$('#flow-info-addbarcodetoselection').removeClass('d-none');
	$('#barcode-lookup-disabled-hint').removeClass('d-none');
}

Grocy.Components.ProductPicker.PopupOpen = false;
$('#product_id_text_input').on('blur', function(e)
{
	if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
	{
		return;
	}

	var input = $('#product_id_text_input').val().toString();
	var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();

	if (GetUriParam('addbarcodetoselection') === undefined && possibleOptionElement.length > 0)
	{
		$('#product_id').val(possibleOptionElement.val());
		$('#product_id').data('combobox').refresh();
		$('#product_id').trigger('change');
	}
	else
	{
		if (Grocy.Components.ProductPicker.PopupOpen === true)
		{
			return;
		}

		var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
		if (input.length > 0 && optionElement.length === 0 && typeof GetUriParam('addbarcodetoselection') === "undefined" && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
		{
			var addProductWorkflowsAdditionalCssClasses = "";
			if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
			{
				addProductWorkflowsAdditionalCssClasses = "d-none";
			}

			Grocy.Components.ProductPicker.PopupOpen = true;
			bootbox.dialog({
				message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input),
				title: __t('Create or assign product'),
				onEscape: function()
				{
					Grocy.Components.ProductPicker.PopupOpen = false;
					Grocy.Components.ProductPicker.SetValue('');
				},
				size: 'large',
				backdrop: true,
				buttons: {
					cancel: {
						label: __t('Cancel'),
						className: 'btn-secondary responsive-button',
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							Grocy.Components.ProductPicker.SetValue('');
						}
					},
					addnewproduct: {
						label: 'P ' + __t('Add as new product'),
						className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative));
						}
					},
					addbarcode: {
						label: 'B ' + __t('Add as barcode to existing product'),
						className: 'btn-info add-new-barcode-dialog-button responsive-button',
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U(Grocy.CurrentUrlRelative + '?addbarcodetoselection=' + encodeURIComponent(input));
						}
					},
					addnewproductwithbarcode: {
						label: 'A ' + __t('Add as new product and prefill barcode'),
						className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative));
						}
					}
				}
			}).on('keypress', function(e)
			{
				if (e.key === 'B' || e.key === 'b')
				{
					$('.add-new-barcode-dialog-button').not(".d-none").click();
				}
				if (e.key === 'p' || e.key === 'P')
				{
					$('.add-new-product-dialog-button').not(".d-none").click();
				}
				if (e.key === 'a' || e.key === 'A')
				{
					$('.add-new-product-with-barcode-dialog-button').not(".d-none").click();
				}
			});
		}
	}
});

$(document).on("Grocy.BarcodeScanned", function(e, barcode)
{
	// Don't know why the blur event does not fire immediately ... this works...

	Grocy.Components.ProductPicker.GetInputElement().focusout();
	Grocy.Components.ProductPicker.GetInputElement().focus();
	Grocy.Components.ProductPicker.GetInputElement().blur();

	Grocy.Components.ProductPicker.GetInputElement().val(barcode);

	setTimeout(function()
	{
		Grocy.Components.ProductPicker.GetInputElement().focusout();
		Grocy.Components.ProductPicker.GetInputElement().focus();
		Grocy.Components.ProductPicker.GetInputElement().blur();
	}, 200);
});
<commit_msg>Improved the responsiveness of the "Create or assign product" dialog<commit_after>Grocy.Components.ProductPicker = { };

Grocy.Components.ProductPicker.GetPicker = function()
{
	return $('#product_id');
}

Grocy.Components.ProductPicker.GetInputElement = function()
{
	return $('#product_id_text_input');
}

Grocy.Components.ProductPicker.GetValue = function()
{
	return $('#product_id').val();
}

Grocy.Components.ProductPicker.SetValue = function(value)
{
	Grocy.Components.ProductPicker.GetInputElement().val(value);
	Grocy.Components.ProductPicker.GetInputElement().trigger('change');
}

Grocy.Components.ProductPicker.SetId = function(value)
{
	Grocy.Components.ProductPicker.GetPicker().val(value);
	Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
	Grocy.Components.ProductPicker.GetInputElement().trigger('change');
}

Grocy.Components.ProductPicker.Clear = function()
{
	Grocy.Components.ProductPicker.SetValue('');
	Grocy.Components.ProductPicker.SetId(null);
}

Grocy.Components.ProductPicker.InProductAddWorkflow = function()
{
	return typeof GetUriParam('createdproduct') !== "undefined" || typeof GetUriParam('product') !== "undefined";
}

Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
{
	return typeof GetUriParam('addbarcodetoselection') !== "undefined";
}

Grocy.Components.ProductPicker.ShowCustomError = function(text)
{
	var element = $("#custom-productpicker-error");
	element.text(text);
	element.removeClass("d-none");
}

Grocy.Components.ProductPicker.HideCustomError = function()
{
	$("#custom-productpicker-error").addClass("d-none");
}

Grocy.Components.ProductPicker.Disable = function()
{
	Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
	$("#barcodescanner-start-button").attr("disabled", "");
	$("#barcodescanner-start-button").addClass("disabled");
}

Grocy.Components.ProductPicker.Enable = function()
{
	Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled");
	$("#barcodescanner-start-button").removeAttr("disabled");
	$("#barcodescanner-start-button").removeClass("disabled");
}

$('.product-combobox').combobox({
	appendId: '_text_input',
	bsVersion: '4',
	clearIfNoMatch: false
});

var prefillProduct = GetUriParam('createdproduct');
var prefillProduct2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-name').toString();
if (!prefillProduct2.isEmpty())
{
	prefillProduct = prefillProduct2;
}
if (typeof prefillProduct !== "undefined")
{
	var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
	if (possibleOptionElement.length === 0)
	{
		possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first();
	}

	if (possibleOptionElement.length > 0)
	{
		$('#product_id').val(possibleOptionElement.val());
		$('#product_id').data('combobox').refresh();
		$('#product_id').trigger('change');

		var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
		nextInputElement.focus();
	}
}

var prefillProductId = GetUriParam("product");
var prefillProductId2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-id').toString();
if (!prefillProductId2.isEmpty())
{
	prefillProductId = prefillProductId2;
}
if (typeof prefillProductId !== "undefined")
{
	$('#product_id').val(prefillProductId);
	$('#product_id').data('combobox').refresh();
	$('#product_id').trigger('change');

	var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
	nextInputElement.focus();
}

var addBarcode = GetUriParam('addbarcodetoselection');
if (addBarcode !== undefined)
{
	$('#addbarcodetoselection').text(addBarcode);
	$('#flow-info-addbarcodetoselection').removeClass('d-none');
	$('#barcode-lookup-disabled-hint').removeClass('d-none');
}

Grocy.Components.ProductPicker.PopupOpen = false;
$('#product_id_text_input').on('blur', function(e)
{
	if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
	{
		return;
	}

	var input = $('#product_id_text_input').val().toString();
	var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();

	if (GetUriParam('addbarcodetoselection') === undefined && possibleOptionElement.length > 0)
	{
		$('#product_id').val(possibleOptionElement.val());
		$('#product_id').data('combobox').refresh();
		$('#product_id').trigger('change');
	}
	else
	{
		if (Grocy.Components.ProductPicker.PopupOpen === true)
		{
			return;
		}

		var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
		if (input.length > 0 && optionElement.length === 0 && typeof GetUriParam('addbarcodetoselection') === "undefined" && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
		{
			var addProductWorkflowsAdditionalCssClasses = "";
			if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
			{
				addProductWorkflowsAdditionalCssClasses = "d-none";
			}

			Grocy.Components.ProductPicker.PopupOpen = true;
			bootbox.dialog({
				message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input),
				title: __t('Create or assign product'),
				onEscape: function()
				{
					Grocy.Components.ProductPicker.PopupOpen = false;
					Grocy.Components.ProductPicker.SetValue('');
				},
				size: 'large',
				backdrop: true,
				buttons: {
					cancel: {
						label: __t('Cancel'),
						className: 'btn-secondary responsive-button',
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							Grocy.Components.ProductPicker.SetValue('');
						}
					},
					addnewproduct: {
						label: 'P ' + __t('Add as new product'),
						className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative));
						}
					},
					addbarcode: {
						label: 'B ' + __t('Add as barcode to existing product'),
						className: 'btn-info add-new-barcode-dialog-button responsive-button',
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U(Grocy.CurrentUrlRelative + '?addbarcodetoselection=' + encodeURIComponent(input));
						}
					},
					addnewproductwithbarcode: {
						label: 'A ' + __t('Add as new product and prefill barcode'),
						className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
						callback: function()
						{
							Grocy.Components.ProductPicker.PopupOpen = false;
							window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative));
						}
					}
				}
			}).on('keypress', function(e)
			{
				if (e.key === 'B' || e.key === 'b')
				{
					$('.add-new-barcode-dialog-button').not(".d-none").click();
				}
				if (e.key === 'p' || e.key === 'P')
				{
					$('.add-new-product-dialog-button').not(".d-none").click();
				}
				if (e.key === 'a' || e.key === 'A')
				{
					$('.add-new-product-with-barcode-dialog-button').not(".d-none").click();
				}
			});
		}
	}
});

$(document).on("Grocy.BarcodeScanned", function(e, barcode)
{
	// Don't know why the blur event does not fire immediately ... this works...

	Grocy.Components.ProductPicker.GetInputElement().focusout();
	Grocy.Components.ProductPicker.GetInputElement().focus();
	Grocy.Components.ProductPicker.GetInputElement().blur();

	Grocy.Components.ProductPicker.GetInputElement().val(barcode);

	setTimeout(function()
	{
		Grocy.Components.ProductPicker.GetInputElement().focusout();
		Grocy.Components.ProductPicker.GetInputElement().focus();
		Grocy.Components.ProductPicker.GetInputElement().blur();
	}, 200);
});

$(document).on("shown.bs.modal", function(e)
{
	$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
	$(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0");
})
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import OnClickOutside from 'react-onclickoutside'
import CustomTimeIndicator from 'shared/components/CustomTimeIndicator'

const LayoutCellMenu = OnClickOutside(
  ({
    cell,
    onEdit,
    queries,
    onDelete,
    isEditable,
    dataExists,
    isDeleting,
    onDeleteClick,
    onCSVDownload,
  }) =>

        {queries && queries.length && <CustomTimeIndicator queries={queries} />}

      {isEditable &&

          {dataExists &&

}
          {isDeleting
            ?

                  Confirm

            :

}

}

)

const {arrayOf, bool, func, shape} = PropTypes

LayoutCellMenu.propTypes = {
  isDeleting: bool,
  onEdit: func,
  onDelete: func,
  onDeleteClick: func,
  cell: shape(),
  isEditable: bool,
  dataExists: bool,
  queries: arrayOf(shape()),
}

export default LayoutCellMenu
<commit_msg>Fix style name<commit_after>import React, {PropTypes} from 'react'
import OnClickOutside from 'react-onclickoutside'
import CustomTimeIndicator from 'shared/components/CustomTimeIndicator'

const LayoutCellMenu = OnClickOutside(
  ({
    cell,
    onEdit,
    queries,
    onDelete,
    isEditable,
    dataExists,
    isDeleting,
    onDeleteClick,
    onCSVDownload,
  }) =>

        {queries && <CustomTimeIndicator queries={queries} />}

      {isEditable &&

          {dataExists &&

}
          {isDeleting
            ?

                  Confirm

            :

}

}

)

const {arrayOf, bool, func, shape} = PropTypes

LayoutCellMenu.propTypes = {
  isDeleting: bool,
  onEdit: func,
  onDelete: func,
  onDeleteClick: func,
  cell: shape(),
  isEditable: bool,
  dataExists: bool,
  queries: arrayOf(shape()),
}

export default LayoutCellMenu
<|endoftext|>


<commit_before>var urlValue,
  re = /=(.+)/,
  videoID = '',
  tag,
  firstScriptTag,
  player,
  rates,
  currentRate,
  rateIndex,
  popcornVideo,
  subtitles,
  sub,
  subtitleChangeInterval,
  timeStart,
  timeEnd,
  prevSub,
  nextSub,
  editSub,
  videoData,
  currentSubIndex,
  startCaption;

function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    height: '340',
    width: '560',
    videoId: videoID,
    playerVars: {
      'cc_load_policy': 1,
      'controls': 0
    },
    events: {
      'onReady': onPlayerReady,
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerReady(event) {
  rates = player.getAvailablePlaybackRates();
  currentRate = player.getPlaybackRate();
  rateIndex = rates.indexOf(currentRate);
  event.target.playVideo();
  subtitleChangeInterval = setInterval(subtitleRefresh, 100);

  $('.sub-edit').focus(function() {
    player.pauseVideo();
    clearInterval(subtitleChangeInterval);
  });
  $('.sub-edit').focusout(function() {
    player.playVideo();
    subtitleChangeInterval = setInterval(subtitleRefresh, 100);
  });
}

function onPlayerStateChange(event) {
}

function pauseVideo() {
  if (player.getPlayerState() === 1) {
    player.pauseVideo();
  }
  else {
    player.playVideo();
  }
}

function slowdownVideo() {
  if (rateIndex > 0) {
    player.setPlaybackRate(rates[rateIndex-1]);
    rateIndex -= 1;
  }
}

function speedupVideo() {
  if (rateIndex < rates.length -1) {
    player.setPlaybackRate(rates[rateIndex+1]);
    rateIndex += 1;
  }
}

function rewindVideo() {
  player.seekTo((player.getCurrentTime()-2), true);
  player.playVideo();
  console.log(player.getCurrentTime());
}

function forwardVideo() {
  player.seekTo((player.getCurrentTime()+2), true);
  player.playVideo();
  console.log(player.getCurrentTime());
}

Mousetrap.bind('ctrl+1', pauseVideo);
Mousetrap.bind('ctrl+4', slowdownVideo);
Mousetrap.bind('ctrl+5', speedupVideo);
Mousetrap.bind('ctrl+2', rewindVideo);
Mousetrap.bind('ctrl+3', forwardVideo);
Mousetrap.bind('ctrl+shift+s', saveCaption);
Mousetrap.bind('ctrl+shift+q', addCaption);
Mousetrap.bind('ctrl+shift+d', deleteCaption);

function subtitleRefresh() {
  console.log(player.getCurrentTime());
  if (player.getPlayerState() === 1) {
    for (sub in subtitles) {
      timeStart = subtitles[sub].start,
      timeEnd = subtitles[sub].start + subtitles[sub].dur;
      // if (player.getCurrentTime() < subtitles[0].start) {
      //   currentSubIndex = sub;
      //   editSub = subtitles[0];
      //   nextSub = subtitles[1];
      //   $('.sub-edit').val('');
      //   $('.edit-start').val('');
      //   $('.edit-end').val('');
      //   $('.sub-next').val('');
      //   $('.next-start').val('');
      //   $('.next-end').val('');
      //   $('.sub-prev').val('');
      //   $('.prev-start').val('');
      //   $('.prev-end').val('');
      //   return false;
      // }

      if (player.getCurrentTime() >= timeStart && player.getCurrentTime() <= timeEnd) {
        currentSubIndex = sub;
        editSub = subtitles[sub];
        prevSub = subtitles[parseInt(sub)-1];
        nextSub = subtitles[parseInt(sub)+1];
        console.log(currentSubIndex);
        $('#subtitles h3').text(editSub.value);
        $('.sub-edit').val(editSub.value);
        $('.edit-start').val(editSub.start);
        $('.edit-end').val(editSub.start + editSub.dur);
        $('.sub-next').val(nextSub.value);
        $('.next-start').val(nextSub.start);
        $('.next-end').val(nextSub.start + nextSub.dur);
        $('.sub-prev').val(prevSub.value);
        $('.prev-start').val(prevSub.start);
        $('.prev-end').val(prevSub.start + prevSub.dur);
        return false;
      }

      if (sub === subtitles[subtitles.length-1]) {
        console.log(sub);
        $('.sub-edit').val('');
        $('.edit-start').val('');
        $('.edit-end').val('');
        $('.sub-next').val('');
        $('.next-start').val('');
        $('.next-end').val('');
        $('.sub-prev').val(subtitles[subtitles.length-1].value);
        $('.prev-start').val(subtitles[subtitles.length-1].start);
        $('.prev-end').val(subtitles[subtitles.length-1].start + subtitles[subtitles.length-1].dur);
        return false;
      }
    }
  }
}

function deleteCaption() {
  videoData.captions.splice(currentSubIndex, 1);
  $.ajax({
    url: '/video/' + videoID,
    type: 'POST',
    data: JSON.stringify(videoData),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json'
  });
  subtitles = videoData.captions;
  $('.sub-edit').focusout();
  rewindVideo();
  player.playVideo();
  // console.log(videoData);
  // subtitleChangeInterval = setInterval(subtitleRefresh, 100);
}

function addCaption() {
  console.log(videoData);
  if (currentSubIndex) {
    startCaption =
    {
      'start': player.getCurrentTime(),
      'dur': 5.0,
      'value': 'Add caption text and change beginning and ending times',
      'extra_data': []
    };
    videoData.captions.splice(currentSubIndex, 0, startCaption);
    subtitles = videoData.captions;
    editSub = subtitles[currentSubIndex];
    prevSub = subtitles[parseInt(currentSubIndex)-1];
    nextSub = subtitles[parseInt(currentSubIndex)+1];
    $('#subtitles h3').text(editSub.value);
    $('.sub-edit').val(editSub.value);
    $('.edit-start').val(editSub.start);
    $('.edit-end').val(editSub.start + editSub.dur);
    $('.sub-next').val(nextSub.value);
    $('.next-start').val(nextSub.start);
    $('.next-end').val(nextSub.start + nextSub.dur);
    $('.sub-prev').val(prevSub.value);
    $('.prev-start').val(prevSub.start);
    $('.prev-end').val(prevSub.start + prevSub.dur);
    saveCaption();
  }
  else {
    currentSubIndex = 0;
    startCaption =
    {
      'start': player.getCurrentTime(),
      'dur': 5.0,
      'value': 'Add caption text and change beginning and ending times',
      'extra_data': []
    };
    videoData.captions.push(startCaption);
    subtitles = videoData.captions;
    console.log(subtitles);
    editSub = subtitles[currentSubIndex];
    console.log(editSub);
    console.log(editSub.value)
    $('#subtitles h3').text(editSub.value);
    $('.sub-edit').val(editSub.value);
    $('.edit-start').val(editSub.start);
    $('.edit-end').val(editSub.start + editSub.dur);
    saveCaption();
  }
}

function saveCaption() {
  console.log(currentSubIndex);
  videoData.captions[currentSubIndex].value = $('.sub-edit').val();
  videoData.captions[currentSubIndex].start = parseFloat($('.edit-start').val());
  videoData.captions[currentSubIndex].dur = parseFloat($('.edit-end').val()) - parseFloat($('.edit-start').val());
  if (currentSubIndex > 0) {
    videoData.captions[parseInt(currentSubIndex)-1].start = parseFloat($('.prev-start').val());
    videoData.captions[parseInt(currentSubIndex)-1].dur = parseFloat($('.prev-end').val()) - parseFloat($('.prev-start').val());
  }
  if (currentSubIndex < videoData.captions.length-1) {
    videoData.captions[parseInt(currentSubIndex)+1].start = parseFloat($('.next-start').val());
    videoData.captions[parseInt(currentSubIndex)+1].dur = parseFloat($('.next-end').val()) - parseFloat($('.next-start').val());
  }
  subtitles = videoData.captions;
  if (!(videoData.captions[currentSubIndex].dur)) {
    console.log("No duration")
    return false;
  }
  $.ajax({
    url: '/video/' + videoID,
    type: 'POST',
    data: JSON.stringify(videoData),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json'
  });
  $('.sub-edit').focusout();
  rewindVideo();
  player.playVideo();
  // console.log(videoData);
  subtitleChangeInterval = setInterval(subtitleRefresh, 100);
}

$(document).ready(function() {

  $('#the-button').click(function() {
    urlValue = $('#link-value').val();
    tag = document.createElement('script');
    firstScriptTag = document.getElementsByTagName('script')[0];

    if (urlValue.length > 11) {
      videoID = urlValue.match(re)[1].trim();
    }
    else {
      videoID = urlValue.trim();
    }
    $.getJSON('/video/youtube' + videoID, function(data) {
      videoData = data;
      subtitles = videoData.captions;
      $('.search-bar').hide();
      $('.main-button').hide();
      $('.shortcuts').fadeIn();
      $('.video-main').show();
      $('#player').show();
      $('#overlay').show();
      $('.sub-prev').show();
      $('.sub-edit').show();
      $('.sub-next').show();
      $('#subtitles').show();
      $('.prev-head').show();
      $('.edit-head').show();
      $('.next-head').show();
      tag.src = "https://www.youtube.com/iframe_api";
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    });
  });

  //https://www.youtube.com/watch?v=MftOONlDQac
  //https://www.youtube.com/watch?v=fPloDzu_wcI
  //https://www.youtube.com/watch?v=poL7l-Uk3I8
});<commit_msg>Remove onplayerstate function<commit_after>var urlValue,
  re = /=(.+)/,
  videoID = '',
  tag,
  firstScriptTag,
  player,
  rates,
  currentRate,
  rateIndex,
  popcornVideo,
  subtitles,
  sub,
  subtitleChangeInterval,
  timeStart,
  timeEnd,
  prevSub,
  nextSub,
  editSub,
  videoData,
  currentSubIndex,
  startCaption;

function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    height: '340',
    width: '560',
    videoId: videoID,
    playerVars: {
      'cc_load_policy': 1,
      'controls': 0
    },
    events: {
      'onReady': onPlayerReady,
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerReady(event) {
  rates = player.getAvailablePlaybackRates();
  currentRate = player.getPlaybackRate();
  rateIndex = rates.indexOf(currentRate);
  event.target.playVideo();
  subtitleChangeInterval = setInterval(subtitleRefresh, 100);

  $('.sub-edit').focus(function() {
    player.pauseVideo();
    clearInterval(subtitleChangeInterval);
  });
  $('.sub-edit').focusout(function() {
    player.playVideo();
    subtitleChangeInterval = setInterval(subtitleRefresh, 100);
  });
}

function pauseVideo() {
  if (player.getPlayerState() === 1) {
    player.pauseVideo();
  }
  else {
    player.playVideo();
  }
}

function slowdownVideo() {
  if (rateIndex > 0) {
    player.setPlaybackRate(rates[rateIndex-1]);
    rateIndex -= 1;
  }
}

function speedupVideo() {
  if (rateIndex < rates.length -1) {
    player.setPlaybackRate(rates[rateIndex+1]);
    rateIndex += 1;
  }
}

function rewindVideo() {
  player.seekTo((player.getCurrentTime()-2), true);
  player.playVideo();
  console.log(player.getCurrentTime());
}

function forwardVideo() {
  player.seekTo((player.getCurrentTime()+2), true);
  player.playVideo();
  console.log(player.getCurrentTime());
}

Mousetrap.bind('ctrl+1', pauseVideo);
Mousetrap.bind('ctrl+4', slowdownVideo);
Mousetrap.bind('ctrl+5', speedupVideo);
Mousetrap.bind('ctrl+2', rewindVideo);
Mousetrap.bind('ctrl+3', forwardVideo);
Mousetrap.bind('ctrl+shift+s', saveCaption);
Mousetrap.bind('ctrl+shift+q', addCaption);
Mousetrap.bind('ctrl+shift+d', deleteCaption);

function subtitleRefresh() {
  console.log(player.getCurrentTime());
  if (player.getPlayerState() === 1) {
    for (sub in subtitles) {
      timeStart = subtitles[sub].start,
      timeEnd = subtitles[sub].start + subtitles[sub].dur;
      // if (player.getCurrentTime() < subtitles[0].start) {
      //   currentSubIndex = sub;
      //   editSub = subtitles[0];
      //   nextSub = subtitles[1];
      //   $('.sub-edit').val('');
      //   $('.edit-start').val('');
      //   $('.edit-end').val('');
      //   $('.sub-next').val('');
      //   $('.next-start').val('');
      //   $('.next-end').val('');
      //   $('.sub-prev').val('');
      //   $('.prev-start').val('');
      //   $('.prev-end').val('');
      //   return false;
      // }

      if (player.getCurrentTime() >= timeStart && player.getCurrentTime() <= timeEnd) {
        currentSubIndex = sub;
        editSub = subtitles[sub];
        prevSub = subtitles[parseInt(sub)-1];
        nextSub = subtitles[parseInt(sub)+1];
        console.log(currentSubIndex);
        $('#subtitles h3').text(editSub.value);
        $('.sub-edit').val(editSub.value);
        $('.edit-start').val(editSub.start);
        $('.edit-end').val(editSub.start + editSub.dur);
        $('.sub-next').val(nextSub.value);
        $('.next-start').val(nextSub.start);
        $('.next-end').val(nextSub.start + nextSub.dur);
        $('.sub-prev').val(prevSub.value);
        $('.prev-start').val(prevSub.start);
        $('.prev-end').val(prevSub.start + prevSub.dur);
        return false;
      }

      if (sub === subtitles[subtitles.length-1]) {
        console.log(sub);
        $('.sub-edit').val('');
        $('.edit-start').val('');
        $('.edit-end').val('');
        $('.sub-next').val('');
        $('.next-start').val('');
        $('.next-end').val('');
        $('.sub-prev').val(subtitles[subtitles.length-1].value);
        $('.prev-start').val(subtitles[subtitles.length-1].start);
        $('.prev-end').val(subtitles[subtitles.length-1].start + subtitles[subtitles.length-1].dur);
        return false;
      }
    }
  }
}

function deleteCaption() {
  videoData.captions.splice(currentSubIndex, 1);
  $.ajax({
    url: '/video/' + videoID,
    type: 'POST',
    data: JSON.stringify(videoData),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json'
  });
  subtitles = videoData.captions;
  $('.sub-edit').focusout();
  rewindVideo();
  player.playVideo();
  // console.log(videoData);
  // subtitleChangeInterval = setInterval(subtitleRefresh, 100);
}

function addCaption() {
  console.log(videoData);
  if (currentSubIndex) {
    startCaption =
    {
      'start': player.getCurrentTime(),
      'dur': 5.0,
      'value': 'Add caption text and change beginning and ending times',
      'extra_data': []
    };
    videoData.captions.splice(currentSubIndex, 0, startCaption);
    subtitles = videoData.captions;
    editSub = subtitles[currentSubIndex];
    prevSub = subtitles[parseInt(currentSubIndex)-1];
    nextSub = subtitles[parseInt(currentSubIndex)+1];
    $('#subtitles h3').text(editSub.value);
    $('.sub-edit').val(editSub.value);
    $('.edit-start').val(editSub.start);
    $('.edit-end').val(editSub.start + editSub.dur);
    $('.sub-next').val(nextSub.value);
    $('.next-start').val(nextSub.start);
    $('.next-end').val(nextSub.start + nextSub.dur);
    $('.sub-prev').val(prevSub.value);
    $('.prev-start').val(prevSub.start);
    $('.prev-end').val(prevSub.start + prevSub.dur);
    saveCaption();
  }
  else {
    currentSubIndex = 0;
    startCaption =
    {
      'start': player.getCurrentTime(),
      'dur': 5.0,
      'value': 'Add caption text and change beginning and ending times',
      'extra_data': []
    };
    videoData.captions.push(startCaption);
    subtitles = videoData.captions;
    console.log(subtitles);
    editSub = subtitles[currentSubIndex];
    console.log(editSub);
    console.log(editSub.value)
    $('#subtitles h3').text(editSub.value);
    $('.sub-edit').val(editSub.value);
    $('.edit-start').val(editSub.start);
    $('.edit-end').val(editSub.start + editSub.dur);
    saveCaption();
  }
}

function saveCaption() {
  console.log(currentSubIndex);
  videoData.captions[currentSubIndex].value = $('.sub-edit').val();
  videoData.captions[currentSubIndex].start = parseFloat($('.edit-start').val());
  videoData.captions[currentSubIndex].dur = parseFloat($('.edit-end').val()) - parseFloat($('.edit-start').val());
  if (currentSubIndex > 0) {
    videoData.captions[parseInt(currentSubIndex)-1].start = parseFloat($('.prev-start').val());
    videoData.captions[parseInt(currentSubIndex)-1].dur = parseFloat($('.prev-end').val()) - parseFloat($('.prev-start').val());
  }
  if (currentSubIndex < videoData.captions.length-1) {
    videoData.captions[parseInt(currentSubIndex)+1].start = parseFloat($('.next-start').val());
    videoData.captions[parseInt(currentSubIndex)+1].dur = parseFloat($('.next-end').val()) - parseFloat($('.next-start').val());
  }
  subtitles = videoData.captions;
  if (!(videoData.captions[currentSubIndex].dur)) {
    console.log("No duration")
    return false;
  }
  $.ajax({
    url: '/video/' + videoID,
    type: 'POST',
    data: JSON.stringify(videoData),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json'
  });
  $('.sub-edit').focusout();
  rewindVideo();
  player.playVideo();
  // console.log(videoData);
  subtitleChangeInterval = setInterval(subtitleRefresh, 100);
}

$(document).ready(function() {

  $('#the-button').click(function() {
    urlValue = $('#link-value').val();
    tag = document.createElement('script');
    firstScriptTag = document.getElementsByTagName('script')[0];

    if (urlValue.length > 11) {
      videoID = urlValue.match(re)[1].trim();
    }
    else {
      videoID = urlValue.trim();
    }
    $.getJSON('/video/youtube' + videoID, function(data) {
      videoData = data;
      subtitles = videoData.captions;
      $('.search-bar').hide();
      $('.main-button').hide();
      $('.shortcuts').fadeIn();
      $('.video-main').show();
      $('#player').show();
      $('#overlay').show();
      $('.sub-prev').show();
      $('.sub-edit').show();
      $('.sub-next').show();
      $('#subtitles').show();
      $('.prev-head').show();
      $('.edit-head').show();
      $('.next-head').show();
      tag.src = "https://www.youtube.com/iframe_api";
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    });
  });

  //https://www.youtube.com/watch?v=MftOONlDQac
  //https://www.youtube.com/watch?v=fPloDzu_wcI
  //https://www.youtube.com/watch?v=poL7l-Uk3I8
});<|endoftext|>


<commit_before>VISH.Editor.Image = (function(V,$,undefined){

	var contentToAdd = null;
	var contentAddMode = VISH.Constant.NONE;
	var uploadDivId = "tab_pic_upload_content";
	var urlDivId = "tab_pic_from_url_content";
	var urlInputId = "picture_url";

	var init = function(){
		VISH.Editor.Image.Flikr.init();
		VISH.Editor.Image.Repository.init();

		//Load from URL
		$("#" + urlDivId + " .previewButton").click(function(event) {
			if(VISH.Police.validateObject($("#" + urlInputId).val())[0]){
				contentToAdd = VISH.Editor.Utils.autocompleteUrls($("#" + urlInputId).val());
				VISH.Editor.Object.drawPreview(urlDivId, contentToAdd)
			}
		});

		//Upload content
		var options = VISH.Editor.getOptions();
		var tagList = $("#" + uploadDivId + " .tagList")
		var bar = $("#" + uploadDivId + " .upload_progress_bar");
		var percent = $("#" + uploadDivId + " .upload_progress_bar_percent");

		$("#" + uploadDivId + " input[name='document[file]']").change(function () {
			var filterFilePath = VISH.Editor.Utils.filterFilePath($("#" + uploadDivId + " input:file").val());
			$("#" + uploadDivId + " input[name='document[title]']").val(filterFilePath);
			_resetUploadFields();
			$(tagList).parent().show();
			$("#" + uploadDivId + ' form' + ' .button').show();
			$("#" + uploadDivId + " .upload_progress_bar_wrapper").hide();
		});

		$("#" + uploadDivId + " #upload_document_submit").click(function(event) {
			if(!VISH.Police.validateFileUpload($("#" + uploadDivId + " input[name='document[file]']").val())[0]){
				event.preventDefault();
			} else {
				if (options) {
					var description = "Uploaded by " + VISH.User.getName() + " via Vish Editor";
					$("#" + uploadDivId + " input[name='document[description]']").val(description);
					$("#" + uploadDivId + " input[name='document[owner_id]']").val(VISH.User.getId());
					$("#" + uploadDivId + " input[name='authenticity_token']").val(VISH.User.getToken());
					$("#" + uploadDivId + " .documentsForm").attr("action", VISH.UploadImagePath);
					$("#" + uploadDivId + " input[name='tags']").val(VISH.Editor.Utils.convertToTagsArray($(tagList).tagit("tags")));
					var tagList = $("#" + uploadDivId + " .tagList");
					$(tagList).parent().hide();
					$("#" + uploadDivId + " .upload_progress_bar_wrapper").show();
				}
			}
		});

		$("#" + uploadDivId + ' form').ajaxForm({
			beforeSend: function() {
				var percentVal = '0%';
				bar.width(percentVal);
				percent.html(percentVal);
			},
			uploadProgress: function(event, position, total, percentComplete) {
				var percentVal = percentComplete + '%';
				bar.width(percentVal)
				percent.html(percentVal);
			},
			complete: function(xhr) {
				switch(VISH.Configuration.getConfiguration()["mode"]){
					case VISH.Constant.NOSERVER:
						processResponse("{\"src\":\"/vishEditor/images/excursion_thumbnails/excursion-01.png\"}");
					break;
					case VISH.Constant.VISH:
						processResponse(xhr.responseText);
					break;
					case VISH.Constant.STANDALONE:
						processResponse(xhr.responseText);
					break;
				}
				var percentVal = '100%';
				bar.width(percentVal)
				percent.html(percentVal);
			},
			error: function(error){
				VISH.Debugging.log("Upload error");
				VISH.Debugging.log(error);
			}
		});
	};

	var onLoadTab = function(tab){
		if(tab=="upload"){
			_onLoadUploadTab();
		}
		if(tab=="url"){
			_onLoadURLTab();
		}
	};

	var _onLoadURLTab = function(){
		contentToAdd = null;
		VISH.Editor.Object.resetPreview(urlDivId);
		$("#" + urlInputId).val("");
	}

	var _onLoadUploadTab = function(){
		contentToAdd = null;

		//Hide and reset elements
		var tagList = $("#" + uploadDivId + " .tagList")
		$(tagList).parent().hide();
		$("#" + uploadDivId + ' form' + ' .button').hide();
		$("#" + uploadDivId + " .upload_progress_bar_wrapper").hide();
		$("#" + uploadDivId + " input[name='document[file]']").val("");
		_resetUploadFields();

		VISH.Editor.API.requestTags(_onTagsReceived)
	}

	var _resetUploadFields = function(){
		var bar = $("#" + uploadDivId + " .upload_progress_bar");
		var percent = $("#" + uploadDivId + " .upload_progress_bar_percent");

		bar.width('0%');
		percent.html('0%');
		VISH.Editor.Object.resetPreview(uploadDivId)

		var tagList = $("#" + uploadDivId + " .tagList")
		if($(tagList)[0].children.length!==0){
			$(tagList).tagit("reset")
		}
	}

	var _onTagsReceived = function(data){
		var tagList = $("#" + uploadDivId + " .tagList");

		if ($(tagList).children().length == 0){
			//Insert the three first tags. //DEPRECATED
			// $.each(data, function(index, tag) {
			// 	if(index==3){
			// 		return false; //break the bucle
			// 	}
			// 	$(tagList).append("
" + tag + "
")
			// });

			$(tagList).tagit({tagSource:data, sortable:true, maxLength:15, maxTags:8 ,
			watermarkAllowMessage: VISH.Editor.I18n.getTrans("i.AddTags"), watermarkDenyMessage: VISH.Editor.I18n.getTrans("i.limitReached")});
		}
	}

	var processResponse = function(response){
		try  {
			var jsonResponse = JSON.parse(response)
			if(jsonResponse.src){
				if (VISH.Police.validateObject(jsonResponse.src)[0]) {
					VISH.Editor.Object.drawPreview(uploadDivId,jsonResponse.src)
					contentToAdd = jsonResponse.src
				}
			}
		} catch(e) {
			//No JSON response
		}
	}

	var addContent = function(content){
		if(content){
			contentToAdd = content;
		}
		switch(contentAddMode){
			case VISH.Constant.FLASHCARD:
				VISH.Editor.Flashcard.onBackgroundSelected(contentToAdd);
				break;
			case VISH.Constant.THUMBNAIL:
				VISH.Editor.AvatarPicker.onCustomThumbnailSelected(contentToAdd);
				break;
			default:
				VISH.Editor.Object.drawPreviewObject(contentToAdd);
		}
		//Reset contentAddMode
		contentAddMode = VISH.Constant.NONE;
	}

   /**
	* Function to draw an image in a zone of the template
	* the zone to draw is the one in current_area
	* this function also makes the image draggable
	* param area: optional param indicating the area to add the image, used for editing presentations
	* param style: optional param with the style, used in editing presentation
	*/
	var drawImage = function(image_url, area, style, hyperlink){
		_drawImageInArea(image_url, area, style, hyperlink);
	};

	var _drawImageInArea = function(image_url, area, style, hyperlink){
		var current_area;

		if(area){
			current_area = area;
		}	else {
			current_area = VISH.Editor.getCurrentArea();
		}

		if(style){
			style = V.Editor.Utils.setStyleInPixels(style,current_area);
		} else {
			var image_width = $(current_area).width(); //default image width
			style = "width:"+image_width+"px;";
		}

		var template = VISH.Editor.getTemplate();
		var nextImageId = VISH.Utils.getId();
		var idToDragAndResize = "draggable" + nextImageId;
		current_area.attr('type','image');
		if(hyperlink){
			current_area.attr('hyperlink',hyperlink);
		}
		current_area.html("");

		//Adjust dimensions after drawing
		var theImg = $("#"+idToDragAndResize);
		if($(theImg).height()>$(current_area).height()){
			$(theImg).height($(current_area).height());
		}

		V.Editor.addDeleteButton(current_area);

		$("#" + idToDragAndResize).draggable({
			cursor: "move",
			stop: function(){
				$(this).parent().click();  //call parent click to select it in case it was unselected
			}
		});

	};

	var setAddContentMode = function(mode){
		contentAddMode = mode;
	}

	return {
		init 				: init,
		onLoadTab 			: onLoadTab,
		drawImage 			: drawImage,
		addContent 			: addContent,
		setAddContentMode	: setAddContentMode
	};

}) (VISH, jQuery);
<commit_msg>Minor bug fixed<commit_after>VISH.Editor.Image = (function(V,$,undefined){

	var contentToAdd = null;
	var contentAddMode = VISH.Constant.NONE;
	var uploadDivId = "tab_pic_upload_content";
	var urlDivId = "tab_pic_from_url_content";
	var urlInputId = "picture_url";

	var init = function(){
		VISH.Editor.Image.Flikr.init();
		VISH.Editor.Image.Repository.init();

		//Load from URL
		$("#" + urlDivId + " .previewButton").click(function(event) {
			if(VISH.Police.validateObject($("#" + urlInputId).val())[0]){
				contentToAdd = VISH.Editor.Utils.autocompleteUrls($("#" + urlInputId).val());
				VISH.Editor.Object.drawPreview(urlDivId, contentToAdd)
			}
		});

		//Upload content
		var options = VISH.Editor.getOptions();
		var tagList = $("#" + uploadDivId + " .tagList")
		var bar = $("#" + uploadDivId + " .upload_progress_bar");
		var percent = $("#" + uploadDivId + " .upload_progress_bar_percent");

		$("#" + uploadDivId + " input[name='document[file]']").change(function () {
			var filterFilePath = VISH.Editor.Utils.filterFilePath($("#" + uploadDivId + " input:file").val());
			$("#" + uploadDivId + " input[name='document[title]']").val(filterFilePath);
			_resetUploadFields();
			$(tagList).parent().show();
			$("#" + uploadDivId + ' form' + ' .button').show();
			$("#" + uploadDivId + " .upload_progress_bar_wrapper").hide();
		});

		$("#" + uploadDivId + " #upload_document_submit").click(function(event) {
			if(!VISH.Police.validateFileUpload($("#" + uploadDivId + " input[name='document[file]']").val())[0]){
				event.preventDefault();
			} else {
				if (options) {
					var description = "Uploaded by " + VISH.User.getName() + " via Vish Editor";
					$("#" + uploadDivId + " input[name='document[description]']").val(description);
					$("#" + uploadDivId + " input[name='document[owner_id]']").val(VISH.User.getId());
					$("#" + uploadDivId + " input[name='authenticity_token']").val(VISH.User.getToken());
					$("#" + uploadDivId + " .documentsForm").attr("action", VISH.UploadImagePath);
					$("#" + uploadDivId + " input[name='tags']").val(VISH.Editor.Utils.convertToTagsArray($(tagList).tagit("tags")));
					var tagList = $("#" + uploadDivId + " .tagList");
					$(tagList).parent().hide();
					$("#" + uploadDivId + " .upload_progress_bar_wrapper").show();
				}
			}
		});

		$("#" + uploadDivId + ' form').ajaxForm({
			beforeSend: function() {
				var percentVal = '0%';
				bar.width(percentVal);
				percent.html(percentVal);
			},
			uploadProgress: function(event, position, total, percentComplete) {
				var percentVal = percentComplete + '%';
				bar.width(percentVal)
				percent.html(percentVal);
			},
			complete: function(xhr) {
				switch(VISH.Configuration.getConfiguration()["mode"]){
					case VISH.Constant.NOSERVER:
						processResponse("{\"src\":\"/vishEditor/images/excursion_thumbnails/excursion-01.png\"}");
					break;
					case VISH.Constant.VISH:
						processResponse(xhr.responseText);
					break;
					case VISH.Constant.STANDALONE:
						processResponse(xhr.responseText);
					break;
				}
				var percentVal = '100%';
				bar.width(percentVal)
				percent.html(percentVal);
			},
			error: function(error){
				VISH.Debugging.log("Upload error");
				VISH.Debugging.log(error);
			}
		});
	};

	var onLoadTab = function(tab){
		if(tab=="upload"){
			_onLoadUploadTab();
		}
		if(tab=="url"){
			_onLoadURLTab();
		}
	};

	var _onLoadURLTab = function(){
		contentToAdd = null;
		VISH.Editor.Object.resetPreview(urlDivId);
		$("#" + urlInputId).val("");
	}

	var _onLoadUploadTab = function(){
		contentToAdd = null;

		//Hide and reset elements
		var tagList = $("#" + uploadDivId + " .tagList")
		$(tagList).parent().hide();
		$("#" + uploadDivId + ' form' + ' .button').hide();
		$("#" + uploadDivId + " .upload_progress_bar_wrapper").hide();
		$("#" + uploadDivId + " input[name='document[file]']").val("");
		_resetUploadFields();

		VISH.Editor.API.requestTags(_onTagsReceived)
	}

	var _resetUploadFields = function(){
		var bar = $("#" + uploadDivId + " .upload_progress_bar");
		var percent = $("#" + uploadDivId + " .upload_progress_bar_percent");

		bar.width('0%');
		percent.html('0%');
		VISH.Editor.Object.resetPreview(uploadDivId)

		var tagList = $("#" + uploadDivId + " .tagList")
		if($(tagList)[0].children.length!==0){
			$(tagList).tagit("reset")
		}
	}

	var _onTagsReceived = function(data){
		var tagList = $("#" + uploadDivId + " .tagList");

		if ($(tagList).children().length == 0){
			//Insert the three first tags. //DEPRECATED
			// $.each(data, function(index, tag) {
			// 	if(index==3){
			// 		return false; //break the bucle
			// 	}
			// 	$(tagList).append("
" + tag + "
")
			// });

			$(tagList).tagit({tagSource:data, sortable:true, maxLength:15, maxTags:8 ,
			watermarkAllowMessage: VISH.Editor.I18n.getTrans("i.AddTags"), watermarkDenyMessage: VISH.Editor.I18n.getTrans("i.limitReached")});
		}
	}

	var processResponse = function(response){
		try  {
			var jsonResponse = JSON.parse(response)
			if(jsonResponse.src){
				if (VISH.Police.validateObject(jsonResponse.src)[0]) {
					VISH.Editor.Object.drawPreview(uploadDivId,jsonResponse.src)
					contentToAdd = jsonResponse.src
				}
			}
		} catch(e) {
			//No JSON response
		}
	}

	var addContent = function(content){
		if(content){
			contentToAdd = content;
		}
		switch(contentAddMode){
			case VISH.Constant.FLASHCARD:
				VISH.Editor.Flashcard.onBackgroundSelected(contentToAdd);
				break;
			case VISH.Constant.THUMBNAIL:
				VISH.Editor.AvatarPicker.onCustomThumbnailSelected(contentToAdd);
				break;
			default:
				VISH.Editor.Object.drawPreviewObject(contentToAdd);
		}
		//Reset contentAddMode
		contentAddMode = VISH.Constant.NONE;
	}

   /**
	* Function to draw an image in a zone of the template
	* the zone to draw is the one in current_area
	* this function also makes the image draggable
	* param area: optional param indicating the area to add the image, used for editing presentations
	* param style: optional param with the style, used in editing presentation
	*/
	var drawImage = function(image_url, area, style, hyperlink){
		_drawImageInArea(image_url, area, style, hyperlink);
	};

	var _drawImageInArea = function(image_url, area, style, hyperlink){
		var current_area;

		if(area){
			current_area = area;
		}	else {
			current_area = VISH.Editor.getCurrentArea();
		}

		if(style){
			style = V.Editor.Utils.setStyleInPixels(style,current_area);
		} else {
			var image_width = $(current_area).width(); //default image width
			style = "width:"+image_width+"px;";
		}

		var template = VISH.Editor.getTemplate();
		var nextImageId = VISH.Utils.getId();
		var idToDragAndResize = "draggable" + nextImageId;
		current_area.attr('type','image');
		if(hyperlink){
			current_area.attr('hyperlink',hyperlink);
		}
		current_area.html("");

		//Adjust dimensions after drawing
		var theImg = $("#"+idToDragAndResize);
		var dimentionsToDraw = VISH.Editor.Utils.dimentionToDraw($(current_area).width(), $(current_area).height(), $(theImg).width(), $(theImg).height());
		$(theImg).height(dimentionsToDraw.height);
		$(theImg).width(dimentionsToDraw.width);

		V.Editor.addDeleteButton(current_area);

		$("#" + idToDragAndResize).draggable({
			cursor: "move",
			stop: function(){
				$(this).parent().click();  //call parent click to select it in case it was unselected
			}
		});

	};

	var setAddContentMode = function(mode){
		contentAddMode = mode;
	}

	return {
		init 				: init,
		onLoadTab 			: onLoadTab,
		drawImage 			: drawImage,
		addContent 			: addContent,
		setAddContentMode	: setAddContentMode
	};

}) (VISH, jQuery);
<|endoftext|>


<commit_before>(function($) {
  $('#submit').on('click', function (e) {
    e.preventDefault();

    $('#error, #success').hide();

    var url = $('input[name="url"]');
    if (url.val() === '' || url.val() === url.data('placeholder')) {
      $('#error').text('Please enter in a URL.').show();
      return;
    }

    url = url.val();
    $.getJSON($(this).data('href'), {
      url: url
    }).done(function (data) {
      $('#success').text(data.url).show();
    }).fail(function (data) {
      if (typeof data.responseJSON.error !== 'undefined') {
        $('#error').text(data.responseJSON.error).show();
      } else {
        $('#error').text('An unknown error occured.').show();
      }
    });
  })
})(jQuery);
<commit_msg>Hitting enter in the URL input now submits the form.<commit_after>(function($) {
  $('#url').keypress(function (e) {
    if (e.which === 13) {
      e.preventDefault();
      $('#submit').click();
    }
  });

  $('#submit').on('click', function (e) {
    e.preventDefault();

    $('#error, #success').hide();

    var url = $('#url');
    if (url.val() === '' || url.val() === url.data('placeholder')) {
      $('#error').text('Please enter in a URL.').show();
      return;
    }

    url = url.val();
    $.getJSON($(this).data('href'), {
      url: url
    }).done(function (data) {
      $('#success').text(data.url).show();
    }).fail(function (data) {
      if (typeof data.responseJSON.error !== 'undefined') {
        $('#error').text(data.responseJSON.error).show();
      } else {
        $('#error').text('An unknown error occured.').show();
      }
    });
  })
})(jQuery);
<|endoftext|>