<commit_before>import EDiscovery from '@webex/internal-plugin-ediscovery';
import MockWebex from '@webex/test-helper-mock-webex';
import sinon from 'sinon';
import {assert, expect} from '@webex/test-helper-chai';
import config from '@webex/internal-plugin-ediscovery/src/config';

/* eslint-disable max-len */
describe('EDiscovery Content API Tests', () => {
  let webex;
  const uuid = 'cc06f622-46ab-45b9-b3a6-5d70bad1d70a';
  const defaultTimeout = 30000;

  beforeEach(() => {
    webex = new MockWebex({
      children: {
        ediscovery: EDiscovery
      }
    });
    webex.config.ediscovery = config.ediscovery;
  });

  describe('GetContent Tests', () => {
    it('GetContent succeeds', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res.statusCode).equal(200);
        });

      return result;
    });

    it('GetContent fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContent()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContent timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContent(uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContent timeout defaults to 30s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContent timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContent timeout can be overwritten to 5s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1, timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
          sinon.assert.calledWith(webex.request, sinon.match.has('qs', {offset: 0, size: 1}));
        });

      return result;
    });
  });

  describe('GetContentSummary Tests', () => {
    it('GetContentSummary succeeds', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res.statusCode).equal(200);
        });

      return result;
    });

    it('GetContentSummary fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContentSummary()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContentSummary timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummary timeout defaults to 30s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummary timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContentSummary timeout can be overwritten to 5s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1, timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
          sinon.assert.calledWith(webex.request, sinon.match.has('qs', {offset: 0, size: 1}));
        });

      return result;
    });

    it('GetContentSummary sets report in contentSummaryBySpaceIdCache if it does not exist', async () => {
      const mockResponse = {body: [{spaceId: uuid}]};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        set: sinon.stub(),
        has: sinon.stub().returns(false),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then(() => {
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, uuid);
          assert.calledOnce(webex.internal.ediscovery.contentSummaryBySpaceIdCache.clear);
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.set, uuid, new Map());
        });

      return result;
    });

    it('GetContentSummary does not retrieves space from contentSummaryBySpaceIdCache if available', async () => {
      const mockCachedSpace = {spaceId: uuid};
      const spaceIdMap = {
        get: sinon.stub().withArgs(uuid).returns(mockCachedSpace),
        set: sinon.stub(),
        has: sinon.stub().returns(true)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res).to.not.equal(mockCachedSpace);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 0);
          assert.callCount(spaceIdMap.has, 0);
          assert.callCount(spaceIdMap.get, 0);
        });

      return result;
    });

    it('GetContentSummary populates contentSummaryBySpaceIdCache with space when retrieved', async () => {
      const mockResponse = {body: [{spaceId: uuid}]};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 1);
          assert.calledWith(spaceIdMap.set, mockResponse.body[0].spaceId, mockResponse.body[0]);
        });

      return result;
    });

    it('GetContentSummary submits an audit log request', () => {
      const result = webex.internal.ediscovery.postAuditLog(uuid, {timeoutMs: 1000})
        .then((res) => {
          expect(res.statusCode).equal(200);
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 1000));
        });

      return result;
    });

    it('GetContentSummary submits a malformed audit request and gets rejected', () => {
      const noUuid = '';
      const result = expect(webex.internal.ediscovery.postAuditLog(noUuid, {timeoutMs: 1000})).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContentSummary skips audit log on http error', () => {
      webex.request = sinon.stub().returns(Promise.resolve({
        body: {},
        statusCode: 400
      }));

      const result = webex.internal.ediscovery.postAuditLog(uuid, {timeoutMs: 1000})
        .then((res) => {
          expect(res.statusCode).equal(400);
        });

      return result;
    });
  });

  describe('GetContentSummaryBySpaceId Tests', () => {
    it('GetContentSummaryBySpaceId succeeds', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.not.equal(undefined);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContentSummaryBySpaceId()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContentSummaryBySpaceId timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummaryBySpaceId timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContentSummaryBySpaceId sets report in contentSummaryBySpaceIdCache if it does not exist', async () => {
      const mockResponse = {body: {spaceId: uuid}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        set: sinon.stub(),
        has: sinon.stub().returns(false),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then(() => {
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, uuid);
          assert.calledOnce(webex.internal.ediscovery.contentSummaryBySpaceIdCache.clear);
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.set, uuid, new Map());
        });

      return result;
    });

    it('GetContentSummaryBySpaceId retrieves space from contentSummaryBySpaceIdCache if available', async () => {
      const mockCachedSpace = {spaceId: uuid};

      const spaceIdMap = {
        get: sinon.stub().withArgs(uuid).returns(mockCachedSpace),
        set: sinon.stub(),
        has: sinon.stub().returns(true)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res.body).to.equal(mockCachedSpace);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 1);
          assert.callCount(spaceIdMap.has, 1);
          assert.callCount(spaceIdMap.get, 1);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId populates contentSummaryBySpaceIdCache with space when retrieved', async () => {
      const mockResponse = {body: {spaceId: uuid}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 2);
          assert.callCount(spaceIdMap.has, 1);
          assert.calledWith(spaceIdMap.set, mockResponse.body.spaceId, mockResponse.body);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId cannot write space to contentSummaryBySpaceIdCache without spaceId', async () => {
      const mockResponse = {body: {}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 2);
          assert.callCount(spaceIdMap.has, 1);
          assert.notCalled(spaceIdMap.set);
        });

      return result;
    });
  });
});
<commit_msg>refactor(ediscovery): change based on review<commit_after>import EDiscovery from '@webex/internal-plugin-ediscovery';
import MockWebex from '@webex/test-helper-mock-webex';
import sinon from 'sinon';
import {assert, expect} from '@webex/test-helper-chai';
import config from '@webex/internal-plugin-ediscovery/src/config';

/* eslint-disable max-len */
describe('EDiscovery Content API Tests', () => {
  let webex;
  const uuid = 'cc06f622-46ab-45b9-b3a6-5d70bad1d70a';
  const defaultTimeout = 30000;

  beforeEach(() => {
    webex = new MockWebex({
      children: {
        ediscovery: EDiscovery
      }
    });
    webex.config.ediscovery = config.ediscovery;
  });

  describe('GetContent Tests', () => {
    it('GetContent succeeds', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res.statusCode).equal(200);
        });

      return result;
    });

    it('GetContent fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContent()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContent timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContent(uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContent timeout defaults to 30s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContent timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContent timeout can be overwritten to 5s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContent(uuid, {offset: 0, size: 1, timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
          sinon.assert.calledWith(webex.request, sinon.match.has('qs', {offset: 0, size: 1}));
        });

      return result;
    });
  });

  describe('GetContentSummary Tests', () => {
    it('GetContentSummary succeeds', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res.statusCode).equal(200);
        });

      return result;
    });

    it('GetContentSummary fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContentSummary()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContentSummary timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummary timeout defaults to 30s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummary timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContentSummary timeout can be overwritten to 5s when other options are specified', async () => {
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1, timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
          sinon.assert.calledWith(webex.request, sinon.match.has('qs', {offset: 0, size: 1}));
        });

      return result;
    });

    it('GetContentSummary sets report in contentSummaryBySpaceIdCache if it does not exist', async () => {
      const mockResponse = {body: [{spaceId: uuid}]};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        set: sinon.stub(),
        has: sinon.stub().returns(false),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then(() => {
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, uuid);
          assert.calledOnce(webex.internal.ediscovery.contentSummaryBySpaceIdCache.clear);
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.set, uuid, new Map());
        });

      return result;
    });

    it('GetContentSummary does not retrieves space from contentSummaryBySpaceIdCache if available', async () => {
      const mockCachedSpace = {spaceId: uuid};
      const spaceIdMap = {
        get: sinon.stub().withArgs(uuid).returns(mockCachedSpace),
        set: sinon.stub(),
        has: sinon.stub().returns(true)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res).to.not.equal(mockCachedSpace);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 0);
          assert.callCount(spaceIdMap.has, 0);
          assert.callCount(spaceIdMap.get, 0);
        });

      return result;
    });

    it('GetContentSummary populates contentSummaryBySpaceIdCache with space when retrieved', async () => {
      const mockResponse = {body: [{spaceId: uuid}]};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummary(uuid, {offset: 0, size: 1})
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 1);
          assert.calledWith(spaceIdMap.set, mockResponse.body[0].spaceId, mockResponse.body[0]);
        });

      return result;
    });

    it('PostAuditLog with valid reportId succeeds', () => {
      webex.internal.ediscovery.postAuditLog(uuid, {timeoutMs: 1000})
        .then((res) => {
          expect(res.statusCode).equal(200);
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 1000));
        });
    });

    it('PostAuditLog with no reportId is rejected', () => {
      const noUuid = '';
      const result = expect(webex.internal.ediscovery.postAuditLog(noUuid, {timeoutMs: 1000})).to.be.rejectedWith(Error, 'No report ID specified');

      return result;
    });

    it('PostAuditLog propagates http error on failure', () => {
      webex.request = sinon.stub().returns(Promise.resolve({
        body: {},
        statusCode: 400
      }));

      webex.internal.ediscovery.postAuditLog(uuid, {timeoutMs: 1000})
        .then((res) => {
          expect(res.statusCode).equal(400);
        });
    });
  });

  describe('GetContentSummaryBySpaceId Tests', () => {
    it('GetContentSummaryBySpaceId succeeds', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.not.equal(undefined);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId fails with no params', async () => {
      const result = expect(webex.internal.ediscovery.getContentSummaryBySpaceId()).to.be.rejectedWith(Error, 'Undefined parameter');

      return result;
    });

    it('GetContentSummaryBySpaceId timeout defaults to 30s', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', defaultTimeout));
        });

      return result;
    });

    it('GetContentSummaryBySpaceId timeout can be overwritten to 5s with timeout as the only option', async () => {
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid, {timeoutMs: 5000})
        .then(() => {
          sinon.assert.calledWith(webex.request, sinon.match.has('timeout', 5000));
        });

      return result;
    });

    it('GetContentSummaryBySpaceId sets report in contentSummaryBySpaceIdCache if it does not exist', async () => {
      const mockResponse = {body: {spaceId: uuid}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        set: sinon.stub(),
        has: sinon.stub().returns(false),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then(() => {
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, uuid);
          assert.calledOnce(webex.internal.ediscovery.contentSummaryBySpaceIdCache.clear);
          assert.calledWith(webex.internal.ediscovery.contentSummaryBySpaceIdCache.set, uuid, new Map());
        });

      return result;
    });

    it('GetContentSummaryBySpaceId retrieves space from contentSummaryBySpaceIdCache if available', async () => {
      const mockCachedSpace = {spaceId: uuid};

      const spaceIdMap = {
        get: sinon.stub().withArgs(uuid).returns(mockCachedSpace),
        set: sinon.stub(),
        has: sinon.stub().returns(true)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res.body).to.equal(mockCachedSpace);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 1);
          assert.callCount(spaceIdMap.has, 1);
          assert.callCount(spaceIdMap.get, 1);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId populates contentSummaryBySpaceIdCache with space when retrieved', async () => {
      const mockResponse = {body: {spaceId: uuid}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 2);
          assert.callCount(spaceIdMap.has, 1);
          assert.calledWith(spaceIdMap.set, mockResponse.body.spaceId, mockResponse.body);
        });

      return result;
    });

    it('GetContentSummaryBySpaceId cannot write space to contentSummaryBySpaceIdCache without spaceId', async () => {
      const mockResponse = {body: {}};

      webex.internal.ediscovery.request = sinon.stub().resolves(mockResponse);

      const spaceIdMap = {
        set: sinon.stub(),
        has: sinon.stub().returns(false)
      };

      webex.internal.ediscovery.contentSummaryBySpaceIdCache = {
        get: sinon.stub().withArgs(uuid).returns(spaceIdMap),
        set: sinon.stub(),
        has: sinon.stub().returns(true),
        clear: sinon.stub()
      };
      const result = webex.internal.ediscovery.getContentSummaryBySpaceId(uuid, uuid)
        .then((res) => {
          expect(res).to.equal(mockResponse);
          assert.callCount(webex.internal.ediscovery.contentSummaryBySpaceIdCache.has, 2);
          assert.callCount(spaceIdMap.has, 1);
          assert.notCalled(spaceIdMap.set);
        });

      return result;
    });
  });
});
<|endoftext|>


<commit_before>/**
 * @ngdoc module
 * @module hs.utils
 * @name hs.utils
 * @description Utility module which contains few utility functions.
 */
define(['angular', 'app', 'ol'], function (angular, app, ol) {
    angular.module('hs.utils', ['hs'])

        /**
         * @ngdoc service
         * @name hs.utils.service
         * @module hs.utils
         * @param {object} config - Application configuration
         * @description Service for containing various utility functions used throughout HSL modules.
         * Add few utility functions and also enrich some data types with additional functions (mainly Date and String).
         */
        .service('hs.utils.service', ['config', function (config) {
            var me = this;
            /**
            * @ngdoc method
            * @name hs.utils.service#proxify
            * @public
            * @param {String} url Url to proxify
            * @param {Boolean} toEncoding Optional parameter if UTF-8 encoding shouldn´t be used for non-image Urls.
            * @returns {String} Encoded Url with path to hsproxy.cgi script
            * @description Add path to proxy cgi script (hsproxy.cgi) into Url and encode rest of Url if valid http Url is send and proxy use is allowed.
            */
            this.proxify = function (url, toEncoding) {
                toEncoding = angular.isUndefined(toEncoding) ? true : toEncoding;
                var outUrl = url;
                if ((url.substring(0, 4) == 'http' && url.indexOf(window.location.origin) == -1) || getPortFromUrl(url) != window.location.port) {
                    if (typeof use_proxy === 'undefined' || use_proxy === true) {

                        outUrl = "/cgi-bin/hsproxy.cgi?";
                        if (toEncoding && (url.indexOf('GetMap') == -1 || url.indexOf('GetFeatureInfo') > -1)) outUrl += "toEncoding=utf-8&";
                        outUrl = outUrl + "url=" + encodeURIComponent(url);
                    }
                }
                return outUrl;
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#shortUrl
            * @public
            * @param {String} url Url to shorten
            * @returns {String} Shortened url
            * @description Promise which shortens url by using some url shortener.
            * By default tinyurl is used, but user provided function in config.shortenUrl can be used. Example: function(url) {
                    return new Promise(function(resolve, reject){
                        $http.get("http://tinyurl.com/api-create.php?url=" + url, {
                            longUrl: url
                        }).success(function(data, status, headers, config) {
                            resolve(data);
                        }).error(function(data, status, headers, config) {
                            reject()
                        })
                    })
                }
            */
            this.shortUrl = function (url) {
                if (config.shortenUrl) return config.shortenUrl(url);
                return new Promise(function (resolve, reject) {
                    $http.get(me.proxify("http://tinyurl.com/api-create.php?url=" + url), {
                        longUrl: url
                    }).success(function (data, status, headers, config) {
                        resolve(data);
                    }).error(function (data, status, headers, config) {
                        reject()
                    })
                })
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#getPortFromUrl
            * @private
            * @param {String} url Url for which to determine port number
            * @returns {String} Port number
            */
            function getPortFromUrl(url) {
                var link = document.createElement('a');
                link.setAttribute('href', url);
                return link.port;
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#parseHexString
            * @deprecated
            */
            this.parseHexString = function (hex) {
                for (var bytes = [], c = 0; c < hex.length; c += 2)
                    bytes.push(parseInt(hex.substr(c, 2), 16));
                return bytes;
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#createHexString
            * @deprecated
            */
            this.createHexString = function (bytes) {
                for (var hex = [], i = 0; i < bytes.length; i++) {
                    hex.push((bytes[i] >>> 4).toString(16));
                    hex.push((bytes[i] & 0xF).toString(16));
                }
                return hex.join("");
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#getParamsFromUrl
            * @public
            * @param {String} str Url to parse for parameters
            * @returns {Object} Object with parsed parameters as properties
            * @description Parse parameters and their values from Url string
            */
            this.getParamsFromUrl = function (str) {
                if (typeof str !== 'string') {
                    return {};
                }

                if (str.indexOf('?') > -1)
                    str = str.substring(str.indexOf("?") + 1);
                else
                    return {};

                return str.trim().split('&').reduce(function (ret, param) {
                    var parts = param.replace(/\+/g, ' ').split('=');
                    var key = parts[0];
                    var val = parts[1];

                    key = decodeURIComponent(key);
                    // missing `=` should be `null`:
                    // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
                    val = val === undefined ? null : decodeURIComponent(val);

                    if (!ret.hasOwnProperty(key)) {
                        ret[key] = val;
                    } else if (Array.isArray(ret[key])) {
                        ret[key].push(val);
                    } else {
                        ret[key] = [ret[key], val];
                    }

                    return ret;
                }, {});
            };
            /**
            * @ngdoc method
            * @name hs.utils.service#paramsToUrl
            * @public
            * @param {Object} array Parameter object with parameter key-value pairs
            * @returns {String} Joined encoded Url query string
            * @description Create encoded Url string from object with parameters
            */
            this.paramsToURL = function (array) {
                var pairs = [];
                for (var key in array)
                    if (array.hasOwnProperty(key))

                        pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(array[key]));
                return pairs.join('&');
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#paramsToUrlWoEncode
            * @public
            * @param {Object} array Parameter object with parameter key-value pairs
            * @returns {String} Joined Url query string
            * @description Create Url string from object with parameters without encoding
            */
            this.paramsToURLWoEncode = function (array) {
                var pairs = [];
                for (var key in array)
                    if (array.hasOwnProperty(key))

                        pairs.push(key + '=' + array[key]);
                return pairs.join('&');
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#generateUuid
            * @public
            * @returns {String} Random uuid
            * @description Generate randomized uuid
            */
            this.generateUuid = function () {
                return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0,
                        v = c == 'x' ? r : r & 0x3 | 0x8;
                    return v.toString(16);
                });
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#rainbow
            * @public
            * @returns {String} CSS color
            * @description Generates css color string (rgba(0, 0, 0, 1)) from given range and value for which to have color
            */
            this.rainbow = function (numOfSteps, step, opacity) {
                // based on http://stackoverflow.com/a/7419630
                // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
                // Adam Cole, 2011-Sept-14
                // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
                var r, g, b;
                var h = step / (numOfSteps * 1.00000001);
                var i = ~~(h * 4);
                var f = h * 4 - i;
                var q = 1 - f;
                switch (i % 4) {
                    case 2:
                        r = f, g = 1, b = 0;
                        break;
                    case 0:
                        r = 0, g = f, b = 1;
                        break;
                    case 3:
                        r = 1, g = q, b = 0;
                        break;
                    case 1:
                        r = 0, g = 1, b = q;
                        break;
                }
                var c = "rgba(" + ~~(r * 235) + "," + ~~(g * 235) + "," + ~~(b * 235) + ", " + opacity + ")";
                return (c);
            }

            Date.isLeapYear = function (year) {
                return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
            };

            Date.getDaysInMonth = function (year, month) {
                return [31, (Date.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
            };

            Date.prototype.isLeapYear = function () {
                return Date.isLeapYear(this.getFullYear());
            };

            Date.prototype.getDaysInMonth = function () {
                return Date.getDaysInMonth(this.getFullYear(), this.getMonth());
            };

            Date.prototype.addMonths = function (value) {
                var n = this.getDate();
                this.setDate(1);
                this.setMonth(this.getMonth() + value);
                this.setDate(Math.min(n, this.getDaysInMonth()));
                return this;
            };

            Date.prototype.monthDiff = function (d2) {
                var months;
                months = (d2.getFullYear() - this.getFullYear()) * 12;
                months -= this.getMonth() + 1;
                months += d2.getMonth();
                return months <= 0 ? 0 : months;
            }

            String.prototype.hashCode = function () {
                var hash = 0;
                if (this.length == 0) return hash;
                for (i = 0; i < this.length; i++) {
                    char = this.charCodeAt(i);
                    hash = ((hash << 5) - hash) + char;
                    hash = hash & hash; // Convert to 32bit integer
                }
                return hash;
            }

            String.prototype.replaceAll = function (search, replacement) {
                var target = this;
                return target.replace(new RegExp(search, 'g'), replacement);
            };

            if (!String.prototype.format) {
                String.prototype.format = function () {
                    var args = arguments;
                    return this.replace(/{(\d+)}/g, function (match, number) {
                        return typeof args[number] != 'undefined' ?
                            args[number] :
                            match;
                    });
                };
            }

            if (!String.prototype.capitalizeFirstLetter) {
                String.prototype.capitalizeFirstLetter = function () {
                    return this.charAt(0).toUpperCase() + this.slice(1);
                }
            }

        }])

        /**
         * @ngdoc service
         * @name hs.utils.layerUtilsService
         * @module hs.utils
         * @param {object} config - Application configuration
         * @description Service containing varius functions for testing layer functionalities
         */
        .service('hs.utils.layerUtilsService', ['config', function (config) {
            var me = this;

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerIsZoomable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for layer with BoundingBox property, for WMS layer or for layer, which has source with extent
             * @description Determines if layer have properties needed for Zoom to layer function.
             */
            this.layerIsZoomable = function (layer) {
                if (typeof layer == 'undefined') return false;
                if (layer.get("BoundingBox")) return true;
                if (me.isLayerWMS(layer)) return true;
                if (layer.getSource().getExtent && layer.getSource().getExtent() && !ol.extent.isEmpty(layer.getSource().getExtent())) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerIsStyleable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Vector
             * @description Determines if layer is a Vector layer and therefore styleable
             */
            this.layerIsStyleable = function (layer) {
                if (typeof layer == 'undefined') return false;
                if (layer instanceof ol.layer.Vector /*&& layer.getSource().styleAble*/) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#isLayerQueryable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Tile and ol.layer.Image with INFO_FORMAT in params
             * @description Test if layer is queryable (WMS layer with Info format)
             */
            this.isLayerQueryable = function (layer) {
                if (layer instanceof ol.layer.Tile &&
                    (layer.getSource() instanceof ol.source.TileWMS) &&
                    layer.getSource().getParams().INFO_FORMAT) return true;
                if (layer instanceof ol.layer.Image &&
                    layer.getSource() instanceof ol.source.ImageWMS &&
                    layer.getSource().getParams().INFO_FORMAT) return true;
                return false;
            }
            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#isLayerWMS
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Tile and ol.layer.Image
             * @description Test if layer is WMS layer
             */
            this.isLayerWMS = function (layer) {
                if (layer instanceof ol.layer.Tile &&
                    (layer.getSource() instanceof ol.source.TileWMS)) return true;
                if (layer instanceof ol.layer.Image &&
                    layer.getSource() instanceof ol.source.ImageWMS) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerLoaded
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True loaded / False not (fully) loaded
             * @description Test if layers source is loaded
             */
            this.layerLoaded = function (layer) {
                return layer.getSource().loaded
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerInvalid
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True invalid, false valid source
             * @description Test if layers source is validly loaded (!true for invalid)
             */
            this.layerInvalid = function (layer) {
                return layer.getSource().error;
            }

        }])
})
<commit_msg>Configurable proxy script/server address<commit_after>/**
 * @ngdoc module
 * @module hs.utils
 * @name hs.utils
 * @description Utility module which contains few utility functions.
 */
define(['angular', 'app', 'ol'], function (angular, app, ol) {
    angular.module('hs.utils', ['hs'])

        /**
         * @ngdoc service
         * @name hs.utils.service
         * @module hs.utils
         * @param {object} config - Application configuration
         * @description Service for containing various utility functions used throughout HSL modules.
         * Add few utility functions and also enrich some data types with additional functions (mainly Date and String).
         */
        .service('hs.utils.service', ['config', function (config) {
            var me = this;
            /**
            * @ngdoc method
            * @name hs.utils.service#proxify
            * @public
            * @param {String} url Url to proxify
            * @param {Boolean} toEncoding Optional parameter if UTF-8 encoding shouldn´t be used for non-image Urls.
            * @returns {String} Encoded Url with path to hsproxy.cgi script
            * @description Add path to proxy cgi script (hsproxy.cgi) into Url and encode rest of Url if valid http Url is send and proxy use is allowed.
            */
            this.proxify = function (url, toEncoding) {
                toEncoding = angular.isUndefined(toEncoding) ? true : toEncoding;
                var outUrl = url;
                if ((url.substring(0, 4) == 'http' && url.indexOf(window.location.origin) == -1) || getPortFromUrl(url) != window.location.port) {
                    if (typeof use_proxy === 'undefined' || use_proxy === true) {

                        outUrl = config.proxyPrefix || "/cgi-bin/hsproxy.cgi?";
                        if (toEncoding && (url.indexOf('GetMap') == -1 || url.indexOf('GetFeatureInfo') > -1)) outUrl += "toEncoding=utf-8&";
                        outUrl = outUrl + "url=" + encodeURIComponent(url);
                    }
                }
                return outUrl;
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#shortUrl
            * @public
            * @param {String} url Url to shorten
            * @returns {String} Shortened url
            * @description Promise which shortens url by using some url shortener.
            * By default tinyurl is used, but user provided function in config.shortenUrl can be used. Example: function(url) {
                    return new Promise(function(resolve, reject){
                        $http.get("http://tinyurl.com/api-create.php?url=" + url, {
                            longUrl: url
                        }).success(function(data, status, headers, config) {
                            resolve(data);
                        }).error(function(data, status, headers, config) {
                            reject()
                        })
                    })
                }
            */
            this.shortUrl = function (url) {
                if (config.shortenUrl) return config.shortenUrl(url);
                return new Promise(function (resolve, reject) {
                    $http.get(me.proxify("http://tinyurl.com/api-create.php?url=" + url), {
                        longUrl: url
                    }).success(function (data, status, headers, config) {
                        resolve(data);
                    }).error(function (data, status, headers, config) {
                        reject()
                    })
                })
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#getPortFromUrl
            * @private
            * @param {String} url Url for which to determine port number
            * @returns {String} Port number
            */
            function getPortFromUrl(url) {
                var link = document.createElement('a');
                link.setAttribute('href', url);
                return link.port;
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#parseHexString
            * @deprecated
            */
            this.parseHexString = function (hex) {
                for (var bytes = [], c = 0; c < hex.length; c += 2)
                    bytes.push(parseInt(hex.substr(c, 2), 16));
                return bytes;
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#createHexString
            * @deprecated
            */
            this.createHexString = function (bytes) {
                for (var hex = [], i = 0; i < bytes.length; i++) {
                    hex.push((bytes[i] >>> 4).toString(16));
                    hex.push((bytes[i] & 0xF).toString(16));
                }
                return hex.join("");
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#getParamsFromUrl
            * @public
            * @param {String} str Url to parse for parameters
            * @returns {Object} Object with parsed parameters as properties
            * @description Parse parameters and their values from Url string
            */
            this.getParamsFromUrl = function (str) {
                if (typeof str !== 'string') {
                    return {};
                }

                if (str.indexOf('?') > -1)
                    str = str.substring(str.indexOf("?") + 1);
                else
                    return {};

                return str.trim().split('&').reduce(function (ret, param) {
                    var parts = param.replace(/\+/g, ' ').split('=');
                    var key = parts[0];
                    var val = parts[1];

                    key = decodeURIComponent(key);
                    // missing `=` should be `null`:
                    // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
                    val = val === undefined ? null : decodeURIComponent(val);

                    if (!ret.hasOwnProperty(key)) {
                        ret[key] = val;
                    } else if (Array.isArray(ret[key])) {
                        ret[key].push(val);
                    } else {
                        ret[key] = [ret[key], val];
                    }

                    return ret;
                }, {});
            };
            /**
            * @ngdoc method
            * @name hs.utils.service#paramsToUrl
            * @public
            * @param {Object} array Parameter object with parameter key-value pairs
            * @returns {String} Joined encoded Url query string
            * @description Create encoded Url string from object with parameters
            */
            this.paramsToURL = function (array) {
                var pairs = [];
                for (var key in array)
                    if (array.hasOwnProperty(key))

                        pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(array[key]));
                return pairs.join('&');
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#paramsToUrlWoEncode
            * @public
            * @param {Object} array Parameter object with parameter key-value pairs
            * @returns {String} Joined Url query string
            * @description Create Url string from object with parameters without encoding
            */
            this.paramsToURLWoEncode = function (array) {
                var pairs = [];
                for (var key in array)
                    if (array.hasOwnProperty(key))

                        pairs.push(key + '=' + array[key]);
                return pairs.join('&');
            }
            /**
            * @ngdoc method
            * @name hs.utils.service#generateUuid
            * @public
            * @returns {String} Random uuid
            * @description Generate randomized uuid
            */
            this.generateUuid = function () {
                return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0,
                        v = c == 'x' ? r : r & 0x3 | 0x8;
                    return v.toString(16);
                });
            }

            /**
            * @ngdoc method
            * @name hs.utils.service#rainbow
            * @public
            * @returns {String} CSS color
            * @description Generates css color string (rgba(0, 0, 0, 1)) from given range and value for which to have color
            */
            this.rainbow = function (numOfSteps, step, opacity) {
                // based on http://stackoverflow.com/a/7419630
                // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
                // Adam Cole, 2011-Sept-14
                // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
                var r, g, b;
                var h = step / (numOfSteps * 1.00000001);
                var i = ~~(h * 4);
                var f = h * 4 - i;
                var q = 1 - f;
                switch (i % 4) {
                    case 2:
                        r = f, g = 1, b = 0;
                        break;
                    case 0:
                        r = 0, g = f, b = 1;
                        break;
                    case 3:
                        r = 1, g = q, b = 0;
                        break;
                    case 1:
                        r = 0, g = 1, b = q;
                        break;
                }
                var c = "rgba(" + ~~(r * 235) + "," + ~~(g * 235) + "," + ~~(b * 235) + ", " + opacity + ")";
                return (c);
            }

            Date.isLeapYear = function (year) {
                return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
            };

            Date.getDaysInMonth = function (year, month) {
                return [31, (Date.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
            };

            Date.prototype.isLeapYear = function () {
                return Date.isLeapYear(this.getFullYear());
            };

            Date.prototype.getDaysInMonth = function () {
                return Date.getDaysInMonth(this.getFullYear(), this.getMonth());
            };

            Date.prototype.addMonths = function (value) {
                var n = this.getDate();
                this.setDate(1);
                this.setMonth(this.getMonth() + value);
                this.setDate(Math.min(n, this.getDaysInMonth()));
                return this;
            };

            Date.prototype.monthDiff = function (d2) {
                var months;
                months = (d2.getFullYear() - this.getFullYear()) * 12;
                months -= this.getMonth() + 1;
                months += d2.getMonth();
                return months <= 0 ? 0 : months;
            }

            String.prototype.hashCode = function () {
                var hash = 0;
                if (this.length == 0) return hash;
                for (i = 0; i < this.length; i++) {
                    char = this.charCodeAt(i);
                    hash = ((hash << 5) - hash) + char;
                    hash = hash & hash; // Convert to 32bit integer
                }
                return hash;
            }

            String.prototype.replaceAll = function (search, replacement) {
                var target = this;
                return target.replace(new RegExp(search, 'g'), replacement);
            };

            if (!String.prototype.format) {
                String.prototype.format = function () {
                    var args = arguments;
                    return this.replace(/{(\d+)}/g, function (match, number) {
                        return typeof args[number] != 'undefined' ?
                            args[number] :
                            match;
                    });
                };
            }

            if (!String.prototype.capitalizeFirstLetter) {
                String.prototype.capitalizeFirstLetter = function () {
                    return this.charAt(0).toUpperCase() + this.slice(1);
                }
            }

        }])

        /**
         * @ngdoc service
         * @name hs.utils.layerUtilsService
         * @module hs.utils
         * @param {object} config - Application configuration
         * @description Service containing varius functions for testing layer functionalities
         */
        .service('hs.utils.layerUtilsService', ['config', function (config) {
            var me = this;

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerIsZoomable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for layer with BoundingBox property, for WMS layer or for layer, which has source with extent
             * @description Determines if layer have properties needed for Zoom to layer function.
             */
            this.layerIsZoomable = function (layer) {
                if (typeof layer == 'undefined') return false;
                if (layer.get("BoundingBox")) return true;
                if (me.isLayerWMS(layer)) return true;
                if (layer.getSource().getExtent && layer.getSource().getExtent() && !ol.extent.isEmpty(layer.getSource().getExtent())) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerIsStyleable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Vector
             * @description Determines if layer is a Vector layer and therefore styleable
             */
            this.layerIsStyleable = function (layer) {
                if (typeof layer == 'undefined') return false;
                if (layer instanceof ol.layer.Vector /*&& layer.getSource().styleAble*/) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#isLayerQueryable
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Tile and ol.layer.Image with INFO_FORMAT in params
             * @description Test if layer is queryable (WMS layer with Info format)
             */
            this.isLayerQueryable = function (layer) {
                if (layer instanceof ol.layer.Tile &&
                    (layer.getSource() instanceof ol.source.TileWMS) &&
                    layer.getSource().getParams().INFO_FORMAT) return true;
                if (layer instanceof ol.layer.Image &&
                    layer.getSource() instanceof ol.source.ImageWMS &&
                    layer.getSource().getParams().INFO_FORMAT) return true;
                return false;
            }
            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#isLayerWMS
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True for ol.layer.Tile and ol.layer.Image
             * @description Test if layer is WMS layer
             */
            this.isLayerWMS = function (layer) {
                if (layer instanceof ol.layer.Tile &&
                    (layer.getSource() instanceof ol.source.TileWMS)) return true;
                if (layer instanceof ol.layer.Image &&
                    layer.getSource() instanceof ol.source.ImageWMS) return true;
                return false;
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerLoaded
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True loaded / False not (fully) loaded
             * @description Test if layers source is loaded
             */
            this.layerLoaded = function (layer) {
                return layer.getSource().loaded
            }

            /**
             * @ngdoc method
             * @name hs.utils.layerUtilsService#layerInvalid
             * @param {Ol.layer} layer Selected layer
             * @returns {Boolean} True invalid, false valid source
             * @description Test if layers source is validly loaded (!true for invalid)
             */
            this.layerInvalid = function (layer) {
                return layer.getSource().error;
            }

        }])
})
<|endoftext|>


<commit_before>'use strict';

var events = require('events');

function CurrentURLModel(fb) {
  var firebase = fb;
  var currentUrlData = null;
  firebase.child('currenturl').on('value', function(snapshot) {
    var newdata = snapshot.val();
    if (!newdata) {
      return;
    }

    var emitUrlChange = currentUrlData ? newdata.url !== currentUrlData.url : true;
    currentUrlData = newdata;

    if (emitUrlChange) {
      this.emit('URLChange', currentUrlData.url);
    } else {
      this.emit('URLResultsChange', currentUrlData);
    }
  }.bind(this));

  this.getFirebase = function() {
    return firebase;
  };

  this.getUrl = function() {
    if (!currentUrlData) {
      return null;
    }

    return currentUrlData.url;
  };

  this.getData = function() {
    return currentUrlData;
  };
}

CurrentURLModel.prototype = events.EventEmitter.prototype;

CurrentURLModel.prototype.setNewUrl = function(url) {
  this.getFirebase().child('currenturl').set({url: url});
};

CurrentURLModel.prototype.setPSI = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/psi').set(results);
};

CurrentURLModel.prototype.setWPT = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/wpt').set(results);
};

CurrentURLModel.prototype.setOWP = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/owp').set(results);
};

module.exports = CurrentURLModel;
<commit_msg>Return at least something for get data in current url model<commit_after>'use strict';

var events = require('events');

function CurrentURLModel(fb) {
  var firebase = fb;
  var currentUrlData = null;
  firebase.child('currenturl').on('value', function(snapshot) {
    var newdata = snapshot.val();
    if (!newdata) {
      return;
    }

    var emitUrlChange = currentUrlData ? newdata.url !== currentUrlData.url : true;
    currentUrlData = newdata;

    if (emitUrlChange) {
      this.emit('URLChange', currentUrlData.url);
    } else {
      this.emit('URLResultsChange', currentUrlData);
    }
  }.bind(this));

  this.getFirebase = function() {
    return firebase;
  };

  this.getUrl = function() {
    if (!currentUrlData) {
      return null;
    }

    return currentUrlData.url;
  };

  this.getData = function() {
    if (!currentUrlData) {
      return {};
    }
    return currentUrlData;
  };
}

CurrentURLModel.prototype = events.EventEmitter.prototype;

CurrentURLModel.prototype.setNewUrl = function(url) {
  this.getFirebase().child('currenturl').set({url: url});
};

CurrentURLModel.prototype.setPSI = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/psi').set(results);
};

CurrentURLModel.prototype.setWPT = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/wpt').set(results);
};

CurrentURLModel.prototype.setOWP = function(url, results) {
  if (url !== this.getUrl()) {
    return;
  }

  this.getFirebase().child('currenturl/owp').set(results);
};

module.exports = CurrentURLModel;
<|endoftext|>


<commit_before>//How big the grid is in pixels
var gridWidthPx = 800, gridHeightPx = 448;
var gridPx = 32;

//Grid size in actual units
var gridWidth = gridWidthPx / gridPx;
var gridHeight = gridHeightPx / gridPx;

//Storage for the current agents and obstacles
var agents = new Array();
var obstacles = new Array();

//The physics world
var world = new B2World(B2Vec2.Zero, true);

//Defines an agent that moves
Agent = function (pos) {
	this.rotation = 0;

	this.maxForce = 20; //rate of acceleration
	this.maxSpeed = 4; //grid squares / second

	this.radius = 0.4;
	this.minSeparation = 0.8; // We'll move away from anyone nearer than this

	this.maxCohesion = 3.5; //We'll move closer to anyone within this bound

	this.maxForceSquared = this.maxForce * this.maxForce;
	this.maxSpeedSquared = this.maxSpeed * this.maxSpeed;

	//Create physics objects for the agent
	var fixDef = new B2FixtureDef();
	var bodyDef = new B2BodyDef();

	fixDef.density = 1.0;
	fixDef.friction = 0.5;
	fixDef.restitution = 0.2;
	fixDef.shape = new B2CircleShape(this.radius);

	bodyDef.type = B2Body.b2_dynamicBody;
	bodyDef.position.SetV(pos);

	this.body = world.CreateBody(bodyDef);
	this.fixture = this.body.CreateFixture(fixDef);
};
Agent.prototype.position = function () {
	return this.body.GetPosition();
};
Agent.prototype.velocity = function () {
	return this.body.GetLinearVelocity();
};

var destination = new B2Vec2(gridWidth - 2, gridHeight / 2); //middle right

//Called to start the game
function startGame() {
	for (var yPos = 1; yPos < gridHeight - 1; yPos++) {
		agents.push(new Agent(new B2Vec2(0, yPos)));
	}
	for (var i = 0; i < 30; i++) {
		var x = 1 + Math.floor(Math.random() * (gridWidth - 3));
		var y = Math.floor(Math.random() * (gridHeight - 2));
		obstacles.push(new B2Vec2(x, y));
	}
	obstacles.push(new B2Vec2(gridWidth - 5, gridHeight / 2));

	generateDijkstraGrid();
	generateFlowField();

	stage.addEventListener('stagemouseup', function (ev) {
		destination.x = ev.stageX / gridPx - 0.5;
		destination.y = ev.stageY / gridPx - 0.5;
	});
}

function round(val) {
	return val.toFixed(1);
}

//called periodically to update the game
//dt is the change of time since the last update (in seconds)
function gameTick(dt) {
	var i, agent;

	//Calculate steering and flocking forces for all agents
	for (i = agents.length - 1; i >= 0; i--) {
		agent = agents[i];

		//Work out our behaviours
		var ff = steeringBehaviourFlowField(agent);
		var lc = steeringBehaviourLowestCost(agent);
		agent.forceToApply = ff.Add(lc.Multiply(0.3));

		var lengthSquared = agent.forceToApply.LengthSquared();
		if (lengthSquared > agent.maxForceSquared) {
			agent.forceToApply.Multiply(agent.maxForce / Math.sqrt(lengthSquared));
		}
	}

	//Move agents based on forces being applied (aka physics)
	for (i = agents.length - 1; i >= 0; i--) {
		agent = agents[i];

		//Apply the force
		agent.body.ApplyImpulse(agent.forceToApply.Multiply(dt), agent.position());

		//Calculate our new movement angle TODO: Should probably be done after running step
		agent.rotation = agent.velocity().Angle();
	}

	world.Step(dt, 10, 10);
	world.ClearForces();
}

function steeringBehaviourFlowField(agent) {

	//Work out the force to apply to us based on the flow field grid squares we are on.
	//we apply bilinear interpolation on the 4 grid squares nearest to us to work out our force.
	// http://en.wikipedia.org/wiki/Bilinear_interpolation#Nonlinear

	//Top left Coordinate of the 4
	var floor = agent.position().Copy().Floor();

	//The 4 weights we'll interpolate, see http://en.wikipedia.org/wiki/File:Bilininterp.png for the coordinates
	var f00 = (isValid(floor.x, floor.y) ? flowField[floor.x][floor.y] : B2Vec2.Zero).Copy();
	var f01 = (isValid(floor.x, floor.y + 1) ? flowField[floor.x][floor.y + 1] : B2Vec2.Zero).Copy();
	var f10 = (isValid(floor.x + 1, floor.y) ? flowField[floor.x + 1][floor.y] : B2Vec2.Zero).Copy();
	var f11 = (isValid(floor.x + 1, floor.y + 1) ? flowField[floor.x + 1][floor.y + 1] : B2Vec2.Zero).Copy();

	//Do the x interpolations
	var xWeight = agent.position().x - floor.x;

	var top = f00.Multiply(1 - xWeight).Add(f10.Multiply(xWeight));
	var bottom = f01.Multiply(1 - xWeight).Add(f11.Multiply(xWeight));

	//Do the y interpolation
	var yWeight = agent.position().y - floor.y;

	//This is now the direction we want to be travelling in (needs to be normalized)
	var desiredDirection = top.Multiply(1 - yWeight).Add(bottom.Multiply(yWeight));
	desiredDirection.Normalize();

	//If we are centered on a grid square with no vector this will happen
	if (isNaN(desiredDirection.LengthSquared())) {
		return desiredDirection.SetZero();
	}

	return steerTowards(agent, desiredDirection);
}

function steeringBehaviourLowestCost(agent) {

	//Do nothing if the agent isn't moving
	if (agent.velocity().LengthSquared() == 0) {
		return new B2Vec2();
	}

	//Find our 4 closest neighbours
	var floor = agent.position().Copy().Floor(); //Top left Coordinate of the 4
	var f00 = isValid(floor.x, floor.y) ? dijkstraGrid[floor.x][floor.y] : Number.MAX_VALUE;
	var f01 = isValid(floor.x, floor.y + 1) ? dijkstraGrid[floor.x][floor.y + 1] : Number.MAX_VALUE;
	var f10 = isValid(floor.x + 1, floor.y) ? dijkstraGrid[floor.x + 1][floor.y] : Number.MAX_VALUE;
	var f11 = isValid(floor.x + 1, floor.y + 1) ? dijkstraGrid[floor.x + 1][floor.y + 1] : Number.MAX_VALUE;

	//Find the position(s) of the lowest, there may be multiple
	var minVal = Math.min(f00, f01, f10, f11);
	var minCoord = [];

	if (f00 == minVal) {
		minCoord.push(floor.Copy());
	}
	if (f01 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(0, 1)));
	}
	if (f10 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(1, 0)));
	}
	if (f11 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(1, 1)));
	}

	//Tie-break by choosing the one we are most aligned with
	var currentDirection = agent.velocity().Copy();
	currentDirection.Normalize();

	var desiredDirection;
	minVal = Number.MAX_VALUE;
	for (var i = 0; i < minCoord.length; i++) {
		minCoord[i].Subtract(agent.position()).Normalize();
		var directionTo = minCoord[i];
		var lengthSquared = directionTo.Copy().Subtract(currentDirection).LengthSquared();
		if (lengthSquared < minVal) {
			minVal = lengthSquared;
			desiredDirection = directionTo;
		}
	}

	//Steer towards it
	return steerTowards(agent, desiredDirection);
}

function steerTowards(agent, desiredDirection) {
	//Multiply our direction by speed for our desired speed
	var desiredVelocity = desiredDirection.Multiply(agent.maxSpeed);

	//The velocity change we want
	var velocityChange = desiredVelocity.Subtract(agent.velocity());
	//Convert to a force
	return velocityChange.Multiply(agent.maxForce / agent.maxSpeed);
}

var dijkstraGrid;
var flowField;

function generateDijkstraGrid() {
	//Generate an empty grid, set all places as weight null, which will stand for unvisited
	dijkstraGrid = new Array(gridWidth);
	for (var x = 0; x < gridWidth; x++) {
		var arr = new Array(gridHeight);
		for (var y = 0; y < gridHeight; y++) {
			arr[y] = null;
		}
		dijkstraGrid[x] = arr;
	}

	//Set all places where obstacles are as being weight MAXINT, which will stand for not able to go here
	for (var i = 0; i < obstacles.length; i++) {
		var t = obstacles[i];

		dijkstraGrid[t.x][t.y] = Number.MAX_VALUE;
	}

	//flood fill out from the end point
	var pathEnd = destination.Copy();
	pathEnd.Round();
	pathEnd.distance = 0;
	dijkstraGrid[pathEnd.x][pathEnd.y] = 0;

	var toVisit = [pathEnd];

	//for each node we need to visit, starting with the pathEnd
	for (i = 0; i < toVisit.length; i++) {
		var neighbours = straightNeighboursOf(toVisit[i]);

		//for each neighbour of this node (only straight line neighbours, not diagonals)
		for (var j = 0; j < neighbours.length; j++) {
			var n = neighbours[j];

			//We will only ever visit every node once as we are always visiting nodes in the most efficient order
			if (dijkstraGrid[n.x][n.y] === null) {
				n.distance = toVisit[i].distance + 1;
				dijkstraGrid[n.x][n.y] = n.distance;
				toVisit.push(n);
			}
		}
	}
}

function generateFlowField() {
	var x, y;

	//Generate an empty grid, set all places as Vector2.zero, which will stand for no good direction
	flowField = new Array(gridWidth);
	for (x = 0; x < gridWidth; x++) {
		var arr = new Array(gridHeight);
		for (y = 0; y < gridHeight; y++) {
			arr[y] = B2Vec2.Zero;
		}
		flowField[x] = arr;
	}

	//for each grid square
	for (x = 0; x < gridWidth; x++) {
		for (y = 0; y < gridHeight; y++) {

			//Obstacles have no flow value
			if (dijkstraGrid[x][y] == Number.MAX_VALUE) {
				continue;
			}

			var pos = new B2Vec2(x, y);
			var neighbours = allNeighboursOf(pos);

			//Go through all neighbours and find the one with the lowest distance
			var min = null;
			var minDist = 0;
			for (var i = 0; i < neighbours.length; i++) {
				var n = neighbours[i];
				var dist = dijkstraGrid[n.x][n.y] - dijkstraGrid[pos.x][pos.y];

				if (dist < minDist) {
					min = n;
					minDist = dist;
				}
			}

			//If we found a valid neighbour, point in its direction
			if (min != null) {
				var v = min.Copy().Subtract(pos);
				v.Normalize();
				flowField[x][y] = v;
			}
		}
	}
}

function straightNeighboursOf(v) {
	var res = [];
	if (v.x > 0) {
		res.push(new B2Vec2(v.x - 1, v.y));
	}
	if (v.y > 0) {
		res.push(new B2Vec2(v.x, v.y - 1));
	}

	if (v.x < gridWidth - 1) {
		res.push(new B2Vec2(v.x + 1, v.y));
	}
	if (v.y < gridHeight - 1) {
		res.push(new B2Vec2(v.x, v.y + 1));
	}

	return res;
}

//Helper method. Returns true if this grid location is on the grid and not impassable
function isValid(x, y) {
	return x >= 0 && y >= 0 && x < gridWidth && y < gridHeight && dijkstraGrid[x][y] != Number.MAX_VALUE;
}

//Returns the non-obstructed neighbours of the given grid location.
//Diagonals are only included if their neighbours are also not obstructed
function allNeighboursOf(v) {
	var res = [],
		x = v.x,
		y = v.y;

	var up = isValid(x, y - 1),
		down = isValid(x, y + 1),
		left = isValid(x - 1, y),
		right = isValid(x + 1, y);

	//We test each straight direction, then subtest the next one clockwise

	if (left) {
		res.push(new B2Vec2(x - 1, y));

		//left up
		if (up && isValid(x - 1, y - 1)) {
			res.push(new B2Vec2(x - 1, y - 1));
		}
	}

	if (up) {
		res.push(new B2Vec2(x, y - 1));

		//up right
		if (right && isValid(x + 1, y - 1)) {
			res.push(new B2Vec2(x + 1, y - 1));
		}
	}

	if (right) {
		res.push(new B2Vec2(x + 1, y));

		//right down
		if (down && isValid(x + 1, y + 1)) {
			res.push(new B2Vec2(x + 1, y + 1));
		}
	}

	if (down) {
		res.push(new B2Vec2(x, y + 1));

		//down left
		if (left && isValid(x - 1, y + 1)) {
			res.push(new B2Vec2(x - 1, y + 1));
		}
	}

	return res;
}<commit_msg>Give the obstacles physics bodies too. Now agents get stuck all the time :)<commit_after>//How big the grid is in pixels
var gridWidthPx = 800, gridHeightPx = 448;
var gridPx = 32;

//Grid size in actual units
var gridWidth = gridWidthPx / gridPx;
var gridHeight = gridHeightPx / gridPx;

//Storage for the current agents and obstacles
var agents = new Array();
var obstacles = new Array();

//The physics world
var world = new B2World(B2Vec2.Zero, true);

//Defines an agent that moves
Agent = function (pos) {
	this.rotation = 0;

	this.maxForce = 20; //rate of acceleration
	this.maxSpeed = 4; //grid squares / second

	this.radius = 0.4;
	this.minSeparation = 0.8; // We'll move away from anyone nearer than this

	this.maxCohesion = 3.5; //We'll move closer to anyone within this bound

	this.maxForceSquared = this.maxForce * this.maxForce;
	this.maxSpeedSquared = this.maxSpeed * this.maxSpeed;

	//Create a physics body for the agent
	var fixDef = new B2FixtureDef();
	var bodyDef = new B2BodyDef();

	fixDef.density = 1.0;
	fixDef.friction = 0.5;
	fixDef.restitution = 0.2;
	fixDef.shape = new B2CircleShape(this.radius);

	bodyDef.type = B2Body.b2_dynamicBody;
	bodyDef.position.SetV(pos);

	this.body = world.CreateBody(bodyDef);
	this.fixture = this.body.CreateFixture(fixDef);
};
Agent.prototype.position = function () {
	return this.body.GetPosition();
};
Agent.prototype.velocity = function () {
	return this.body.GetLinearVelocity();
};

var destination = new B2Vec2(gridWidth - 2, gridHeight / 2); //middle right

//Called to start the game
function startGame() {
	for (var yPos = 1; yPos < gridHeight - 1; yPos++) {
		agents.push(new Agent(new B2Vec2(0, yPos)));
	}
	for (var i = 0; i < 30; i++) {
		var x = 1 + Math.floor(Math.random() * (gridWidth - 3));
		var y = Math.floor(Math.random() * (gridHeight - 2));
		var pos = new B2Vec2(x, y);
		obstacles.push(pos);

		//Create a physics body for the agent
		var fixDef = new B2FixtureDef();
		var bodyDef = new B2BodyDef();

		fixDef.density = 1.0;
		fixDef.friction = 0.5;
		fixDef.restitution = 0.2;
		fixDef.shape = new B2PolygonShape();

		bodyDef.type = B2Body.b2_staticBody;
		fixDef.shape.SetAsBox(0.5, 0.5);
		bodyDef.position.SetV(pos);

		world.CreateBody(bodyDef).CreateFixture(fixDef);
	}

	generateDijkstraGrid();
	generateFlowField();

	stage.addEventListener('stagemouseup', function (ev) {
		destination.x = ev.stageX / gridPx - 0.5;
		destination.y = ev.stageY / gridPx - 0.5;
	});
}

function round(val) {
	return val.toFixed(1);
}

//called periodically to update the game
//dt is the change of time since the last update (in seconds)
function gameTick(dt) {
	var i, agent;

	//Calculate steering and flocking forces for all agents
	for (i = agents.length - 1; i >= 0; i--) {
		agent = agents[i];

		//Work out our behaviours
		var ff = steeringBehaviourFlowField(agent);
		var lc = steeringBehaviourLowestCost(agent);
		agent.forceToApply = ff.Add(lc.Multiply(0.3));

		var lengthSquared = agent.forceToApply.LengthSquared();
		if (lengthSquared > agent.maxForceSquared) {
			agent.forceToApply.Multiply(agent.maxForce / Math.sqrt(lengthSquared));
		}
	}

	//Move agents based on forces being applied (aka physics)
	for (i = agents.length - 1; i >= 0; i--) {
		agent = agents[i];

		//Apply the force
		agent.body.ApplyImpulse(agent.forceToApply.Multiply(dt), agent.position());

		//Calculate our new movement angle TODO: Should probably be done after running step
		agent.rotation = agent.velocity().Angle();
	}

	world.Step(dt, 10, 10);
	world.ClearForces();
}

function steeringBehaviourFlowField(agent) {

	//Work out the force to apply to us based on the flow field grid squares we are on.
	//we apply bilinear interpolation on the 4 grid squares nearest to us to work out our force.
	// http://en.wikipedia.org/wiki/Bilinear_interpolation#Nonlinear

	//Top left Coordinate of the 4
	var floor = agent.position().Copy().Floor();

	//The 4 weights we'll interpolate, see http://en.wikipedia.org/wiki/File:Bilininterp.png for the coordinates
	var f00 = (isValid(floor.x, floor.y) ? flowField[floor.x][floor.y] : B2Vec2.Zero).Copy();
	var f01 = (isValid(floor.x, floor.y + 1) ? flowField[floor.x][floor.y + 1] : B2Vec2.Zero).Copy();
	var f10 = (isValid(floor.x + 1, floor.y) ? flowField[floor.x + 1][floor.y] : B2Vec2.Zero).Copy();
	var f11 = (isValid(floor.x + 1, floor.y + 1) ? flowField[floor.x + 1][floor.y + 1] : B2Vec2.Zero).Copy();

	//Do the x interpolations
	var xWeight = agent.position().x - floor.x;

	var top = f00.Multiply(1 - xWeight).Add(f10.Multiply(xWeight));
	var bottom = f01.Multiply(1 - xWeight).Add(f11.Multiply(xWeight));

	//Do the y interpolation
	var yWeight = agent.position().y - floor.y;

	//This is now the direction we want to be travelling in (needs to be normalized)
	var desiredDirection = top.Multiply(1 - yWeight).Add(bottom.Multiply(yWeight));
	desiredDirection.Normalize();

	//If we are centered on a grid square with no vector this will happen
	if (isNaN(desiredDirection.LengthSquared())) {
		return desiredDirection.SetZero();
	}

	return steerTowards(agent, desiredDirection);
}

function steeringBehaviourLowestCost(agent) {

	//Do nothing if the agent isn't moving
	if (agent.velocity().LengthSquared() == 0) {
		return new B2Vec2();
	}

	//Find our 4 closest neighbours
	var floor = agent.position().Copy().Floor(); //Top left Coordinate of the 4
	var f00 = isValid(floor.x, floor.y) ? dijkstraGrid[floor.x][floor.y] : Number.MAX_VALUE;
	var f01 = isValid(floor.x, floor.y + 1) ? dijkstraGrid[floor.x][floor.y + 1] : Number.MAX_VALUE;
	var f10 = isValid(floor.x + 1, floor.y) ? dijkstraGrid[floor.x + 1][floor.y] : Number.MAX_VALUE;
	var f11 = isValid(floor.x + 1, floor.y + 1) ? dijkstraGrid[floor.x + 1][floor.y + 1] : Number.MAX_VALUE;

	//Find the position(s) of the lowest, there may be multiple
	var minVal = Math.min(f00, f01, f10, f11);
	var minCoord = [];

	if (f00 == minVal) {
		minCoord.push(floor.Copy());
	}
	if (f01 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(0, 1)));
	}
	if (f10 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(1, 0)));
	}
	if (f11 == minVal) {
		minCoord.push(floor.Copy().Add(new B2Vec2(1, 1)));
	}

	//Tie-break by choosing the one we are most aligned with
	var currentDirection = agent.velocity().Copy();
	currentDirection.Normalize();

	var desiredDirection;
	minVal = Number.MAX_VALUE;
	for (var i = 0; i < minCoord.length; i++) {
		minCoord[i].Subtract(agent.position()).Normalize();
		var directionTo = minCoord[i];
		var lengthSquared = directionTo.Copy().Subtract(currentDirection).LengthSquared();
		if (lengthSquared < minVal) {
			minVal = lengthSquared;
			desiredDirection = directionTo;
		}
	}

	//Steer towards it
	return steerTowards(agent, desiredDirection);
}

function steerTowards(agent, desiredDirection) {
	//Multiply our direction by speed for our desired speed
	var desiredVelocity = desiredDirection.Multiply(agent.maxSpeed);

	//The velocity change we want
	var velocityChange = desiredVelocity.Subtract(agent.velocity());
	//Convert to a force
	return velocityChange.Multiply(agent.maxForce / agent.maxSpeed);
}

var dijkstraGrid;
var flowField;

function generateDijkstraGrid() {
	//Generate an empty grid, set all places as weight null, which will stand for unvisited
	dijkstraGrid = new Array(gridWidth);
	for (var x = 0; x < gridWidth; x++) {
		var arr = new Array(gridHeight);
		for (var y = 0; y < gridHeight; y++) {
			arr[y] = null;
		}
		dijkstraGrid[x] = arr;
	}

	//Set all places where obstacles are as being weight MAXINT, which will stand for not able to go here
	for (var i = 0; i < obstacles.length; i++) {
		var t = obstacles[i];

		dijkstraGrid[t.x][t.y] = Number.MAX_VALUE;
	}

	//flood fill out from the end point
	var pathEnd = destination.Copy();
	pathEnd.Round();
	pathEnd.distance = 0;
	dijkstraGrid[pathEnd.x][pathEnd.y] = 0;

	var toVisit = [pathEnd];

	//for each node we need to visit, starting with the pathEnd
	for (i = 0; i < toVisit.length; i++) {
		var neighbours = straightNeighboursOf(toVisit[i]);

		//for each neighbour of this node (only straight line neighbours, not diagonals)
		for (var j = 0; j < neighbours.length; j++) {
			var n = neighbours[j];

			//We will only ever visit every node once as we are always visiting nodes in the most efficient order
			if (dijkstraGrid[n.x][n.y] === null) {
				n.distance = toVisit[i].distance + 1;
				dijkstraGrid[n.x][n.y] = n.distance;
				toVisit.push(n);
			}
		}
	}
}

function generateFlowField() {
	var x, y;

	//Generate an empty grid, set all places as Vector2.zero, which will stand for no good direction
	flowField = new Array(gridWidth);
	for (x = 0; x < gridWidth; x++) {
		var arr = new Array(gridHeight);
		for (y = 0; y < gridHeight; y++) {
			arr[y] = B2Vec2.Zero;
		}
		flowField[x] = arr;
	}

	//for each grid square
	for (x = 0; x < gridWidth; x++) {
		for (y = 0; y < gridHeight; y++) {

			//Obstacles have no flow value
			if (dijkstraGrid[x][y] == Number.MAX_VALUE) {
				continue;
			}

			var pos = new B2Vec2(x, y);
			var neighbours = allNeighboursOf(pos);

			//Go through all neighbours and find the one with the lowest distance
			var min = null;
			var minDist = 0;
			for (var i = 0; i < neighbours.length; i++) {
				var n = neighbours[i];
				var dist = dijkstraGrid[n.x][n.y] - dijkstraGrid[pos.x][pos.y];

				if (dist < minDist) {
					min = n;
					minDist = dist;
				}
			}

			//If we found a valid neighbour, point in its direction
			if (min != null) {
				var v = min.Copy().Subtract(pos);
				v.Normalize();
				flowField[x][y] = v;
			}
		}
	}
}

function straightNeighboursOf(v) {
	var res = [];
	if (v.x > 0) {
		res.push(new B2Vec2(v.x - 1, v.y));
	}
	if (v.y > 0) {
		res.push(new B2Vec2(v.x, v.y - 1));
	}

	if (v.x < gridWidth - 1) {
		res.push(new B2Vec2(v.x + 1, v.y));
	}
	if (v.y < gridHeight - 1) {
		res.push(new B2Vec2(v.x, v.y + 1));
	}

	return res;
}

//Helper method. Returns true if this grid location is on the grid and not impassable
function isValid(x, y) {
	return x >= 0 && y >= 0 && x < gridWidth && y < gridHeight && dijkstraGrid[x][y] != Number.MAX_VALUE;
}

//Returns the non-obstructed neighbours of the given grid location.
//Diagonals are only included if their neighbours are also not obstructed
function allNeighboursOf(v) {
	var res = [],
		x = v.x,
		y = v.y;

	var up = isValid(x, y - 1),
		down = isValid(x, y + 1),
		left = isValid(x - 1, y),
		right = isValid(x + 1, y);

	//We test each straight direction, then subtest the next one clockwise

	if (left) {
		res.push(new B2Vec2(x - 1, y));

		//left up
		if (up && isValid(x - 1, y - 1)) {
			res.push(new B2Vec2(x - 1, y - 1));
		}
	}

	if (up) {
		res.push(new B2Vec2(x, y - 1));

		//up right
		if (right && isValid(x + 1, y - 1)) {
			res.push(new B2Vec2(x + 1, y - 1));
		}
	}

	if (right) {
		res.push(new B2Vec2(x + 1, y));

		//right down
		if (down && isValid(x + 1, y + 1)) {
			res.push(new B2Vec2(x + 1, y + 1));
		}
	}

	if (down) {
		res.push(new B2Vec2(x, y + 1));

		//down left
		if (left && isValid(x - 1, y + 1)) {
			res.push(new B2Vec2(x - 1, y + 1));
		}
	}

	return res;
}<|endoftext|>


<commit_before>var should = require('should'),
    needle = require('./../'),
    http = require('http'),
    zlib = require('zlib'),
    stream = require('stream'),
    port = 11111,
    server;

describe('compression', function(){

  require.bind(null, 'zlib').should.not.throw()

  var jsonData = '{"foo":"bar"}';

  describe('when server supports compression', function(){

    before(function(){
      server = http.createServer(function(req, res) {
        var raw = new stream.PassThrough();

        var acceptEncoding = req.headers['accept-encoding'];
        if (!acceptEncoding) {
          acceptEncoding = '';
        }

        if (acceptEncoding.match(/\bdeflate\b/)) {
          res.setHeader('Content-Encoding', 'deflate');
          raw.pipe(zlib.createDeflate()).pipe(res);
        } else if (acceptEncoding.match(/\bgzip\b/)) {
          res.setHeader('Content-Encoding', 'gzip');
          raw.pipe(zlib.createGzip()).pipe(res);
        } else {
          raw.pipe(res);
        }

        res.setHeader('Content-Type', 'application/json')
        raw.end(jsonData)
      }).listen(port);
    });

    after(function(){
      server.close();
    })

    describe('and client requests no compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.equal(jsonData.length);
          done();
        })
      })
    })

    describe('and client requests gzip compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'gzip'}}, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.not.equal(jsonData.length);
          done();
        })
      })
    })

    describe('and client requests deflate compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate'}}, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.not.equal(jsonData.length);
          done();
        })
      })
    })
  })
})
<commit_msg>test case for low level decompression errors<commit_after>var should = require('should'),
    needle = require('./../'),
    http = require('http'),
    zlib = require('zlib'),
    stream = require('stream'),
    port = 11111,
    server;

describe('compression', function(){

  require.bind(null, 'zlib').should.not.throw()

  var jsonData = '{"foo":"bar"}';

  describe('when server supports compression', function(){

    before(function(){
      server = http.createServer(function(req, res) {
        var raw = new stream.PassThrough();

        var acceptEncoding = req.headers['accept-encoding'];
        if (!acceptEncoding) {
          acceptEncoding = '';
        }

        if (acceptEncoding.match(/\bdeflate\b/)) {
          res.setHeader('Content-Encoding', 'deflate');
          raw.pipe(zlib.createDeflate()).pipe(res);
        } else if (acceptEncoding.match(/\bgzip\b/)) {
          res.setHeader('Content-Encoding', 'gzip');
          raw.pipe(zlib.createGzip()).pipe(res);
        } else {
          raw.pipe(res);
        }

        res.setHeader('Content-Type', 'application/json')
        if(req.headers['with-bad']) {
          res.end(null);
        } else {
          raw.end(jsonData)
        }

      }).listen(port);
    });

    after(function(){
      server.close();
    })

    describe('and client requests no compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.equal(jsonData.length);
          done();
        })
      })
    })

    describe('and client requests gzip compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'gzip'}}, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.not.equal(jsonData.length);
          done();
        })
      })
    })

    describe('and client requests deflate compression', function() {
      it('should have the body decompressed', function(done){
        needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate'}}, function(err, response, body){
          should.ifError(err);
          body.should.have.property('foo', 'bar');
          response.bytes.should.not.equal(jsonData.length);
          done();
        })
      })

      it('should rethrow errors from decompressors', function(done){
        needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate', 'With-Bad': 'true'}}, function(err, response, body){
          should.exist(err);
          done();
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>import check from './support/check';

describe('conditionals', () => {
  it('surrounds `if` conditions in parentheses and bodies in curly braces', () => {
    check(`
      if a
        b
    `, `
      if (a) {
        b;
      }
    `);
  });

  it('surrounds `unless` conditions in parentheses and precedes it with a `!` operator', () => {
    check(`
      unless a
        b
    `, `
      if (!a) {
        b;
      }
    `);
  });

  it('surrounds `unless` conditions in additional parentheses if needed for the `!` operator', () => {
    check(`
      unless a == b
        c
    `, `
      if (a !== b) {
        c;
      }
    `);
  });

  it('does not add parentheses if the condition is already surrounded by them', () => {
    check(`
      if (a)
        b
    `, `
      if (a) {
        b;
      }
    `);
    check(`
      unless (a)
        b
    `, `
      if (!a) {
        b;
      }
    `);
  });

  it('correctly inserts parentheses when an `unless` condition requires them when it was surrounded by parentheses', () => {
    check(`
      unless (a == b)
        c
    `, `
      if (a !== b) {
        c;
      }
    `);
  });

  it('handles indented `if` statements correctly', () => {
    check(`
      if a
        if b
          c
    `, `
      if (a) {
        if (b) {
          c;
        }
      }
    `);
  });

  it('surrounds the `else` clause of an `if` statement in curly braces', () => {
    check(`
      if a
        b
      else
        c
    `, `
      if (a) {
        b;
      } else {
        c;
      }
    `);
  });

  it('surrounds the `else if` condition in parentheses', () => {
    check(`
      if a
        b
      else if c
        d
    `, `
      if (a) {
        b;
      } else if (c) {
        d;
      }
    `);
  });

  it('works with several `else if` clauses and an `else`', () => {
    check(`
      if a
        b
      else if c
        d
      else if e
        f
      else
        g
    `, `
      if (a) {
        b;
      } else if (c) {
        d;
      } else if (e) {
        f;
      } else {
        g;
      }
    `);
  });

  it('works with `if` with immediate return', () => {
    check(`
      ->
        if a
          b
      c
    `, `
      (function() {
        if (a) {
          return b;
        }
      });
      c;
    `);
  });

  it('keeps single-line `if` statements on one line', () => {
    check(`if a then b`, `if (a) { b; }`);
  });

  it('keeps single-line `if` with `else` on one line', () => {
    check(`if a then b else c`, `if (a) { b; } else { c; }`);
  });

  it('handles `if` statements with an implicit call in the condition', () => {
    check(`if a b then c`, `if (a(b)) { c; }`);
  });

  it('turns simple `if` with `else` as an expression into a ternary operator', () => {
    check(`a(if b then c else d)`, `a(b ? c : d);`);
  });

  it('turns simple `if` without `else` as an expression into a ternary operator with undefined', () => {
    check(`a(if b then c)`, `a(b ? c : undefined);`);
  });

  it('adds ternary operator code after any insertions for the consequent', () => {
    check(`a(if b then c: d)`, `a(b ? {c: d} : undefined);`);
  });

  it('turns multi-line `if` into a ternary with sequence expressions', () => {
    check(`
      ->
        z(if a
          c = a
          a + c
        else
          d = a
          a - d)
    `, `
      (function() {
        var c;
        var d;
        return z(a ?
          (c = a,
          a + c)
          :
          (d = a,
          a - d));
      });
    `);
  });

  it('keeps single-line POST-`if`', () => {
    check(`a if b`, `if (b) { a; }`);
    check(`
      ->
        return if a is b
        null
      `, `
        (function() {
          if (a === b) { return; }
          return null;
        });
    `);
  });

  it('keeps single-line POST-`unless`', () => {
    check(`a unless b`, `if (!b) { a; }`);
  });

  it('pushes returns into `if` statements', () => {
    check(`
      ->
        if a
          b
    `, `
      (function() {
        if (a) {
          return b;
        }
      });
    `);
  });

  it('pushes returns into `else if` blocks', () => {
    check(`
      ->
        if a
          b
        else if c
          d
    `, `
      (function() {
        if (a) {
          return b;
        } else if (c) {
          return d;
        }
      });
    `);
  });

  it('works with if then throw inline', () => {
    check(`if a then throw new Error "Error"`, `if (a) { throw new Error("Error"); }`);
  });

  it('works with POST-`if` as the body of a function', () => {
    check(`-> a if b`, `(function() { return b ? a : undefined; });`);
  });
});
<commit_msg>Match up the `:` with the `else`.<commit_after>import check from './support/check';

describe('conditionals', () => {
  it('surrounds `if` conditions in parentheses and bodies in curly braces', () => {
    check(`
      if a
        b
    `, `
      if (a) {
        b;
      }
    `);
  });

  it('surrounds `unless` conditions in parentheses and precedes it with a `!` operator', () => {
    check(`
      unless a
        b
    `, `
      if (!a) {
        b;
      }
    `);
  });

  it('surrounds `unless` conditions in additional parentheses if needed for the `!` operator', () => {
    check(`
      unless a == b
        c
    `, `
      if (a !== b) {
        c;
      }
    `);
  });

  it('does not add parentheses if the condition is already surrounded by them', () => {
    check(`
      if (a)
        b
    `, `
      if (a) {
        b;
      }
    `);
    check(`
      unless (a)
        b
    `, `
      if (!a) {
        b;
      }
    `);
  });

  it('correctly inserts parentheses when an `unless` condition requires them when it was surrounded by parentheses', () => {
    check(`
      unless (a == b)
        c
    `, `
      if (a !== b) {
        c;
      }
    `);
  });

  it('handles indented `if` statements correctly', () => {
    check(`
      if a
        if b
          c
    `, `
      if (a) {
        if (b) {
          c;
        }
      }
    `);
  });

  it('surrounds the `else` clause of an `if` statement in curly braces', () => {
    check(`
      if a
        b
      else
        c
    `, `
      if (a) {
        b;
      } else {
        c;
      }
    `);
  });

  it('surrounds the `else if` condition in parentheses', () => {
    check(`
      if a
        b
      else if c
        d
    `, `
      if (a) {
        b;
      } else if (c) {
        d;
      }
    `);
  });

  it('works with several `else if` clauses and an `else`', () => {
    check(`
      if a
        b
      else if c
        d
      else if e
        f
      else
        g
    `, `
      if (a) {
        b;
      } else if (c) {
        d;
      } else if (e) {
        f;
      } else {
        g;
      }
    `);
  });

  it('works with `if` with immediate return', () => {
    check(`
      ->
        if a
          b
      c
    `, `
      (function() {
        if (a) {
          return b;
        }
      });
      c;
    `);
  });

  it('keeps single-line `if` statements on one line', () => {
    check(`if a then b`, `if (a) { b; }`);
  });

  it('keeps single-line `if` with `else` on one line', () => {
    check(`if a then b else c`, `if (a) { b; } else { c; }`);
  });

  it('handles `if` statements with an implicit call in the condition', () => {
    check(`if a b then c`, `if (a(b)) { c; }`);
  });

  it('turns simple `if` with `else` as an expression into a ternary operator', () => {
    check(`a(if b then c else d)`, `a(b ? c : d);`);
  });

  it('turns simple `if` without `else` as an expression into a ternary operator with undefined', () => {
    check(`a(if b then c)`, `a(b ? c : undefined);`);
  });

  it('adds ternary operator code after any insertions for the consequent', () => {
    check(`a(if b then c: d)`, `a(b ? {c: d} : undefined);`);
  });

  it('turns multi-line `if` into a ternary with sequence expressions', () => {
    check(`
      ->
        z(if a
          c = a
          a + c
        else
          d = a
          a - d)
    `, `
      (function() {
        var c;
        var d;
        return z(a ?
          (c = a,
          a + c)
        :
          (d = a,
          a - d));
      });
    `);
  });

  it('keeps single-line POST-`if`', () => {
    check(`a if b`, `if (b) { a; }`);
    check(`
      ->
        return if a is b
        null
      `, `
        (function() {
          if (a === b) { return; }
          return null;
        });
    `);
  });

  it('keeps single-line POST-`unless`', () => {
    check(`a unless b`, `if (!b) { a; }`);
  });

  it('pushes returns into `if` statements', () => {
    check(`
      ->
        if a
          b
    `, `
      (function() {
        if (a) {
          return b;
        }
      });
    `);
  });

  it('pushes returns into `else if` blocks', () => {
    check(`
      ->
        if a
          b
        else if c
          d
    `, `
      (function() {
        if (a) {
          return b;
        } else if (c) {
          return d;
        }
      });
    `);
  });

  it('works with if then throw inline', () => {
    check(`if a then throw new Error "Error"`, `if (a) { throw new Error("Error"); }`);
  });

  it('works with POST-`if` as the body of a function', () => {
    check(`-> a if b`, `(function() { return b ? a : undefined; });`);
  });
});
<|endoftext|>