<commit_before>/*global require, describe, it, expect, beforeEach, jasmine*/
'use strict';
var underTest = require('../../lib/facebook/setup');
describe('Facebook setup', () => {
  var api, bot, logError, parser, responder, botPromise, botResolve, botReject;
  beforeEach(() => {
    api = jasmine.createSpyObj('api', ['get', 'post', 'addPostDeployStep']);
    botPromise = new Promise((resolve, reject) => {
      botResolve = resolve;
      botReject = reject;
    });
    bot = jasmine.createSpy().and.returnValue(botPromise);
    parser = jasmine.createSpy();
    logError = jasmine.createSpy();
    responder = jasmine.createSpy();
    underTest(api, bot, logError, parser, responder);
  });
  describe('token validator', () => {
    it('wires the GET request for facebook to a token validator', () => {
      expect(api.get.calls.count()).toEqual(1);
      expect(api.get).toHaveBeenCalledWith('/facebook', jasmine.any(Function), {success: {contentType: 'text/plain'}});
    });
    it('replies with hub.challenge when tokens match', () => {
      let handler = api.get.calls.argsFor(0)[1],
        result = handler({
          queryString: {
            'hub.verify_token': '12345',
            'hub.challenge': 'XXAA'
          }, env: {
            facebookVerifyToken: '12345'
          }
        });
      expect(result).toEqual('XXAA');
    });
    it('replies with Error when tokens do not match', () => {
      let handler = api.get.calls.argsFor(0)[1],
        result = handler({
          queryString: {
            'hub.verify_token': '12345',
            'hub.challenge': 'XXAA'
          }, env: {
            facebookVerifyToken: '12346'
          }
        });
      expect(result).toEqual('Error');
    });
  });
  describe('message processor', () => {
    const singleMessageTemplate = {
      'object':'page',
      'entry':[{
        'id': 'PAGE_ID',
        'time': 1457764198246,
        'messaging':[
          { 'A': 'B' }
        ]
      }]
    };
    it('wires the POST request for facebook to the message processor', () => {
      expect(api.post.calls.count()).toEqual(1);
      expect(api.post).toHaveBeenCalledWith('/facebook', jasmine.any(Function));
    });
    describe('processing a single message', () => {
      var handler;
      beforeEach(() => {
        handler = api.post.calls.argsFor(0)[1];
      });
      it('breaks down the message and puts it into the parser', () => {
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        expect(parser).toHaveBeenCalledWith({'A': 'B'});
      });
      it('passes the parsed value to the bot if a message can be parsed', (done) => {
        parser.and.returnValue('MSG1');
        handler({body: singleMessageTemplate});
        Promise.resolve().then(() => {
          expect(bot).toHaveBeenCalledWith('MSG1', { body: singleMessageTemplate });
        }).then(done, done.fail);
      });
      it('does not invoke the bot if the message cannot be parsed', (done) => {
        parser.and.returnValue(false);
        handler({body: singleMessageTemplate}).then((message) => {
          expect(message).toBe('ok');
          expect(bot).not.toHaveBeenCalled();
        }).then(done, done.fail);
      });
      it('responds when the bot resolves', (done) => {
        parser.and.returnValue({sender: 'user1', text: 'MSG1'});
        botResolve('Yes Yes');
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).toHaveBeenCalledWith('user1', 'Yes Yes', 'ABC');
        }).then(done, done.fail);
      });
      it('can work with bot responses as strings', (done) => {
        bot.and.returnValue('Yes!');
        parser.and.returnValue({sender: 'user1', text: 'MSG1'});
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).toHaveBeenCalledWith('user1', 'Yes!', 'ABC');
        }).then(done, done.fail);

      });
      it('logs error when the bot rejects without responding', (done) => {
        parser.and.returnValue('MSG1');

        handler({body: singleMessageTemplate}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).not.toHaveBeenCalled();
          expect(logError).toHaveBeenCalledWith('No No');
        }).then(done, done.fail);

        botReject('No No');
      });
      it('logs the error when the responder throws an error', (done) => {
        parser.and.returnValue('MSG1');
        responder.and.throwError('XXX');
        botResolve('Yes');
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(logError).toHaveBeenCalledWith(jasmine.any(Error));
        }).then(done, done.fail);
      });
      describe('working with promises in responders', () => {
        var responderResolve, responderReject, responderPromise, hasResolved;
        beforeEach(() => {
          responderPromise = new Promise((resolve, reject) => {
            responderResolve = resolve;
            responderReject = reject;
          });
          responder.and.returnValue(responderPromise);

          parser.and.returnValue('MSG1');
        });
        it('waits for the responders to resolve before completing the request', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then(() => {
            hasResolved = true;
          });

          botPromise.then(() => {
            expect(hasResolved).toBeFalsy();
          }).then(done, done.fail);

          botResolve('YES');
        });
        it('resolves when the responder resolves', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
            expect(message).toEqual('ok');
          }).then(done, done.fail);

          botPromise.then(() => {
            responderResolve('As Promised!');
          });
          botResolve('YES');
        });
        it('logs error when the responder rejects', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
            expect(message).toEqual('ok');
            expect(logError).toHaveBeenCalledWith('Bomb!');
          }).then(done, done.fail);

          botPromise.then(() => {
            responderReject('Bomb!');
          });
          botResolve('YES');
        });
      });
    });
    describe('multiple messages', () => {
      const multiMessageTemplate = {
        'object':'page',
        'entry':[{
          'id': 'PAGE_ID',
          'time': 1457764198246,
          'messaging':[
            { 'A': 'B' },
            { 'C': 'D' }
          ]
        }, {
          'id': 'PAGE_ID',
          'time': 1457764198246,
          'messaging':[
            { 'E': 'F' },
            { 'G': 'H' }
          ]
        }]
      };
      var botPromises,
        responderPromises,
        handler,
        buildPromiseFor = (array) => {
          var pResolve, pReject, promise;
          promise = new Promise((resolve, reject) => {
            pResolve = resolve;
            pReject = reject;
          });
          promise.resolve = pResolve;
          promise.reject = pReject;
          array.push(promise);
          return promise;
        };

      beforeEach(() => {
        var index = 0;
        botPromises = [];
        responderPromises = [];
        bot.and.callFake(() => {
          return buildPromiseFor(botPromises);
        });
        responder.and.callFake(() => {
          return buildPromiseFor(responderPromises);
        });
        handler = api.post.calls.argsFor(0)[1];
        parser.and.callFake(() => {
          index += 1;
          return {
            sender: 'sender' + index,
            text: 'text' + index
          };
        });
      });
      it('parses messages in sequence', () => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        expect(parser.calls.count()).toBe(4);
        expect(parser).toHaveBeenCalledWith({'A': 'B'});
        expect(parser).toHaveBeenCalledWith({'C': 'D'});
        expect(parser).toHaveBeenCalledWith({'E': 'F'});
        expect(parser).toHaveBeenCalledWith({'G': 'H'});
      });
      it('calls the bot for each message individually', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        Promise.resolve().then(() => {
          expect(bot.calls.count()).toEqual(4);
          expect(bot).toHaveBeenCalledWith({sender: 'sender1', text: 'text1'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender2', text: 'text2'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender3', text: 'text3'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender4', text: 'text4'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        }).then(done, done.fail);
      });
      it('calls the responders for each bot response individually', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        Promise.resolve().then(() => {
          botPromises[0].resolve('From first');
          botPromises[1].resolve('From second');
          return botPromises[1];
        }).then(() => {
          expect(responder).toHaveBeenCalledWith('sender1', 'From first', 'ABC');
          expect(responder).toHaveBeenCalledWith('sender2', 'From second', 'ABC');
        }).then(done, done.fail);
      });
      it('does not resolve until all the responders resolve', (done) => {
        var hasResolved;
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then(() => {
          hasResolved = true;
        }).then(done.fail, done.fail);
        Promise.resolve().then(() => {
          botPromises.forEach((p) => p.resolve('group'));
          return botPromises.pop();
        }).then(() => {
          responderPromises.slice(1).forEach((p) => p.resolve('result'));
          return responderPromises.pop();
        }).then(() => {
          expect(hasResolved).toBeFalsy();
        }).then(done, done.fail);
      });
      it('resolves when all the responders resolve', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toEqual('ok');
        }).then(done, done.fail);
        Promise.resolve().then(() => {
          botPromises.forEach((p) => p.resolve('group'));
          return botPromises.pop();
        }).then(() => {
          responderPromises.forEach((p) => p.resolve('result'));
          return responderPromises.pop();
        });
      });
    });

  });
});
<commit_msg>Fix tests for fb security<commit_after>/*global require, describe, it, expect, beforeEach, jasmine*/
'use strict';
var underTest = require('../../lib/facebook/setup');
describe('Facebook setup', () => {
  var api, bot, logError, parser, responder, botPromise, botResolve, botReject;
  beforeEach(() => {
    api = jasmine.createSpyObj('api', ['get', 'post', 'addPostDeployStep']);
    botPromise = new Promise((resolve, reject) => {
      botResolve = resolve;
      botReject = reject;
    });
    bot = jasmine.createSpy().and.returnValue(botPromise);
    parser = jasmine.createSpy();
    logError = jasmine.createSpy();
    responder = jasmine.createSpy();
    underTest(api, bot, logError, parser, responder);
  });
  describe('token validator', () => {
    it('wires the GET request for facebook to a token validator', () => {
      expect(api.get.calls.count()).toEqual(1);
      expect(api.get).toHaveBeenCalledWith('/facebook', jasmine.any(Function), {success: {contentType: 'text/plain'}});
    });
    it('replies with hub.challenge when tokens match', () => {
      let handler = api.get.calls.argsFor(0)[1],
        result = handler({
          queryString: {
            'hub.verify_token': '12345',
            'hub.challenge': 'XXAA'
          }, env: {
            facebookVerifyToken: '12345'
          }
        });
      expect(result).toEqual('XXAA');
    });
    it('replies with Error when tokens do not match', () => {
      let handler = api.get.calls.argsFor(0)[1],
        result = handler({
          queryString: {
            'hub.verify_token': '12345',
            'hub.challenge': 'XXAA'
          }, env: {
            facebookVerifyToken: '12346'
          }
        });
      expect(result).toEqual('Error');
    });
  });
  describe('message processor', () => {
    const singleMessageTemplate = {
      'object':'page',
      'entry':[{
        'id': 'PAGE_ID',
        'time': 1457764198246,
        'messaging':[
          { 'A': 'B' }
        ]
      }]
    };
    it('wires the POST request for facebook to the message processor', () => {
      expect(api.post.calls.count()).toEqual(1);
      expect(api.post).toHaveBeenCalledWith('/facebook', jasmine.any(Function));
    });
    describe('processing a single message', () => {
      var handler;
      beforeEach(() => {
        handler = api.post.calls.argsFor(0)[1];
      });
      it('breaks down the message and puts it into the parser', () => {
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        expect(parser).toHaveBeenCalledWith({'A': 'B'});
      });
      it('passes the parsed value to the bot if a message can be parsed', (done) => {
        parser.and.returnValue('MSG1');
        handler({body: singleMessageTemplate, env: {}});
        Promise.resolve().then(() => {
          expect(bot).toHaveBeenCalledWith('MSG1', { body: singleMessageTemplate, env: {} });
        }).then(done, done.fail);
      });
      it('does not invoke the bot if the message cannot be parsed', (done) => {
        parser.and.returnValue(false);
        handler({body: singleMessageTemplate, env: {}}).then((message) => {
          expect(message).toBe('ok');
          expect(bot).not.toHaveBeenCalled();
        }).then(done, done.fail);
      });
      it('responds when the bot resolves', (done) => {
        parser.and.returnValue({sender: 'user1', text: 'MSG1'});
        botResolve('Yes Yes');
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).toHaveBeenCalledWith('user1', 'Yes Yes', 'ABC');
        }).then(done, done.fail);
      });
      it('can work with bot responses as strings', (done) => {
        bot.and.returnValue('Yes!');
        parser.and.returnValue({sender: 'user1', text: 'MSG1'});
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).toHaveBeenCalledWith('user1', 'Yes!', 'ABC');
        }).then(done, done.fail);

      });
      it('logs error when the bot rejects without responding', (done) => {
        parser.and.returnValue('MSG1');

        handler({body: singleMessageTemplate, env: {}}).then((message) => {
          expect(message).toBe('ok');
          expect(responder).not.toHaveBeenCalled();
          expect(logError).toHaveBeenCalledWith('No No');
        }).then(done, done.fail);

        botReject('No No');
      });
      it('logs the error when the responder throws an error', (done) => {
        parser.and.returnValue('MSG1');
        responder.and.throwError('XXX');
        botResolve('Yes');
        handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toBe('ok');
          expect(logError).toHaveBeenCalledWith(jasmine.any(Error));
        }).then(done, done.fail);
      });
      describe('working with promises in responders', () => {
        var responderResolve, responderReject, responderPromise, hasResolved;
        beforeEach(() => {
          responderPromise = new Promise((resolve, reject) => {
            responderResolve = resolve;
            responderReject = reject;
          });
          responder.and.returnValue(responderPromise);

          parser.and.returnValue('MSG1');
        });
        it('waits for the responders to resolve before completing the request', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then(() => {
            hasResolved = true;
          });

          botPromise.then(() => {
            expect(hasResolved).toBeFalsy();
          }).then(done, done.fail);

          botResolve('YES');
        });
        it('resolves when the responder resolves', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
            expect(message).toEqual('ok');
          }).then(done, done.fail);

          botPromise.then(() => {
            responderResolve('As Promised!');
          });
          botResolve('YES');
        });
        it('logs error when the responder rejects', (done) => {
          handler({body: singleMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
            expect(message).toEqual('ok');
            expect(logError).toHaveBeenCalledWith('Bomb!');
          }).then(done, done.fail);

          botPromise.then(() => {
            responderReject('Bomb!');
          });
          botResolve('YES');
        });
      });
    });
    describe('multiple messages', () => {
      const multiMessageTemplate = {
        'object':'page',
        'entry':[{
          'id': 'PAGE_ID',
          'time': 1457764198246,
          'messaging':[
            { 'A': 'B' },
            { 'C': 'D' }
          ]
        }, {
          'id': 'PAGE_ID',
          'time': 1457764198246,
          'messaging':[
            { 'E': 'F' },
            { 'G': 'H' }
          ]
        }]
      };
      var botPromises,
        responderPromises,
        handler,
        buildPromiseFor = (array) => {
          var pResolve, pReject, promise;
          promise = new Promise((resolve, reject) => {
            pResolve = resolve;
            pReject = reject;
          });
          promise.resolve = pResolve;
          promise.reject = pReject;
          array.push(promise);
          return promise;
        };

      beforeEach(() => {
        var index = 0;
        botPromises = [];
        responderPromises = [];
        bot.and.callFake(() => {
          return buildPromiseFor(botPromises);
        });
        responder.and.callFake(() => {
          return buildPromiseFor(responderPromises);
        });
        handler = api.post.calls.argsFor(0)[1];
        parser.and.callFake(() => {
          index += 1;
          return {
            sender: 'sender' + index,
            text: 'text' + index
          };
        });
      });
      it('parses messages in sequence', () => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        expect(parser.calls.count()).toBe(4);
        expect(parser).toHaveBeenCalledWith({'A': 'B'});
        expect(parser).toHaveBeenCalledWith({'C': 'D'});
        expect(parser).toHaveBeenCalledWith({'E': 'F'});
        expect(parser).toHaveBeenCalledWith({'G': 'H'});
      });
      it('calls the bot for each message individually', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        Promise.resolve().then(() => {
          expect(bot.calls.count()).toEqual(4);
          expect(bot).toHaveBeenCalledWith({sender: 'sender1', text: 'text1'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender2', text: 'text2'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender3', text: 'text3'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
          expect(bot).toHaveBeenCalledWith({sender: 'sender4', text: 'text4'}, {body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        }).then(done, done.fail);
      });
      it('calls the responders for each bot response individually', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}});
        Promise.resolve().then(() => {
          botPromises[0].resolve('From first');
          botPromises[1].resolve('From second');
          return botPromises[1];
        }).then(() => {
          expect(responder).toHaveBeenCalledWith('sender1', 'From first', 'ABC');
          expect(responder).toHaveBeenCalledWith('sender2', 'From second', 'ABC');
        }).then(done, done.fail);
      });
      it('does not resolve until all the responders resolve', (done) => {
        var hasResolved;
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then(() => {
          hasResolved = true;
        }).then(done.fail, done.fail);
        Promise.resolve().then(() => {
          botPromises.forEach((p) => p.resolve('group'));
          return botPromises.pop();
        }).then(() => {
          responderPromises.slice(1).forEach((p) => p.resolve('result'));
          return responderPromises.pop();
        }).then(() => {
          expect(hasResolved).toBeFalsy();
        }).then(done, done.fail);
      });
      it('resolves when all the responders resolve', (done) => {
        handler({body: multiMessageTemplate, env: {facebookAccessToken: 'ABC'}}).then((message) => {
          expect(message).toEqual('ok');
        }).then(done, done.fail);
        Promise.resolve().then(() => {
          botPromises.forEach((p) => p.resolve('group'));
          return botPromises.pop();
        }).then(() => {
          responderPromises.forEach((p) => p.resolve('result'));
          return responderPromises.pop();
        });
      });
    });

  });
});
<|endoftext|>


<commit_before><commit_msg>Update ogario.v4.js<commit_after><|endoftext|>


<commit_before>/**!
 *
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {SparkPlugin} from '@ciscospark/spark-core';
import {Context, Request, Response} from 'node-kms';
import KMSBatcher, {TIMEOUT_SYMBOL} from './kms-batcher';
import jose from 'node-jose';
import {omit} from 'lodash';

const contexts = new WeakMap();
const kmsDetails = new WeakMap();
const partialContexts = new WeakMap();

/**
 * @class
 */
const KMS = SparkPlugin.extend({
  namespace: `Encryption`,

  children: {
    batcher: KMSBatcher
  },

  /**
   * Binds a key to a resource
   * @param {Object} options
   * @param {KMSResourceObject} options.kro
   * @param {string} options.kroUri
   * @param {Key} options.key
   * @param {string} options.keyUri
   * @returns {Promise<Key>}
   */
  bindKey({kro, kroUri, key, keyUri}) {
    kroUri = kroUri || kro.uri;
    keyUri = keyUri || key.uri;

    this.logger.info(`kms: binding key to resource`);

    /* istanbul ignore if */
    if (!kroUri) {
      return Promise.reject(new Error(`\`kro\` or \`kroUri\` is required`));
    }

    /* istanbul ignore if */
    if (!keyUri) {
      return Promise.reject(new Error(`\`key\` or \`keyUri\` is required`));
    }

    return this.request({
      method: `update`,
      resourceUri: kroUri,
      uri: keyUri
    })
      .then((res) => {
        this.logger.info(`kms: bound key to resource`);
        return res.key;
      });
  },

  /**
   * Creates a new KMS Resource
   * @param {Object} options
   * @param {Array<string>} options.userIds
   * @param {Array<string>} options.keyUris
   * @param {Key} options.key
   * @param {Array<Keys>} options.keys
   * @returns {Promise<KMSResourceObject>}
   */
  createResource({userIds, keyUris, key, keys}) {
    keyUris = keyUris || [];
    /* istanbul ignore if */
    if (keys) {
      keyUris = keys.reduce((uris, k) => {
        uris.push(k.uri);
        return uris;
      }, keyUris);
    }

    /* istanbul ignore else */
    if (key) {
      keyUris.push(key.uri);
    }

    /* istanbul ignore if */
    if (keyUris.length === 0) {
      return Promise.reject(new Error(`Cannot create KMS Resource without at least one keyUri`));
    }

    this.logger.info(`kms: creating resource`);

    return this.request({
      method: `create`,
      uri: `/resources`,
      userIds,
      keyUris
    })
      .then((res) => {
        this.logger.info(`kms: created resource`);
        return res.resource;
      });
  },

  /**
   * Requests `count` unbound keys from the kms
   * @param {Object} options
   * @param {Number} options.count
   * @returns {Array<Key>}
   */
  createUnboundKeys({count}) {
    this.logger.info(`kms: request ${count} unbound keys`);

    /* istanbul ignore if */
    if (!count) {
      return Promise.reject(new Error(`\`options.count\` is required`));
    }

    return this.request({
      method: `create`,
      uri: `/keys`,
      count
    })
      .then((res) => {
        this.logger.info(`kms: received unbound keys`);
        return Promise.all(res.keys.map(this.asKey));
      });
  },

  /**
   * Fetches the specified key from the kms
   * @param {Object} options
   * @param {string} options.uri
   * @returns {Promise<Key>}
   */
  fetchKey({uri}) {
    /* istanbul ignore if */
    if (!uri) {
      return Promise.reject(new Error(`\`options.uri\` is required`));
    }

    this.logger.info(`kms: fetching key`);

    return this.request({
      method: `retrieve`,
      uri
    })
      .then((res) => {
        this.logger.info(`kms: fetched key`);
        return this.asKey(res.key);
      });
  },

  /**
   * Pings the kms. Mostly for testing
   * @returns {Promise}
   */
  ping() {
    return this.request({
      method: `update`,
      uri: `/ping`
    });
  },

  /**
   * Ensures a key obect is Key instance
   * @param {Object} key
   * @returns {Promise<Key>}
   */
  asKey(key) {
    return jose.JWK.asKey(key.jwk)
      .then((jwk) => {
        key.jwk = jwk;
        return key;
      });
  },

  /**
   * Adds appropriate metadata to the KMS request
   * @param {Object} payload
   * @returns {Promise<KMS.Request>}
   */
  prepareRequest(payload) {
    const isECDHRequest = payload.method === `create` && payload.uri.includes(`/ecdhe`);
    return Promise.resolve(isECDHRequest ? partialContexts.get(this) : this._getContext())
      .then((context) => {
        this.logger.info(`kms: wrapping ${isECDHRequest ? `ephemeral key` : `kms`} request`);
        const req = new Request(payload);
        return req.wrap(context, {serverKey: isECDHRequest})
          .then(() => {
            /* istanbul ignore else */
            if (process.env.NODE_ENV !== `production`) {
              this.logger.info(`kms: request payload`, omit(JSON.parse(JSON.stringify(req)), `wrapped`));
            }
            return req;
          });
      });
  },

  /**
   * Accepts a kms message event, decrypts it, and passes it to the batcher
   * @param {Object} event
   * @returns {Promise<Object>}
   */
  processKmsMessageEvent(event) {
    this.logger.info(`kms: received kms message`);
    return Promise.all(event.encryption.kmsMessages.map((kmsMessage, index) => this._isECDHEMessage(kmsMessage)
      .then((isECDHMessage) => {
        this.logger.info(`kms: received ${isECDHMessage ? `ecdhe` : `normal`} message`);
        const res = new Response(kmsMessage);
        return Promise.resolve(isECDHMessage ? partialContexts.get(this) : contexts.get(this))
          // eslint-disable-next-line max-nested-callbacks
          .then((context) => res.unwrap(context))
          // eslint-disable-next-line max-nested-callbacks
          .then(() => {event.encryption.kmsMessages[index] = res;})
          // eslint-disable-next-line max-nested-callbacks
          .then(() => res);
      })
    ))
      .then(() => this.batcher.processKmsMessageEvent(event))
      .catch((reason) => {
        this.logger.error(`kms: decrypt failed`, reason.stack);
        return Promise.reject(reason);
      })
      .then(() => event);
  },

  /**
   * Decrypts a kms message
   * @param {Object} kmsMessage
   * @returns {Promise<Object>}
   */
  decryptKmsMessage(kmsMessage) {
    const res = new Response(kmsMessage);
    return contexts.get(this)
      .then((context) => res.unwrap(context))
      .then(() => res.body);
  },

  /**
   * Determines if the kms message is an ecdhe message or a normal message
   * @param {Object} kmsMessage
   * @returns {Promise<boolean>}
   */
  _isECDHEMessage(kmsMessage) {
    return this._getKMSStaticPubKey()
      .then((kmsStaticPubKey) => {
        const fields = kmsMessage.split(`.`);

        if (fields.length !== 3) {
          return false;
        }

        const header = JSON.parse(jose.util.base64url.decode(fields[0]));

        return header.kid === kmsStaticPubKey.kid;
      });
  },

  /**
   * Sends a request to the kms
   * @param {Object} payload
   * @param {Number} timeout (internal)
   * @returns {Promise<Object>}
   */
  request(payload, timeout) {
    timeout = timeout || this.config.kmsInitialTimeout;

    // Note: this should only happen when we're using the async kms batcher;
    // once we implement the sync batcher, this'll need to be smarter.
    return this.spark.mercury.connect()
      .then(() => this.prepareRequest(payload))
      .then((req) => {
        req[TIMEOUT_SYMBOL] = timeout;
        return this.batcher.request(req);
      })
      .catch((reason) => {
        // Ideally, most or all of the code below would go in kms-batcher, but
        // but batching needs at least one more round of refactoring for that to
        // work.
        if (!reason.statusCode && !reason.status) {
          /* istanbul ignore else */
          if (process.env.NODE_ENV !== `production`) {
            /* istanbul ignore next: reason.stack vs stack difficult to control in test */
            this.logger.info(`kms: request error`, reason.stack || reason);
          }

          timeout = timeout * 2;

          if (timeout >= this.config.kmsMaxTimeout) {
            this.logger.info(`kms: exceeded maximum KMS request retries; negotiating new ecdh key`);

            /* istanbul ignore else */
            if (process.env.NODE_ENV !== `production`) {
              this.logger.info(`kms: timeout/maxtimeout`, timeout, this.config.kmsMaxTimeout);
            }

            contexts.delete(this);
            timeout = 0;
          }

          return this.request(payload, timeout);
        }

        return Promise.reject(reason);
      });
  },

  /**
   * @private
   * @returns {Promise<string>}
   */
  _getAuthorization() {
    // Remove the `Bearer` prefix from the token; we'll improve this when we add
    // token downscoping.
    return this.spark.credentials.getAuthorization()
      .then((authorization) => authorization.toString().replace(/[Bb]earer\s+/, ``));
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getContext() {
    let promise = contexts.get(this);
    if (!promise) {
      promise = this._prepareContext();
      contexts.set(this, promise);
      promise.then((context) => {
        const expiresIn = context.ephemeralKey.expirationDate - Date.now() - 30000;
        setTimeout(() => contexts.unset(this), expiresIn);
      });
    }

    return Promise.all([
      promise,
      this._getAuthorization()
    ])
      .then(([context, authorization]) => {
        context.clientInfo.credential.bearer = authorization;
        return context;
      });
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSCluster() {
    this.logger.info(`kms: retrieving KMS cluster`);
    return this._getKMSDetails()
      .then(({kmsCluster}) => kmsCluster);
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSDetails() {
    let details = kmsDetails.get(this);
    if (!details) {
      this.logger.info(`kms: fetching KMS details`);
      details = this.spark.request({
        service: `encryption`,
        resource: `/kms/${this.spark.device.userId}`
      })
        .then((res) => {
          this.logger.info(`kms: fetched KMS details`);
          const body = res.body;
          body.rsaPublicKey = JSON.parse(body.rsaPublicKey);
          return body;
        })
        .catch((reason) => {
          this.logger.error(`kms: failed to fetch KMS details`, reason);
          return Promise.reject(reason);
        });

      kmsDetails.set(this, details);
    }

    return details;
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSStaticPubKey() {
    this.logger.info(`kms: retrieving KMS static public key`);
    return this._getKMSDetails()
      .then(({rsaPublicKey}) => rsaPublicKey);
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _prepareContext() {
    this.logger.info(`kms: creating context`);
    const context = new Context();

    return Promise.all([
      this._getKMSStaticPubKey(),
      this._getAuthorization()
    ])
      .then(([kmsStaticPubKey, authorization]) => {
        context.clientInfo = {
          clientId: this.spark.device.url,
          credential: {
            userId: this.spark.device.userId,
            bearer: authorization
          }
        };

        context.serverInfo = {
          key: kmsStaticPubKey
        };

        this.logger.info(`kms: creating local ephemeral key`);
        return context.createECDHKey();
      })
      .then((localECDHKey) => {
        context.ephemeralKey = localECDHKey;
        partialContexts.set(this, context);
        return Promise.all([localECDHKey.asKey(), this._getKMSCluster()]);
      })
      .then(([localECDHKey, cluster]) => {
        this.logger.info(`kms: submitting ephemeral key request`);
        return this.request({
          uri: `${cluster}/ecdhe`,
          method: `create`,
          jwk: localECDHKey.toJSON()
        });
      })
      .then((res) => {
        this.logger.info(`kms: deriving final ephemeral key`);
        return context.deriveEphemeralKey(res.key);
      })
      .then((key) => {
        context.ephemeralKey = key;
        partialContexts.delete(this);
        this.logger.info(`kms: derived final ephemeral key`);
        return context;
      })
      .catch((reason) => {
        this.logger.error(`kms: failed to negotiate ephemeral key`, reason);
        return Promise.reject(reason);
      });
  }
});

export default KMS;
<commit_msg>fix(plugin-encryption): do not request the same key multiple times in parallel<commit_after>/**!
 *
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {oneFlight} from '@ciscospark/common';
import {SparkPlugin} from '@ciscospark/spark-core';
import {Context, Request, Response} from 'node-kms';
import KMSBatcher, {TIMEOUT_SYMBOL} from './kms-batcher';
import jose from 'node-jose';
import {omit} from 'lodash';

const contexts = new WeakMap();
const kmsDetails = new WeakMap();
const partialContexts = new WeakMap();

/**
 * @class
 */
const KMS = SparkPlugin.extend({
  namespace: `Encryption`,

  children: {
    batcher: KMSBatcher
  },

  /**
   * Binds a key to a resource
   * @param {Object} options
   * @param {KMSResourceObject} options.kro
   * @param {string} options.kroUri
   * @param {Key} options.key
   * @param {string} options.keyUri
   * @returns {Promise<Key>}
   */
  bindKey({kro, kroUri, key, keyUri}) {
    kroUri = kroUri || kro.uri;
    keyUri = keyUri || key.uri;

    this.logger.info(`kms: binding key to resource`);

    /* istanbul ignore if */
    if (!kroUri) {
      return Promise.reject(new Error(`\`kro\` or \`kroUri\` is required`));
    }

    /* istanbul ignore if */
    if (!keyUri) {
      return Promise.reject(new Error(`\`key\` or \`keyUri\` is required`));
    }

    return this.request({
      method: `update`,
      resourceUri: kroUri,
      uri: keyUri
    })
      .then((res) => {
        this.logger.info(`kms: bound key to resource`);
        return res.key;
      });
  },

  /**
   * Creates a new KMS Resource
   * @param {Object} options
   * @param {Array<string>} options.userIds
   * @param {Array<string>} options.keyUris
   * @param {Key} options.key
   * @param {Array<Keys>} options.keys
   * @returns {Promise<KMSResourceObject>}
   */
  createResource({userIds, keyUris, key, keys}) {
    keyUris = keyUris || [];
    /* istanbul ignore if */
    if (keys) {
      keyUris = keys.reduce((uris, k) => {
        uris.push(k.uri);
        return uris;
      }, keyUris);
    }

    /* istanbul ignore else */
    if (key) {
      keyUris.push(key.uri);
    }

    /* istanbul ignore if */
    if (keyUris.length === 0) {
      return Promise.reject(new Error(`Cannot create KMS Resource without at least one keyUri`));
    }

    this.logger.info(`kms: creating resource`);

    return this.request({
      method: `create`,
      uri: `/resources`,
      userIds,
      keyUris
    })
      .then((res) => {
        this.logger.info(`kms: created resource`);
        return res.resource;
      });
  },

  /**
   * Requests `count` unbound keys from the kms
   * @param {Object} options
   * @param {Number} options.count
   * @returns {Array<Key>}
   */
  createUnboundKeys({count}) {
    this.logger.info(`kms: request ${count} unbound keys`);

    /* istanbul ignore if */
    if (!count) {
      return Promise.reject(new Error(`\`options.count\` is required`));
    }

    return this.request({
      method: `create`,
      uri: `/keys`,
      count
    })
      .then((res) => {
        this.logger.info(`kms: received unbound keys`);
        return Promise.all(res.keys.map(this.asKey));
      });
  },

  /**
   * Fetches the specified key from the kms
   * @param {Object} options
   * @param {string} options.uri
   * @returns {Promise<Key>}
   */
  // Ideally, this would be done via the kms batcher, but other than request id,
  // there isn't any other userful key in a kms response to match it to a
  // request. as such, we need the batcher to group requests, but one flight to
  // make sure we don't make the same request multiple times.
  @oneFlight({
    keyFactory: ({uri}) => uri
  })
  fetchKey({uri}) {
    /* istanbul ignore if */
    if (!uri) {
      return Promise.reject(new Error(`\`options.uri\` is required`));
    }

    this.logger.info(`kms: fetching key`);

    return this.request({
      method: `retrieve`,
      uri
    })
      .then((res) => {
        this.logger.info(`kms: fetched key`);
        return this.asKey(res.key);
      });
  },

  /**
   * Pings the kms. Mostly for testing
   * @returns {Promise}
   */
  ping() {
    return this.request({
      method: `update`,
      uri: `/ping`
    });
  },

  /**
   * Ensures a key obect is Key instance
   * @param {Object} key
   * @returns {Promise<Key>}
   */
  asKey(key) {
    return jose.JWK.asKey(key.jwk)
      .then((jwk) => {
        key.jwk = jwk;
        return key;
      });
  },

  /**
   * Adds appropriate metadata to the KMS request
   * @param {Object} payload
   * @returns {Promise<KMS.Request>}
   */
  prepareRequest(payload) {
    const isECDHRequest = payload.method === `create` && payload.uri.includes(`/ecdhe`);
    return Promise.resolve(isECDHRequest ? partialContexts.get(this) : this._getContext())
      .then((context) => {
        this.logger.info(`kms: wrapping ${isECDHRequest ? `ephemeral key` : `kms`} request`);
        const req = new Request(payload);
        return req.wrap(context, {serverKey: isECDHRequest})
          .then(() => {
            /* istanbul ignore else */
            if (process.env.NODE_ENV !== `production`) {
              this.logger.info(`kms: request payload`, omit(JSON.parse(JSON.stringify(req)), `wrapped`));
            }
            return req;
          });
      });
  },

  /**
   * Accepts a kms message event, decrypts it, and passes it to the batcher
   * @param {Object} event
   * @returns {Promise<Object>}
   */
  processKmsMessageEvent(event) {
    this.logger.info(`kms: received kms message`);
    return Promise.all(event.encryption.kmsMessages.map((kmsMessage, index) => this._isECDHEMessage(kmsMessage)
      .then((isECDHMessage) => {
        this.logger.info(`kms: received ${isECDHMessage ? `ecdhe` : `normal`} message`);
        const res = new Response(kmsMessage);
        return Promise.resolve(isECDHMessage ? partialContexts.get(this) : contexts.get(this))
          // eslint-disable-next-line max-nested-callbacks
          .then((context) => res.unwrap(context))
          // eslint-disable-next-line max-nested-callbacks
          .then(() => {
            if (process.env.NODE_ENV !== `production`) {
              this.logger.info(`kms: response payload`, omit(JSON.parse(JSON.stringify(res)), `wrapped`));
            }
          })
          // eslint-disable-next-line max-nested-callbacks
          .then(() => {event.encryption.kmsMessages[index] = res;})
          // eslint-disable-next-line max-nested-callbacks
          .then(() => res);
      })
    ))
      .then(() => this.batcher.processKmsMessageEvent(event))
      .catch((reason) => {
        this.logger.error(`kms: decrypt failed`, reason.stack);
        return Promise.reject(reason);
      })
      .then(() => event);
  },

  /**
   * Decrypts a kms message
   * @param {Object} kmsMessage
   * @returns {Promise<Object>}
   */
  decryptKmsMessage(kmsMessage) {
    const res = new Response(kmsMessage);
    return contexts.get(this)
      .then((context) => res.unwrap(context))
      .then(() => res.body);
  },

  /**
   * Determines if the kms message is an ecdhe message or a normal message
   * @param {Object} kmsMessage
   * @returns {Promise<boolean>}
   */
  _isECDHEMessage(kmsMessage) {
    return this._getKMSStaticPubKey()
      .then((kmsStaticPubKey) => {
        const fields = kmsMessage.split(`.`);

        if (fields.length !== 3) {
          return false;
        }

        const header = JSON.parse(jose.util.base64url.decode(fields[0]));

        return header.kid === kmsStaticPubKey.kid;
      });
  },

  /**
   * Sends a request to the kms
   * @param {Object} payload
   * @param {Number} timeout (internal)
   * @returns {Promise<Object>}
   */
  request(payload, timeout) {
    timeout = timeout || this.config.kmsInitialTimeout;

    // Note: this should only happen when we're using the async kms batcher;
    // once we implement the sync batcher, this'll need to be smarter.
    return this.spark.mercury.connect()
      .then(() => this.prepareRequest(payload))
      .then((req) => {
        req[TIMEOUT_SYMBOL] = timeout;
        return this.batcher.request(req);
      })
      .catch((reason) => {
        // Ideally, most or all of the code below would go in kms-batcher, but
        // but batching needs at least one more round of refactoring for that to
        // work.
        if (!reason.statusCode && !reason.status) {
          /* istanbul ignore else */
          if (process.env.NODE_ENV !== `production`) {
            /* istanbul ignore next: reason.stack vs stack difficult to control in test */
            this.logger.info(`kms: request error`, reason.stack || reason);
          }

          timeout = timeout * 2;

          if (timeout >= this.config.kmsMaxTimeout) {
            this.logger.info(`kms: exceeded maximum KMS request retries; negotiating new ecdh key`);

            /* istanbul ignore else */
            if (process.env.NODE_ENV !== `production`) {
              this.logger.info(`kms: timeout/maxtimeout`, timeout, this.config.kmsMaxTimeout);
            }

            contexts.delete(this);
            timeout = 0;
          }

          return this.request(payload, timeout);
        }

        return Promise.reject(reason);
      });
  },

  /**
   * @private
   * @returns {Promise<string>}
   */
  _getAuthorization() {
    // Remove the `Bearer` prefix from the token; we'll improve this when we add
    // token downscoping.
    return this.spark.credentials.getAuthorization()
      .then((authorization) => authorization.toString().replace(/[Bb]earer\s+/, ``));
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getContext() {
    let promise = contexts.get(this);
    if (!promise) {
      promise = this._prepareContext();
      contexts.set(this, promise);
      promise.then((context) => {
        const expiresIn = context.ephemeralKey.expirationDate - Date.now() - 30000;
        setTimeout(() => contexts.unset(this), expiresIn);
      });
    }

    return Promise.all([
      promise,
      this._getAuthorization()
    ])
      .then(([context, authorization]) => {
        context.clientInfo.credential.bearer = authorization;
        return context;
      });
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSCluster() {
    this.logger.info(`kms: retrieving KMS cluster`);
    return this._getKMSDetails()
      .then(({kmsCluster}) => kmsCluster);
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSDetails() {
    let details = kmsDetails.get(this);
    if (!details) {
      this.logger.info(`kms: fetching KMS details`);
      details = this.spark.request({
        service: `encryption`,
        resource: `/kms/${this.spark.device.userId}`
      })
        .then((res) => {
          this.logger.info(`kms: fetched KMS details`);
          const body = res.body;
          body.rsaPublicKey = JSON.parse(body.rsaPublicKey);
          return body;
        })
        .catch((reason) => {
          this.logger.error(`kms: failed to fetch KMS details`, reason);
          return Promise.reject(reason);
        });

      kmsDetails.set(this, details);
    }

    return details;
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _getKMSStaticPubKey() {
    this.logger.info(`kms: retrieving KMS static public key`);
    return this._getKMSDetails()
      .then(({rsaPublicKey}) => rsaPublicKey);
  },

  /**
   * @private
   * @returns {Promise<Object>}
   */
  _prepareContext() {
    this.logger.info(`kms: creating context`);
    const context = new Context();

    return Promise.all([
      this._getKMSStaticPubKey(),
      this._getAuthorization()
    ])
      .then(([kmsStaticPubKey, authorization]) => {
        context.clientInfo = {
          clientId: this.spark.device.url,
          credential: {
            userId: this.spark.device.userId,
            bearer: authorization
          }
        };

        context.serverInfo = {
          key: kmsStaticPubKey
        };

        this.logger.info(`kms: creating local ephemeral key`);
        return context.createECDHKey();
      })
      .then((localECDHKey) => {
        context.ephemeralKey = localECDHKey;
        partialContexts.set(this, context);
        return Promise.all([localECDHKey.asKey(), this._getKMSCluster()]);
      })
      .then(([localECDHKey, cluster]) => {
        this.logger.info(`kms: submitting ephemeral key request`);
        return this.request({
          uri: `${cluster}/ecdhe`,
          method: `create`,
          jwk: localECDHKey.toJSON()
        });
      })
      .then((res) => {
        this.logger.info(`kms: deriving final ephemeral key`);
        return context.deriveEphemeralKey(res.key);
      })
      .then((key) => {
        context.ephemeralKey = key;
        partialContexts.delete(this);
        this.logger.info(`kms: derived final ephemeral key`);
        return context;
      })
      .catch((reason) => {
        this.logger.error(`kms: failed to negotiate ephemeral key`, reason);
        return Promise.reject(reason);
      });
  }
});

export default KMS;
<|endoftext|>


<commit_before>module.exports = start;

function start(config){
    var app = config.app;

    return (new API(config));
};

function API(config){
    this.config = config;
}

//exec callback function
API.prototype.isAvailable = function(ID,right,callback){
    var sql_string = 'SELECT * FROM RightsTable WHERE ID=' + ID;

    this.config.modules['saferman'].sql(sql_string,function(results){
        var value;

        if(results[0] != undefined){
            var match = new RegExp(right);
            value = (match.exec(results[0].Rights)!=null);

            if(callback!=undefined)
                callback(value);
        }else{
            value = false;

            if(callback!=undefined)
                callback(value);
        }
    });
}

API.prototype.Add = function(ID,right){
    this.config.modules['saferman'].sql("UPDATE RightsTable SET rights='write' WHERE ID=0");
}

API.prototype.Delete = function(ID,right){
    this.config.modules['saferman'].sql('SELECT * FROM RightsTable',function(results){
        var value;

        if(results[0] != undefined){
            var match = new RegExp(right);
            value = (match.exec(results[0].Rights)!=null);

            callback(value);
        }else{
            value = false;

            callback(value);
        }
    });
}
<commit_msg>update rights_management<commit_after>module.exports = start;

function start(config){
    var app = config.app;

    return (new API(config));
};

function API(config){
    this.config = config;
}

//exec callback function
API.prototype.isAvailable = function(ID,right,callback){
    var sql_string = 'SELECT * FROM RightsTable WHERE ID=' + ID;

    this.config.modules['saferman'].sql(sql_string,function(results){
        var value;

        if(results[0] != undefined){
            var match = new RegExp(right);
            value = (match.exec(results[0].Rights)!=null);

            if(callback!=undefined)
                callback(value);
        }else{
            value = false;

            if(callback!=undefined)
                callback(value);
        }
    });
}

API.prototype.Add = function(ID,right){

    //check rights string

    //get rights string
    var sql_string = 'SELECT * FROM RightsTable WHERE ID=' + ID;

    this.config.modules['saferman'].sql(sql_string,function(results){
        var value;

        if(results[0] != undefined){
            var match = new RegExp(right);
            value = (match.exec(results[0].Rights)!=null);

            if(callback!=undefined)
                callback(value);
        }else{
            value = false;

            if(callback!=undefined)
                callback(value);
        }
    });

    //update rights string
    this.config.modules['saferman'].sql("UPDATE RightsTable SET rights='write' WHERE ID=0");
}

API.prototype.Delete = function(ID,right){
    this.config.modules['saferman'].sql('SELECT * FROM RightsTable',function(results){
        var value;

        if(results[0] != undefined){
            var match = new RegExp(right);
            value = (match.exec(results[0].Rights)!=null);

            callback(value);
        }else{
            value = false;

            callback(value);
        }
    });
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport');

module.exports = function(app) {
	// User Routes
	var users = require('../../app/controllers/users.server.controller');

	// Setting up the users profile api
	app.route('/users/me').get(users.me);
	app.route('/users').put(users.update);
	app.route('/users/accounts').delete(users.removeOAuthProvider);

	// Setting up the users password api
	app.route('/users/password').post(users.changePassword);
	app.route('/auth/forgot').post(users.forgot);
	app.route('/auth/reset/:token').get(users.validateResetToken);
	app.route('/auth/reset/:token').post(users.reset);

	// Setting up the users authentication api
	app.route('/auth/signup').post(users.signup);
	app.route('/auth/signin').post(users.signin);
	app.route('/auth/signout').get(users.signout);

	// Setting the facebook oauth routes
	app.route('/auth/facebook').get(passport.authenticate('facebook', {
		scope: ['email']
	}));
	app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));

	// Setting the twitter oauth routes
	app.route('/auth/twitter').get(passport.authenticate('twitter'));
	app.route('/auth/twitter/callback').get(users.oauthCallback('twitter'));

	// Setting the google oauth routes
	app.route('/auth/google').get(passport.authenticate('google', {
		scope: [
			'https://www.googleapis.com/auth/userinfo.profile',
			'https://www.googleapis.com/auth/userinfo.email'
		]
	}));
	app.route('/auth/google/callback').get(users.oauthCallback('google'));

	// Setting the linkedin oauth routes
	app.route('/auth/linkedin').get(passport.authenticate('linkedin'));
	app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin'));

	// Setting the github oauth routes
	app.route('/auth/github').get(passport.authenticate('github'));
	app.route('/auth/github/callback').get(users.oauthCallback('github'));

	app.get('/auth/openid', passport.authenticate('openid'));

	app.get('/auth/openid/return', passport.authenticate('openid', {
	    successRedirect: 'localhost:3000',
	    failureRedirect: '/cp3101b'
	}));

	// Finish by binding the user middleware
	app.param('userId', users.userByID);
};
<commit_msg>changed to app.post<commit_after>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport');

module.exports = function(app) {
	// User Routes
	var users = require('../../app/controllers/users.server.controller');

	// Setting up the users profile api
	app.route('/users/me').get(users.me);
	app.route('/users').put(users.update);
	app.route('/users/accounts').delete(users.removeOAuthProvider);

	// Setting up the users password api
	app.route('/users/password').post(users.changePassword);
	app.route('/auth/forgot').post(users.forgot);
	app.route('/auth/reset/:token').get(users.validateResetToken);
	app.route('/auth/reset/:token').post(users.reset);

	// Setting up the users authentication api
	app.route('/auth/signup').post(users.signup);
	app.route('/auth/signin').post(users.signin);
	app.route('/auth/signout').get(users.signout);

	// Setting the facebook oauth routes
	app.route('/auth/facebook').get(passport.authenticate('facebook', {
		scope: ['email']
	}));
	app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));

	// Setting the twitter oauth routes
	app.route('/auth/twitter').get(passport.authenticate('twitter'));
	app.route('/auth/twitter/callback').get(users.oauthCallback('twitter'));

	// Setting the google oauth routes
	app.route('/auth/google').get(passport.authenticate('google', {
		scope: [
			'https://www.googleapis.com/auth/userinfo.profile',
			'https://www.googleapis.com/auth/userinfo.email'
		]
	}));
	app.route('/auth/google/callback').get(users.oauthCallback('google'));

	// Setting the linkedin oauth routes
	app.route('/auth/linkedin').get(passport.authenticate('linkedin'));
	app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin'));

	// Setting the github oauth routes
	app.route('/auth/github').get(passport.authenticate('github'));
	app.route('/auth/github/callback').get(users.oauthCallback('github'));

	app.post('/auth/openid', passport.authenticate('openid'));

	app.get('/auth/openid/return', passport.authenticate('openid', {
	    successRedirect: 'localhost:3000',
	    failureRedirect: '/'
	}));

	// Finish by binding the user middleware
	app.param('userId', users.userByID);
};
<|endoftext|>


<commit_before>import { color } from 'd3-color';
import * as PIXI from 'pixi.js';

import PixiTrack from './PixiTrack';

import pubSub from './services/pub-sub';

// Maximum delay in ms between mousedown and mouseup that is registered as a
// click. Note we need to use mousedown and mouseup as PIXI doesn't recognize
// click events with out current setup. Since most UIs treat long clicks as
// either something special or a cancelation we follow best practices and
// implement a threshold on the delay as well.
const MAX_CLICK_DELAY = 300;

class Annotations1dTrack extends PixiTrack {
  constructor(scene, options, isVertical) {
    super(scene, options);

    this.options = options;
    this.isVertical = isVertical;

    this.rects = {};

    this.defaultColor = color('red');
  }

  draw() {
    const globalMinRectWidth = typeof this.options.minRectWidth !== 'undefined'
      ? this.options.minRectWidth
      : 10;

    const globalFill = typeof this.options.fill !== 'undefined'
      ? color(this.options.fill)
      : this.defaultColor;

    const globalFillOpacity = typeof this.options.fillOpacity !== 'undefined'
      ? +this.options.fillOpacity
      : 0.2;

    const globalStroke = typeof this.options.stroke !== 'undefined'
      ? color(this.options.stroke)
      : this.defaultColor;

    const globalStrokeWidth = typeof this.options.strokeWidth !== 'undefined'
      ? +this.options.strokeWidth
      : 1;

    const globalStrokeOpacity = typeof this.options.strokeOpacity !== 'undefined'
      ? +this.options.strokeOpacity
      : 0;

    let strokePos;
    if (this.options.strokePos && this.options.strokePos.length) {
      strokePos = Array.isArray(this.options.strokePos)
        ? this.options.strokePos
        : [this.options.strokePos];
    }

    super.draw();
    const graphics = this.pMain;
    graphics.clear();

    // The time stamp is used to keep track which rectangles have been drawn per
    // draw call. Each rectangle previously drawn that is not visible anymore
    // (i.e., is not drawn in the current draw call) will be removed at the end
    // by checking against the time stamp.
    const timeStamp = performance.now();

    // Regions have to follow the following form:
    // start, end, fill, stroke, fillOpacity, strokeOpcaity, min-size
    // If `color-line` is not given, `color-fill` is used
    this.options.regions.forEach((region) => {
      const id = `${region[0]}-${region[1]}`;

      if (!this.rects[id]) {
        this.rects[id] = { graphics: new PIXI.Graphics() };
        graphics.addChild(this.rects[id].graphics);
      }

      this.rects[id].timeStamp = timeStamp;

      const fill = color(region[2]) || globalFill;
      let stroke = color(region[3]) || globalStroke;

      if (!stroke) { stroke = fill; }

      const fillHex = PIXI.utils.rgb2hex(
        [fill.r / 255.0, fill.g / 255.0, fill.b / 255.0],
      );
      const strokeHex = PIXI.utils.rgb2hex(
        [stroke.r / 255.0, stroke.g / 255.0, stroke.b / 255.0],
      );

      if (strokePos) {
        graphics.lineStyle(1, strokeHex, 0);
        graphics.beginFill(strokeHex, +region[5] || globalStrokeOpacity);
      } else {
        graphics.lineStyle(
          globalStrokeWidth, strokeHex, +region[5] || globalStrokeOpacity
        );
        graphics.beginFill(fillHex, +region[4] || globalFillOpacity);
      }

      const scale = this.isVertical ? this._yScale : this._xScale;

      let start = scale(+region[0]);
      const end = scale(+region[1]);

      let width = end - start;

      const minRectWidth = typeof region[6] !== 'undefined'
        ? region[6]
        : globalMinRectWidth;

      if (width < minRectWidth) {
        // this region is too small to draw so center it on the location
        // where it would be drawn
        start = ((start + end) / 2) - (minRectWidth / 2);
        width = minRectWidth;
      }

      if (strokePos) {
        graphics.lineStyle(1, strokeHex, 0);
        graphics.beginFill(strokeHex, +region[5] || globalStrokeOpacity);

        strokePos.forEach((pos) => {
          if (pos === 'top' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(0, start, globalStrokeWidth, width);
            } else {
              graphics.drawRect(start, 0, width, globalStrokeWidth);
            }
          }

          if (pos === 'right' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                0, start, this.dimensions[0], globalStrokeWidth
              );
            } else {
              graphics.drawRect(
                start, 0, globalStrokeWidth, this.dimensions[1]
              );
            }
          }

          if (pos === 'bottom' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                this.dimensions[0] - globalStrokeWidth,
                start,
                globalStrokeWidth,
                width,
              );
            } else {
              graphics.drawRect(
                start,
                this.dimensions[1] - globalStrokeWidth,
                width,
                globalStrokeWidth,
              );
            }
          }

          if (pos === 'left' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                0,
                start + width - globalStrokeWidth,
                this.dimensions[0],
                globalStrokeWidth
              );
            } else {
              graphics.drawRect(
                start + width - globalStrokeWidth,
                0,
                globalStrokeWidth,
                this.dimensions[1]
              );
            }
          }
        });
      } else {
        graphics.lineStyle(
          globalStrokeWidth, strokeHex, +region[5] || globalStrokeOpacity
        );
      }

      // Make annotation clickable
      this.rects[id].graphics.clear();
      this.rects[id].graphics.interactive = true;
      this.rects[id].graphics.buttonMode = true;

      graphics.beginFill(fillHex, +region[4] || globalFillOpacity);
      if (this.isVertical) {
        graphics.drawRect(0, start, this.dimensions[0], width);
        this.rects[id].graphics.hitArea = new PIXI.Rectangle(
          0, start, this.dimensions[0], width
        );
      } else {
        graphics.drawRect(start, 0, width, this.dimensions[1]);
        this.rects[id].graphics.hitArea = new PIXI.Rectangle(
          start, 0, width, this.dimensions[1]
        );
      }

      this.rects[id].graphics.mousedown = () => {
        this.rects[id].mouseDownTime = performance.now();
      };

      this.rects[id].graphics.mouseup = (event) => {
        if (performance.now() - this.rects[id].mouseDownTime < MAX_CLICK_DELAY) {
          pubSub.publish('app.click', {
            type: 'annotation',
            event,
            payload: region
          });
        }
      };
    });

    // Remove outdated rects, i.e., rects whos time stamp is not the current
    // time stamp stored above.
    Object
      .values(this.rects)
      .filter(rect => rect.timeStamp !== timeStamp)
      .forEach(rect => graphics.removeChild(rect.graphics));
  }

  setPosition(newPosition) {
    super.setPosition(newPosition);

    this.pMain.position.y = this.position[1];
    this.pMain.position.x = this.position[0];
  }

  zoomed(newXScale, newYScale) {
    this.xScale(newXScale);
    this.yScale(newYScale);

    this.draw();
  }
}

export default Annotations1dTrack;
<commit_msg>Fix typo<commit_after>import { color } from 'd3-color';
import * as PIXI from 'pixi.js';

import PixiTrack from './PixiTrack';

import pubSub from './services/pub-sub';

// Maximum delay in ms between mousedown and mouseup that is registered as a
// click. Note we need to use mousedown and mouseup as PIXI doesn't recognize
// click events with out current setup. Since most UIs treat long clicks as
// either something special or a cancelation we follow best practices and
// implement a threshold on the delay as well.
const MAX_CLICK_DELAY = 300;

class Annotations1dTrack extends PixiTrack {
  constructor(scene, options, isVertical) {
    super(scene, options);

    this.options = options;
    this.isVertical = isVertical;

    this.rects = {};

    this.defaultColor = color('red');
  }

  draw() {
    const globalMinRectWidth = typeof this.options.minRectWidth !== 'undefined'
      ? this.options.minRectWidth
      : 10;

    const globalFill = typeof this.options.fill !== 'undefined'
      ? color(this.options.fill)
      : this.defaultColor;

    const globalFillOpacity = typeof this.options.fillOpacity !== 'undefined'
      ? +this.options.fillOpacity
      : 0.2;

    const globalStroke = typeof this.options.stroke !== 'undefined'
      ? color(this.options.stroke)
      : this.defaultColor;

    const globalStrokeWidth = typeof this.options.strokeWidth !== 'undefined'
      ? +this.options.strokeWidth
      : 1;

    const globalStrokeOpacity = typeof this.options.strokeOpacity !== 'undefined'
      ? +this.options.strokeOpacity
      : 0;

    let strokePos;
    if (this.options.strokePos && this.options.strokePos.length) {
      strokePos = Array.isArray(this.options.strokePos)
        ? this.options.strokePos
        : [this.options.strokePos];
    }

    super.draw();
    const graphics = this.pMain;
    graphics.clear();

    // The time stamp is used to keep track which rectangles have been drawn per
    // draw call. Each rectangle previously drawn that is not visible anymore
    // (i.e., is not drawn in the current draw call) will be removed at the end
    // by checking against the time stamp.
    const timeStamp = performance.now();

    // Regions have to follow the following form:
    // start, end, fill, stroke, fillOpacity, strokeOpcaity, min-size
    // If `color-line` is not given, `color-fill` is used
    this.options.regions.forEach((region) => {
      const id = `${region[0]}-${region[1]}`;

      if (!this.rects[id]) {
        this.rects[id] = { graphics: new PIXI.Graphics() };
        graphics.addChild(this.rects[id].graphics);
      }

      this.rects[id].timeStamp = timeStamp;

      const fill = color(region[2]) || globalFill;
      let stroke = color(region[3]) || globalStroke;

      if (!stroke) { stroke = fill; }

      const fillHex = PIXI.utils.rgb2hex(
        [fill.r / 255.0, fill.g / 255.0, fill.b / 255.0],
      );
      const strokeHex = PIXI.utils.rgb2hex(
        [stroke.r / 255.0, stroke.g / 255.0, stroke.b / 255.0],
      );

      if (strokePos) {
        graphics.lineStyle(1, strokeHex, 0);
        graphics.beginFill(strokeHex, +region[5] || globalStrokeOpacity);
      } else {
        graphics.lineStyle(
          globalStrokeWidth, strokeHex, +region[5] || globalStrokeOpacity
        );
        graphics.beginFill(fillHex, +region[4] || globalFillOpacity);
      }

      const scale = this.isVertical ? this._yScale : this._xScale;

      let start = scale(+region[0]);
      const end = scale(+region[1]);

      let width = end - start;

      const minRectWidth = typeof region[6] !== 'undefined'
        ? region[6]
        : globalMinRectWidth;

      if (width < minRectWidth) {
        // this region is too small to draw so center it on the location
        // where it would be drawn
        start = ((start + end) / 2) - (minRectWidth / 2);
        width = minRectWidth;
      }

      if (strokePos) {
        graphics.lineStyle(1, strokeHex, 0);
        graphics.beginFill(strokeHex, +region[5] || globalStrokeOpacity);

        strokePos.forEach((pos) => {
          if (pos === 'top' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(0, start, globalStrokeWidth, width);
            } else {
              graphics.drawRect(start, 0, width, globalStrokeWidth);
            }
          }

          if (pos === 'right' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                0, start, this.dimensions[0], globalStrokeWidth
              );
            } else {
              graphics.drawRect(
                start, 0, globalStrokeWidth, this.dimensions[1]
              );
            }
          }

          if (pos === 'bottom' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                this.dimensions[0] - globalStrokeWidth,
                start,
                globalStrokeWidth,
                width,
              );
            } else {
              graphics.drawRect(
                start,
                this.dimensions[1] - globalStrokeWidth,
                width,
                globalStrokeWidth,
              );
            }
          }

          if (pos === 'left' || pos === 'around') {
            if (this.isVertical) {
              graphics.drawRect(
                0,
                start + width - globalStrokeWidth,
                this.dimensions[0],
                globalStrokeWidth
              );
            } else {
              graphics.drawRect(
                start + width - globalStrokeWidth,
                0,
                globalStrokeWidth,
                this.dimensions[1]
              );
            }
          }
        });
      } else {
        graphics.lineStyle(
          globalStrokeWidth, strokeHex, +region[5] || globalStrokeOpacity
        );
      }

      // Make annotation clickable
      this.rects[id].graphics.clear();
      this.rects[id].graphics.interactive = true;
      this.rects[id].graphics.buttonMode = true;

      graphics.beginFill(fillHex, +region[4] || globalFillOpacity);
      if (this.isVertical) {
        graphics.drawRect(0, start, this.dimensions[0], width);
        this.rects[id].graphics.hitArea = new PIXI.Rectangle(
          0, start, this.dimensions[0], width
        );
      } else {
        graphics.drawRect(start, 0, width, this.dimensions[1]);
        this.rects[id].graphics.hitArea = new PIXI.Rectangle(
          start, 0, width, this.dimensions[1]
        );
      }

      this.rects[id].graphics.mousedown = () => {
        this.rects[id].mouseDownTime = performance.now();
      };

      this.rects[id].graphics.mouseup = (event) => {
        if (performance.now() - this.rects[id].mouseDownTime < MAX_CLICK_DELAY) {
          pubSub.publish('app.click', {
            type: 'annotation',
            event,
            payload: region
          });
        }
      };
    });

    // Remove outdated rects, i.e., rects whose time stamp is not the current
    // time stamp stored above.
    Object
      .values(this.rects)
      .filter(rect => rect.timeStamp !== timeStamp)
      .forEach(rect => graphics.removeChild(rect.graphics));
  }

  setPosition(newPosition) {
    super.setPosition(newPosition);

    this.pMain.position.y = this.position[1];
    this.pMain.position.x = this.position[0];
  }

  zoomed(newXScale, newYScale) {
    this.xScale(newXScale);
    this.yScale(newYScale);

    this.draw();
  }
}

export default Annotations1dTrack;
<|endoftext|>


<commit_before>import TiledPixiTrack from './TiledPixiTrack';

// Services
import { tileProxy } from './services';

// Utils
import { colorToHex } from './utils';

class Annotations2dTrack extends TiledPixiTrack {
  constructor(scene, dataConfig, handleTilesetInfoReceived, option, animate) {
    super(scene, dataConfig, handleTilesetInfoReceived, option, animate);

    this.drawnRects = {};

    this.options.minSquareSize = +this.options.minSquareSize;
  }

  /**
   * The local tile identifier
   *
   * @param  {Array}  tile  Array containing [zoomLevel, xPos, yPos]
   * @return  {String}  Joined ID string
   */
  tileToLocalId(tile) {
    // tile contains [zoomLevel, xPos, yPos]
    return `${tile.join('.')}`;
  }

  /**
   * The tile identifier used on the server
   *
   * @param  {Array}  tile  Array containing [zoomLevel, xPos, yPos]
   * @return  {String}  Joined ID string
   */
  tileToRemoteId(tile) {
    return `${tile.join('.')}`;
  }

  localToRemoteId(remoteId) {
    const idParts = remoteId.split('.');
    return idParts.slice(0, idParts.length - 1).join('.');
  }

  calculateZoomLevel() {
    const xZoomLevel = tileProxy.calculateZoomLevel(
      this._xScale, 0, this.tilesetInfo.max_size
    );
    const yZoomLevel = tileProxy.calculateZoomLevel(
      this._yScale, 0, this.tilesetInfo.max_size
    );

    return Math.min(Math.max(xZoomLevel, yZoomLevel), this.maxZoom);
  }

  /**
   * Set which tiles are visible right now.
   *
   * @param tiles: A set of tiles which will be considered the currently visible
   * tile positions.
   */
  setVisibleTiles(tilePositions) {
    this.visibleTiles = tilePositions.map(x => ({
      tileId: this.tileToLocalId(x),
      remoteId: this.tileToRemoteId(x),
    }));

    this.visibleTileIds = new Set(this.visibleTiles.map(x => x.tileId));
  }

  calculateVisibleTiles() {
    // if we don't know anything about this dataset, no point
    // in trying to get tiles
    if (!this.tilesetInfo) return;

    this.zoomLevel = this.calculateZoomLevel();

    this.xTiles = tileProxy.calculateTiles(
      this.zoomLevel,
      this._xScale,
      0,
      this.tilesetInfo.max_size,
      this.tilesetInfo.max_zoom,
      this.tilesetInfo.max_size
    );

    this.yTiles = tileProxy.calculateTiles(
      this.zoomLevel,
      this._yScale,
      0,
      this.tilesetInfo.max_size,
      this.tilesetInfo.max_zoom,
      this.tilesetInfo.max_size
    );

    const zoomLevel = this.zoomLevel;

    // if we're mirroring tiles, then we only need tiles along the diagonal
    const tiles = [];

    // calculate the ids of the tiles that should be visible
    for (let i = 0; i < this.xTiles.length; i++) {
      for (let j = 0; j < this.yTiles.length; j++) {
        tiles.push([zoomLevel, this.xTiles[i], this.yTiles[j]]);
      }
    }

    this.setVisibleTiles(tiles);
  }

  // initTile(tile) {}

  // destroyTile(tile, graphics) {}

  draw() {
    this.drawnRects = {};

    super.draw();
  }

  drawTile(tile) {
    if (!tile.graphics) return;

    const graphics = tile.graphics;
    graphics.clear();

    const stroke = colorToHex(this.options.rectangleDomainStrokeColor || 'black');
    const fill = colorToHex(this.options.rectangleDomainFillColor || 'grey');

    graphics.lineStyle(
      typeof this.options.rectangleDomainStrokeWidth !== 'undefined'
        ? this.options.rectangleDomainStrokeWidth
        : 1,
      stroke,
      typeof this.options.rectangleDomainStrokeOpacity !== 'undefined'
        ? this.options.rectangleDomainStrokeOpacity
        : 1,
    );
    graphics.beginFill(
      fill,
      typeof this.options.rectangleDomainFillOpacity !== 'undefined'
        ? this.options.rectangleDomainFillOpacity
        : 0.4,
    );

    graphics.alpha = this.options.rectangleDomainOpacity || 0.5;

    if (!tile.tileData.length) return;

    tile.tileData
      .filter(td => !(td.uid in this.drawnRects))
      .forEach((td) => {
        const startX = this._xScale(td.xStart);
        const endX = this._xScale(td.xEnd);

        const startY = this._yScale(td.yStart);
        const endY = this._yScale(td.yEnd);

        const uid = td.uid;

        const width = endX - startX;
        const height = endY - startY;

        let drawnRect = {
          x: startX,
          y: startY,
          width,
          height
        };

        if (this.options.minSquareSize) {
          if (
            width < this.options.minSquareSize
            || height < this.options.minSquareSize
          ) {
            drawnRect = {
              x: (startX + endX) / 2,
              y: (startY + endY) / 2,
              width: this.options.minSquareSize,
              height: this.options.minSquareSize
            };
          }
        }

        this.drawnRects[uid] = drawnRect;

        graphics.drawRect(
          drawnRect.x, drawnRect.y, drawnRect.width, drawnRect.height
        );
      });
  }

  exportSVG() {
    let track = null;
    let base = null;

    if (super.exportSVG) {
      [base, track] = super.exportSVG();
    } else {
      base = document.createElement('g');
      track = base;
    }

    const output = document.createElement('g');
    output.setAttribute(
      'transform',
      `translate(${this.position[0]},${this.position[1]})`
    );

    track.appendChild(output);

    this.visibleAndFetchedTiles()
      .filter(tile => tile.tileData && tile.tileData.length)
      .map(tile => ({ graphics: tile.graphics, td: tile.tileData }))
      .forEach(({ td, graphics }) => {
        const gTile = document.createElement('g');

        gTile.setAttribute(
          'transform',
          `translate(${graphics.position.x},${graphics.position.y})scale(${graphics.scale.x},${graphics.scale.y})`
        );
        output.appendChild(gTile);

        if (td.uid in this.drawnRects) {
          const rect = this.drawnRects[td.uid];
          const r = document.createElement('rect');

          r.setAttribute('x', rect.x);
          r.setAttribute('y', rect.y);
          r.setAttribute('width', rect.width);
          r.setAttribute('height', rect.height);

          r.setAttribute('fill', this.options.fillColor || 'grey');
          r.setAttribute('opacity', 0.3);

          r.style.stroke = this.options.fillColor || 'grey';
          r.style.strokeWidth = '1px';

          gTile.appendChild(r);
        }
      });

    return [base, base];
  }

  setPosition(newPosition) {
    super.setPosition(newPosition);

    this.pMain.position.y = this.position[1];
    this.pMain.position.x = this.position[0];
  }

  zoomed(newXScale, newYScale) {
    this.xScale(newXScale);
    this.yScale(newYScale);

    this.refreshTiles();

    this.draw();
  }
}

export default Annotations2dTrack;
<commit_msg>Publish drawn annotations<commit_after>import TiledPixiTrack from './TiledPixiTrack';

// Services
import { tileProxy } from './services';
import { create } from './services/pub-sub';

// Utils
import { colorToHex } from './utils';

class Annotations2dTrack extends TiledPixiTrack {
  constructor(scene, dataConfig, handleTilesetInfoReceived, option, animate) {
    super(scene, dataConfig, handleTilesetInfoReceived, option, animate);

    this.drawnRects = {};

    this.options.minSquareSize = +this.options.minSquareSize;

    const { publish, subscribe, unsubscribe } = create({});
    this.publish = publish;
    this.subscribe = subscribe;
    this.unsubscribe = unsubscribe;
  }

  /**
   * The local tile identifier
   *
   * @param  {Array}  tile  Array containing [zoomLevel, xPos, yPos]
   * @return  {String}  Joined ID string
   */
  tileToLocalId(tile) {
    // tile contains [zoomLevel, xPos, yPos]
    return `${tile.join('.')}`;
  }

  /**
   * The tile identifier used on the server
   *
   * @param  {Array}  tile  Array containing [zoomLevel, xPos, yPos]
   * @return  {String}  Joined ID string
   */
  tileToRemoteId(tile) {
    return `${tile.join('.')}`;
  }

  localToRemoteId(remoteId) {
    const idParts = remoteId.split('.');
    return idParts.slice(0, idParts.length - 1).join('.');
  }

  calculateZoomLevel() {
    const xZoomLevel = tileProxy.calculateZoomLevel(
      this._xScale, 0, this.tilesetInfo.max_size
    );
    const yZoomLevel = tileProxy.calculateZoomLevel(
      this._yScale, 0, this.tilesetInfo.max_size
    );

    return Math.min(Math.max(xZoomLevel, yZoomLevel), this.maxZoom);
  }

  /**
   * Set which tiles are visible right now.
   *
   * @param tiles: A set of tiles which will be considered the currently visible
   * tile positions.
   */
  setVisibleTiles(tilePositions) {
    this.visibleTiles = tilePositions.map(x => ({
      tileId: this.tileToLocalId(x),
      remoteId: this.tileToRemoteId(x),
    }));

    this.visibleTileIds = new Set(this.visibleTiles.map(x => x.tileId));
  }

  calculateVisibleTiles() {
    // if we don't know anything about this dataset, no point
    // in trying to get tiles
    if (!this.tilesetInfo) return;

    this.zoomLevel = this.calculateZoomLevel();

    this.xTiles = tileProxy.calculateTiles(
      this.zoomLevel,
      this._xScale,
      0,
      this.tilesetInfo.max_size,
      this.tilesetInfo.max_zoom,
      this.tilesetInfo.max_size
    );

    this.yTiles = tileProxy.calculateTiles(
      this.zoomLevel,
      this._yScale,
      0,
      this.tilesetInfo.max_size,
      this.tilesetInfo.max_zoom,
      this.tilesetInfo.max_size
    );

    const zoomLevel = this.zoomLevel;

    // if we're mirroring tiles, then we only need tiles along the diagonal
    const tiles = [];

    // calculate the ids of the tiles that should be visible
    for (let i = 0; i < this.xTiles.length; i++) {
      for (let j = 0; j < this.yTiles.length; j++) {
        tiles.push([zoomLevel, this.xTiles[i], this.yTiles[j]]);
      }
    }

    this.setVisibleTiles(tiles);
  }

  // initTile(tile) {}

  // destroyTile(tile, graphics) {}

  draw() {
    this.drawnRects = {};

    super.draw();
  }

  drawTile(tile) {
    if (!tile.graphics) return;

    const graphics = tile.graphics;
    graphics.clear();

    const stroke = colorToHex(this.options.rectangleDomainStrokeColor || 'black');
    const fill = colorToHex(this.options.rectangleDomainFillColor || 'grey');

    graphics.lineStyle(
      typeof this.options.rectangleDomainStrokeWidth !== 'undefined'
        ? this.options.rectangleDomainStrokeWidth
        : 1,
      stroke,
      typeof this.options.rectangleDomainStrokeOpacity !== 'undefined'
        ? this.options.rectangleDomainStrokeOpacity
        : 1,
    );
    graphics.beginFill(
      fill,
      typeof this.options.rectangleDomainFillOpacity !== 'undefined'
        ? this.options.rectangleDomainFillOpacity
        : 0.4,
    );

    graphics.alpha = this.options.rectangleDomainOpacity || 0.5;

    if (!tile.tileData.length) return;

    tile.tileData
      .filter(td => !(td.uid in this.drawnRects))
      .forEach((td) => {
        const startX = this._xScale(td.xStart);
        const endX = this._xScale(td.xEnd);

        const startY = this._yScale(td.yStart);
        const endY = this._yScale(td.yEnd);

        const uid = td.uid;

        const width = endX - startX;
        const height = endY - startY;

        let drawnRect = {
          x: startX,
          y: startY,
          width,
          height
        };

        if (this.options.minSquareSize) {
          if (
            width < this.options.minSquareSize
            || height < this.options.minSquareSize
          ) {
            drawnRect = {
              x: (startX + endX) / 2,
              y: (startY + endY) / 2,
              width: this.options.minSquareSize,
              height: this.options.minSquareSize
            };
          }
        }

        this.drawnRects[uid] = drawnRect;

        graphics.drawRect(
          drawnRect.x, drawnRect.y, drawnRect.width, drawnRect.height
        );

        this.publish('annotationDrawn', {
          uid,
          viewPos: [drawnRect.x, drawnRect.y, drawnRect.width, drawnRect.height],
          dataPos: [td.xStart, td.xEnd, td.yStart, td.yEnd]
        });
      });
  }

  exportSVG() {
    let track = null;
    let base = null;

    if (super.exportSVG) {
      [base, track] = super.exportSVG();
    } else {
      base = document.createElement('g');
      track = base;
    }

    const output = document.createElement('g');
    output.setAttribute(
      'transform',
      `translate(${this.position[0]},${this.position[1]})`
    );

    track.appendChild(output);

    this.visibleAndFetchedTiles()
      .filter(tile => tile.tileData && tile.tileData.length)
      .map(tile => ({ graphics: tile.graphics, td: tile.tileData }))
      .forEach(({ td, graphics }) => {
        const gTile = document.createElement('g');

        gTile.setAttribute(
          'transform',
          `translate(${graphics.position.x},${graphics.position.y})scale(${graphics.scale.x},${graphics.scale.y})`
        );
        output.appendChild(gTile);

        if (td.uid in this.drawnRects) {
          const rect = this.drawnRects[td.uid];
          const r = document.createElement('rect');

          r.setAttribute('x', rect.x);
          r.setAttribute('y', rect.y);
          r.setAttribute('width', rect.width);
          r.setAttribute('height', rect.height);

          r.setAttribute('fill', this.options.fillColor || 'grey');
          r.setAttribute('opacity', 0.3);

          r.style.stroke = this.options.fillColor || 'grey';
          r.style.strokeWidth = '1px';

          gTile.appendChild(r);
        }
      });

    return [base, base];
  }

  setPosition(newPosition) {
    super.setPosition(newPosition);

    this.pMain.position.y = this.position[1];
    this.pMain.position.x = this.position[0];
  }

  zoomed(newXScale, newYScale) {
    this.xScale(newXScale);
    this.yScale(newYScale);

    this.refreshTiles();

    this.draw();
  }
}

export default Annotations2dTrack;
<|endoftext|>


<commit_before>'use strict';

angular.module('app')
	.controller('HeaderCtrl', function($scope, $location) {
		$scope.leftMenu = [];

	    $scope.rightMenu = [
	    	{'title': 'About', 'link': '#/about'}
	    ];

		$scope.isActive = function (viewLocation) {
			console.log("viewLocation: " + viewLocation);
			console.log("$location.path(): " + $location.path());
			return viewLocation === "#" + $location.path();
		};
	});<commit_msg>Update header.js<commit_after>'use strict';

angular.module('app')
	.controller('HeaderCtrl', function($scope, $location) {
		$scope.leftMenu = [];

	    $scope.rightMenu = [
	    	{'title': 'About Me', 'link': '#/about'}
	    ];

		$scope.isActive = function (viewLocation) {
			console.log("viewLocation: " + viewLocation);
			console.log("$location.path(): " + $location.path());
			return viewLocation === "#" + $location.path();
		};
	});
<|endoftext|>


<commit_before>(function () {
	function seekBar($document) {

		var calculatePercent = function (seekBar, event) {
			var offsetX = event.pageX - seekBar.offset().left;
			var seekBarWidth = seekBar.width();
			var offsetXPercent = offsetX / seekBarWidth;
			offsetXPercent = Math.max(0, offsetXPercent);
			offsetXPercent = Math.min(1, offsetXPercent);
			return offsetXPercent;
		};

		return {
			templateUrl: '/templates/directives/seek_bar.html',
			replace: true,
			restrict: 'E',
			scope: {
                onChange: '&'
            },

			link: function (scope, element, attributes) {
				scope.value = 0;
				scope.max = 100;

				var seekBar = $(element);

                attributes.$observe('value', function(newValue) {
                    scope.value = newValue;
 });

                attributes.$observe('max', function(newValue) {
                    scope.max = newValue;
 });

        var percentString = function () {
            var value = scope.value;
            var max = scope.max;
            var percent = value / max * 100;
            return percent + "%";
				};

				scope.fillStyle = function () {
					return {
						width: percentString()
					};
				};

				scope.thumbStyle = function () {
					return {
						left: percentString()
					};
				};

				scope.onClickSeekBar = function (event) {
					var percent = calculatePercent(seekBar, event);
					scope.value = percent * scope.max;
                    notifyOnChange(scope.value);
				};

				scope.trackThumb = function () {
					$document.bind('mousemove.thumb', function (event) {
						var percent = calculatePercent(seekBar, event);
				        scope.$apply(function () {
				        scope.value = percent * scope.max;
                        notifyOnChange(scope.value);
						});
					});

					$document.bind('mouseup.thumb', function () {
						$document.unbind('mousemove.thumb');
						$document.unbind('mouseup.thumb');
					});
				};

			}
		};
	}

	angular
		.module('blocJams')
		.directive('seekBar', ['$document', seekBar]);
})();
<commit_msg>checkpoint 11 second revise<commit_after>(function() {
    function seekBar($document) {
        var calculatePercent = function(seekBar, event) {
            var offsetX = event.pageX - seekBar.offset().left;
            var seekBarWidth = seekBar.width();
            var offsetXPercent = offsetX / seekBarWidth;
            offsetXPercent = Math.max(0, offsetXPercent);
            offsetXPercent = Math.min(1, offsetXPercent);
            return offsetXPercent;
        };

        return {
            templateUrl: '/templates/directives/seek_bar.html',
            replace: true,
            restrict: 'E',
            scope: {
                onChange: '&'
            },
            link: function(scope, element, attributes) {
                scope.value = 0;
                scope.max = 100;

                var seekBar = $(element);

                attributes.$observe('value', function(newValue) {
                    scope.value = newValue;
                });

                attributes.$observe('max', function(newValue) {
                    scope.max = newValue;
                });

                var percentString = function() {
                    var value = scope.value;
                    var max = scope.max;
                    var percent = value / max * 100;
                    return percent + "%";
                };

                scope.fillStyle = function() {
                    return {width: percentString()};
                };

                scope.thumbStyle = function() {
                    return {left: percentString()};
                };

                scope.onClickSeekBar = function(event) {
                    var percent = calculatePercent(seekBar, event);
                    scope.value = percent * scope.max;
                    notifyOnChange(scope.value);
                };

                scope.trackThumb = function() {
                    $document.bind('mousemove.thumb', function(event) {
                        var percent = calculatePercent(seekBar, event);
                        scope.$apply(function() {
                            scope.value = percent * scope.max;
                            notifyOnChange(scope.value);
                        });
                    });

                    $document.bind('mouseup.thumb', function() {
                        $document.unbind('mousemove.thumb');
                        $document.unbind('mouseup.thumb');
                    });
                };

                var notifyOnChange = function(newValue) {
                    if (typeof scope.onChange === 'function') {
                        scope.onChange({value: newValue});
                    }
                };
            }
        };
    }

    angular
        .module('blocJams')
        .directive('seekBar', ['$document', seekBar]);
})();
<|endoftext|>