<commit_before>export default function(config = {}, win = window, doc = document) {
  const dev = function() {
    return doc.cookie
      .split(';')
      .filter(item => item !== '')
      .map(item => item.trim().split('='));
  };
  const user = function(str) {
    const item = win.localStorage.getItem(str);
    return item === null ? undefined : item;
  };
  const getCurrentResource = function(scripts) {
    const current = scripts[scripts.length - 1];
    try {
      return win.performance.getEntriesByType('resource').find(item => {
        // current is based on the assumption that whereever this script is it's
        // likely to be the same as the xmlhttprequests
        return item.initiatorType === 'script' && current.src === item.name;
      });
    } catch (e) {
      return {};
    }
  };
  const resource = getCurrentResource(doc.getElementsByTagName('script'));

  // TODO: Look to see if we can pull in HTTP headers here
  // so we can let things be controlled via HTTP proxies, for example
  // turning off blocking queries if its a busy cluster etc
  const operator = function(str, env) {
    let protocol;
    switch (str) {
      case 'CONSUL_HTTP_PROTOCOL':
        return resource.nextHopProtocol || 'http/1.1';
      case 'CONSUL_HTTP_MAX_CONNECTIONS':
        protocol = env('CONSUL_HTTP_PROTOCOL');
        // http/2, http2+QUIC/39 and SPDY don't have connection limits
        switch (true) {
          case protocol.indexOf('h2') === 0:
          case protocol.indexOf('hq') === 0:
          case protocol.indexOf('spdy') === 0:
            // TODO: Change this to return -1 so we try to consistently
            // return a value from env vars
            return;
          default:
            // generally 6 are available
            // reserve 1 for traffic that we can't manage
            return 5;
        }
    }
  };
  const ui = function(key) {
    let $;
    switch (config.environment) {
      case 'development':
      case 'staging':
      case 'coverage':
      case 'test':
        $ = dev().reduce(function(prev, [key, value]) {
          switch (key) {
            case 'CONSUL_ACLS_ENABLE':
              prev['CONSUL_ACLS_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
              break;
            case 'CONSUL_NSPACES_ENABLE':
              prev['CONSUL_NSPACES_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
              break;
            default:
              prev[key] = value;
          }
          return prev;
        }, {});
        if (typeof $[key] !== 'undefined') {
          return $[key];
        }
        break;
    }
    return config[key];
  };
  return function env(str) {
    switch (str) {
      // All user providable values should start with CONSUL_UI
      // We allow the user to set these ones via localStorage
      // user value is preferred.
      case 'CONSUL_UI_DISABLE_REALTIME':
      case 'CONSUL_UI_DISABLE_ANCHOR_SELECTION':
        // these are booleans cast things out
        return !!JSON.parse(String(user(str) || 0).toLowerCase()) || ui(str);
      case 'CONSUL_UI_REALTIME_RUNNER':
        // these are strings
        return user(str) || ui(str);

      case 'CONSUL_HTTP_PROTOCOL':
      case 'CONSUL_HTTP_MAX_CONNECTIONS':
        // We allow the operator to set these ones via various methods
        // although UI developer config is preferred
        return ui(str) || operator(str, env);
      default:
        return ui(str);
    }
  };
}
<commit_msg>ui: Lazily detect HTTP protocol (#7644)<commit_after>export default function(config = {}, win = window, doc = document) {
  const dev = function() {
    return doc.cookie
      .split(';')
      .filter(item => item !== '')
      .map(item => item.trim().split('='));
  };
  const user = function(str) {
    const item = win.localStorage.getItem(str);
    return item === null ? undefined : item;
  };
  const getResourceFor = function(src) {
    try {
      return (
        win.performance.getEntriesByType('resource').find(item => {
          return item.initiatorType === 'script' && src === item.name;
        }) || {}
      );
    } catch (e) {
      return {};
    }
  };
  const scripts = doc.getElementsByTagName('script');
  const currentSrc = scripts[scripts.length - 1].src;
  let resource;

  // TODO: Look to see if we can pull in HTTP headers here
  // so we can let things be controlled via HTTP proxies, for example
  // turning off blocking queries if its a busy cluster etc
  const operator = function(str, env) {
    let protocol;
    switch (str) {
      case 'CONSUL_HTTP_PROTOCOL':
        if (typeof resource === 'undefined') {
          // resource needs to be retrieved lazily as entries aren't guaranteed
          // to be available at script execution time (caching seems to affect this)
          // waiting until we retrieve this value lazily at runtime means that
          // the entries are always available as these values are only retrieved
          // after initialization
          // current is based on the assumption that whereever this script is it's
          // likely to be the same as the xmlhttprequests
          resource = getResourceFor(currentSrc);
        }
        return resource.nextHopProtocol || 'http/1.1';
      case 'CONSUL_HTTP_MAX_CONNECTIONS':
        protocol = env('CONSUL_HTTP_PROTOCOL');
        // http/2, http2+QUIC/39 and SPDY don't have connection limits
        switch (true) {
          case protocol.indexOf('h2') === 0:
          case protocol.indexOf('hq') === 0:
          case protocol.indexOf('spdy') === 0:
            // TODO: Change this to return -1 so we try to consistently
            // return a value from env vars
            return;
          default:
            // generally 6 are available
            // reserve 1 for traffic that we can't manage
            return 5;
        }
    }
  };
  const ui = function(key) {
    let $;
    switch (config.environment) {
      case 'development':
      case 'staging':
      case 'coverage':
      case 'test':
        $ = dev().reduce(function(prev, [key, value]) {
          switch (key) {
            case 'CONSUL_ACLS_ENABLE':
              prev['CONSUL_ACLS_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
              break;
            case 'CONSUL_NSPACES_ENABLE':
              prev['CONSUL_NSPACES_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
              break;
            default:
              prev[key] = value;
          }
          return prev;
        }, {});
        if (typeof $[key] !== 'undefined') {
          return $[key];
        }
        break;
    }
    return config[key];
  };
  return function env(str) {
    switch (str) {
      // All user providable values should start with CONSUL_UI
      // We allow the user to set these ones via localStorage
      // user value is preferred.
      case 'CONSUL_UI_DISABLE_REALTIME':
      case 'CONSUL_UI_DISABLE_ANCHOR_SELECTION':
        // these are booleans cast things out
        return !!JSON.parse(String(user(str) || 0).toLowerCase()) || ui(str);
      case 'CONSUL_UI_REALTIME_RUNNER':
        // these are strings
        return user(str) || ui(str);

      case 'CONSUL_HTTP_PROTOCOL':
      case 'CONSUL_HTTP_MAX_CONNECTIONS':
        // We allow the operator to set these ones via various methods
        // although UI developer config is preferred
        return ui(str) || operator(str, env);
      default:
        return ui(str);
    }
  };
}
<|endoftext|>


<commit_before>import React from 'react';
import assert from 'assert';
import { mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import talkClient from 'panoptes-client/lib/talk-client';
import EmailSettings from './email';

function mockTalkResource(type, options) {
  const resource = talkClient.type(type).create(options);
  talkClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const talkPreferences = [
  {
    id: 0,
    category: 'participating_discussions',
    email_digest: 'immediate'
  },
  {
    id: 1,
    category: 'followed_discussions',
    email_digest: 'daily'
  },
  {
    id: 2,
    category: 'mentions',
    email_digest: 'immediate'
  },
  {
    id: 3,
    category: 'group_mentions',
    email_digest: 'immediate'
  },
  {
    id: 4,
    category: 'messages',
    email_digest: 'daily'
  },
  {
    id: 5,
    category: 'started_discussions',
    email_digest: 'weekly'
  }
];

const projects = [
  {
    id: 'a',
    display_name: 'A test project',
    title: 'A test project'
  },
  {
    id: 'b',
    display_name: 'Another test project',
    title: 'Another test project'
  }
];

const projectPreferences = [
  {
    id: '1',
    email_communication: true,
    links: {
      project: 'a'
    }
  },
  {
    id: '2',
    email_communication: false,
    links: {
      project: 'b'
    }
  }
];

const user = {
  email: 'An email address',
  beta_email_communication: false,
  global_email_communication: true,
  project_email_communication: true,
  get() {
    return Promise.resolve([]);
  }
};

describe('EmailSettings', function () {
  let wrapper;
  let projectPreferenceSpy;

  before(function () {
    sinon.stub(apiClient, 'request').callsFake(() => Promise.resolve([]));
    sinon.stub(talkClient, 'request').callsFake(() => Promise.resolve([]));
    wrapper = mount(<EmailSettings user={user} />);
    projectPreferenceSpy = sinon.spy(wrapper.instance(), 'getProjectForPreferences');
  });

  beforeEach(function () {
    projectPreferenceSpy.resetHistory();
    wrapper.update();
  });

  after(function () {
    apiClient.request.restore();
    talkClient.request.restore();
  });

  it('renders the email address', function () {
    const email = wrapper.find('input[name="email"]');
    assert.equal(email.prop('value'), user.email);
  });

  it('shows global email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="global_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.global_email_communication);
  });

  it('shows project email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="project_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.project_email_communication);
  });

  it('shows beta email preference correctly', function () {
    const betaEmail = wrapper.find('input[name="beta_email_communication"]');
    assert.equal(betaEmail.prop('checked'), user.beta_email_communication);
  });

  describe('project listing', function () {
    let projectSettings;

    before(function () {
      const mockTalkPreferences = talkPreferences.map(preference => mockTalkResource('subscription_preferences', preference));
      const mockProjects = projects.map(project => mockPanoptesResource('projects', project));
      const mockProjectPreferences = projectPreferences.map(preference => mockPanoptesResource('project_preferences', preference));
      wrapper.setState({
        meta: {},
        projectPreferences: mockProjectPreferences,
        projects: mockProjects,
        talkPreferences: mockTalkPreferences
      });
      wrapper.update();
    });

    beforeEach(function () {
      projectSettings = wrapper.find('table').last().find('tbody tr');
    });

    it('lists two projects', function () {
      assert.equal(projectSettings.length, 2);
    });

    projects.forEach((project, i) => {
      it(`shows project ${i} email input correctly`, function () {
        const email = projectSettings.at(i).find('input');
        assert.equal(email.prop('checked'), projectPreferences[i].email_communication);
      });

      it(`updates project ${i} email settings on change`, function () {
        const emailPreference = projectPreferences[i].email_communication;
        const email = projectSettings.at(i).find('input');
        const fakeEvent = {
          target: {
            type: email.prop('type'),
            name: email.prop('name'),
            checked: !email.prop('checked')
          }
        };
        email.simulate('change', fakeEvent);
        assert.equal(wrapper.state().projectPreferences[i].email_communication, !emailPreference);
      });

      it(`shows project ${i} name correctly`, function () {
        const name = projectSettings.at(i).find('td').last();
        assert.equal(name.text(), project.display_name);
      });
    });
  });

  describe('Talk email preferences', function () {
    talkPreferences.forEach((preference) => {
      it(`lists ${preference.category} preferences correctly`, function () {
        const selector = `input[name="${preference.category}"][value="${preference.email_digest}"]`;
        assert.equal(wrapper.find(selector).prop('checked'), true);
      });
    });

    talkPreferences.forEach((preference, i) => {
      it(`${preference.category} updates correctly when preferences are changed`, function () {
        const selector = `input[name="${preference.category}"][value="never"]`;
        wrapper.find(selector).simulate('change');
        assert.equal(wrapper.state().talkPreferences[i].email_digest, 'never');
      });
    });
  });

  describe('Project pagination', function () {
    it('defaults to page 1', function () {
      assert.equal(wrapper.state().page, 1);
    });

    it('should be disabled with less than one page of projects', function () {
      const meta = { page_count: 1 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), true);
    });

    it('should be enabled with more than one page of projects', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), false);
    });

    it('should update the page number on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 3
        }
      };
      pageSelector.simulate('change', fakeEvent);
      assert.equal(wrapper.state().page, 3);
    });

    it('should update the project list on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 5
        }
      };
      pageSelector.simulate('change', fakeEvent);
      wrapper.update();
      assert.equal(wrapper.state().page, 5);
      sinon.assert.calledOnce(projectPreferenceSpy);
    });
  });
});
<commit_msg>edit resources so tests break<commit_after>import React from 'react';
import assert from 'assert';
import { mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import talkClient from 'panoptes-client/lib/talk-client';
import EmailSettings from './email';

function mockTalkResource(type, options) {
  const resource = talkClient.type(type).create(options);
  talkClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const talkPreferences = [
  {
    id: 0,
    category: 'participating_discussions',
    email_digest: 'immediate'
  },
  {
    id: 1,
    category: 'followed_discussions',
    email_digest: 'daily'
  },
  {
    id: 2,
    category: 'mentions',
    email_digest: 'immediate'
  },
  {
    id: 3,
    category: 'group_mentions',
    email_digest: 'immediate'
  },
  {
    id: 4,
    category: 'messages',
    email_digest: 'daily'
  },
  {
    id: 5,
    category: 'started_discussions',
    email_digest: 'weekly'
  }
];

const projects = [
  {
    id: 'a',
    display_name: 'A test project',
    title: 'A test project'
  },
  {
    id: 'b',
    display_name: 'Another test project',
    title: 'Another test project'
  }
];

const projectPreferences = [
  {
    id: '3',
    email_communication: false,
    links: {
      project: 'c'
    }
  },
  {
    id: '2',
    email_communication: false,
    links: {
      project: 'b'
    }
  },
  {
    id: '1',
    email_communication: true,
    links: {
      project: 'a'
    }
  }
];

const user = {
  email: 'An email address',
  beta_email_communication: false,
  global_email_communication: true,
  project_email_communication: true,
  get() {
    return Promise.resolve([]);
  }
};

describe('EmailSettings', function () {
  let wrapper;
  let projectPreferenceSpy;

  before(function () {
    sinon.stub(apiClient, 'request').callsFake(() => Promise.resolve([]));
    sinon.stub(talkClient, 'request').callsFake(() => Promise.resolve([]));
    wrapper = mount(<EmailSettings user={user} />);
    projectPreferenceSpy = sinon.spy(wrapper.instance(), 'getProjectForPreferences');
  });

  beforeEach(function () {
    projectPreferenceSpy.resetHistory();
    wrapper.update();
  });

  after(function () {
    apiClient.request.restore();
    talkClient.request.restore();
  });

  it('renders the email address', function () {
    const email = wrapper.find('input[name="email"]');
    assert.equal(email.prop('value'), user.email);
  });

  it('shows global email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="global_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.global_email_communication);
  });

  it('shows project email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="project_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.project_email_communication);
  });

  it('shows beta email preference correctly', function () {
    const betaEmail = wrapper.find('input[name="beta_email_communication"]');
    assert.equal(betaEmail.prop('checked'), user.beta_email_communication);
  });

  describe('project listing', function () {
    let projectSettings;

    before(function () {
      const mockTalkPreferences = talkPreferences.map(preference => mockTalkResource('subscription_preferences', preference));
      const mockProjects = projects.map(project => mockPanoptesResource('projects', project));
      const mockProjectPreferences = projectPreferences.map(preference => mockPanoptesResource('project_preferences', preference));
      wrapper.setState({
        meta: {},
        projectPreferences: mockProjectPreferences,
        projects: mockProjects,
        talkPreferences: mockTalkPreferences
      });
      wrapper.update();
    });

    beforeEach(function () {
      projectSettings = wrapper.find('table').last().find('tbody tr');
    });

    it('lists two projects', function () {
      assert.equal(projectSettings.length, 2);
    });

    projects.forEach((project, i) => {
      it(`shows project ${i} email input correctly`, function () {
        const email = projectSettings.at(i).find('input');
        assert.equal(email.prop('checked'), projectPreferences[i].email_communication);
      });

      it(`updates project ${i} email settings on change`, function () {
        const emailPreference = projectPreferences[i].email_communication;
        const email = projectSettings.at(i).find('input');
        const fakeEvent = {
          target: {
            type: email.prop('type'),
            name: email.prop('name'),
            checked: !email.prop('checked')
          }
        };
        email.simulate('change', fakeEvent);
        assert.equal(wrapper.state().projectPreferences[i].email_communication, !emailPreference);
      });

      it(`shows project ${i} name correctly`, function () {
        const name = projectSettings.at(i).find('td').last();
        assert.equal(name.text(), project.display_name);
      });
    });
  });

  describe('Talk email preferences', function () {
    talkPreferences.forEach((preference) => {
      it(`lists ${preference.category} preferences correctly`, function () {
        const selector = `input[name="${preference.category}"][value="${preference.email_digest}"]`;
        assert.equal(wrapper.find(selector).prop('checked'), true);
      });
    });

    talkPreferences.forEach((preference, i) => {
      it(`${preference.category} updates correctly when preferences are changed`, function () {
        const selector = `input[name="${preference.category}"][value="never"]`;
        wrapper.find(selector).simulate('change');
        assert.equal(wrapper.state().talkPreferences[i].email_digest, 'never');
      });
    });
  });

  describe('Project pagination', function () {
    it('defaults to page 1', function () {
      assert.equal(wrapper.state().page, 1);
    });

    it('should be disabled with less than one page of projects', function () {
      const meta = { page_count: 1 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), true);
    });

    it('should be enabled with more than one page of projects', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), false);
    });

    it('should update the page number on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 3
        }
      };
      pageSelector.simulate('change', fakeEvent);
      assert.equal(wrapper.state().page, 3);
    });

    it('should update the project list on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 5
        }
      };
      pageSelector.simulate('change', fakeEvent);
      wrapper.update();
      assert.equal(wrapper.state().page, 5);
      sinon.assert.calledOnce(projectPreferenceSpy);
    });
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import assert from 'assert';
import { mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import talkClient from 'panoptes-client/lib/talk-client';
import EmailSettings from './email';

function mockTalkResource(type, options) {
  const resource = talkClient.type(type).create(options);
  talkClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const talkPreferences = [
  {
    id: 0,
    category: 'participating_discussions',
    email_digest: 'immediate'
  },
  {
    id: 1,
    category: 'followed_discussions',
    email_digest: 'daily'
  },
  {
    id: 2,
    category: 'mentions',
    email_digest: 'immediate'
  },
  {
    id: 3,
    category: 'group_mentions',
    email_digest: 'immediate'
  },
  {
    id: 4,
    category: 'messages',
    email_digest: 'daily'
  },
  {
    id: 5,
    category: 'started_discussions',
    email_digest: 'weekly'
  }
];

const projects = [
  {
    id: 'a',
    display_name: 'A test project',
    title: 'A test project'
  },
  {
    id: 'b',
    display_name: 'Another test project',
    title: 'Another test project'
  }
];

const projectPreferences = [
  {
    id: '3',
    email_communication: false,
    links: {
      project: 'c'
    }
  },
  {
    id: '2',
    email_communication: false,
    links: {
      project: 'b'
    }
  },
  {
    id: '1',
    email_communication: true,
    links: {
      project: 'a'
    }
  }
];

const user = {
  email: 'An email address',
  beta_email_communication: false,
  global_email_communication: true,
  project_email_communication: true,
  get() {
    return Promise.resolve([]);
  }
};

describe('EmailSettings', function () {
  let wrapper;
  let projectPreferenceSpy;

  before(function () {
    sinon.stub(apiClient, 'request').callsFake(() => Promise.resolve([]));
    sinon.stub(talkClient, 'request').callsFake(() => Promise.resolve([]));
    wrapper = mount(<EmailSettings user={user} />);
    projectPreferenceSpy = sinon.spy(wrapper.instance(), 'getProjectForPreferences');
  });

  beforeEach(function () {
    projectPreferenceSpy.resetHistory();
    wrapper.update();
  });

  after(function () {
    apiClient.request.restore();
    talkClient.request.restore();
  });

  it('renders the email address', function () {
    const email = wrapper.find('input[name="email"]');
    assert.equal(email.prop('value'), user.email);
  });

  it('shows global email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="global_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.global_email_communication);
  });

  it('shows project email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="project_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.project_email_communication);
  });

  it('shows beta email preference correctly', function () {
    const betaEmail = wrapper.find('input[name="beta_email_communication"]');
    assert.equal(betaEmail.prop('checked'), user.beta_email_communication);
  });

  describe('project listing', function () {
    let projectSettings;

    before(function () {
      const mockTalkPreferences = talkPreferences.map(preference => mockTalkResource('subscription_preferences', preference));
      const mockProjects = projects.map(project => mockPanoptesResource('projects', project));
      const mockProjectPreferences = projectPreferences.map(preference => mockPanoptesResource('project_preferences', preference));
      wrapper.setState({
        meta: {},
        projectPreferences: mockProjectPreferences,
        projects: mockProjects,
        talkPreferences: mockTalkPreferences
      });
      wrapper.update();
    });

    beforeEach(function () {
      projectSettings = wrapper.find('table').last().find('tbody tr');
    });

    it('lists two projects', function () {
      assert.equal(projectSettings.length, 2);
    });

    projects.forEach((project, i) => {
      it(`shows project ${i} email input correctly`, function () {
        const email = projectSettings.at(i).find('input');
        assert.equal(email.prop('checked'), projectPreferences[i].email_communication);
      });

      it(`updates project ${i} email settings on change`, function () {
        const emailPreference = projectPreferences[i].email_communication;
        const email = projectSettings.at(i).find('input');
        const fakeEvent = {
          target: {
            type: email.prop('type'),
            name: email.prop('name'),
            checked: !email.prop('checked')
          }
        };
        email.simulate('change', fakeEvent);
        assert.equal(wrapper.state().projectPreferences[i].email_communication, !emailPreference);
      });

      it(`shows project ${i} name correctly`, function () {
        const name = projectSettings.at(i).find('td').last();
        assert.equal(name.text(), project.display_name);
      });
    });
  });

  describe('Talk email preferences', function () {
    talkPreferences.forEach((preference) => {
      it(`lists ${preference.category} preferences correctly`, function () {
        const selector = `input[name="${preference.category}"][value="${preference.email_digest}"]`;
        assert.equal(wrapper.find(selector).prop('checked'), true);
      });
    });

    talkPreferences.forEach((preference, i) => {
      it(`${preference.category} updates correctly when preferences are changed`, function () {
        const selector = `input[name="${preference.category}"][value="never"]`;
        wrapper.find(selector).simulate('change');
        assert.equal(wrapper.state().talkPreferences[i].email_digest, 'never');
      });
    });
  });

  describe('Project pagination', function () {
    it('defaults to page 1', function () {
      assert.equal(wrapper.state().page, 1);
    });

    it('should be disabled with less than one page of projects', function () {
      const meta = { page_count: 1 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), true);
    });

    it('should be enabled with more than one page of projects', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), false);
    });

    it('should update the page number on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 3
        }
      };
      pageSelector.simulate('change', fakeEvent);
      assert.equal(wrapper.state().page, 3);
    });

    it('should update the project list on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 5
        }
      };
      pageSelector.simulate('change', fakeEvent);
      wrapper.update();
      assert.equal(wrapper.state().page, 5);
      sinon.assert.calledOnce(projectPreferenceSpy);
    });
  });
});
<commit_msg>Fix project email settings tests Tests can't rely on array index since the projects and preferences are in a different order, and have different lengths. These tests match preferences to projects by project ID.<commit_after>import React from 'react';
import assert from 'assert';
import { mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import talkClient from 'panoptes-client/lib/talk-client';
import EmailSettings from './email';

function mockTalkResource(type, options) {
  const resource = talkClient.type(type).create(options);
  talkClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const talkPreferences = [
  {
    id: 0,
    category: 'participating_discussions',
    email_digest: 'immediate'
  },
  {
    id: 1,
    category: 'followed_discussions',
    email_digest: 'daily'
  },
  {
    id: 2,
    category: 'mentions',
    email_digest: 'immediate'
  },
  {
    id: 3,
    category: 'group_mentions',
    email_digest: 'immediate'
  },
  {
    id: 4,
    category: 'messages',
    email_digest: 'daily'
  },
  {
    id: 5,
    category: 'started_discussions',
    email_digest: 'weekly'
  }
];

const projects = [
  {
    id: 'a',
    display_name: 'A test project',
    title: 'A test project'
  },
  {
    id: 'b',
    display_name: 'Another test project',
    title: 'Another test project'
  }
];

const projectPreferences = [
  {
    id: '3',
    email_communication: false,
    links: {
      project: 'c'
    }
  },
  {
    id: '2',
    email_communication: false,
    links: {
      project: 'b'
    }
  },
  {
    id: '1',
    email_communication: true,
    links: {
      project: 'a'
    }
  }
];

const user = {
  email: 'An email address',
  beta_email_communication: false,
  global_email_communication: true,
  project_email_communication: true,
  get() {
    return Promise.resolve([]);
  }
};

describe('EmailSettings', function () {
  let wrapper;
  let projectPreferenceSpy;

  before(function () {
    sinon.stub(apiClient, 'request').callsFake(() => Promise.resolve([]));
    sinon.stub(talkClient, 'request').callsFake(() => Promise.resolve([]));
    wrapper = mount(<EmailSettings user={user} />);
    projectPreferenceSpy = sinon.spy(wrapper.instance(), 'getProjectForPreferences');
  });

  beforeEach(function () {
    projectPreferenceSpy.resetHistory();
    wrapper.update();
  });

  after(function () {
    apiClient.request.restore();
    talkClient.request.restore();
  });

  it('renders the email address', function () {
    const email = wrapper.find('input[name="email"]');
    assert.equal(email.prop('value'), user.email);
  });

  it('shows global email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="global_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.global_email_communication);
  });

  it('shows project email preference correctly', function () {
    const projectEmail = wrapper.find('input[name="project_email_communication"]');
    assert.equal(projectEmail.prop('checked'), user.project_email_communication);
  });

  it('shows beta email preference correctly', function () {
    const betaEmail = wrapper.find('input[name="beta_email_communication"]');
    assert.equal(betaEmail.prop('checked'), user.beta_email_communication);
  });

  describe('project listing', function () {
    let projectSettings;

    before(function () {
      const mockTalkPreferences = talkPreferences.map(preference => mockTalkResource('subscription_preferences', preference));
      const mockProjects = projects.map(project => mockPanoptesResource('projects', project));
      const mockProjectPreferences = projectPreferences.map(preference => mockPanoptesResource('project_preferences', preference));
      wrapper.setState({
        meta: {},
        projectPreferences: mockProjectPreferences,
        projects: mockProjects,
        talkPreferences: mockTalkPreferences
      });
      wrapper.update();
    });

    beforeEach(function () {
      projectSettings = wrapper.find('table').last().find('tbody tr');
    });

    it('lists two projects', function () {
      assert.equal(projectSettings.length, 2);
    });

    projects.forEach((project, i) => {
      it(`shows project ${i} email input correctly`, function () {
        const projectPreference = projectPreferences.filter(pref => pref.links.project === project.id)[0];
        const email = projectSettings.find(`input[id="${projectPreference.id}"]`);
        assert.equal(email.prop('checked'), projectPreference.email_communication);
      });

      it(`updates project ${i} email settings on change`, function () {
        const projectPreference = projectPreferences.filter(pref => pref.links.project === project.id)[0];
        const emailPreference = projectPreference.email_communication;
        const email = projectSettings.find(`input[id="${projectPreference.id}"]`);
        const index = projectPreferences.indexOf(projectPreference);
        const fakeEvent = {
          target: {
            type: email.prop('type'),
            name: email.prop('name'),
            checked: !email.prop('checked')
          }
        };
        email.simulate('change', fakeEvent);
        assert.equal(wrapper.state().projectPreferences[index].email_communication, !emailPreference);
      });

      it(`shows project ${i} name correctly`, function () {
        const projectPreference = projectPreferences.filter(pref => pref.links.project === project.id)[0];
        const name = projectSettings.find(`label[htmlFor="${projectPreference.id}"]`);
        assert.equal(name.text(), project.display_name);
      });
    });
  });

  describe('Talk email preferences', function () {
    talkPreferences.forEach((preference) => {
      it(`lists ${preference.category} preferences correctly`, function () {
        const selector = `input[name="${preference.category}"][value="${preference.email_digest}"]`;
        assert.equal(wrapper.find(selector).prop('checked'), true);
      });
    });

    talkPreferences.forEach((preference, i) => {
      it(`${preference.category} updates correctly when preferences are changed`, function () {
        const selector = `input[name="${preference.category}"][value="never"]`;
        wrapper.find(selector).simulate('change');
        assert.equal(wrapper.state().talkPreferences[i].email_digest, 'never');
      });
    });
  });

  describe('Project pagination', function () {
    it('defaults to page 1', function () {
      assert.equal(wrapper.state().page, 1);
    });

    it('should be disabled with less than one page of projects', function () {
      const meta = { page_count: 1 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), true);
    });

    it('should be enabled with more than one page of projects', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      assert.equal(pageSelector.prop('disabled'), false);
    });

    it('should update the page number on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 3
        }
      };
      pageSelector.simulate('change', fakeEvent);
      assert.equal(wrapper.state().page, 3);
    });

    it('should update the project list on change', function () {
      const meta = { page_count: 2 };
      wrapper.setState({ meta });
      const pageSelector = wrapper.find('nav.pagination select');
      const fakeEvent = {
        target: {
          value: 5
        }
      };
      pageSelector.simulate('change', fakeEvent);
      wrapper.update();
      assert.equal(wrapper.state().page, 5);
      sinon.assert.calledOnce(projectPreferenceSpy);
    });
  });
});
<|endoftext|>


<commit_before>var thermostat = require('../services/thermostat');
var presence = require('../services/presenceArp');

const PERIOD = 60 * 1000;

module.exports = {

  lastChangedMode: undefined,
  interval: undefined,

  start: function() {
    var ref = this;
    this.interval = setInterval(function() {

      var getUsersAtHomePromise = presence.getUsersAtHome();
      var thermStatePromise = thermostat.getThermostatsData();

      Promise.all([ getUsersAtHomePromise, thermStatePromise ]).then(values => {
        var usersAtHome = values[0];
        var peopleAtHome = usersAtHome.length > 0;;
        var thermState = values[1];

        var currentMode = thermState.setpoint.setpoint_mode;
        var suggestedMode = ref.getSuggestedMode(peopleAtHome, thermState.setpoint.setpoint_mode);

        if (ref.lastChangedMode === undefined || currentMode == suggestedMode) {
          ref.lastChangedMode = currentMode;
        }

        console.log(new Date().toISOString(), 'People', usersAtHome, 'current mode [', currentMode, '] suggested [', suggestedMode, '] last [', ref.lastChangedMode, '] temp', thermState.measured.temperature);

        if (!ref.userHasChangedMode(currentMode) && suggestedMode && suggestedMode != currentMode) {
          ref.setThermPointMode(suggestedMode);
        }
      }).catch(error => {
        console.log(error);
      });
    }, PERIOD);
  },

  stop: function() {
    if (this.interval !== undefined) {
      clearInterval(this.interval);
    }
  },

  setThermPointMode: function(mode, endTime, temp) {
    thermostat.setThermostatPoint(mode, endTime, temp).then(status => {
      console.log('Changed mode to ', mode);
      this.lastChangedMode = mode;
    }).catch(err => {
      console.log(err);
    });
  },

  userHasChangedMode: function(currentMode) {
    return currentMode !== this.lastChangedMode;
  },

  getSuggestedMode: function(peopleAtHome, currentMode) {
    if (currentMode != 'program' && currentMode != 'away') {
      return null;
    }
    return peopleAtHome ? 'program' : 'away';
  }

};<commit_msg>Log to console only when change mode or error<commit_after>var thermostat = require('../services/thermostat');
var presence = require('../services/presenceArp');

const PERIOD = 60 * 1000;

module.exports = {

  lastChangedMode: undefined,
  interval: undefined,

  start: function() {
    var ref = this;
    this.interval = setInterval(function() {

      var getUsersAtHomePromise = presence.getUsersAtHome();
      var thermStatePromise = thermostat.getThermostatsData();

      Promise.all([ getUsersAtHomePromise, thermStatePromise ]).then(values => {
        var usersAtHome = values[0];
        var peopleAtHome = usersAtHome.length > 0;;
        var thermState = values[1];

        var currentMode = thermState.setpoint.setpoint_mode;
        var suggestedMode = ref.getSuggestedMode(peopleAtHome, thermState.setpoint.setpoint_mode);

        if (ref.lastChangedMode === undefined || currentMode == suggestedMode) {
          ref.lastChangedMode = currentMode;
        }

        if (!ref.userHasChangedMode(currentMode) && suggestedMode && suggestedMode != currentMode) {
          console.log('People', usersAtHome, 'current mode [', currentMode, '] suggested [', suggestedMode, '] last [', ref.lastChangedMode, '] temp', thermState.measured.temperature);
          ref.setThermPointMode(suggestedMode);
        }
      }).catch(error => {
        console.error(error);
      });
    }, PERIOD);
  },

  stop: function() {
    if (this.interval !== undefined) {
      clearInterval(this.interval);
    }
  },

  setThermPointMode: function(mode, endTime, temp) {
    thermostat.setThermostatPoint(mode, endTime, temp).then(status => {
      console.log('Changed mode to ', mode);
      this.lastChangedMode = mode;
    }).catch(err => {
      console.log(err);
    });
  },

  userHasChangedMode: function(currentMode) {
    return currentMode !== this.lastChangedMode;
  },

  getSuggestedMode: function(peopleAtHome, currentMode) {
    if (currentMode != 'program' && currentMode != 'away') {
      return null;
    }
    return peopleAtHome ? 'program' : 'away';
  }

};<|endoftext|>


<commit_before>"use strict";

var _ = require('lodash'),
    express = require('express'),
    bodyParser = require('body-parser'),
    logger = require('winston'),
    fs = require('fs-extra'),
    helmet = require('helmet'),
    cluster = require('cluster'),
    config = require('./config'),
    routes = require('./routes'),
    filters = require('./filters'),
    utils = require('./utils'),

    DEF_LOGGER_LEVEL = 'info',
    DEF_LOGGER_SILENT = false;

/* Logging system */

function initLogging(conf) {
    logger.setLevels({
        debug: 0,
        info: 1,
        silly: 2,
        warn: 3,
        error: 4
    });
    logger.addColors({
        debug: 'green',
        info: 'cyan',
        silly: 'magenta',
        warn: 'yellow',
        error: 'red'
    });

    logger.remove(logger.transports.Console);
    logger.add(logger.transports.Console, {
        level: conf.level || DEF_LOGGER_LEVEL,
        silent: conf.silent || DEF_LOGGER_SILENT,
        colorize: true
    });
}

/* Termination & Errors handling */

function initExitHandling() {
    var onExit = function () {
        process.exit(0);
    };
    process.on('SIGTERM', onExit);
    process.on('SIGINT', onExit);
}

/* Init FS services */

function cleanupFsStorage(conf) {
    if (conf.cleanup) {
        var storagePath = conf.storage,
            files = fs.readdirSync(storagePath);

        _.forEach(files, function(file) {
            var filePath = utils.filePath(file, storagePath);
            try {
                fs.removeSync(filePath, {force: true});
            } catch (err) {}
        });
    }
}

function initFsWatchdog(conf) {
    var timeout = conf.cache * 1000,
        dir = conf.storage;

    utils.runFsWatchdog(dir, timeout, function (file) {
        return fs.unlink(file, function (err) {
            if (err) {
                return logger.error(err);
            }
            logger.info('Deleted file: %s', file);
        });
    });
}

function initFsStorage(conf) {
    cleanupFsStorage(conf);
    initFsWatchdog(conf);
}

/* Web service */

function addUsage(conf, chain) {
    if (conf.ui) {
        chain.push(filters.usage);
    }
    return chain;
}

function createWebApplication(conf) {
    var app = express(),
        index = routes.index(conf),
        urlencoded = bodyParser.urlencoded({
            extended: false
        }),
        json = bodyParser.json(),
        noCache = helmet.noCache();

    app.use(express.static(utils.filePath('../public')));

    app.get('/', addUsage(conf, [noCache, filters.merge]), index);
    app.post('/', addUsage(conf, [urlencoded, json, noCache, filters.merge]), index);

    return app;
}

function runWebServer(conf, onStart) {
    var app = createWebApplication(conf),
        host = conf.host,
        port = conf.port,
        server = app.listen(port, host, function () {
            if (onStart) {
                onStart(server);
            }
        });
    logger.info('Manet server started on %s:%d', host, port);
}

/* Initialize and run server */

function main(onStart) {
    var confPath = config.defaultConfigPath(),
        conf = config.read();

    initLogging(conf);
    initExitHandling();
    initFsStorage(conf);

    logger.debug('Default configuration file: %s', confPath);
    logger.debug('Configuration parameters: %s', JSON.stringify(conf));

    runWebServer(conf, onStart);
}

/* Exported functions */

module.exports = {
    main: main
};
<commit_msg>Fix lint warning<commit_after>"use strict";

var _ = require('lodash'),
    express = require('express'),
    bodyParser = require('body-parser'),
    logger = require('winston'),
    fs = require('fs-extra'),
    helmet = require('helmet'),
    config = require('./config'),
    routes = require('./routes'),
    filters = require('./filters'),
    utils = require('./utils'),

    DEF_LOGGER_LEVEL = 'info',
    DEF_LOGGER_SILENT = false;

/* Logging system */

function initLogging(conf) {
    logger.setLevels({
        debug: 0,
        info: 1,
        silly: 2,
        warn: 3,
        error: 4
    });
    logger.addColors({
        debug: 'green',
        info: 'cyan',
        silly: 'magenta',
        warn: 'yellow',
        error: 'red'
    });

    logger.remove(logger.transports.Console);
    logger.add(logger.transports.Console, {
        level: conf.level || DEF_LOGGER_LEVEL,
        silent: conf.silent || DEF_LOGGER_SILENT,
        colorize: true
    });
}

/* Termination & Errors handling */

function initExitHandling() {
    var onExit = function () {
        process.exit(0);
    };
    process.on('SIGTERM', onExit);
    process.on('SIGINT', onExit);
}

/* Init FS services */

function cleanupFsStorage(conf) {
    if (conf.cleanup) {
        var storagePath = conf.storage,
            files = fs.readdirSync(storagePath);

        _.forEach(files, function(file) {
            var filePath = utils.filePath(file, storagePath);
            try {
                fs.removeSync(filePath, {force: true});
            } catch (err) {}
        });
    }
}

function initFsWatchdog(conf) {
    var timeout = conf.cache * 1000,
        dir = conf.storage;

    utils.runFsWatchdog(dir, timeout, function (file) {
        return fs.unlink(file, function (err) {
            if (err) {
                return logger.error(err);
            }
            logger.info('Deleted file: %s', file);
        });
    });
}

function initFsStorage(conf) {
    cleanupFsStorage(conf);
    initFsWatchdog(conf);
}

/* Web service */

function addUsage(conf, chain) {
    if (conf.ui) {
        chain.push(filters.usage);
    }
    return chain;
}

function createWebApplication(conf) {
    var app = express(),
        index = routes.index(conf),
        urlencoded = bodyParser.urlencoded({
            extended: false
        }),
        json = bodyParser.json(),
        noCache = helmet.noCache();

    app.use(express.static(utils.filePath('../public')));

    app.get('/', addUsage(conf, [noCache, filters.merge]), index);
    app.post('/', addUsage(conf, [urlencoded, json, noCache, filters.merge]), index);

    return app;
}

function runWebServer(conf, onStart) {
    var app = createWebApplication(conf),
        host = conf.host,
        port = conf.port,
        server = app.listen(port, host, function () {
            if (onStart) {
                onStart(server);
            }
        });
    logger.info('Manet server started on %s:%d', host, port);
}

/* Initialize and run server */

function main(onStart) {
    var confPath = config.defaultConfigPath(),
        conf = config.read();

    initLogging(conf);
    initExitHandling();
    initFsStorage(conf);

    logger.debug('Default configuration file: %s', confPath);
    logger.debug('Configuration parameters: %s', JSON.stringify(conf));

    runWebServer(conf, onStart);
}

/* Exported functions */

module.exports = {
    main: main
};
<|endoftext|>


<commit_before>import rbush from 'rbush';

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

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

class AnnotationsInsets {
  constructor(insetsTrack, options, getTrackByUid, animate) {
    this.getTrackByUid = getTrackByUid;
    this.animate = animate;
    this.options = options;

    this.insetsTrack = getTrackByUid(insetsTrack);

    if (!this.insetsTrack) {
      console.warn(`Insets track (uid: ${insetsTrack}) not found`);
      return;
    }

    this.insetsTrack.subscribe('zoom', this.zoomHandler.bind(this));

    this.annotationTrackIds = new Set();
    this.annotationTracks = options.annotationTracks
      .map((uid) => {
        const track = getTrackByUid(uid);

        if (!track) console.warn(`Child track (uid: ${uid}) not found`);
        else this.annotationTrackIds.add(track.uuid);

        return track;
      })
      .filter(track => track);

    // Augment annotation tracks
    this.annotationTracks.forEach((track) => {
      track.subscribe('annotationDrawn', this.annotationDrawnHandler.bind(this));
    });

    this.currK = 1;  // Current scale
    this.drawnAnnoIdx = new Set();
    this.insets = {};

    this.initTree();

    this.pubSubs = [];
    this.pubSubs.push(
      pubSub.subscribe('TiledPixiTrack.tilesDrawn', this.tilesDrawnHandler.bind(this))
    );
  }

  /**
   * Handles annotation drawn events
   *
   * @param  {String}  event.uid  UID of the view that triggered the event.
   * @param  {Array}  event.viewPos  View position (i.e., [x, y, width, height])
   *   of the drawn annotation.
   * @param  {Array}  event.dataPos  Data position of the drawn annotation.
   */
  annotationDrawnHandler({ uid, viewPos, dataPos }) {
    const locus = {
      uid,
      minX: viewPos[0],
      minY: viewPos[1],
      maxX: viewPos[0] + viewPos[2],
      maxY: viewPos[1] + viewPos[3],
      cX1: dataPos[0],
      cX2: dataPos[1],
      cY1: dataPos[2],
      cY2: dataPos[3]
    };

    this.newAnno = !this.drawnAnnoIdxOld.has(uid);
    this.drawnAnnoIdx.add(uid);

    if (
      viewPos[2] <= this.options.insetThreshold ||
      viewPos[3] <= this.options.insetThreshold
    ) {
      this.insetsToBeDrawn.push(locus);
      this.insetsToBeDrawnIds.add(uid);
    } else {
      this.drawnAnnotations.push(locus);
    }
  }

  /**
   * Build region tree of drawn annotations and trigger the creation of insets.
   */
  buildTree() {
    if (!this.drawnAnnotations.length) return;

    this.drawnAnnotations = [
      ...this.insetsToBeDrawn,
      ...this.drawnAnnotations
    ];
    if (this.newAnno) this.tree.load(this.drawnAnnotations);
    this.createInsets();
  }

  /**
   * Create insets.
   */
  createInsets() {
    this.drawInsets(this.positionInsets(), this.insetsToBeDrawnIds);
  }

  /**
   * Draw positioned insets
   *
   * @param  {Array}  insets  Inset positions to be drawn.
   * @param  {Set}  insetIds  Inset IDs to be drawn.
   * @return  {Object}  Promise resolving once all insets are drawn.
   */
  drawInsets(insets, insetIds) {
    return Promise.all(this.insetsTrack.drawInsets(insets, insetIds))
      .then(() => { this.animate(); })
      .catch((e) => { this.animate(); console.error(e); });
  }

  /**
   * Initialize annotation RTree
   */
  initTree() {
    this.tree = rbush();
    this.oldAnnotations = this.drawnAnnotations;
    this.drawnAnnotations = [];
    this.oldInsets = this.insetsToBeDrawn;
    this.insetsToBeDrawn = [];
    this.insetsToBeDrawnIds = new Set();
    this.drawnAnnoIdxOld = this.drawnAnnoIdx;
    this.drawnAnnoIdx = new Set();
    this.numTracksDrawn = 0;
    this.newAnno = false;
  }

  /**
   * Position insets using simulated annealing
   *
   * @return  {Array}  Position and dimension of the insets.
   */
  positionInsets() {
    if (!this.insetsToBeDrawn.length) return [];

    const anchors = this.drawnAnnotations.map(obj => ({
      t: 1,
      x: (obj.maxX + obj.minX) / 2,
      y: (obj.maxY + obj.minY) / 2,
      ox: (obj.maxX + obj.minX) / 2,  // Origin x
      oy: (obj.maxY + obj.minY) / 2,  // Origin y
      wh: (obj.maxX - obj.minX) / 2,  // Width half
      hh: (obj.maxY - obj.minY) / 2,  // Heigth half
      ...obj
    }));

    const insetRes = this.insetsTrack.insetRes * this.insetsTrack.insetScale;
    const insetResH = insetRes / 2;

    const insets = this.insetsToBeDrawn
      .map((inset) => {
        if (!this.insets[inset.uid]) {
          // Add new inset
          this.insets[inset.uid] = {
            t: 1.0,
            x: (inset.maxX + inset.minX) / 2,
            y: (inset.maxY + inset.minY) / 2,
            ox: (inset.maxX + inset.minX) / 2,  // Origin x
            oy: (inset.maxY + inset.minY) / 2,  // Origin y
            width: insetRes,
            height: insetRes,
            wh: insetResH,  // Width half
            hh: insetResH,  // Heigth half
            ...inset
          };
        } else {
          // Update existing inset positions
          const newOx = (inset.maxX + inset.minX) / 2;
          const newOy = (inset.maxY + inset.minY) / 2;
          const dX = this.insets[inset.uid].ox - newOx;
          const dY = this.insets[inset.uid].oy - newOy;

          this.insets[inset.uid].ox = newOx;
          this.insets[inset.uid].oy = newOy;

          this.insets[inset.uid].x -= dX;
          this.insets[inset.uid].y -= dY;

          this.insets[inset.uid].t = this.scaleChanged ? 0.5 : 0;
        }

        return this.insets[inset.uid];
      });

    const insetsToBePositioned = insets
      .filter((inset) => {
        if (inset.t) return true;

        // Inset has cooled down (i.e., is already positions), hence, it is
        // filtered out and added to anchors instead.
        anchors.push(inset);
        return false;
      });

    if (insetsToBePositioned.length) {
      const t0 = performance.now();
      const n = insetsToBePositioned.length;

      positionLabels
        // Insets, i.e., labels
        .label(insetsToBePositioned)
        // Anchors, i.e., label origins, already positioned labels, and other
        // annotations
        .anchor(anchors)
        .width(this.insetsTrack.dimensions[0])
        .height(this.insetsTrack.dimensions[1])
        .start(Math.round(Math.max(2, Math.min(100 * Math.log(n) / n))));

      console.log(`Labeling took ${performance.now() - t0} msec`);
    }

    const pos = insets.map(inset => ([
      inset.uid,
      inset.x,
      inset.y,
      inset.width,
      inset.height,
      inset.ox,
      inset.oy,
      inset.cX1,
      inset.cX2,
      inset.cY1,
      inset.cY2
    ]));

    return pos;
  }

  /**
   * Remove this track.
   */
  remove() {
    this.pubSubs.forEach(sub => pubSub.unsubscribe(sub));
    this.pubSubs = undefined;
    this.annotationTracks.forEach((track) => {
      track.unsubscribe('annotationDrawn', this.annotationDrawnHandler.bind(this));
    });
  }

  /**
   * Callback function passed into the annotation tracks to trigger tree
   * building of the spatial RTree.
   *
   * @description
   * Simple counter that call `this.buildTree()` once the number of annotation
   * tracks is reached. This might need to be improved!=
   */
  tilesDrawnHandler({ uuid }) {
    if (!this.annotationTrackIds.has(uuid)) return;

    this.numTracksDrawn += 1;
    if (!(this.numTracksDrawn % this.annotationTracks.length)) this.buildTree();
  }

  zoomHandler({ k }) {
    this.initTree();

    this.scaleChanged = this.currK !== k;
    this.currK = k;
  }
}

export default AnnotationsInsets;
<commit_msg>Very early version of a gallery view layouting algorithm<commit_after>import rbush from 'rbush';

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

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

class AnnotationsInsets {
  constructor(insetsTrack, options, getTrackByUid, animate) {
    this.getTrackByUid = getTrackByUid;
    this.animate = animate;
    this.options = options;

    this.insetsTrack = getTrackByUid(insetsTrack);

    if (!this.insetsTrack) {
      console.warn(`Insets track (uid: ${insetsTrack}) not found`);
      return;
    }

    this.insetsTrack.subscribe('zoom', this.zoomHandler.bind(this));

    this.annotationTrackIds = new Set();
    this.annotationTracks = options.annotationTracks
      .map((uid) => {
        const track = getTrackByUid(uid);

        if (!track) console.warn(`Child track (uid: ${uid}) not found`);
        else this.annotationTrackIds.add(track.uuid);

        return track;
      })
      .filter(track => track);

    // Augment annotation tracks
    this.annotationTracks.forEach((track) => {
      track.subscribe('annotationDrawn', this.annotationDrawnHandler.bind(this));
    });

    this.currK = 1;  // Current scale
    this.drawnAnnoIdx = new Set();
    this.insets = {};

    this.initTree();

    this.pubSubs = [];
    this.pubSubs.push(
      pubSub.subscribe('TiledPixiTrack.tilesDrawn', this.tilesDrawnHandler.bind(this))
    );
  }

  /**
   * Handles annotation drawn events
   *
   * @param  {String}  event.uid  UID of the view that triggered the event.
   * @param  {Array}  event.viewPos  View position (i.e., [x, y, width, height])
   *   of the drawn annotation.
   * @param  {Array}  event.dataPos  Data position of the drawn annotation.
   */
  annotationDrawnHandler({ uid, viewPos, dataPos }) {
    const locus = {
      uid,
      minX: viewPos[0],
      minY: viewPos[1],
      maxX: viewPos[0] + viewPos[2],
      maxY: viewPos[1] + viewPos[3],
      cX1: dataPos[0],
      cX2: dataPos[1],
      cY1: dataPos[2],
      cY2: dataPos[3]
    };

    this.newAnno = !this.drawnAnnoIdxOld.has(uid);
    this.drawnAnnoIdx.add(uid);

    if (
      viewPos[2] <= this.options.insetThreshold ||
      viewPos[3] <= this.options.insetThreshold
    ) {
      this.insetsToBeDrawn.push(locus);
      this.insetsToBeDrawnIds.add(uid);
    } else {
      this.drawnAnnotations.push(locus);
    }
  }

  /**
   * Build region tree of drawn annotations and trigger the creation of insets.
   */
  buildTree() {
    if (!this.drawnAnnotations.length) return;

    this.drawnAnnotations = [
      ...this.insetsToBeDrawn,
      ...this.drawnAnnotations
    ];
    if (this.newAnno) this.tree.load(this.drawnAnnotations);
    this.createInsets();
  }

  /**
   * Create insets.
   */
  createInsets() {
    this.drawInsets(this.positionInsets(), this.insetsToBeDrawnIds);
  }

  /**
   * Draw positioned insets
   *
   * @param  {Array}  insets  Inset positions to be drawn.
   * @param  {Set}  insetIds  Inset IDs to be drawn.
   * @return  {Object}  Promise resolving once all insets are drawn.
   */
  drawInsets(insets, insetIds) {
    return Promise.all(this.insetsTrack.drawInsets(insets, insetIds))
      .then(() => { this.animate(); })
      .catch((e) => { this.animate(); console.error(e); });
  }

  /**
   * Initialize annotation RTree
   */
  initTree() {
    this.tree = rbush();
    this.oldAnnotations = this.drawnAnnotations;
    this.drawnAnnotations = [];
    this.oldInsets = this.insetsToBeDrawn;
    this.insetsToBeDrawn = [];
    this.insetsToBeDrawnIds = new Set();
    this.drawnAnnoIdxOld = this.drawnAnnoIdx;
    this.drawnAnnoIdx = new Set();
    this.numTracksDrawn = 0;
    this.newAnno = false;
  }

  /**
   * Position insets using simulated annealing
   *
   * @return  {Array}  Position and dimension of the insets.
   */
  positionInsets() {
    if (!this.insetsToBeDrawn.length) return [];

    if (this.insetsTrack.positioning.location === 'gallery') {
      return this.positionInsetsGallery();
    }

    return this.positionInsetsCenter();
  }
  /**
   * Position insets within the heatmap using simulated annealing
   *
   * @param  {Array}  insetsToBeDrawn  Insets to be drawn
   * @return  {Array}  Position and dimension of the insets.
   */
  positionInsetsCenter(insetsToBeDrawn = this.insetsToBeDrawn) {
    const anchors = this.drawnAnnotations.map(obj => ({
      t: 1,
      x: (obj.maxX + obj.minX) / 2,
      y: (obj.maxY + obj.minY) / 2,
      ox: (obj.maxX + obj.minX) / 2,  // Origin x
      oy: (obj.maxY + obj.minY) / 2,  // Origin y
      wh: (obj.maxX - obj.minX) / 2,  // Width half
      hh: (obj.maxY - obj.minY) / 2,  // Heigth half
      ...obj
    }));

    const insetRes = this.insetsTrack.insetRes * this.insetsTrack.insetScale;
    const insetResH = insetRes / 2;

    const insets = insetsToBeDrawn
      .map((inset) => {
        if (!this.insets[inset.uid]) {
          // Add new inset
          this.insets[inset.uid] = {
            t: 1.0,
            x: (inset.maxX + inset.minX) / 2,
            y: (inset.maxY + inset.minY) / 2,
            ox: (inset.maxX + inset.minX) / 2,  // Origin x
            oy: (inset.maxY + inset.minY) / 2,  // Origin y
            width: insetRes,
            height: insetRes,
            wh: insetResH,  // Width half
            hh: insetResH,  // Heigth half
            ...inset
          };
        } else {
          // Update existing inset positions
          const newOx = (inset.maxX + inset.minX) / 2;
          const newOy = (inset.maxY + inset.minY) / 2;
          const dX = this.insets[inset.uid].ox - newOx;
          const dY = this.insets[inset.uid].oy - newOy;

          this.insets[inset.uid].ox = newOx;
          this.insets[inset.uid].oy = newOy;

          this.insets[inset.uid].x -= dX;
          this.insets[inset.uid].y -= dY;

          this.insets[inset.uid].t = this.scaleChanged ? 0.5 : 0;
        }

        return this.insets[inset.uid];
      });

    const insetsToBePositioned = insets
      .filter((inset) => {
        if (inset.t) return true;

        // Inset has cooled down (i.e., is already positions), hence, it is
        // filtered out and added to anchors instead.
        anchors.push(inset);
        return false;
      });

    if (insetsToBePositioned.length) {
      const t0 = performance.now();
      const n = insetsToBePositioned.length;

      positionLabels
        // Insets, i.e., labels
        .label(insetsToBePositioned)
        // Anchors, i.e., label origins, already positioned labels, and other
        // annotations
        .anchor(anchors)
        .width(this.insetsTrack.dimensions[0])
        .height(this.insetsTrack.dimensions[1])
        .start(Math.round(Math.max(2, Math.min(100 * Math.log(n) / n))));

      console.log(`Labeling took ${performance.now() - t0} msec`);
    }

    const pos = insets.map(inset => ([
      inset.uid,
      inset.x,
      inset.y,
      inset.width,
      inset.height,
      inset.ox,
      inset.oy,
      inset.cX1,
      inset.cX2,
      inset.cY1,
      inset.cY2
    ]));

    return pos;
  }

  /**
   * Position insets along the gallery.
   *
   * @description
   * Technically we should not call the snippets insets anymore because they are
   * not drawn within the matrix anymore
   *
   * @param  {Array}  insetsToBeDrawn  Insets to be drawn
   * @return  {Array}  Position and dimension of the insets.
   */
  positionInsetsGallery(insetsToBeDrawn = this.insetsToBeDrawn) {
    const insetRes = this.insetsTrack.insetRes * this.insetsTrack.insetScale;
    const trackHeightH = this.insetsTrack.positioning.height / 2;
    const padding = 6;
    const insetResWithPad = insetRes + padding;
    const offset = this.insetsTrack.positioning.offsetX;

    const numPosX = Math.floor(
      this.insetsTrack.dimensions[0] / insetResWithPad
    );

    const numPosY = Math.floor(
      this.insetsTrack.dimensions[1] / insetResWithPad
    );

    const getPos = (index) => {
      const numPerSide = (numPosX + numPosY - 2);
      const lowerLeft = Math.floor(index / (numPosX + numPosY - 2));
      let x = 0;
      let y = 0;

      if (lowerLeft) {
        x = (numPosX - 1) - (index - numPerSide);
        y = (numPosY - 1) - Math.min(0, (index - numPerSide) - (numPosX - 1));
      } else {
        x = Math.min(index, numPosX - 1);
        y = Math.max(0, index - (numPosX - 1));
      }

      return [
        (x * insetResWithPad) + trackHeightH,
        (y * insetResWithPad) + trackHeightH
      ];
    };

    return insetsToBeDrawn
      .map((inset, index) => {
        const pos = getPos(index + 1);

        return [
          inset.uid,
          pos[0],  // x
          pos[1],  // y
          insetRes,  // width
          insetRes,  // height
          ((inset.maxX + inset.minX) / 2) + offset,  // Origin x
          ((inset.maxY + inset.minY) / 2) + offset,  // Origin y
          inset.cX1,
          inset.cX2,
          inset.cY1,
          inset.cY2
        ];
      });
  }

  /**
   * Remove this track.
   */
  remove() {
    this.pubSubs.forEach(sub => pubSub.unsubscribe(sub));
    this.pubSubs = undefined;
    this.annotationTracks.forEach((track) => {
      track.unsubscribe('annotationDrawn', this.annotationDrawnHandler.bind(this));
    });
  }

  /**
   * Callback function passed into the annotation tracks to trigger tree
   * building of the spatial RTree.
   *
   * @description
   * Simple counter that call `this.buildTree()` once the number of annotation
   * tracks is reached. This might need to be improved!=
   */
  tilesDrawnHandler({ uuid }) {
    if (!this.annotationTrackIds.has(uuid)) return;

    this.numTracksDrawn += 1;
    if (!(this.numTracksDrawn % this.annotationTracks.length)) this.buildTree();
  }

  zoomHandler({ k }) {
    this.initTree();

    this.scaleChanged = this.currK !== k;
    this.currK = k;
  }
}

export default AnnotationsInsets;
<|endoftext|>


<commit_before>/**
 * Common transform functions used.
 */

/* globals _ */
/* exported commonTransforms */
/* jshint camelcase:false */

/* note lodash should be defined in parent scope */
var commonTransforms = (function(_) {

    /**
    * Check for geolocation equality
    */
    function isGeolocationEqual(value, other) {
        return value.latitude === other.latitude && value.longitude === other.longitude;
    }

    return {
        /**
        * Changes the key/value names of buckets given from an aggregation
        * to names preferred by the user.
        */
        transformBuckets: function(buckets, keyName, alternateKey) {
            buckets = _.map(buckets, function(bucket) {
                var obj = {
                    count: bucket.doc_count
                };
                if(alternateKey) {
                    obj[keyName] = bucket[alternateKey];
                } else {
                    obj[keyName] = bucket.key;
                }
                return obj;
            });
            return buckets;
        },

        /**
            Get people aggregation info:

            "people": {
                "names": [
                    {"name": "Emily", "count": 14},
                    {"name": "Erin", "count": 12},
                    {"name": "Jane", "count": 3}
                ],
                "ethnicities": [
                    {"ethnicity": "white", "count": 19}
                ],
                "heights": [{"height": 64, "count":5, "unitOfMeasure": "inches"}],
                "weights": [{"weight": 115, "count": 5, "unitOfMeasure": "pounds"}],
                "ages": [{"age": 30, "count": 9}]
            }
        */
        getPeople: function(aggs) {
            var people = {
                names: this.transformBuckets(aggs.people_names.buckets, 'name'),
                ethnicities: this.transformBuckets(aggs.people_ethnicities.buckets, 'ethnicity'),
                ages: this.transformBuckets(aggs.people_ages.buckets, 'age')
            };

            return people;
        },
        /**
            "locations": [
                {
                    "city": "hawthorn",
                    "state": "california",
                    "latitude": 33.916403,
                    "longitude": -118.352575,
                    "date": "2012-04-23T18:25:43.511Z"
                }
            ]
        */
        getLocations: function(hits) {
            var locations = [];
            _.each(hits, function(hit) {
                var location = hit._source.availableAtOrFrom;
                if(location) {
                    var latitude = _.get(location, 'geo.latitude');
                    var longitude = _.get(location, 'geo.longitude');

                    _.each(location.address, function(address) {
                        locations.push({
                            city: address.addressLocality,
                            state: address.addressRegion,
                            latitude: latitude,
                            longitude: longitude,
                            date: hit._source.validFrom
                        });
                    });
                }
            });

            return locations;
        },
        /**
            where latitude and longitude are required:
            "geoCoordinates": [
                {
                    "city": "hawthorn",
                    "state": "california",
                    "latitude": 33.916403,
                    "longitude": -118.352575
                }
            ]
        */
        getGeoCoordinates: function(records) {
            var geos = [];

            records.forEach(function(record) {
                var addresses = _.get(record, '_source.availableAtOrFrom.address', []);
                //var latitude = _.get(record, '_source.availableAtOrFrom.address[0].geo.latitude');
                //var longitude = _.get(record, '_source.availableAtOrFrom.address[0].geo.longitude');
                addresses.forEach(function(address) {
                    if (latitude && longitude) {
                        var geo = {
                            city: address.addressLocality,
                            state: address.addressRegion,
                            latitude: address.geo.latitude,
                            longitude: address.geo.longitude
                        };
                        geos.push(geo);
                    }
                });
                // Removing duplicates for better map display
                geos = _.q(geos, isGeolocationEqual);
            });

            return geos;
        },
        /** build address object:
        "address": {
            "locality": "Los Angeles",
            "region": "California",
            "formattedAddress": 'Los Angeles, California'
        }
        */
        getAddress: function(record) {
            var address = {};
            address.locality = _.get(record, 'availableAtOrFrom.address[0].addressLocality');
            address.region = _.get(record, 'availableAtOrFrom.address[0].addressRegion');

            var formattedAddress = [];
            if(address.locality) {
                formattedAddress.push(address.locality);
            }

            if(address.region) {
                if(formattedAddress.length > 0) {
                    formattedAddress.push(', ');
                }
                formattedAddress.push(address.region);
            }

            address.formattedAddress = formattedAddress.join('');

            if(_.isEmpty(address.formattedAddress)) {
                address.formattedAddress = 'Address N/A';
            }

            return address;
        },
        /** build an array of strings:
            example: ["1112223333", "0123456789"]
        */
        getArrayOfStrings: function(record, pathToArray, pathToString) {
            var arrayToReturn = [];
            var initialArray = _.get(record, pathToArray, []);

            initialArray.forEach(function(element) {
                arrayToReturn.push(_.get(element, pathToString));
            });

            return arrayToReturn;
        },

        getClickableObjectArr: function (records, type) {
            var result = [];
            if(records) {
                if(records.constructor === Array) {
                    records.forEach(function(record) {
                        if(record.name) {
                            var obj = {
                                _id: record.uri,
                                _type: type,
                                title: record.name,
                                subtitle: ''
                            };
                            result.push(obj);
                        }
                    });
                } else {
                    var obj = {
                        _id: records.uri,
                        _type: type,
                        title: records.name,
                        subtitle: ''
                    };
                    result.push(obj);
                }
            }

            return result;
        },

        combineArrays: function(arr1, arr2) {
            arr2.forEach(function(elem) {
                arr1.push(elem);
            });
            return arr1;
        },

        makeJSONArray: function(val1, val2) {
            return [val1, val2];
        }
    };

})(_);

<commit_msg>add extraction phone and email from mentions<commit_after>/**
 * Common transform functions used.
 */

/* globals _ */
/* exported commonTransforms */
/* jshint camelcase:false */

/* note lodash should be defined in parent scope */
var commonTransforms = (function(_) {

    /**
    * Check for geolocation equality
    */
    function isGeolocationEqual(value, other) {
        return value.latitude === other.latitude && value.longitude === other.longitude;
    }

    return {
        /**
        * Changes the key/value names of buckets given from an aggregation
        * to names preferred by the user.
        */
        transformBuckets: function(buckets, keyName, alternateKey) {
            buckets = _.map(buckets, function(bucket) {
                var obj = {
                    count: bucket.doc_count
                };
                if(alternateKey) {
                    obj[keyName] = bucket[alternateKey];
                } else {
                    obj[keyName] = bucket.key;
                }
                return obj;
            });
            return buckets;
        },

        /**
            Get people aggregation info:

            "people": {
                "names": [
                    {"name": "Emily", "count": 14},
                    {"name": "Erin", "count": 12},
                    {"name": "Jane", "count": 3}
                ],
                "ethnicities": [
                    {"ethnicity": "white", "count": 19}
                ],
                "heights": [{"height": 64, "count":5, "unitOfMeasure": "inches"}],
                "weights": [{"weight": 115, "count": 5, "unitOfMeasure": "pounds"}],
                "ages": [{"age": 30, "count": 9}]
            }
        */
        getPeople: function(aggs) {
            var people = {
                names: this.transformBuckets(aggs.people_names.buckets, 'name'),
                ethnicities: this.transformBuckets(aggs.people_ethnicities.buckets, 'ethnicity'),
                ages: this.transformBuckets(aggs.people_ages.buckets, 'age')
            };

            return people;
        },
        /**
            "locations": [
                {
                    "city": "hawthorn",
                    "state": "california",
                    "latitude": 33.916403,
                    "longitude": -118.352575,
                    "date": "2012-04-23T18:25:43.511Z"
                }
            ]
        */
        getLocations: function(hits) {
            var locations = [];
            _.each(hits, function(hit) {
                var location = hit._source.availableAtOrFrom;
                if(location) {
                    var latitude = _.get(location, 'geo.latitude');
                    var longitude = _.get(location, 'geo.longitude');

                    _.each(location.address, function(address) {
                        locations.push({
                            city: address.addressLocality,
                            state: address.addressRegion,
                            latitude: latitude,
                            longitude: longitude,
                            date: hit._source.validFrom
                        });
                    });
                }
            });

            return locations;
        },
        /**
            where latitude and longitude are required:
            "geoCoordinates": [
                {
                    "city": "hawthorn",
                    "state": "california",
                    "latitude": 33.916403,
                    "longitude": -118.352575
                }
            ]
        */
        getGeoCoordinates: function(records) {
            var geos = [];

            records.forEach(function(record) {
                var addresses = _.get(record, '_source.availableAtOrFrom.address', []);
                //var latitude = _.get(record, '_source.availableAtOrFrom.address[0].geo.latitude');
                //var longitude = _.get(record, '_source.availableAtOrFrom.address[0].geo.longitude');
                addresses.forEach(function(address) {
                    if (latitude && longitude) {
                        var geo = {
                            city: address.addressLocality,
                            state: address.addressRegion,
                            latitude: address.geo.latitude,
                            longitude: address.geo.longitude
                        };
                        geos.push(geo);
                    }
                });
                // Removing duplicates for better map display
                geos = _.q(geos, isGeolocationEqual);
            });

            return geos;
        },
        /** build address object:
        "address": {
            "locality": "Los Angeles",
            "region": "California",
            "formattedAddress": 'Los Angeles, California'
        }
        */
        getAddress: function(record) {
            var address = {};
            address.locality = _.get(record, 'availableAtOrFrom.address[0].addressLocality');
            address.region = _.get(record, 'availableAtOrFrom.address[0].addressRegion');

            var formattedAddress = [];
            if(address.locality) {
                formattedAddress.push(address.locality);
            }

            if(address.region) {
                if(formattedAddress.length > 0) {
                    formattedAddress.push(', ');
                }
                formattedAddress.push(address.region);
            }

            address.formattedAddress = formattedAddress.join('');

            if(_.isEmpty(address.formattedAddress)) {
                address.formattedAddress = 'Address N/A';
            }

            return address;
        },
        /** build an array of strings:
            example: ["1112223333", "0123456789"]
        */
        getArrayOfStrings: function(record, pathToArray, pathToString) {
            var arrayToReturn = [];
            var initialArray = _.get(record, pathToArray, []);

            initialArray.forEach(function(element) {
                arrayToReturn.push(_.get(element, pathToString));
            });

            return arrayToReturn;
        },

        getClickableObjectArr: function (records, type) {
            var result = [];
            if(records) {
                if(records.constructor === Array) {
                    records.forEach(function(record) {
                        if(record.name) {
                            var obj = {
                                _id: record.uri,
                                _type: type,
                                title: record.name,
                                subtitle: ''
                            };
                            result.push(obj);
                        }
                    });
                } else {
                    var obj = {
                        _id: records.uri,
                        _type: type,
                        title: records.name,
                        subtitle: ''
                    };
                    result.push(obj);
                }
            }

            return result;
        },

        combineArrays: function(arr1, arr2) {
            arr2.forEach(function(elem) {
                arr1.push(elem);
            });
            return arr1;
        },
        getEmailAndPhoneFromMentions(mentions) {
            var newData = {};
            newData.phones = [];
            newData.emails = [];
            mentions.forEach(function(elem) {
                type = 'none';
                if(elem.indexOf('phone') != -1) {
                    type = 'phone'
                } else if(elem.indexOf('email') != -1) {
                    type = 'email'
                }
                if(type != 'none') {
                    idx = elem.lastIndexOf("/")
                    text = elem.substring(idx+1)
                    var newObj = {
                        _id: elem,
                        _type: type,
                        title:  text,
                        subtitle: ''
                    }
                    if(type == 'phone')
                        newData.phones.push(newObj);
                    else if(type == 'email')
                        newData.emails.push(newObj);
                }
            });
            return newData;
        },
        makeJSONArray: function(val1, val2) {
            return [val1, val2];
        }
    };

})(_);

<|endoftext|>


<commit_before>'use strict';

var chartController = function($scope, $location, searchResults){
  // check if a search has been carried out, else redirect back to search
  console.log('running chartController');

  if (searchResults.getTrials() === null){
    $location.path('/search');
  } else {

    var trials = searchResults.getTrials();
    console.log(trials);
    // set global options for the 2 charts here
    Highcharts.setOptions({
      title: {
        text: 'source: clinicaltrial.gov'
      }
    });

    $scope.statusChartOptions = {
      chart: {
        type: 'bar',
        renderTo: 'statusChart'
      },
      xAxis: {
        categories: []
      },
      yAxis: {
        title: {
          text: 'Studies'
        }
      },
      series: [{
        data: []
      }]
    };
    // populate categories for x-axis
    for (status in trials){
      $scope.statusChartOptions.xAxis.categories.push(status);
      $scope.statusChartOptions.series[0].data.push(trials[status].trialcount);
    }

    $scope.statusChart = new Highcharts.Chart($scope.statusChartOptions);

  }
};

angular.module('clinicaltrialsApp')
  .controller('chartCtrl', ['$scope', '$location','searchResults', chartController]);
<commit_msg>Displays second chart on click and table on click<commit_after>'use strict';

var chartController = function($scope, $location, searchResults){
  // check if a search has been carried out, else redirect back to search
  console.log('running chartController');

  if (searchResults.getTrials() === null){
    $location.path('/search');
  } else {

    var trials = searchResults.getTrials();
    $scope.tableTrials = [];

    // set global options for the 2 charts here
    Highcharts.setOptions({
      title: {
        text: 'source: clinicaltrial.gov'
      }
    });

    $scope.statusChartOptions = {
      chart: {
        type: 'bar',
        renderTo: 'statusChart'
      },
      xAxis: {
        categories: []
      },
      yAxis: {
        title: {
          text: 'Trials'
        }
      },
      series: [{
        data: []
      }]
    };
    // populate categories for x-axis
    for (var status in trials){
      $scope.statusChartOptions.xAxis.categories.push(status);
      $scope.statusChartOptions.series[0].data.push({
        y: trials[status].trialcount,
        events:{
          click: function(){ //invokes with an jquery event but we don't need it
            console.log(this.category); // the category to use
            $scope.buildCategoryChart(this.category);
          }
        }
      });
    }

    $scope.statusChart = new Highcharts.Chart($scope.statusChartOptions);
    $scope.buildCategoryChart = function(status){ // status is a string
      $scope.categoryChartOptions = {
        chart: {
          type: 'bar',
          renderTo: 'categoryChart'
        },
        xAxis: {
          categories: []
        },
        yAxis: {
          title: {
            text: 'Trials'
          }
        },
        series: [{
          data: []
        }]
      };

      for (var category in trials[status].conditions){
        console.log(category);
        $scope.categoryChartOptions.xAxis.categories.push(category);
        $scope.categoryChartOptions.series[0].data.push({
          y: trials[status].conditions[category].length,
          events: {
            click: function(){
              // ng-repeat watches on the original array so we can't remove it
              // empty the array
              while($scope.tableTrials.length > 0){
                $scope.tableTrials.pop();
              }
              for (var i=0; i<trials[status].conditions[this.category].length; i++){
                $scope.tableTrials = $scope.tableTrials.concat(trials[status].conditions[this.category][i]);
              }
              $scope.$apply();
            }
          }
        });
      }
      $scope.categoryChart = new Highcharts.Chart($scope.categoryChartOptions);
    };
  }
};

angular.module('clinicaltrialsApp')
  .controller('chartCtrl', ['$scope', '$location','searchResults', chartController]);
<|endoftext|>