<commit_before>import {
  beforeEach,
  describe,
  it,
} from '@bigtest/mocha';
import { expect } from 'chai';

import translations from '../../../translations/ui-users/en';

import setupApplication from '../helpers/setup-application';
import UserFormPage from '../interactors/user-form-page';
import InstanceViewPage from '../interactors/user-view-page';
import UsersInteractor from '../interactors/users';

describe.only('User Edit Page', () => {
  const users = new UsersInteractor();
  let user1;
  let user2;

  describe('with all permissions', () => {
    setupApplication();

    beforeEach(async function () {
      user1 = this.server.create('user');
      user2 = this.server.create('user');
      this.server.create('requestPreference', {
        userId: user1.id,
        delivery: true,
        defaultServicePointId: 'servicepointId1',
        defaultDeliveryAddressTypeId: 'Type1',
        fulfillment: 'Delivery',
      });

      this.visit(`/users/${user1.id}/edit`);
      await UserFormPage.whenLoaded();
    });

    it('displays the title in the pane header', () => {
      expect(UserFormPage.title).to.equal(user1.username);
    });

    it('should display create password link', () => {
      expect(UserFormPage.resetPasswordLink.isPresent).to.be.true;
      expect(UserFormPage.resetPasswordLink.text).to.equal(translations['extended.sendCreatePassword']);
    });

    describe('validating user barcode', () => {
      beforeEach(async function () {
        await UserFormPage.barcodeField.fillAndBlur(user2.barcode);
        await UserFormPage.submitButton.click();
      });

      it('should display validation error', () => {
        expect(UserFormPage.feedbackError).to.equal('This barcode has already been taken');
      });
    });

    describe('validating empty user barcode', () => {
      beforeEach(async function () {
        await UserFormPage.barcodeField.fillAndBlur('');
        await UserFormPage.submitButton.click();
      });

      it('should show user detail view', () => {
        expect(InstanceViewPage.isVisible).to.equal(true);
      });
    });

    describe('validating username', () => {
      beforeEach(async function () {
        await UserFormPage.usernameField.fillAndBlur(user2.username);
        await UserFormPage.submitButton.click();
      });

      it('should display validation error', () => {
        expect(UserFormPage.feedbackError).to.equal('This username already exists');
      });
    });

    describe('pane header menu', () => {
      beforeEach(async () => {
        await UserFormPage.cancelButton.click();
      });

      it('should redirect to view users page after click', () => {
        expect(users.$root).to.exist;
      });
    });

    describe('request preferences', () => {
      it('should display "hold shelf" checkbox as checked', () => {
        expect(UserFormPage.holdShelfCheckboxIsChecked).to.be.true;
      });

      it('should display "hold shelf" checkbox as disabled', () => {
        expect(UserFormPage.holdShelfCheckboxIsDisabled).to.be.true;
      });

      describe('when delivery is activated', () => {
        it('should display "delivery" checkbox as checked', () => {
          expect(UserFormPage.deliveryCheckboxIsChecked).to.be.true;
        });

        it('should display selected "Fulfillment preference"', () => {
          expect(UserFormPage.fulfillmentPreference.value).to.equal('Delivery');
        });

        it('should display selected default address type', () => {
          const CLAIM_ADDRESS_TYPE_VALUE = 'Type1';
          expect(UserFormPage.defaultAddressTypeField.value).to.equal(CLAIM_ADDRESS_TYPE_VALUE);
        });

        describe('and selected default address type was changed', () => {
          beforeEach(async () => {
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Home');
            await UserFormPage.firstAddressTypeField.selectAndBlur('Order');
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage).to.equal('Please fill this in to continue');
          });
        });

        describe('and selected default address type was deleted', () => {
          beforeEach(async () => {
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Home');
            await UserFormPage.deleteAddressType();
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage)
              .to.equal('Please, add at least one address inside "Addresses" section');
          });
        });

        describe('and all address types were deleted', () => {
          beforeEach(async () => {
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.secondAddressTypeField.selectAndBlur('Order');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Order');
            await UserFormPage.deleteAddressType();
            await UserFormPage.deleteAddressType();
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage)
              .to.equal('Please, add at least one address inside "Addresses" section');
          });
        });
      });
    });

    describe('when custom fields are in stock', () => {
      it('should show custom fields accordion', () => {
        expect(UserFormPage.customFieldsSection.isPresent).to.be.true;
      });

      it('should display correct accordion label', () => {
        expect(UserFormPage.customFieldsSection.label).to.equal('Custom Fields Test');
      });

      it('should display all visible custom fields', () => {
        expect(UserFormPage.customFieldsSection.fields().length).to.equal(3);
      });

      it('should display popover for the first field', () => {
        expect(UserFormPage.customFieldsSection.fields(0).popoverIsPresent).to.be.true;
      });

      describe('when set empty falue to required field', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(0).input.fillAndBlur('');
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(0).validationMessage).to.equal('Textbox 1 is required');
        });
      });

      describe('when set value to textbox out of length limit', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(0).input.fillAndBlur((new Array(151)).fill('a').join(''));
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(0).validationMessage)
            .to.equal('Textbox 1 character limit has been exceeded. Please revise.');
        });
      });

      describe('when set to textarea value out of length limit', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(2).input.fillAndBlur((new Array(1501)).fill('a').join(''));
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(2).validationMessage)
            .to.equal('Textarea 4 character limit has been exceeded. Please revise.');
        });
      });
    });

    describe('changing status field', () => {
      describe('changing status to inactive', () => {
        beforeEach(async function () {
          await UserFormPage.statusField.selectAndBlur('Inactive');
          await UserFormPage.submitButton.click();
          await InstanceViewPage.whenLoaded();
        });

        it('should display inactive status', () => {
          expect(InstanceViewPage.userInfo.keyValues(5).text).to.equal('Inactive');
        }).timeout(6000);
      });

      describe('clearing expirationDate field', () => {
        beforeEach(async function () {
          await UserFormPage.statusField.selectAndBlur('Active');
          await UserFormPage.clearExpirationDate();
          await UserFormPage.submitButton.click();
          await InstanceViewPage.whenLoaded();
        });

        it('should display active status', () => {
          expect(InstanceViewPage.userInfo.keyValues(5).text).to.equal('Active');
        }).timeout(6000);
      });
    });
  });

  describe('User without permission create/reset password', () => {
    setupApplication({
      permissions: {
        'ui-users.reset-password-link.send': false
      }
    });

    beforeEach(async function () {
      user1 = this.server.create('user');

      this.visit(`/users/${user1.id}/edit`);
      await UserFormPage.whenLoaded();
    });

    it('should not display create/reset password link', () => {
      expect(UserFormPage.resetPasswordLink.isPresent).to.be.false;
    });
  });
});

describe('when custom fields are not in stock', () => {
  setupApplication();

  beforeEach(async function () {
    const user = this.server.create('user');

    this.server.get('/custom-fields', {
      customFields: [],
    });

    this.visit(`/users/${user.id}/edit`);
    await UserFormPage.whenLoaded();
  });

  it('should custom fields accordion does not present', () => {
    expect(UserFormPage.customFieldsSection.isPresent).to.be.false;
  });
});
<commit_msg>UIU-1337: Remove only<commit_after>import {
  beforeEach,
  describe,
  it,
} from '@bigtest/mocha';
import { expect } from 'chai';

import translations from '../../../translations/ui-users/en';

import setupApplication from '../helpers/setup-application';
import UserFormPage from '../interactors/user-form-page';
import InstanceViewPage from '../interactors/user-view-page';
import UsersInteractor from '../interactors/users';

describe('User Edit Page', () => {
  const users = new UsersInteractor();
  let user1;
  let user2;

  describe('with all permissions', () => {
    setupApplication();

    beforeEach(async function () {
      user1 = this.server.create('user');
      user2 = this.server.create('user');
      this.server.create('requestPreference', {
        userId: user1.id,
        delivery: true,
        defaultServicePointId: 'servicepointId1',
        defaultDeliveryAddressTypeId: 'Type1',
        fulfillment: 'Delivery',
      });

      this.visit(`/users/${user1.id}/edit`);
      await UserFormPage.whenLoaded();
    });

    it('displays the title in the pane header', () => {
      expect(UserFormPage.title).to.equal(user1.username);
    });

    it('should display create password link', () => {
      expect(UserFormPage.resetPasswordLink.isPresent).to.be.true;
      expect(UserFormPage.resetPasswordLink.text).to.equal(translations['extended.sendCreatePassword']);
    });

    describe('validating user barcode', () => {
      beforeEach(async function () {
        await UserFormPage.barcodeField.fillAndBlur(user2.barcode);
        await UserFormPage.submitButton.click();
      });

      it('should display validation error', () => {
        expect(UserFormPage.feedbackError).to.equal('This barcode has already been taken');
      });
    });

    describe('validating empty user barcode', () => {
      beforeEach(async function () {
        await UserFormPage.barcodeField.fillAndBlur('');
        await UserFormPage.submitButton.click();
      });

      it('should show user detail view', () => {
        expect(InstanceViewPage.isVisible).to.equal(true);
      });
    });

    describe('validating username', () => {
      beforeEach(async function () {
        await UserFormPage.usernameField.fillAndBlur(user2.username);
        await UserFormPage.submitButton.click();
      });

      it('should display validation error', () => {
        expect(UserFormPage.feedbackError).to.equal('This username already exists');
      });
    });

    describe('pane header menu', () => {
      beforeEach(async () => {
        await UserFormPage.cancelButton.click();
      });

      it('should redirect to view users page after click', () => {
        expect(users.$root).to.exist;
      });
    });

    describe('request preferences', () => {
      it('should display "hold shelf" checkbox as checked', () => {
        expect(UserFormPage.holdShelfCheckboxIsChecked).to.be.true;
      });

      it('should display "hold shelf" checkbox as disabled', () => {
        expect(UserFormPage.holdShelfCheckboxIsDisabled).to.be.true;
      });

      describe('when delivery is activated', () => {
        it('should display "delivery" checkbox as checked', () => {
          expect(UserFormPage.deliveryCheckboxIsChecked).to.be.true;
        });

        it('should display selected "Fulfillment preference"', () => {
          expect(UserFormPage.fulfillmentPreference.value).to.equal('Delivery');
        });

        it('should display selected default address type', () => {
          const CLAIM_ADDRESS_TYPE_VALUE = 'Type1';
          expect(UserFormPage.defaultAddressTypeField.value).to.equal(CLAIM_ADDRESS_TYPE_VALUE);
        });

        describe('and selected default address type was changed', () => {
          beforeEach(async () => {
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Home');
            await UserFormPage.firstAddressTypeField.selectAndBlur('Order');
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage).to.equal('Please fill this in to continue');
          });
        });

        describe('and selected default address type was deleted', () => {
          beforeEach(async () => {
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Home');
            await UserFormPage.deleteAddressType();
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage)
              .to.equal('Please, add at least one address inside "Addresses" section');
          });
        });

        describe('and all address types were deleted', () => {
          beforeEach(async () => {
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.clickAddAddressButton();
            await UserFormPage.firstAddressTypeField.selectAndBlur('Home');
            await UserFormPage.secondAddressTypeField.selectAndBlur('Order');
            await UserFormPage.defaultAddressTypeField.selectAndBlur('Order');
            await UserFormPage.deleteAddressType();
            await UserFormPage.deleteAddressType();
          });

          it('should reset default address type to empty value', () => {
            expect(UserFormPage.defaultAddressTypeField.value).to.equal('');
          });

          it('should display validation message of "Default delivery address field"', () => {
            expect(UserFormPage.defaultAddressTypeValidationMessage)
              .to.equal('Please, add at least one address inside "Addresses" section');
          });
        });
      });
    });

    describe('when custom fields are in stock', () => {
      it('should show custom fields accordion', () => {
        expect(UserFormPage.customFieldsSection.isPresent).to.be.true;
      });

      it('should display correct accordion label', () => {
        expect(UserFormPage.customFieldsSection.label).to.equal('Custom Fields Test');
      });

      it('should display all visible custom fields', () => {
        expect(UserFormPage.customFieldsSection.fields().length).to.equal(3);
      });

      it('should display popover for the first field', () => {
        expect(UserFormPage.customFieldsSection.fields(0).popoverIsPresent).to.be.true;
      });

      describe('when set empty falue to required field', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(0).input.fillAndBlur('');
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(0).validationMessage).to.equal('Textbox 1 is required');
        });
      });

      describe('when set value to textbox out of length limit', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(0).input.fillAndBlur((new Array(151)).fill('a').join(''));
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(0).validationMessage)
            .to.equal('Textbox 1 character limit has been exceeded. Please revise.');
        });
      });

      describe('when set to textarea value out of length limit', () => {
        beforeEach(async () => {
          await UserFormPage.customFieldsSection.fields(2).input.fillAndBlur((new Array(1501)).fill('a').join(''));
        });

        it('should show validation message', () => {
          expect(UserFormPage.customFieldsSection.fields(2).validationMessage)
            .to.equal('Textarea 4 character limit has been exceeded. Please revise.');
        });
      });
    });

    describe('changing status field', () => {
      describe('changing status to inactive', () => {
        beforeEach(async function () {
          await UserFormPage.statusField.selectAndBlur('Inactive');
          await UserFormPage.submitButton.click();
          await InstanceViewPage.whenLoaded();
        });

        it('should display inactive status', () => {
          expect(InstanceViewPage.userInfo.keyValues(5).text).to.equal('Inactive');
        }).timeout(6000);
      });

      describe('clearing expirationDate field', () => {
        beforeEach(async function () {
          await UserFormPage.statusField.selectAndBlur('Active');
          await UserFormPage.clearExpirationDate();
          await UserFormPage.submitButton.click();
          await InstanceViewPage.whenLoaded();
        });

        it('should display active status', () => {
          expect(InstanceViewPage.userInfo.keyValues(5).text).to.equal('Active');
        }).timeout(6000);
      });
    });
  });

  describe('User without permission create/reset password', () => {
    setupApplication({
      permissions: {
        'ui-users.reset-password-link.send': false
      }
    });

    beforeEach(async function () {
      user1 = this.server.create('user');

      this.visit(`/users/${user1.id}/edit`);
      await UserFormPage.whenLoaded();
    });

    it('should not display create/reset password link', () => {
      expect(UserFormPage.resetPasswordLink.isPresent).to.be.false;
    });
  });
});

describe('when custom fields are not in stock', () => {
  setupApplication();

  beforeEach(async function () {
    const user = this.server.create('user');

    this.server.get('/custom-fields', {
      customFields: [],
    });

    this.visit(`/users/${user.id}/edit`);
    await UserFormPage.whenLoaded();
  });

  it('should custom fields accordion does not present', () => {
    expect(UserFormPage.customFieldsSection.isPresent).to.be.false;
  });
});
<|endoftext|>


<commit_before>angular.module('MyApp', ['ngResource', 'ngMessages', 'ngAnimate', 'toastr', 'ui.router', 'satellizer'])
  .config(function($stateProvider, $urlRouterProvider, $authProvider) {

    /**
     * Helper auth functions
     */
    var skipIfLoggedIn = function($q, $auth) {
      var deferred = $q.defer();
      if ($auth.isAuthenticated()) {
        deferred.reject();
      } else {
        deferred.resolve();
      }
      return deferred.promise;
    };

    var loginRequired = function($q, $location, $auth) {
      var deferred = $q.defer();
      if ($auth.isAuthenticated()) {
        deferred.resolve();
      } else {
        $location.path('/login');
      }
      return deferred.promise;
    };

    /**
     * App routes
     */
    $stateProvider
      .state('home', {
        url: '/',
        controller: 'HomeCtrl',
        templateUrl: 'partials/home.html'
      })
      .state('login', {
        url: '/login',
        templateUrl: 'partials/login.html',
        controller: 'LoginCtrl',
        resolve: {
          skipIfLoggedIn: skipIfLoggedIn
        }
      })
      .state('signup', {
        url: '/signup',
        templateUrl: 'partials/signup.html',
        controller: 'SignupCtrl',
        resolve: {
          skipIfLoggedIn: skipIfLoggedIn
        }
      })
      .state('logout', {
        url: '/logout',
        template: null,
        controller: 'LogoutCtrl'
      })
      .state('profile', {
        url: '/profile',
        templateUrl: 'partials/profile.html',
        controller: 'ProfileCtrl',
        resolve: {
          loginRequired: loginRequired
        }
      });
    $urlRouterProvider.otherwise('/');

    /**
     *  Satellizer config
     */
    $authProvider.facebook({
      clientId: '603122136500203'
    });

    $authProvider.google({
      clientId: 'YOUR_GOOGLE_CLIENT_ID'
    });

    $authProvider.github({
      clientId: 'YOUR_GITHUB_CLIENT_ID'
    });

    $authProvider.linkedin({
      clientId: 'YOUR_LINKEDIN_CLIENT_ID'
    });

    $authProvider.instagram({
      clientId: 'YOUR_INSTAGRAM_CLIENT_ID'
    });

    $authProvider.yahoo({
      clientId: 'YOUR_YAHOO_CLIENT_ID'
    });

    $authProvider.live({
      clientId: 'YOUR_MICROSOFT_CLIENT_ID'
    });

    $authProvider.twitch({
      clientId: 'YOUR_TWITCH_CLIENT_ID'
    });

    $authProvider.bitbucket({
      clientId: 'YOUR_BITBUCKET_CLIENT_ID'
    });

    $authProvider.spotify({
      clientId: 'YOUR_SPOTIFY_CLIENT_ID'
    });

    $authProvider.twitter({
      url: '/auth/twitter'
    });

    $authProvider.oauth2({
      name: 'foursquare',
      url: '/auth/foursquare',
      clientId: 'MTCEJ3NGW2PNNB31WOSBFDSAD4MTHYVAZ1UKIULXZ2CVFC2K',
      redirectUri: window.location.origin || window.location.protocol + '//' + window.location.host,
      authorizationEndpoint: 'https://foursquare.com/oauth2/authenticate'
    });
  });
<commit_msg>add angular annotations to auth helper functions<commit_after>angular.module('MyApp', ['ngResource', 'ngMessages', 'ngAnimate', 'toastr', 'ui.router', 'satellizer'])
  .config(function($stateProvider, $urlRouterProvider, $authProvider) {

    /**
     * Helper auth functions
     */
    var skipIfLoggedIn = ['$q', '$auth', function($q, $auth) {
      var deferred = $q.defer();
      if ($auth.isAuthenticated()) {
        deferred.reject();
      } else {
        deferred.resolve();
      }
      return deferred.promise;
    }];

    var loginRequired = ['$q', '$location', '$auth', function($q, $location, $auth) {
      var deferred = $q.defer();
      if ($auth.isAuthenticated()) {
        deferred.resolve();
      } else {
        $location.path('/login');
      }
      return deferred.promise;
    }];

    /**
     * App routes
     */
    $stateProvider
      .state('home', {
        url: '/',
        controller: 'HomeCtrl',
        templateUrl: 'partials/home.html'
      })
      .state('login', {
        url: '/login',
        templateUrl: 'partials/login.html',
        controller: 'LoginCtrl',
        resolve: {
          skipIfLoggedIn: skipIfLoggedIn
        }
      })
      .state('signup', {
        url: '/signup',
        templateUrl: 'partials/signup.html',
        controller: 'SignupCtrl',
        resolve: {
          skipIfLoggedIn: skipIfLoggedIn
        }
      })
      .state('logout', {
        url: '/logout',
        template: null,
        controller: 'LogoutCtrl'
      })
      .state('profile', {
        url: '/profile',
        templateUrl: 'partials/profile.html',
        controller: 'ProfileCtrl',
        resolve: {
          loginRequired: loginRequired
        }
      });
    $urlRouterProvider.otherwise('/');

    /**
     *  Satellizer config
     */
    $authProvider.facebook({
      clientId: '603122136500203'
    });

    $authProvider.google({
      clientId: 'YOUR_GOOGLE_CLIENT_ID'
    });

    $authProvider.github({
      clientId: 'YOUR_GITHUB_CLIENT_ID'
    });

    $authProvider.linkedin({
      clientId: 'YOUR_LINKEDIN_CLIENT_ID'
    });

    $authProvider.instagram({
      clientId: 'YOUR_INSTAGRAM_CLIENT_ID'
    });

    $authProvider.yahoo({
      clientId: 'YOUR_YAHOO_CLIENT_ID'
    });

    $authProvider.live({
      clientId: 'YOUR_MICROSOFT_CLIENT_ID'
    });

    $authProvider.twitch({
      clientId: 'YOUR_TWITCH_CLIENT_ID'
    });

    $authProvider.bitbucket({
      clientId: 'YOUR_BITBUCKET_CLIENT_ID'
    });

    $authProvider.spotify({
      clientId: 'YOUR_SPOTIFY_CLIENT_ID'
    });

    $authProvider.twitter({
      url: '/auth/twitter'
    });

    $authProvider.oauth2({
      name: 'foursquare',
      url: '/auth/foursquare',
      clientId: 'MTCEJ3NGW2PNNB31WOSBFDSAD4MTHYVAZ1UKIULXZ2CVFC2K',
      redirectUri: window.location.origin || window.location.protocol + '//' + window.location.host,
      authorizationEndpoint: 'https://foursquare.com/oauth2/authenticate'
    });
  });
<|endoftext|>


<commit_before>var fs        = require('fs');
var path      = require('path');
var exec      = require('child_process').exec;
var http      = require('http');
var recursive = require('recursive-readdir');

var removeWWW = function(url) {
	if(url.indexOf('www') > -1)
		return url.substring(4, url.length);

	return url;
};

var getFileType = function(file) {
	var suffix = '';
	for (var i = file.length - 1; i >= 0 && file[i] != '.'; i--) {
		suffix += file[i];
	}

	return suffix.split('').reverse().join('');
};

var getFilesFromDirectory = function(directory, callback) {
	var ignoreFunc = function(file, stats) {
	  return (stats.isDirectory() &&
	  		 (path.basename(file) == 'node_modules' ||
	  		  path.basename(file) == '.git'));
	};

	recursive(directory, ['.htaccess', ignoreFunc], function (err, files) {
	  callback(files);
	});
};

var constructURLFromPath = function(dom, filepath) {
	var index = filepath.indexOf(dom.baseDirectory);
	if(index > -1) {
		return filepath.substring(index + dom.baseDirectory.length, filepath.length);
	}

	return '';
};

var executePHP = function(res, file) {
	var cmd = 'php ' + file;
	exec(cmd, function(error, stdout, stderr) {
		res.writeHead(200, {'Content-Type':'text/html'});
		res.end(stdout);
	});
};

SimpleServer = function() {
	var self        = this;
	this.dispatcher = require('httpdispatcher');
	this.domains    = [];
	this.port       = 8080;
	this.server     = http.createServer(function(req, res) {
		try {
	        self.dispatcher.dispatch(req, res);
	    }
	    catch(err) {
	        console.log(err);
	    }
	});

	this.dispatcher.onError(function(req, res) {
	    res.writeHead(404);
	    res.end('404 - Page does not exist.');
	});
};

SimpleServer.prototype.setPort = function(port) {
	this.port = port;
};

SimpleServer.prototype.generateDispatcherRequest = function(requestURL, file) {
	var handleRequest = function(req, res) {
		if(getFileType(file) == 'php')
			executePHP(res, file);
		else {
			var response = fs.readFileSync(file, 'utf8');

			res.writeHead(200, {'Content-Type': 'text/html'});
			res.end(response);
		}
	};

	this.dispatcher.onGet(requestURL, handleRequest);
};

SimpleServer.prototype.setupNewDomain = function(dom) {
	dom.host = removeWWW(dom.host);
	var self = this;
	getFilesFromDirectory(dom.baseDirectory, function(files) {
		for (var i = files.length - 1; i >= 0; i--) {
			if(dom.allowedFileTypes.indexOf(getFileType(files[i])) > -1) {
				var file       = files[i];
				var requestURL = constructURLFromPath(dom, file);
				self.generateDispatcherRequest(requestURL, file);
			}
		}
	});
};

/*
 * This function takes a JSON object with the
 * following fields:
 * - host
 * - baseDirectory
 * - allowedFileTypes (optional)
 */
SimpleServer.prototype.addDomain = function(dom) {
	// if(dom.host.indexOf(':') == -1)
	// 	dom.host += (':' + this.port);

    this.domains.push(dom);
    this.setupNewDomain(dom);
};

//Takes an array of JSON objects as described above
SimpleServer.prototype.addDomains = function(doms) {
	for (var i = doms.length - 1; i >= 0; i--) {
		this.addDomain(doms[i]);
	}
};

SimpleServer.prototype.setAllowedFileTypes = function(host, types) {
    for (var i = this.domains.length - 1; i >= 0; i--) {
        if(this.domains[i].host == host) {
            this.domains[i].allowedFileTypes = types;
            break;
        }
    }
};

SimpleServer.prototype.start = function() {
	var self = this;
	this.server.listen(this.port, function(){
	    console.log('Listening on: http://localhost:%s', self.port);
	});
};<commit_msg>Automatically sets the first found index.* file as default.<commit_after>var fs        = require('fs');
var path      = require('path');
var exec      = require('child_process').exec;
var http      = require('http');
var recursive = require('recursive-readdir');

var removeWWW = function(url) {
	if(url.indexOf('www') > -1)
		return url.substring(4, url.length);

	return url;
};

var getFileType = function(file) {
	var suffix = '';
	for (var i = file.length - 1; i >= 0 && file[i] != '.'; i--) {
		suffix += file[i];
	}

	return suffix.split('').reverse().join('');
};

var getFilesFromDirectory = function(directory, callback) {
	var ignoreFunc = function(file, stats) {
	  return (stats.isDirectory() &&
	  		 (path.basename(file) == 'node_modules' ||
	  		  path.basename(file) == '.git'));
	};

	recursive(directory, ['.htaccess', ignoreFunc], function (err, files) {
	  callback(files);
	});
};

var constructURLFromPath = function(dom, filepath) {
	var index = filepath.indexOf(dom.baseDirectory);
	if(index > -1) {
		return filepath.substring(index + dom.baseDirectory.length, filepath.length);
	}

	return '';
};

var executePHP = function(res, file) {
	var cmd = 'php ' + file;
	exec(cmd, function(error, stdout, stderr) {
		res.writeHead(200, {'Content-Type':'text/html'});
		res.end(stdout);
	});
};

SimpleServer = function() {
	var self        = this;
	this.dispatcher = require('httpdispatcher');
	this.domains    = [];
	this.port       = 8080;
	this.indexSet   = false;
	this.server     = http.createServer(function(req, res) {
		try {
	        self.dispatcher.dispatch(req, res);
	    }
	    catch(err) {
	        console.log(err);
	    }
	});

	this.dispatcher.onError(function(req, res) {
	    res.writeHead(404);
	    res.end('404 - Page does not exist.');
	});
};

SimpleServer.prototype.setPort = function(port) {
	this.port = port;
};

SimpleServer.prototype.generateDispatcherRequest = function(requestURL, file) {
	var handleRequest = function(req, res) {
		if(getFileType(file) == 'php')
			executePHP(res, file);
		else {
			var response = fs.readFileSync(file, 'utf8');

			res.writeHead(200, {'Content-Type': 'text/html'});
			res.end(response);
		}
	};

	this.dispatcher.onGet(requestURL, handleRequest);

	if(!this.indexSet && requestURL.indexOf('index') > -1)
		this.dispatcher.onGet('/', handleRequest);
};

SimpleServer.prototype.setupNewDomain = function(dom) {
	dom.host = removeWWW(dom.host);
	var self = this;
	getFilesFromDirectory(dom.baseDirectory, function(files) {
		for (var i = files.length - 1; i >= 0; i--) {
			if(dom.allowedFileTypes.indexOf(getFileType(files[i])) > -1) {
				var file       = files[i];
				var requestURL = constructURLFromPath(dom, file);
				self.generateDispatcherRequest(requestURL, file);
			}
		}
	});
};

/*
 * This function takes a JSON object with the
 * following fields:
 * - host
 * - baseDirectory
 * - allowedFileTypes (optional)
 */
SimpleServer.prototype.addDomain = function(dom) {
	// if(dom.host.indexOf(':') == -1)
	// 	dom.host += (':' + this.port);

    this.domains.push(dom);
    this.setupNewDomain(dom);
};

//Takes an array of JSON objects as described above
SimpleServer.prototype.addDomains = function(doms) {
	for (var i = doms.length - 1; i >= 0; i--) {
		this.addDomain(doms[i]);
	}
};

SimpleServer.prototype.setAllowedFileTypes = function(host, types) {
    for (var i = this.domains.length - 1; i >= 0; i--) {
        if(this.domains[i].host == host) {
            this.domains[i].allowedFileTypes = types;
            break;
        }
    }
};

SimpleServer.prototype.start = function() {
	var self = this;
	this.server.listen(this.port, function(){
	    console.log('Listening on: http://localhost:%s', self.port);
	});
};<|endoftext|>


<commit_before>import { test } from 'qunit';
import { svg } from 'config/environment';

if ( svg ) {
	const html = 'http://www.w3.org/1999/xhtml';
	const svg = 'http://www.w3.org/2000/svg';

	test( 'Top-level elements have html namespace by default', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `<p>html</p>`
		});

		t.equal( ractive.find( 'p' ).namespaceURI, html );
	});

	test( 'SVG elements have svg namespace', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `<svg><text>svg</text></svg>`
		});

		t.equal( ractive.find( 'svg' ).namespaceURI, svg );
		t.equal( ractive.find( 'text' ).namespaceURI, svg );
	});

	test( 'Top-level elements inherit SVG namespaceURI where appropriate', t => {
		const ractive = new Ractive({
			el: document.createElementNS( svg, 'svg' ),
			template: '<text>svg</text>'
		});

		t.equal( ractive.find( 'text' ).namespaceURI, svg );
	});

	test( 'Triples inside SVG elements result in correct namespaceURI', t => {
		const ractive = new Ractive({
			el: document.createElementNS( svg, 'svg' ),
			template: '{{{code}}}',
			data: {
				code: '<text>works</text>'
			}
		});

		const text = ractive.find( 'text' );
		t.ok( !!text );
		t.equal( text.namespaceURI, svg );
	});

	test( 'Children of foreignObject elements default to html namespace (#713)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: '<svg><foreignObject>
foo
</foreignObject></svg>'
		});

		// We can't do `ractive.find( 'foreignObject' )` because of a longstanding browser bug
		// (https://bugs.webkit.org/show_bug.cgi?id=83438)
		t.equal( ractive.find( 'svg' ).firstChild.namespaceURI, svg );
		t.equal( ractive.find( 'p' ).namespaceURI, html );
	});

	test( 'Top-level elements in components have the correct namespace (#953)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: '<svg><widget message="yup"/></svg>',
			components: {
				widget: Ractive.extend({
					template: '<text>{{message}}</text>'
				})
			}
		});

		t.equal( ractive.find( 'text' ).namespaceURI, svg );
		t.htmlEqual( fixture.innerHTML, '<svg><text>yup</text></svg>' );
	});

	test( 'Custom namespaces are supported (#2038)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `
				<svg xmlns='http://www.w3.org/2000/svg' xmlns:v='http://schemas.microsoft.com/visio/2003/SVGExtensions/' >
					<v:documentProperties v:langID='2057' v:viewMarkup='false'></v:documentProperties>
				</svg>`
		});

		const documentProperties = ractive.find( 'documentProperties' );
		t.equal( documentProperties.namespaceURI, 'http://www.w3.org/2000/svg' );
	});

	test( 'Namespaced attributes are set correctly', t => {
		const ractive = new Ractive({
			template: '<svg><use xlink:href="#yup" /></svg>'
		});

		ractive.render( fixture );

		t.equal(ractive.find('use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'), '#yup');
	});
}
<commit_msg>dammit phantomjs<commit_after>import { test } from 'qunit';
import { svg } from 'config/environment';

if ( svg ) {
	const html = 'http://www.w3.org/1999/xhtml';
	const svg = 'http://www.w3.org/2000/svg';

	test( 'Top-level elements have html namespace by default', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `<p>html</p>`
		});

		t.equal( ractive.find( 'p' ).namespaceURI, html );
	});

	test( 'SVG elements have svg namespace', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `<svg><text>svg</text></svg>`
		});

		t.equal( ractive.find( 'svg' ).namespaceURI, svg );
		t.equal( ractive.find( 'text' ).namespaceURI, svg );
	});

	test( 'Top-level elements inherit SVG namespaceURI where appropriate', t => {
		const ractive = new Ractive({
			el: document.createElementNS( svg, 'svg' ),
			template: '<text>svg</text>'
		});

		t.equal( ractive.find( 'text' ).namespaceURI, svg );
	});

	test( 'Triples inside SVG elements result in correct namespaceURI', t => {
		const ractive = new Ractive({
			el: document.createElementNS( svg, 'svg' ),
			template: '{{{code}}}',
			data: {
				code: '<text>works</text>'
			}
		});

		const text = ractive.find( 'text' );
		t.ok( !!text );
		t.equal( text.namespaceURI, svg );
	});

	test( 'Children of foreignObject elements default to html namespace (#713)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: '<svg><foreignObject>
foo
</foreignObject></svg>'
		});

		// We can't do `ractive.find( 'foreignObject' )` because of a longstanding browser bug
		// (https://bugs.webkit.org/show_bug.cgi?id=83438)
		t.equal( ractive.find( 'svg' ).firstChild.namespaceURI, svg );
		t.equal( ractive.find( 'p' ).namespaceURI, html );
	});

	test( 'Top-level elements in components have the correct namespace (#953)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: '<svg><widget message="yup"/></svg>',
			components: {
				widget: Ractive.extend({
					template: '<text>{{message}}</text>'
				})
			}
		});

		t.equal( ractive.find( 'text' ).namespaceURI, svg );
		t.htmlEqual( fixture.innerHTML, '<svg><text>yup</text></svg>' );
	});

	test( 'Custom namespaces are supported (#2038)', t => {
		const ractive = new Ractive({
			el: fixture,
			template: `
				<svg xmlns='http://www.w3.org/2000/svg' xmlns:v='http://schemas.microsoft.com/visio/2003/SVGExtensions/' >
					<v:documentProperties v:langID='2057' v:viewMarkup='false'></v:documentProperties>
				</svg>`
		});

		const documentProperties = fixture.firstElementChild.firstElementChild;
		t.equal( documentProperties.namespaceURI, 'http://www.w3.org/2000/svg' );
	});

	test( 'Namespaced attributes are set correctly', t => {
		const ractive = new Ractive({
			template: '<svg><use xlink:href="#yup" /></svg>'
		});

		ractive.render( fixture );

		t.equal(ractive.find('use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'), '#yup');
	});
}
<|endoftext|>


<commit_before>var feeds = {};

/**
 * Shows a UI to request an OAuth token. This should only be called in response
 * to user interaction to avoid confusing the user. Since the resulting window
 * is shown with no standard window decorations, it can end up below all other
 * windows, with no way to detect that it was shown, and no way to reposition
 * it either.
 */
feeds.requestInteractiveAuthToken = function() {
  chrome.identity.getAuthToken({'interactive': true}, function (accessToken) {
    if (chrome.runtime.lastError || !authToken) return;

    feeds.fetchCalendars();
  });
};

/**
 * Sends a request to fetch the list of calendars for the currently-logged in
 * user. When calendars are received, it automatically initiates a request
 * for events from those calendars.
 */
feeds.fetchCalendars = function(type) {
  chrome.extension.sendMessage({method: 'sync-icon.spinning.start'});

  chrome.storage.local.get('calendars', function(storage) {

    var storedCalendars = storage['calendars'] || {};
    chrome.identity.getAuthToken({'interactive': true}, function (authToken) {
      if (chrome.runtime.lastError) {
        chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
        feeds.refreshUI();
        return;
      }

      $.ajax(constants.CALENDAR_LIST_API_URL, {
        headers: {
          'Authorization': 'Bearer ' + authToken
        },
        success: function(data) {
          var calendars = {};

          _.each(data.items, function(calendar){
            var storedCalendar = storedCalendars[calendar.id] || {};
            var calendarSelected = (calendar.selected)? true : false;

            var visible = (typeof storedCalendar.selected !== 'undefined') ?
                storedCalendar.selected : calendar.selected;

            var mergedCalendar = {
              id: calendar.id,
              summary: calendar.summary,
              accessRole: calendar.accessRole == 'writer' || calendar.accessRole == 'owner',
              description: calendar.description || '',
              foregroundColor: calendar.foregroundColor,
              backgroundColor: calendar.backgroundColor,
              colorId: calendar.colorId || '',
              selected: visible
            };

            if(storedCalendar.selected !== calendarSelected){
              // feeds.putCalendars(mergedCalendar, function(obj){});
            }

            calendars[calendar.id] = mergedCalendar;
          });

          chrome.storage.local.set({'calendars': calendars}, function() {
            if (chrome.runtime.lastError) {
              background.log('Error saving settings: ', chrome.runtime.lastError.message);
              return;
            }
            feeds.refreshUI();
          });
        },
        error: function(response) {
          chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});

          if (response.status === 401) {
            // feeds.refreshUI();
            chrome.identity.removeCachedAuthToken({ 'token': authToken }, function() {});
          }
        }
      });

    });
  });
};

feeds.putCalendars = function(feed, callback){
  var feedUrl = constants.CALENDAR_LIST_API_URL + '/' + encodeURIComponent(feed.id) + '?' + 'colorRgbFormat=false&fields=colorId,selected';

  var obj = JSON.stringify({
    "selected": (feed.visible)? true : false,
    "colorId": (feed.colorId)? feed.colorId : 11
  });

  console.log(feedUrl);
  console.log(obj);

  chrome.identity.getAuthToken({'interactive': false}, function (authToken) {
    if (chrome.runtime.lastError || !authToken) {
      chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
      return;
    }

    $.ajax({
      type: 'PUT',
      url: feedUrl,
      headers: {
        'Authorization': 'Bearer ' + authToken
      },
      data: obj,
      contentType: "application/json",
      success: (function(resp) {
        console.log('Title: ' + resp.summary + " - selected: " + resp.selected + ' - colorID: ' + resp.colorId + ' - id: ' + resp.id);
        callback(null);

      })(feed),
      error: function(response) {
        chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
        if (response.status === 401) {
          // feeds.refreshUI();
          chrome.identity.removeCachedAuthToken({ 'token': authToken }, function() {});
        }
        // Must callback here, otherwise the caller keeps waiting for all calendars to load.
        callback(response);
      }
    });
  });
};

feeds.updateSets = function(){
  chrome.extension.sendMessage({method: 'sync-icon.spinning.start'});

  chrome.storage.local.get('calendars', function(calendarsObj) {
    chrome.storage.local.get('sets', function(setsObj) {

      var storedCalendars = calendarsObj['calendars'] || {};
      var storedSets = setsObj['sets'] || {};
      var newStoredCalendars = {};

      var setsObj = _.filter(storedSets, function(obj){
        return obj.selected === true;
      });

      _.each(storedCalendars, function(calendar){
        var calenderSelected = _.find(setsObj[0].selection, function(item){
          return calendar.id === item;
        });

        var mergedCalendar = {
          id: calendar.id,
          summary: calendar.summary,
          accessRole: calendar.accessRole == 'writer' || calendar.accessRole == 'owner',
          description: calendar.description || '',
          foregroundColor: calendar.foregroundColor,
          backgroundColor: calendar.backgroundColor,
          colorId: calendar.colorId || '',
          selected: false
        };

        if(calenderSelected){
          mergedCalendar.selected = true;
        }

        newStoredCalendars[mergedCalendar.id] = mergedCalendar;
      });

      async.each(newStoredCalendars, function(obj, callback){
        _.defer(function(){
          feeds.putCalendars(obj, function(response){callback(response)});
        });

      }, function(error){

        // if any of the file processing produced an error, err would equal that error
        if( error ) {
          // One of the iterations produced an error.
          // All processing will now stop.
          console.log('A file failed to process');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
        } else {
          console.log('All files have been processed successfully');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
        }
      });
    });
  });
};

/**
 * Updates the 'minutes/hours/days until' visible badge from the events
 * obtained during the last fetch. Does not fetch new data.
 */
feeds.refreshUI = function() {

  // Notify the browser action in case it's open.
  chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
  chrome.extension.sendMessage({method: 'ui.refresh'});
};
<commit_msg>Change api call<commit_after>var feeds = {};

/**
 * Shows a UI to request an OAuth token. This should only be called in response
 * to user interaction to avoid confusing the user. Since the resulting window
 * is shown with no standard window decorations, it can end up below all other
 * windows, with no way to detect that it was shown, and no way to reposition
 * it either.
 */
feeds.requestInteractiveAuthToken = function() {
  chrome.identity.getAuthToken({'interactive': true}, function (accessToken) {
    if (chrome.runtime.lastError || !authToken) return;

    feeds.fetchCalendars();
  });
};

/**
 * Sends a request to fetch the list of calendars for the currently-logged in
 * user. When calendars are received, it automatically initiates a request
 * for events from those calendars.
 */
feeds.fetchCalendars = function(type) {
  chrome.extension.sendMessage({method: 'sync-icon.spinning.start'});

  chrome.storage.local.get('calendars', function(storage) {

    var storedCalendars = storage['calendars'] || {};
    chrome.identity.getAuthToken({'interactive': true}, function (authToken) {
      if (chrome.runtime.lastError) {
        chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
        feeds.refreshUI();
        return;
      }

      $.ajax(constants.CALENDAR_LIST_API_URL, {
        headers: {
          'Authorization': 'Bearer ' + authToken
        },
        success: function(data) {
          var calendars = {};

          _.each(data.items, function(calendar){
            var storedCalendar = storedCalendars[calendar.id] || {};
            var calendarSelected = (calendar.selected)? true : false;

            var visible = (typeof storedCalendar.selected !== 'undefined') ?
                storedCalendar.selected : calendar.selected;

            var mergedCalendar = {
              id: calendar.id,
              summary: calendar.summary,
              accessRole: calendar.accessRole == 'writer' || calendar.accessRole == 'owner',
              description: calendar.description || '',
              foregroundColor: calendar.foregroundColor,
              backgroundColor: calendar.backgroundColor,
              colorId: calendar.colorId || '',
              selected: visible
            };

            if(storedCalendar.selected !== calendarSelected){
              // feeds.putCalendars(mergedCalendar, function(obj){});
            }

            calendars[calendar.id] = mergedCalendar;
          });

          chrome.storage.local.set({'calendars': calendars}, function() {
            if (chrome.runtime.lastError) {
              background.log('Error saving settings: ', chrome.runtime.lastError.message);
              return;
            }
            feeds.refreshUI();
          });
        },
        error: function(response) {
          chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});

          if (response.status === 401) {
            // feeds.refreshUI();
            chrome.identity.removeCachedAuthToken({ 'token': authToken }, function() {});
          }
        }
      });

    });
  });
};

feeds.putCalendars = function(feed, callback){
  var feedUrl = constants.CALENDAR_LIST_API_URL + '/' + encodeURIComponent(feed.id) + '?' + 'colorRgbFormat=false&fields=colorId,selected';

  var obj = JSON.stringify({
    "selected": (feed.visible)? true : false,
    "colorId": (feed.colorId)? feed.colorId : 11
  });

  console.log(feedUrl);
  console.log(obj);

  chrome.identity.getAuthToken({'interactive': false}, function (authToken) {
    if (chrome.runtime.lastError || !authToken) {
      chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
      return;
    }

    $.ajax({
      type: 'PUT',
      url: feedUrl,
      headers: {
        'Authorization': 'Bearer ' + authToken
      },
      data: obj,
      contentType: "application/json",
      success: (function(response) {
        console.log(response);
        // console.log('Title: ' + resp.summary + " - selected: " + resp.selected + ' - colorID: ' + resp.colorId + ' - id: ' + resp.id);
        callback(null);

      }), error: function(response) {
        chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
        if (response.status === 401) {
          // feeds.refreshUI();
          chrome.identity.removeCachedAuthToken({ 'token': authToken }, function() {});
        }
        // Must callback here, otherwise the caller keeps waiting for all calendars to load.
        callback(response);
      }
    });
  });
};

feeds.updateSets = function(){
  chrome.extension.sendMessage({method: 'sync-icon.spinning.start'});

  chrome.storage.local.get('calendars', function(calendarsObj) {
    chrome.storage.local.get('sets', function(setsObj) {

      var storedCalendars = calendarsObj['calendars'] || {};
      var storedSets = setsObj['sets'] || {};
      var newStoredCalendars = {};

      var setsObj = _.filter(storedSets, function(obj){
        return obj.selected === true;
      });

      _.each(storedCalendars, function(calendar){
        var calenderSelected = _.find(setsObj[0].selection, function(item){
          return calendar.id === item;
        });

        var mergedCalendar = {
          id: calendar.id,
          summary: calendar.summary,
          accessRole: calendar.accessRole == 'writer' || calendar.accessRole == 'owner',
          description: calendar.description || '',
          foregroundColor: calendar.foregroundColor,
          backgroundColor: calendar.backgroundColor,
          colorId: calendar.colorId || '',
          selected: false
        };

        if(calenderSelected){
          mergedCalendar.selected = true;
        }

        newStoredCalendars[mergedCalendar.id] = mergedCalendar;
      });

      async.each(newStoredCalendars, function(obj, callback){
        _.defer(function(){
          feeds.putCalendars(obj, function(response){callback(response)});
        });

      }, function(error){

        // if any of the file processing produced an error, err would equal that error
        if( error ) {
          // One of the iterations produced an error.
          // All processing will now stop.
          console.log('A file failed to process');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
        } else {
          console.log('All files have been processed successfully');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
          console.log('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++');
        }
      });
    });
  });
};

/**
 * Updates the 'minutes/hours/days until' visible badge from the events
 * obtained during the last fetch. Does not fetch new data.
 */
feeds.refreshUI = function() {

  // Notify the browser action in case it's open.
  chrome.extension.sendMessage({method: 'sync-icon.spinning.stop'});
  chrome.extension.sendMessage({method: 'ui.refresh'});
};
<|endoftext|>


<commit_before>// Description:
//   Allows running some simple volume controls on a Sonos system
//
// Dependencies:
//   "sonos": "^0.8.0"
//
// Configuration:
//   HUBOT_SONOS_HOST
//   HUBOT_SONOS_PORT
//   HUBOT_SONOS_MAX_VOLUME
//
// Commands:
//   hubot vol + - Increase the volume a little
//   hubot vol - - Decrease the volume a little
//   hubot vol ++ - Increase the volume by quite a bit
//   hubot vol -- - Decrease the volume by quite a bit
//   hubot set vol to <number> - Set the volume to a specific number (between 0 and 100)
//   hubot lock vol at <number> - Lock the volume for a few minutes at a specific volume
//   hubot mute - Set the volume to 0
//   hubot unmute - Restore volume to level before mute
//
// Author:
//   berg, jballant
//

var logger = require('./support/logger');
var sonos = require('sonos');

var DEFAULT_MAX_VOL = 50;

var maxVol = DEFAULT_MAX_VOL;

var savedVolume = null;

var sonosInstance;

var volumeIsLocked = false;

function getSonos() {

    var host;
    var port;
    if (!sonosInstance) {
        host = process.env.HUBOT_SONOS_HOST || '192.168.96.150';
        port = process.env.HUBOT_SONOS_PORT || 1400;
        maxVol = process.env.HUBOT_SONOS_MAX_VOLUME || DEFAULT_MAX_VOL;
        maxVol = parseInt(maxVol, 10);
        sonosInstance = new sonos.Sonos(host, 1400);
    }
    return sonosInstance;
}

function getVolume (callback) {
    logInfo('Getting sonos volume...');
    getSonos().getVolume(function (err, vol) {
        if (vol) {
            logInfo('Got sonos volume %s', vol);
        }
        callback(err, vol);
    });
}

function calcNewVolume (vol) {
    var ratio = 100 / maxVol;
    logInfo('Calculating new volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.floor(vol / ratio);
}

function calcOrigVolume (vol) {
    var ratio = 100 / maxVol;
    logInfo('Calculating original user input volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.ceil(vol * ratio);
}

function calcVolIncrease (currVol, amount) {
    var vol = calcOrigVolume(currVol) + amount;
    return calcNewVolume(vol);
}

function setVolumeTo (newVolume, callback) {
    if (newVolume < 0) {
        logInfo('New volume less than 0, using 0 instead');
        newVolume = 0;
    } else if (newVolume > maxVol) {
        logInfo('New volume greater than max, using max volume');
        newVolume = maxVol;
    }
    logInfo('Setting sonos volume to %s', String(newVolume));
    getSonos().setVolume(newVolume, callback);
}

function increaseVolByAmount (amount, callback) {
    logInfo('Increasing volume by %s', amount);
    getVolume(function (err, volume) {
        if (err) {
            return callback(err);
        }
        volume = parseInt(volume, 10);
        var newVol = calcNewVolume(calcOrigVolume(volume) + amount);
        logInfo('Calculated new sonos volume: %s', newVol);
        setVolumeTo(newVol, callback);
    });
}

function logErr (err) {
    logger.error(err);
}

function logInfo () {
    logger.minorInfo.apply(logger, arguments);
}

function sendErrorMessage (msg, err) {
    if (err && err.message) {
        logErr(err);
    }
    var text = (err && typeof err === 'string') ? err : 'An error occurred communicating with Sonos';
    return msg.send(text);
}

function lockVolume () {
    var duration = (3 * 60 * 1000);
    logInfo('Locking sonos volume for %s seconds', duration / 1000);
    volumeIsLocked = true;
    setTimeout(function () {
        volumeIsLocked = false;
        logInfo('Volume is now unlocked');
    }, duration);
}

module.exports = function(robot) {

    function checkInputVolume (volume, msg) {
        volume = (volume || volume === 0) ? parseInt(volume, 10) : null;
        if (volume === null || isNaN(volume)) {
            sendErrorMessage(msg, 'Unable to parse volume param');
        }
        return volume < 0 ? 0 : (volume > 100) ? 100 : volume;
    }

    function getVolumeWithMsg (msg, text) {
        getVolume(function (err, volume) {
            if (err) { return sendErrorMessage(msg, err); }
            text = text || 'Volume set to ';
            msg.send(text + calcOrigVolume(volume));
        });
    }

    function sendVolLockedMessage (msg) {
        return sendErrorMessage(msg, ':no_good: Volume is currently locked');
    }

    function increaseVolByAmountWithMessage (msg, amount) {
        if (volumeIsLocked) {
            sendVolLockedMessage(msg);
        }
        increaseVolByAmount(amount, function (err) {
            if (err) { return sendErrorMessage(msg, err); }
            getVolumeWithMsg(msg);
        });
    }

    function setVolumeWithMessage (volume, msg, text) {
        var newVol;
        volume = checkInputVolume(volume, msg);
        newVol = calcNewVolume(volume);
        if (volumeIsLocked) {
            sendVolLockedMessage(msg);
        }
        setVolumeTo(newVol, function (err) {
            if (err) {
                return sendErrorMessage(msg);
            }
            text = text || 'Volume set to ';
            msg.send(text + volume);
        });
    }

    robot.respond(/vol(?:ume)? ?\+$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, 7);
    });

    robot.respond(/vol(?:ume)? ?\-$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, -7);
    });

    robot.respond(/vol(?:ume)? ?\+\+$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, 14);
    });

    robot.respond(/vol(?:ume)? ?\-\-$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, -14);
    });

    robot.respond(/vol(?:ume)?\?/i, function (msg) {
        getVolumeWithMsg(msg, 'Volume is currently ');
    });

    robot.respond(/set vol(?:ume)? to (\d+)/i, function (msg) {
        if (volumeIsLocked) {
            return sendErrorMessage(msg, ':no_good: Volume is currently locked');
        }

        var volume = msg.match[1];
        setVolumeWithMessage(volume, msg);
    });

    robot.respond(/ mute$/i, function (msg) {
        getVolume(function (err, vol) {
            if (err) {
                return sendErrorMessage(msg);
            }
            savedVolume = calcOrigVolume(parseInt(vol, 10));
            setVolumeWithMessage(0, msg);
        });
    });

    robot.respond(/ unmute$/i, function (msg) {
        if (savedVolume !== null) {
            setVolumeWithMessage(savedVolume, msg);
            savedVolume = null;
        } else {
            msg.send('Volume is not muted');
        }
    });

    robot.respond(/lock vol(?:ume)? at (\d+)/i, function (msg) {
        var volume = msg.match[1];
        volume = checkInputVolume(volume);
        if (volume > 70 || volume < 40) {
            return sendErrorMessage(msg, ':no_good: You may only lock at reasonable volumes');
        }
        lockVolume();
        setVolumeWithMessage(volume, msg, 'Volume locked at ');
    });
};
<commit_msg>Fix issue with lock volume<commit_after>// Description:
//   Allows running some simple volume controls on a Sonos system
//
// Dependencies:
//   "sonos": "^0.8.0"
//
// Configuration:
//   HUBOT_SONOS_HOST
//   HUBOT_SONOS_PORT
//   HUBOT_SONOS_MAX_VOLUME
//
// Commands:
//   hubot vol + - Increase the volume a little
//   hubot vol - - Decrease the volume a little
//   hubot vol ++ - Increase the volume by quite a bit
//   hubot vol -- - Decrease the volume by quite a bit
//   hubot set vol to <number> - Set the volume to a specific number (between 0 and 100)
//   hubot lock vol at <number> - Lock the volume for a few minutes at a specific volume
//   hubot mute - Set the volume to 0
//   hubot unmute - Restore volume to level before mute
//
// Author:
//   berg, jballant
//

var logger = require('./support/logger');
var sonos = require('sonos');

var DEFAULT_MAX_VOL = 50;

var maxVol = DEFAULT_MAX_VOL;

var savedVolume = null;

var sonosInstance;

var volumeIsLocked = false;

function getSonos() {

    var host;
    var port;
    if (!sonosInstance) {
        host = process.env.HUBOT_SONOS_HOST || '192.168.96.150';
        port = process.env.HUBOT_SONOS_PORT || 1400;
        maxVol = process.env.HUBOT_SONOS_MAX_VOLUME || DEFAULT_MAX_VOL;
        maxVol = parseInt(maxVol, 10);
        sonosInstance = new sonos.Sonos(host, 1400);
    }
    return sonosInstance;
}

function getVolume (callback) {
    logInfo('Getting sonos volume...');
    getSonos().getVolume(function (err, vol) {
        if (vol) {
            logInfo('Got sonos volume %s', vol);
        }
        callback(err, vol);
    });
}

function calcNewVolume (vol) {
    var ratio = 100 / maxVol;
    logInfo('Calculating new volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.floor(vol / ratio);
}

function calcOrigVolume (vol) {
    var ratio = 100 / maxVol;
    logInfo('Calculating original user input volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.ceil(vol * ratio);
}

function calcVolIncrease (currVol, amount) {
    var vol = calcOrigVolume(currVol) + amount;
    return calcNewVolume(vol);
}

function setVolumeTo (newVolume, callback) {
    if (newVolume < 0) {
        logInfo('New volume less than 0, using 0 instead');
        newVolume = 0;
    } else if (newVolume > maxVol) {
        logInfo('New volume greater than max, using max volume');
        newVolume = maxVol;
    }
    logInfo('Setting sonos volume to %s', String(newVolume));
    getSonos().setVolume(newVolume, callback);
}

function increaseVolByAmount (amount, callback) {
    logInfo('Increasing volume by %s', amount);
    getVolume(function (err, volume) {
        if (err) {
            return callback(err);
        }
        volume = parseInt(volume, 10);
        var newVol = calcNewVolume(calcOrigVolume(volume) + amount);
        logInfo('Calculated new sonos volume: %s', newVol);
        setVolumeTo(newVol, callback);
    });
}

function logErr (err) {
    logger.error(err);
}

function logInfo () {
    logger.minorInfo.apply(logger, arguments);
}

function sendErrorMessage (msg, err) {
    if (err && err.message) {
        logErr(err);
    }
    var text = (err && typeof err === 'string') ? err : 'An error occurred communicating with Sonos';
    return msg.send(text);
}

function lockVolume () {
    var duration = (3 * 60 * 1000);
    logInfo('Locking sonos volume for %s seconds', duration / 1000);
    volumeIsLocked = true;
    setTimeout(function () {
        volumeIsLocked = false;
        logInfo('Volume is now unlocked');
    }, duration);
}

module.exports = function(robot) {

    function checkInputVolume (volume, msg) {
        volume = (volume || volume === 0) ? parseInt(volume, 10) : null;
        if (volume === null || isNaN(volume)) {
            sendErrorMessage(msg, 'Unable to parse volume param');
        }
        return volume < 0 ? 0 : (volume > 100) ? 100 : volume;
    }

    function getVolumeWithMsg (msg, text) {
        getVolume(function (err, volume) {
            if (err) { return sendErrorMessage(msg, err); }
            text = text || 'Volume set to ';
            msg.send(text + calcOrigVolume(volume));
        });
    }

    function sendVolLockedMessage (msg) {
        return sendErrorMessage(msg, ':no_good: Volume is currently locked');
    }

    function increaseVolByAmountWithMessage (msg, amount) {
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        increaseVolByAmount(amount, function (err) {
            if (err) { return sendErrorMessage(msg, err); }
            getVolumeWithMsg(msg);
        });
    }

    function setVolumeWithMessage (volume, msg, text) {
        var newVol;
        volume = checkInputVolume(volume, msg);
        newVol = calcNewVolume(volume);
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        setVolumeTo(newVol, function (err) {
            if (err) {
                return sendErrorMessage(msg);
            }
            text = text || 'Volume set to ';
            msg.send(text + volume);
        });
    }

    robot.respond(/vol(?:ume)? ?\+$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, 7);
    });

    robot.respond(/vol(?:ume)? ?\-$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, -7);
    });

    robot.respond(/vol(?:ume)? ?\+\+$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, 14);
    });

    robot.respond(/vol(?:ume)? ?\-\-$/i, function(msg) {
        increaseVolByAmountWithMessage(msg, -14);
    });

    robot.respond(/vol(?:ume)?\?/i, function (msg) {
        getVolumeWithMsg(msg, 'Volume is currently ');
    });

    robot.respond(/set vol(?:ume)? to (\d+)$/i, function (msg) {
        if (volumeIsLocked) {
            return sendErrorMessage(msg, ':no_good: Volume is currently locked');
        }

        var volume = msg.match[1];
        setVolumeWithMessage(volume, msg);
    });

    robot.respond(/ mute$/i, function (msg) {
        getVolume(function (err, vol) {
            if (err) {
                return sendErrorMessage(msg);
            }
            savedVolume = calcOrigVolume(parseInt(vol, 10));
            setVolumeWithMessage(0, msg);
        });
    });

    robot.respond(/ unmute$/i, function (msg) {
        if (savedVolume !== null) {
            setVolumeWithMessage(savedVolume, msg);
            savedVolume = null;
        } else {
            msg.send('Volume is not muted');
        }
    });

    robot.respond(/lock vol(?:ume)? at (\d+)$/i, function (msg) {
        var volume = msg.match[1];
        volume = checkInputVolume(volume);
        if (volume > 70 || volume < 40) {
            return sendErrorMessage(msg, ':no_good: You may only lock at reasonable volumes');
        }
        setVolumeWithMessage(volume, msg, 'Volume locked at ');
        lockVolume();
    });
};
<|endoftext|>


<commit_before>// Description:
//   Allows running some simple volume controls on a Sonos system
//
// Dependencies:
//   "sonos": "^0.8.0"
//
// Configuration:
//   HUBOT_SONOS_HOST
//   HUBOT_SONOS_PORT
//   HUBOT_SONOS_MAX_VOLUME
//
// Commands:
//   hubot vol + - Increase the volume a little
//   hubot vol - - Decrease the volume a little
//   hubot vol ++ - Increase the volume by quite a bit
//   hubot vol -- - Decrease the volume by quite a bit
//   hubot set vol to <number> - Set the volume to a specific number (between 0 and 100)
//   hubot lock vol at <number> - Lock the volume for a few minutes at a specific volume
//   hubot mute - Set the volume to 0
//   hubot unmute - Restore volume to level before mute
//
// Author:
//   berg, jballant
//

var logger = require('./support/logger');
var sonos = require('sonos');

var DEFAULT_MAX_VOL = 50;

var MAX_PUBLIC_VOLUME = 100;
var MIN_PUBLIC_VOLUME = 0;

var MAX_VOLS = {
    main: DEFAULT_MAX_VOL,
    bar: 60
};

var VOL_RATIOS = {
    main: MAX_PUBLIC_VOLUME / MAX_VOLS.main,
    bar: MAX_PUBLIC_VOLUME / MAX_VOLS.bar
};

var savedVolume = null;

var sonosInstance = [];

var volumeIsLocked = false;

var SONOS_HOST_MAP = {
    main: process.env.HUBOT_SONOS_HOST,
    bar : process.env.HUBOT_SONOS_BAR_HOST
};

var SONOS_PORT_MAP = {
    main: process.env.HUBOT_SONOS_PORT,
    bar : process.env.HUBOT_SONOS_PORT
};

var SONOS_SPEAKERS = {
    main: 'main',
    bar: 'bar'
};

function getSonos(type) {
    var host;
    var port;

    type = type || 'main';

    if (!sonosInstance[type]) {
        host = SONOS_HOST_MAP[type];
        port = SONOS_PORT_MAP[type] || 1400;
        MAX_VOLS[type] = parseInt(MAX_VOLS[type], 10);
        sonosInstance[type] = new sonos.Sonos(host, port);
    }
    return sonosInstance[type];
}

function getPrivateVolume (callback, type) {
    logInfo('Getting sonos volume for %s speaker...', type);
    getSonos(type).getVolume(function (err, vol) {
        if (vol) {
            logInfo('Got sonos volume for %s speaker: %s ', type, vol);
        }
        callback(err, vol);
    });
}

/**
 * Give the public facing volume, and this will translate it into
 * the value to use privately
 *
 * @param vol
 * @param type
 * @returns {number}
 */
function publicToPrivateVolume (vol, type) {
    var ratio = VOL_RATIOS[type];
    logInfo('Calculating new volume for %s with input "%s" and ratio to sonos "%s"', type, vol, ratio);
    return Math.floor(vol / ratio);
}

/**
 * Give the private volume, and this will translate it into
 * the public facing integer
 *
 * @param vol
 * @param type
 * @returns {number}
 */
function privateToPublicVolume (vol, type) {
    var ratio = VOL_RATIOS[type];
    logInfo('Calculating original user input volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.ceil(vol * ratio);
}

function setVolumeTo (newVolume, callback, type) {
    if (newVolume < 0) {
        logInfo('New volume less than 0, using 0 instead');
        newVolume = 0;
    } else if (newVolume > MAX_VOLS[type]) {
        logInfo('New volume greater than max, using max volume');
        newVolume = MAX_VOLS[type];
    }
    logInfo('Setting sonos volume to %s', String(newVolume));
    getSonos(type).setVolume(newVolume, callback);
}

function increaseVolByAmount (amount, callback, type) {
    logInfo('Increasing volume by %s', amount);
    getPrivateVolume(function (err, volume) {
        if (err) {
            return callback(err);
        }
        volume = parseInt(volume, 10);
        var newVol = publicToPrivateVolume(privateToPublicVolume(volume) + amount, type);
        logInfo('Calculated new sonos volume: %s', newVol);
        setVolumeTo(newVol, callback, type);
    });
}

function logErr (err) {
    logger.error(err);
}

function logInfo () {
    logger.minorInfo.apply(logger, arguments);
}

function sendErrorMessage (msg, err) {
    if (err && err.message) {
        logErr(err);
    }
    var text = (err && typeof err === 'string') ? err : 'An error occurred communicating with Sonos';
    return msg.send(text);
}

function lockVolume () {
    var duration = (3 * 60 * 1000);
    logInfo('Locking sonos volume for %s seconds', duration / 1000);
    volumeIsLocked = true;
    setTimeout(function () {
        volumeIsLocked = false;
        logInfo('Volume is now unlocked');
    }, duration);
}

var volumeKeywords = {
    '💯' : MAX_PUBLIC_VOLUME,
    ':100:' : MAX_PUBLIC_VOLUME,
    'max' : MAX_PUBLIC_VOLUME
};

var relativeVolumeKeywords = {
    '-' : function (curVolume) {
        return curVolume - 7;
    },
    '--' : function (curVolume) {
        return curVolume - 14;
    },
    '+' : function (curVolume) {
        return curVolume + 7;
    },
    '++' : function (curVolume) {
        return curVolume + 14;
    }
};

function boundPublicVolume (vol) {
    return Math.min(MAX_PUBLIC_VOLUME, Math.max(0, vol));
}

/**
 * This will be able to handle things like emoji/keywords
 *
 * @param volume
 * @param callback
 * @param type
 */
function volumeToInt(volume, callback, type) {
    var vol = (volume + '').replace(/^\s+|\s+$/g, '');
    if ((/^\d+$/).test(vol)) {
        callback(null, boundPublicVolume(parseInt(vol, 10)));
        return;
    }
    if (volumeKeywords.hasOwnProperty(vol)) {
        callback(null, boundPublicVolume(volumeKeywords[vol]));
        return;
    }
    if (relativeVolumeKeywords.hasOwnProperty(vol)) {
        getPrivateVolume(function (err, privateVolume) {
            if (err) {
                callback(err);
                return;
            }
            var currentVolume = privateToPublicVolume(privateVolume, type);
            callback(null, boundPublicVolume((relativeVolumeKeywords[vol])(currentVolume)));
        }, type);
        return;
    }
    callback('Unable to parse volume param: ' + volume);
}

module.exports = function(robot) {

    function getVolumeWithMsg (msg, text, type) {
        type = type || 'main';
        getPrivateVolume(function (err, volume) {
            if (err) { return sendErrorMessage(msg, err); }
            text = text || 'Volume set to ';
            msg.send(text + privateToPublicVolume(volume, type));
        }, type);
    }

    function sendVolLockedMessage (msg) {
        return sendErrorMessage(msg, ':no_good: Volume is currently locked');
    }

    function increaseVolByAmountWithMessage (msg, amount, type) {
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        increaseVolByAmount(amount, function (err) {
            if (err) { return sendErrorMessage(msg, err); }
            getVolumeWithMsg(msg);
        }, type);
    }

    function setVolumeWithMessage (volume, msg, text, type) {
        var newVol;
        volume = boundPublicVolume(volume);
        newVol = publicToPrivateVolume(volume, type);
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        setVolumeTo(newVol, function (err) {
            if (err) {
                return sendErrorMessage(msg);
            }
            text = text || 'Volume set to ';
            msg.send(text + volume);
        }, type);
    }

    function getSpeakerType(match) {
        var type = match || 'main';
        return type.trim();
    }

    robot.respond(/(.* )?vol(?:ume)?\?/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        getVolumeWithMsg(msg, 'Volume is currently ', type);
    });

    robot.respond(/music status\??/i, function (message) {
        return getVolumeWithMsg(message, 'Volume is currently ');
    });

    robot.respond(/music status\??/i, function (message) {
        return getVolumeWithMsg(message, 'Bar Volume is currently ', 'bar');
    });

    robot.respond(/(?:set )?(.* )?vol(?:ume)?(?: to)? (.+)$/i, function (msg) {
        if (volumeIsLocked) {
            return sendErrorMessage(msg, ':no_good: Volume is currently locked');
        }

        var type = getSpeakerType(msg.match[1]);
        var volume = msg.match[2];

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        volumeToInt(volume, function (err, volume) {
            if (err) {
                sendErrorMessage(err);
                return;
            }
            setVolumeWithMessage(volume, msg, '', type);
        }, type);
    });

    robot.respond(/ mute( .*)?$/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        getPrivateVolume(function (err, vol) {
            if (err) {
                return sendErrorMessage(msg);
            }
            savedVolume = privateToPublicVolume(parseInt(vol, 10), type);
            setVolumeWithMessage(0, msg, '', type);
        }, type);
    });

    robot.respond(/ unmute( .*)?$/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        if (savedVolume !== null) {
            setVolumeWithMessage(savedVolume, msg, '', type);
            savedVolume = null;
        } else {
            msg.send('Volume is not muted');
        }
    });

    robot.respond(/lock vol(?:ume)? at (.+)$/i, function (msg) {
        var volume = msg.match[1];
        volumeToInt(volume, function (err, volume) {
            if (err) {
                sendErrorMessage(err);
                return;
            }
            if (volume > 70 || volume < 40) {
                return sendErrorMessage(msg, ':no_good: You may only lock at reasonable volumes');
            }
            setVolumeWithMessage(volume, msg, 'Volume locked at ');
            lockVolume();
        });
    });

    return robot.router.get("/hubot/volume-decay", function(req, res) {
        var type = SONOS_SPEAKERS.main;
        logInfo('Decaying volume by 5');
        res.end('');
        getPrivateVolume(function (err, privateVolume) {
            var currentVolume = privateToPublicVolume(privateVolume);
            var targetVolume = currentVolume - 5;
            logInfo('Old volume: ' + currentVolume);
            logInfo('New volume: ' + targetVolume);
            setVolumeTo(publicToPrivateVolume(targetVolume), function (res) {
                logInfo(res);
            }, type);
        }, type);
    });

};
<commit_msg>Fixing volume decay endpoint<commit_after>// Description:
//   Allows running some simple volume controls on a Sonos system
//
// Dependencies:
//   "sonos": "^0.8.0"
//
// Configuration:
//   HUBOT_SONOS_HOST
//   HUBOT_SONOS_PORT
//   HUBOT_SONOS_MAX_VOLUME
//
// Commands:
//   hubot vol + - Increase the volume a little
//   hubot vol - - Decrease the volume a little
//   hubot vol ++ - Increase the volume by quite a bit
//   hubot vol -- - Decrease the volume by quite a bit
//   hubot set vol to <number> - Set the volume to a specific number (between 0 and 100)
//   hubot lock vol at <number> - Lock the volume for a few minutes at a specific volume
//   hubot mute - Set the volume to 0
//   hubot unmute - Restore volume to level before mute
//
// Author:
//   berg, jballant
//

var logger = require('./support/logger');
var sonos = require('sonos');

var DEFAULT_MAX_VOL = 50;

var MAX_PUBLIC_VOLUME = 100;
var MIN_PUBLIC_VOLUME = 0;

var MAX_VOLS = {
    main: DEFAULT_MAX_VOL,
    bar: 60
};

var VOL_RATIOS = {
    main: MAX_PUBLIC_VOLUME / MAX_VOLS.main,
    bar: MAX_PUBLIC_VOLUME / MAX_VOLS.bar
};

var savedVolume = null;

var sonosInstance = [];

var volumeIsLocked = false;

var SONOS_HOST_MAP = {
    main: process.env.HUBOT_SONOS_HOST,
    bar : process.env.HUBOT_SONOS_BAR_HOST
};

var SONOS_PORT_MAP = {
    main: process.env.HUBOT_SONOS_PORT,
    bar : process.env.HUBOT_SONOS_PORT
};

var SONOS_SPEAKERS = {
    main: 'main',
    bar: 'bar'
};

function getSonos(type) {
    var host;
    var port;

    type = type || 'main';

    if (!sonosInstance[type]) {
        host = SONOS_HOST_MAP[type];
        port = SONOS_PORT_MAP[type] || 1400;
        MAX_VOLS[type] = parseInt(MAX_VOLS[type], 10);
        sonosInstance[type] = new sonos.Sonos(host, port);
    }
    return sonosInstance[type];
}

function getPrivateVolume (callback, type) {
    logInfo('Getting sonos volume for %s speaker...', type);
    getSonos(type).getVolume(function (err, vol) {
        if (vol) {
            logInfo('Got sonos volume for %s speaker: %s ', type, vol);
        }
        callback(err, vol);
    });
}

/**
 * Give the public facing volume, and this will translate it into
 * the value to use privately
 *
 * @param vol
 * @param type
 * @returns {number}
 */
function publicToPrivateVolume (vol, type) {
    var ratio = VOL_RATIOS[type];
    logInfo('Calculating new volume for %s with input "%s" and ratio to sonos "%s"', type, vol, ratio);
    return Math.floor(vol / ratio);
}

/**
 * Give the private volume, and this will translate it into
 * the public facing integer
 *
 * @param vol
 * @param type
 * @returns {number}
 */
function privateToPublicVolume (vol, type) {
    var ratio = VOL_RATIOS[type];
    logInfo('Calculating original user input volume with input "%s" and ratio to sonos "%s"', vol, ratio);
    return Math.ceil(vol * ratio);
}

function setVolumeTo (newVolume, callback, type) {
    if (newVolume < 0) {
        logInfo('New volume less than 0, using 0 instead');
        newVolume = 0;
    } else if (newVolume > MAX_VOLS[type]) {
        logInfo('New volume greater than max, using max volume');
        newVolume = MAX_VOLS[type];
    }
    logInfo('Setting sonos volume to %s', String(newVolume));
    getSonos(type).setVolume(newVolume, callback);
}

function increaseVolByAmount (amount, callback, type) {
    logInfo('Increasing volume by %s', amount);
    getPrivateVolume(function (err, volume) {
        if (err) {
            return callback(err);
        }
        volume = parseInt(volume, 10);
        var newVol = publicToPrivateVolume(privateToPublicVolume(volume) + amount, type);
        logInfo('Calculated new sonos volume: %s', newVol);
        setVolumeTo(newVol, callback, type);
    });
}

function logErr (err) {
    logger.error(err);
}

function logInfo () {
    logger.minorInfo.apply(logger, arguments);
}

function sendErrorMessage (msg, err) {
    if (err && err.message) {
        logErr(err);
    }
    var text = (err && typeof err === 'string') ? err : 'An error occurred communicating with Sonos';
    return msg.send(text);
}

function lockVolume () {
    var duration = (3 * 60 * 1000);
    logInfo('Locking sonos volume for %s seconds', duration / 1000);
    volumeIsLocked = true;
    setTimeout(function () {
        volumeIsLocked = false;
        logInfo('Volume is now unlocked');
    }, duration);
}

var volumeKeywords = {
    '💯' : MAX_PUBLIC_VOLUME,
    ':100:' : MAX_PUBLIC_VOLUME,
    'max' : MAX_PUBLIC_VOLUME
};

var relativeVolumeKeywords = {
    '-' : function (curVolume) {
        return curVolume - 7;
    },
    '--' : function (curVolume) {
        return curVolume - 14;
    },
    '+' : function (curVolume) {
        return curVolume + 7;
    },
    '++' : function (curVolume) {
        return curVolume + 14;
    }
};

function boundPublicVolume (vol) {
    return Math.min(MAX_PUBLIC_VOLUME, Math.max(0, vol));
}

/**
 * This will be able to handle things like emoji/keywords
 *
 * @param volume
 * @param callback
 * @param type
 */
function volumeToInt(volume, callback, type) {
    var vol = (volume + '').replace(/^\s+|\s+$/g, '');
    if ((/^\d+$/).test(vol)) {
        callback(null, boundPublicVolume(parseInt(vol, 10)));
        return;
    }
    if (volumeKeywords.hasOwnProperty(vol)) {
        callback(null, boundPublicVolume(volumeKeywords[vol]));
        return;
    }
    if (relativeVolumeKeywords.hasOwnProperty(vol)) {
        getPrivateVolume(function (err, privateVolume) {
            if (err) {
                callback(err);
                return;
            }
            var currentVolume = privateToPublicVolume(privateVolume, type);
            callback(null, boundPublicVolume((relativeVolumeKeywords[vol])(currentVolume)));
        }, type);
        return;
    }
    callback('Unable to parse volume param: ' + volume);
}

module.exports = function(robot) {

    function getVolumeWithMsg (msg, text, type) {
        type = type || 'main';
        getPrivateVolume(function (err, volume) {
            if (err) { return sendErrorMessage(msg, err); }
            text = text || 'Volume set to ';
            msg.send(text + privateToPublicVolume(volume, type));
        }, type);
    }

    function sendVolLockedMessage (msg) {
        return sendErrorMessage(msg, ':no_good: Volume is currently locked');
    }

    function increaseVolByAmountWithMessage (msg, amount, type) {
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        increaseVolByAmount(amount, function (err) {
            if (err) { return sendErrorMessage(msg, err); }
            getVolumeWithMsg(msg);
        }, type);
    }

    function setVolumeWithMessage (volume, msg, text, type) {
        var newVol;
        volume = boundPublicVolume(volume);
        newVol = publicToPrivateVolume(volume, type);
        if (volumeIsLocked) {
            return sendVolLockedMessage(msg);
        }
        setVolumeTo(newVol, function (err) {
            if (err) {
                return sendErrorMessage(msg);
            }
            text = text || 'Volume set to ';
            msg.send(text + volume);
        }, type);
    }

    function getSpeakerType(match) {
        var type = match || 'main';
        return type.trim();
    }

    robot.respond(/(.* )?vol(?:ume)?\?/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        getVolumeWithMsg(msg, 'Volume is currently ', type);
    });

    robot.respond(/music status\??/i, function (message) {
        return getVolumeWithMsg(message, 'Volume is currently ');
    });

    robot.respond(/music status\??/i, function (message) {
        return getVolumeWithMsg(message, 'Bar Volume is currently ', 'bar');
    });

    robot.respond(/(?:set )?(.* )?vol(?:ume)?(?: to)? (.+)$/i, function (msg) {
        if (volumeIsLocked) {
            return sendErrorMessage(msg, ':no_good: Volume is currently locked');
        }

        var type = getSpeakerType(msg.match[1]);
        var volume = msg.match[2];

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        volumeToInt(volume, function (err, volume) {
            if (err) {
                sendErrorMessage(err);
                return;
            }
            setVolumeWithMessage(volume, msg, '', type);
        }, type);
    });

    robot.respond(/ mute( .*)?$/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        getPrivateVolume(function (err, vol) {
            if (err) {
                return sendErrorMessage(msg);
            }
            savedVolume = privateToPublicVolume(parseInt(vol, 10), type);
            setVolumeWithMessage(0, msg, '', type);
        }, type);
    });

    robot.respond(/ unmute( .*)?$/i, function (msg) {
        var type = getSpeakerType(msg.match[1]);

        if (!SONOS_SPEAKERS[type]) {
            return sendErrorMessage(msg, ':no_good: ' + type + ' is not a valid speaker');
        }

        if (savedVolume !== null) {
            setVolumeWithMessage(savedVolume, msg, '', type);
            savedVolume = null;
        } else {
            msg.send('Volume is not muted');
        }
    });

    robot.respond(/lock vol(?:ume)? at (.+)$/i, function (msg) {
        var volume = msg.match[1];
        volumeToInt(volume, function (err, volume) {
            if (err) {
                sendErrorMessage(err);
                return;
            }
            if (volume > 70 || volume < 40) {
                return sendErrorMessage(msg, ':no_good: You may only lock at reasonable volumes');
            }
            setVolumeWithMessage(volume, msg, 'Volume locked at ');
            lockVolume();
        });
    });

    return robot.router.get("/hubot/volume-decay", function(req, res) {
        var type = SONOS_SPEAKERS.main;
        logInfo('Decaying volume by 5');
        res.end('');
        getPrivateVolume(function (err, privateVolume) {
            var currentVolume = privateToPublicVolume(privateVolume, 'main');
            var targetVolume = currentVolume - 5;
            logInfo('Old volume: ' + currentVolume);
            logInfo('New volume: ' + targetVolume);
            setVolumeTo(publicToPrivateVolume(targetVolume, 'main'), function (res) {
                logInfo(res);
            }, type);
        }, type);
    });

};
<|endoftext|>