<commit_before>import map from 'lodash/collection/map'
import filter from 'lodash/collection/filter'
import size from 'lodash/collection/size'
import startsWith from 'lodash/string/startsWith'
import present from 'present'
import yaml from 'js-yaml'

import notificationActions from '../flux/notification-actions'
import {status, json, text} from './fetch-helpers'
import db from './db'

import buildDept from '../helpers/build-dept'
import buildDeptNum from '../helpers/build-dept-num'
import splitParagraph from '../helpers/split-paragraph'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'

import debug from 'debug'
let log = debug('gobbldygook:data')
debug.disable('gobbldygook:data')

import uniq from 'lodash/array/uniq'
import sortBy from 'lodash/collection/sortBy'
import flatten from 'lodash/array/flatten'

function prepareCourse(course) {
	course.name = course.name || course.title
	course.dept = course.dept || buildDept(course)
	course.deptnum = course.deptnum || buildDeptNum(course)
	course.offerings = course.offerings || convertTimeStringsToOfferings(course)

	const nameWords = splitParagraph(course.name)
	const notesWords = splitParagraph(course.notes)
	const titleWords = splitParagraph(course.title)
	const descWords = splitParagraph(course.desc)

	course.words = uniq(sortBy([...nameWords, ...notesWords, ...titleWords, ...descWords]), true)
	course.profWords = uniq(sortBy(flatten(map(course.instructors, splitParagraph))), true)

	return course
}

async function storeCourses(item) {
	log('storing courses')
	const start = present()

	let coursesToStore = map(item.data, course => {
		course.sourcePath = item.path
		return prepareCourse(course)
	})

	try {
		await db.store('courses').batch(coursesToStore)
	}
	catch (e) {
		const db = e.target.db.name
		const errorName = e.target.error.name

		if (errorName === 'QuotaExceededError') {
			notificationActions.logError({
				id: 'db-storage-quota-exceeded',
				message: `The database "${db}" has exceeded its storage quota.`,
			})
		}

		console.error(e.target)
		throw e
	}

	log(`Stored ${size(coursesToStore)} courses in ${present() - start}ms.`)
	return item
}

async function storeArea(item) {
	log(item.path, 'called storeArea')

	const id = item.path

	let area = {
		...item.data,
		sourcePath: item.path,
		type: item.data.type.toLowerCase(),
	}

	try {
		await db.store('areas').put(area)
	}
	catch(e) {
		throw e
	}

	return item
}

function storeItem(item) {
	if (item.type === 'courses') {
		return storeCourses(item)
	}
	else if (item.type === 'areas') {
		return storeArea(item)
	}
}

async function cleanPriorData({path, type}) {
	log(`deleting ${type} from ${path}`)

	let oldItems = []

	try {
		oldItems = await db.store(type)
			.index('sourcePath')
			.get(path)
	}
	catch (e) {
		throw e
	}

	oldItems = map(oldItems, item => (
		{[item.clbid || item.sourcePath]: null}
	))

	try {
		await db.store(type).batch(oldItems)
	}
	catch (e) {
		throw e
	}

	localStorage.removeItem(path)
}

async function cacheItemHash(item) {
	log(`${item.path} called cacheItemHash`)
	localStorage.setItem(item.path, item.hash)
	return item
}

async function updateDatabase(type, infoFromServer, infoFileBase, notificationId, count) {
	let {path, hash} = infoFromServer
	let oldHash = localStorage.getItem(path)

	let itemUrl = `/${path}?v=${hash}`

	if (hash === oldHash) {
		log('skipped ' + itemUrl)
		return true
	}

	notificationActions.startProgress(notificationId, `Loading ${type}`, {max: count}, true)

	log(`need to add ${path}`)

	const url = infoFileBase + itemUrl
	let data = undefined
	try {
		data = await fetch(url)
			.then(status)
			.then(text)
	}
	catch (err) {
		console.error('Could not fetch ${url}')
		return false
	}

	try {
		data = JSON.parse(data)
	}
	catch (err) {
		data = yaml.safeLoad(data)
	}

	const item = {
		...infoFromServer,
		path,
		hash,
		data,
		type,
		count: size(data),
	}

	try {
		await cleanPriorData(item)
		await storeItem(item)
		await cacheItemHash(item)
	}
	catch (e) {
		throw e
	}

	log(`added ${item.path} (${item.count} ${item.type})`)
	notificationActions.incrementProgress(notificationId)
}

async function loadDataFiles(infoFile, infoFileBase) {
	log('load data files', infoFile)

	let notificationId = infoFile.type
	let filesToLoad = infoFile.files

	if (infoFile.type === 'courses') {
		// Only get the last four years of data
		const oldestYear = new Date().getFullYear() - 4
		filesToLoad = filter(filesToLoad, file => file.year >= oldestYear)
	}

	// Load them into the database
	await* map(filesToLoad, file =>
		updateDatabase(infoFile.type, file, infoFileBase, notificationId, size(filesToLoad))
			.catch(err => {
				throw err
			}))

	notificationActions.removeNotification(notificationId, 1500)
}

async function loadInfoFile(url, infoFileBase) {
	log('loading ' + url)
	let infoFile
	try {
		infoFile = await fetch(url).then(status).then(json)
	}
	catch (err) {
		if (startsWith(err.message, 'Failed to fetch')) {
			console.error(`loadInfoFile(): Failed to fetch ${url}`)
			return false
		}
		else {
			throw err
		}
	}

	try {
		await loadDataFiles(infoFile, infoFileBase)
	}
	catch (err) {
		throw err
	}
}

export default async function loadData() {
	const cachebuster = Date.now()

	const infoFiles = [
		'./courseData.url',
		'./areaData.url',
	]

	const processedFiles = await* map(infoFiles,
		file => fetch(file)
			.then(status)
			.then(text)
			.then(path => path.trim())
			.catch(err => console.error(err)))

	try {
		await* map(processedFiles, path => loadInfoFile(`${path}/info.json?${cachebuster}`, path))
	}
	catch (err) {
		console.error(err)
		throw err
	}
}
<commit_msg>when logging skipped paths, don't log the query string<commit_after>import map from 'lodash/collection/map'
import filter from 'lodash/collection/filter'
import size from 'lodash/collection/size'
import startsWith from 'lodash/string/startsWith'
import present from 'present'
import yaml from 'js-yaml'

import notificationActions from '../flux/notification-actions'
import {status, json, text} from './fetch-helpers'
import db from './db'

import buildDept from '../helpers/build-dept'
import buildDeptNum from '../helpers/build-dept-num'
import splitParagraph from '../helpers/split-paragraph'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'

import debug from 'debug'
let log = debug('gobbldygook:data')
debug.disable('gobbldygook:data')

import uniq from 'lodash/array/uniq'
import sortBy from 'lodash/collection/sortBy'
import flatten from 'lodash/array/flatten'

function prepareCourse(course) {
	course.name = course.name || course.title
	course.dept = course.dept || buildDept(course)
	course.deptnum = course.deptnum || buildDeptNum(course)
	course.offerings = course.offerings || convertTimeStringsToOfferings(course)

	const nameWords = splitParagraph(course.name)
	const notesWords = splitParagraph(course.notes)
	const titleWords = splitParagraph(course.title)
	const descWords = splitParagraph(course.desc)

	course.words = uniq(sortBy([...nameWords, ...notesWords, ...titleWords, ...descWords]), true)
	course.profWords = uniq(sortBy(flatten(map(course.instructors, splitParagraph))), true)

	return course
}

async function storeCourses(item) {
	log('storing courses')
	const start = present()

	let coursesToStore = map(item.data, course => {
		course.sourcePath = item.path
		return prepareCourse(course)
	})

	try {
		await db.store('courses').batch(coursesToStore)
	}
	catch (e) {
		const db = e.target.db.name
		const errorName = e.target.error.name

		if (errorName === 'QuotaExceededError') {
			notificationActions.logError({
				id: 'db-storage-quota-exceeded',
				message: `The database "${db}" has exceeded its storage quota.`,
			})
		}

		console.error(e.target)
		throw e
	}

	log(`Stored ${size(coursesToStore)} courses in ${present() - start}ms.`)
	return item
}

async function storeArea(item) {
	log(item.path, 'called storeArea')

	const id = item.path

	let area = {
		...item.data,
		sourcePath: item.path,
		type: item.data.type.toLowerCase(),
	}

	try {
		await db.store('areas').put(area)
	}
	catch(e) {
		throw e
	}

	return item
}

function storeItem(item) {
	if (item.type === 'courses') {
		return storeCourses(item)
	}
	else if (item.type === 'areas') {
		return storeArea(item)
	}
}

async function cleanPriorData({path, type}) {
	log(`deleting ${type} from ${path}`)

	let oldItems = []

	try {
		oldItems = await db.store(type)
			.index('sourcePath')
			.get(path)
	}
	catch (e) {
		throw e
	}

	oldItems = map(oldItems, item => (
		{[item.clbid || item.sourcePath]: null}
	))

	try {
		await db.store(type).batch(oldItems)
	}
	catch (e) {
		throw e
	}

	localStorage.removeItem(path)
}

async function cacheItemHash(item) {
	log(`${item.path} called cacheItemHash`)
	localStorage.setItem(item.path, item.hash)
	return item
}

async function updateDatabase(type, infoFromServer, infoFileBase, notificationId, count) {
	let {path, hash} = infoFromServer
	let oldHash = localStorage.getItem(path)

	let itemUrl = `/${path}?v=${hash}`

	if (hash === oldHash) {
		log(`skipped ${path}`)
		return true
	}

	notificationActions.startProgress(notificationId, `Loading ${type}`, {max: count}, true)

	log(`need to add ${path}`)

	const url = infoFileBase + itemUrl
	let data = undefined
	try {
		data = await fetch(url)
			.then(status)
			.then(text)
	}
	catch (err) {
		console.error('Could not fetch ${url}')
		return false
	}

	try {
		data = JSON.parse(data)
	}
	catch (err) {
		data = yaml.safeLoad(data)
	}

	const item = {
		...infoFromServer,
		path,
		hash,
		data,
		type,
		count: size(data),
	}

	try {
		await cleanPriorData(item)
		await storeItem(item)
		await cacheItemHash(item)
	}
	catch (e) {
		throw e
	}

	log(`added ${item.path} (${item.count} ${item.type})`)
	notificationActions.incrementProgress(notificationId)
}

async function loadDataFiles(infoFile, infoFileBase) {
	log('load data files', infoFile)

	let notificationId = infoFile.type
	let filesToLoad = infoFile.files

	if (infoFile.type === 'courses') {
		// Only get the last four years of data
		const oldestYear = new Date().getFullYear() - 4
		filesToLoad = filter(filesToLoad, file => file.year >= oldestYear)
	}

	// Load them into the database
	await* map(filesToLoad, file =>
		updateDatabase(infoFile.type, file, infoFileBase, notificationId, size(filesToLoad))
			.catch(err => {
				throw err
			}))

	notificationActions.removeNotification(notificationId, 1500)
}

async function loadInfoFile(url, infoFileBase) {
	log('loading ' + url)
	let infoFile
	try {
		infoFile = await fetch(url).then(status).then(json)
	}
	catch (err) {
		if (startsWith(err.message, 'Failed to fetch')) {
			console.error(`loadInfoFile(): Failed to fetch ${url}`)
			return false
		}
		else {
			throw err
		}
	}

	try {
		await loadDataFiles(infoFile, infoFileBase)
	}
	catch (err) {
		throw err
	}
}

export default async function loadData() {
	const cachebuster = Date.now()

	const infoFiles = [
		'./courseData.url',
		'./areaData.url',
	]

	const processedFiles = await* map(infoFiles,
		file => fetch(file)
			.then(status)
			.then(text)
			.then(path => path.trim())
			.catch(err => console.error(err)))

	try {
		await* map(processedFiles, path => loadInfoFile(`${path}/info.json?${cachebuster}`, path))
	}
	catch (err) {
		console.error(err)
		throw err
	}
}
<|endoftext|>


<commit_before>/**
 * @jest-environment ./test/helpers/jest-thunderbird-environment.cjs
 */

import { ComposeWindow } from "./../../../addon/ad/compose_window";
import { LoggerStub } from './../../../addon/lib/logger_stub.js';

import { jest } from '@jest/globals'

describe('ComposeWindow', () => {
  var factory = function () {
    return new ComposeWindow({
      ad: {
        window: { id: 'stubbed-window-id' },
        addEventListener: jest.fn()
      },
      logger: LoggerStub,
      logo_url: 'stubbed-logo-url',
      notification_time: 4000
    });
  };
  var eventEmitterFactory = function () {
    return {
      addListener: jest.fn(),
      addEventListener: jest.fn(),
      removeListener: jest.fn(),
      removeEventListener: jest.fn()
    }
  }
  beforeEach(() => {
    browser.compose_ext = {
      onRecipientsChange: eventEmitterFactory(),
      showNotification: jest.fn(),
    };
    browser.windows.onFocusChanged = eventEmitterFactory();
    browser.windows.get = jest.fn().mockResolvedValue({
      tabs: [{ id: 'stubbed-tab-id' }]
    })
  })

  describe('canManageWindow', () => {
    it("detects matching window locations", () => {
      var matching_urls = [
        'chrome://whatever/messengercompose.xul',
        'chrome://whatever/messengercompose.xhtml'
      ]
      matching_urls.forEach((url) => {
        var window_stub = { document: { location: url } };
        expect(ComposeWindow.canManageWindow(window_stub)).toBe(true);
      })
    });

    it('rejects other locations', () => {
      var rejected_urls = [
        'chrome://messenger/content/messenger.xhmtl',
        'chrome://mozapps/content/extensions/aboutaddons.html'
      ];
      rejected_urls.forEach((url) => {
        var window_stub = { document: { location: url } };
        expect(ComposeWindow.canManageWindow(window_stub)).toBe(false);
      })
    });
  });

  describe('setListeners', () => {
    it('sets listeners on compose_ext', () => {
      var compose_window = factory();
      compose_window.setListeners();
      expect(browser.compose_ext.onRecipientsChange.addListener).toHaveBeenCalled()
      expect(browser.windows.onFocusChanged.addListener).toHaveBeenCalled();
    });
  });

  describe('recipients', () => {
    describe('when recipients is a single string', () => {
      it('returns the recipients from that tab', async () => {
        var details = { 'to': 'Joe' }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(['Joe']);
      });
    })

    describe('when recipients is an array of strings', () => {
      it('returns the recipients from that tab', async () => {
        var details = { 'to': ['Joe'] }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(['Joe']);
      });
    })

    describe('when recipients is an array of strings that needs to be normalized', () => {
      it('returns the recipients normalized', async () => {
        var details = { 'to': ['Joe <joe@example.com>', 'Big band! <big.+band@example.com'] }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(
          ['joe@example.com', 'big.+band@example.com']);
      });
    })

    describe('when recipients is a compose recipients object', () => {
      //var composeRecipientsFactory = function()
      it('returns the recipients from that tab', async () => {
        var composeDetails = {
          to: [
            { type: 'contact', id: 'contact-recipient-id' },
            { type: 'mailingList', id: 'mailing-list-recipient-id' }
          ]
        }
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(composeDetails);
        browser.contacts.get = jest.fn().mockResolvedValue({
          properties: { PrimaryEmail: 'contact-email@example.com' }
        })
        browser.mailingLists.get = jest.fn().mockResolvedValue({
          properties: { name: 'mailinglist-email@example.com' }
        });
        var compose_window = factory();

        expect(await compose_window.recipients()).toStrictEqual(
          ["contact-email@example.com", "mailinglist-email@example.com"]);

        expect(browser.compose.getComposeDetails).toHaveBeenCalledWith('stubbed-tab-id');
        expect(browser.contacts.get).toHaveBeenCalledWith('contact-recipient-id');
        expect(browser.mailingLists.get).toHaveBeenCalledWith('mailing-list-recipient-id')
      });
    })
  });

  describe('changeLabel', () => {
    it('forwards to compose_ext', async () => {
      var compose_window = factory();
      await compose_window.changeLabel('message')

      expect(browser.compose_ext.showNotification).toHaveBeenCalledWith(
        'stubbed-tab-id',
        'message',
        {
          logo_url: 'stubbed-logo-url',
          notification_time: 4000
        }
      )
    })
  });
})
<commit_msg>Revert test changes to recover coverage of existing functions<commit_after>/**
 * @jest-environment ./test/helpers/jest-thunderbird-environment.cjs
 */

import { ComposeWindow } from "./../../../addon/ad/compose_window";
import { LoggerStub } from './../../../addon/lib/logger_stub.js';

import { jest } from '@jest/globals'

describe('ComposeWindow', () => {
  var factory = function () {
    return new ComposeWindow({
      ad: {
        window: { id: 'stubbed-window-id' },
        addEventListener: jest.fn()
      },
      logger: LoggerStub,
      logo_url: 'stubbed-logo-url',
      notification_time: 4000
    });
  };
  var eventEmitterFactory = function () {
    return {
      addListener: jest.fn(),
      addEventListener: jest.fn(),
      removeListener: jest.fn(),
      removeEventListener: jest.fn()
    }
  }
  beforeEach(() => {
    browser.compose_ext = {
      onLanguageChange: eventEmitterFactory(),
      onRecipientsChange: eventEmitterFactory(),
      showNotification: jest.fn(),
      setSpellCheckerLanguage: jest.fn(),
      canSpellCheck: jest.fn().mockResolvedValue(true)
    };
    browser.windows.onFocusChanged = eventEmitterFactory();
    browser.windows.get = jest.fn().mockResolvedValue({
      tabs: [{ id: 'stubbed-tab-id' }]
    })
  })

  describe('canManageWindow', () => {
    it("detects matching window locations", () => {
      var matching_urls = [
        'chrome://whatever/messengercompose.xul',
        'chrome://whatever/messengercompose.xhtml'
      ]
      matching_urls.forEach((url) => {
        var window_stub = { document: { location: url } };
        expect(ComposeWindow.canManageWindow(window_stub)).toBe(true);
      })
    });

    it('rejects other locations', () => {
      var rejected_urls = [
        'chrome://messenger/content/messenger.xhmtl',
        'chrome://mozapps/content/extensions/aboutaddons.html'
      ];
      rejected_urls.forEach((url) => {
        var window_stub = { document: { location: url } };
        expect(ComposeWindow.canManageWindow(window_stub)).toBe(false);
      })
    });
  });

  describe('setListeners', () => {
    it('sets listeners on compose_ext', () => {
      var compose_window = factory();
      compose_window.setListeners();
      expect(browser.compose_ext.onLanguageChange.addListener).toHaveBeenCalled()
      expect(browser.compose_ext.onRecipientsChange.addListener).toHaveBeenCalled()
      expect(browser.windows.onFocusChanged.addListener).toHaveBeenCalled();
    });
  });

  describe('getCurrentLang', () => {
    it('returns what compose_ext returns', async () => {
      var compose_window = factory();
      browser.compose_ext.getCurrentLanguage = jest.fn().mockReturnValue('ca')

      expect(await compose_window.getCurrentLang()).toBe('ca')
      expect(browser.compose_ext.getCurrentLanguage).toHaveBeenCalledWith('stubbed-tab-id')
    })
  });

  describe('recipients', () => {
    describe('when recipients is a single string', () => {
      it('returns the recipients from that tab', async () => {
        var details = { 'to': 'Joe' }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(['Joe']);
      });
    })

    describe('when recipients is an array of strings', () => {
      it('returns the recipients from that tab', async () => {
        var details = { 'to': ['Joe'] }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(['Joe']);
      });
    })

    describe('when recipients is an array of strings that needs to be normalized', () => {
      it('returns the recipients normalized', async () => {
        var details = { 'to': ['Joe <joe@example.com>', 'Big band! <big.+band@example.com'] }
        var compose_window = factory();
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(details);

        expect(compose_window.recipients()).resolves.toStrictEqual(
          ['joe@example.com', 'big.+band@example.com']);
      });
    })

    describe('when recipients is a compose recipients object', () => {
      //var composeRecipientsFactory = function()
      it('returns the recipients from that tab', async () => {
        var composeDetails = {
          to: [
            { type: 'contact', id: 'contact-recipient-id' },
            { type: 'mailingList', id: 'mailing-list-recipient-id' }
          ]
        }
        browser.compose.getComposeDetails = jest.fn().mockResolvedValue(composeDetails);
        browser.contacts.get = jest.fn().mockResolvedValue({
          properties: { PrimaryEmail: 'contact-email@example.com' }
        })
        browser.mailingLists.get = jest.fn().mockResolvedValue({
          properties: { name: 'mailinglist-email@example.com' }
        });
        var compose_window = factory();

        expect(await compose_window.recipients()).toStrictEqual(
          ["contact-email@example.com", "mailinglist-email@example.com"]);

        expect(browser.compose.getComposeDetails).toHaveBeenCalledWith('stubbed-tab-id');
        expect(browser.contacts.get).toHaveBeenCalledWith('contact-recipient-id');
        expect(browser.mailingLists.get).toHaveBeenCalledWith('mailing-list-recipient-id')
      });
    })
  });

  describe('changeLabel', () => {
    it('forwards to compose_ext', async () => {
      var compose_window = factory();
      await compose_window.changeLabel('message')

      expect(browser.compose_ext.showNotification).toHaveBeenCalledWith(
        'stubbed-tab-id',
        'message',
        {
          logo_url: 'stubbed-logo-url',
          notification_time: 4000
        }
      )
    })
  });

  describe('changeLanguage', () => {
    it('sets spellchecker language via compose_ext', async () => {
      var compose_window = factory();
      await compose_window.changeLanguage('en')

      expect(browser.compose_ext.setSpellCheckerLanguage).toHaveBeenCalledWith('stubbed-tab-id', 'en')
    })
  })

  describe('canSpellCheck', () => {
    it('returns true when spellchecker is available', async () => {
      var compose_window = factory();
      expect(await compose_window.canSpellCheck()).toBe(true)

      expect(browser.compose_ext.canSpellCheck).toHaveBeenCalledWith('stubbed-tab-id')
    })
  })
})<|endoftext|>


<commit_before>import { bindActionCreators } from 'redux';
import { UPDATE_STATE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';
import { DISCONNECTED } from 'remotedev-app/lib/constants/socketActionTypes';
import { nonReduxDispatch } from 'remotedev-app/lib/utils/monitorActions';
import { showNotification } from 'remotedev-app/lib/actions';
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
import { SET_DEBUGGER_WORKER } from '../actions/debugger';

const unboundActions = {
  showNotification,
  updateState: request => ({
    type: UPDATE_STATE,
    request: request.data ? { ...request.data, id: request.id } : request,
  }),
};
let actions;
let worker;
let store;

const toWorker = ({ message, action, state, toAll }) => {
  if (!worker) return;

  const instances = store.getState().instances;
  const instanceId = getActiveInstance(instances);
  const id = instances.options[instanceId].connectionId;
  worker.postMessage({
    method: 'emitReduxMessage',
    content: {
      type: message,
      action,
      state: nonReduxDispatch(store, message, instanceId, action, state, instances),
      instanceId,
      id,
      toAll,
    },
  });
};

// Receive messages from worker
const messaging = message => {
  const { data } = message;
  if (!data || !data.__IS_REDUX_NATIVE_MESSAGE__) return;

  const { content: request } = data;
  if (request.type === 'ERROR') {
    actions.showNotification(request.payload);
    return;
  }
  actions.updateState(request);
};

const initWorker = wkr => {
  wkr.addEventListener('message', messaging);
  worker = wkr;
};

const removeWorker = () => {
  worker = null;
};

export default ({ dispatch }) => {
  actions = bindActionCreators(unboundActions, dispatch);
  return next => action => {
    if (action.type === SET_DEBUGGER_WORKER) {
      if (action.worker) {
        initWorker(action.worker);
      } else {
        removeWorker(action.worker);
        next({ type: DISCONNECTED });
      }
    }
    if (action.type === LIFTED_ACTION) toWorker(action);
    return next(action);
  };
};
<commit_msg>Fix store.getState of reduxAPI middleware<commit_after>import { bindActionCreators } from 'redux';
import { UPDATE_STATE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';
import { DISCONNECTED } from 'remotedev-app/lib/constants/socketActionTypes';
import { nonReduxDispatch } from 'remotedev-app/lib/utils/monitorActions';
import { showNotification } from 'remotedev-app/lib/actions';
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
import { SET_DEBUGGER_WORKER } from '../actions/debugger';

const unboundActions = {
  showNotification,
  updateState: request => ({
    type: UPDATE_STATE,
    request: request.data ? { ...request.data, id: request.id } : request,
  }),
};
let actions;
let worker;
let store;

const toWorker = ({ message, action, state, toAll }) => {
  if (!worker) return;

  const instances = store.getState().instances;
  const instanceId = getActiveInstance(instances);
  const id = instances.options[instanceId].connectionId;
  worker.postMessage({
    method: 'emitReduxMessage',
    content: {
      type: message,
      action,
      state: nonReduxDispatch(store, message, instanceId, action, state, instances),
      instanceId,
      id,
      toAll,
    },
  });
};

// Receive messages from worker
const messaging = message => {
  const { data } = message;
  if (!data || !data.__IS_REDUX_NATIVE_MESSAGE__) return;

  const { content: request } = data;
  if (request.type === 'ERROR') {
    actions.showNotification(request.payload);
    return;
  }
  actions.updateState(request);
};

const initWorker = wkr => {
  wkr.addEventListener('message', messaging);
  worker = wkr;
};

const removeWorker = () => {
  worker = null;
};

export default inStore => {
  store = inStore;
  actions = bindActionCreators(unboundActions, store.dispatch);
  return next => action => {
    if (action.type === SET_DEBUGGER_WORKER) {
      if (action.worker) {
        initWorker(action.worker);
      } else {
        removeWorker(action.worker);
        next({ type: DISCONNECTED });
      }
    }
    if (action.type === LIFTED_ACTION) toWorker(action);
    return next(action);
  };
};
<|endoftext|>


<commit_before>var transit = (function () {
    return {
        initMap : function () {
            var mapDet = {
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };

            var map = new google.maps.Map(document.getElementById('transitMap'), mapDet);
            return map;
        },

        initMarker : function (coords, mouseoverText, map) {
            mouseoverText = (typeof mouseoverText == 'undefined') ? 'Marker' : mouseoverText;
            var markerPos = new google.maps.LatLng(coords[0], coords[1]);

            var markerIcon = {
                path: google.maps.SymbolPath.CIRCLE,
                fillColor: 'red',
                fillOpacity: 0.8,
                scale: 5
            };

            var mouseoverInfo = new google.maps.InfoWindow({
                content: mouseoverText,
                map: map
            });

            var marker = {
                position: markerPos,
                title: mouseoverText,
                icon: markerIcon
            };

            marker = new google.maps.Marker(marker);

            google.maps.event.addListener(marker, 'mouseover', function() {
            	mouseoverInfo.open(map, this);
            });

            google.maps.event.addListener(marker, 'mouseout', function() {
            	mouseoverInfo.close();
            });

            return marker;
        },

        moveMarkers : function (markerSet, interval, map) {
            interval = (typeof interval == 'undefined') ? 1000 : interval;
            var transition = setInterval(
                    function () {
                        for (var counter = 0; counter < markerSet.length; counter++) {
                            for (var i = 0; i < markerSet.length; i++) {
                                var curPos = new google.maps.LatLng(markerSet[i].coordSet[counter][0],
                                    markerSet[i].coordSet[counter][1]);
                                markerSet[i].marker.position = curPos;
                                markerSet[i].marker.setMap(map);
                            }
                        }
                    },
                    interval);
        },

        constructMarkerSet : function (markers) {
            var markerSet = new Array();
            for (var counter = 0; counter < markers.length; counter++) {
                var markerObj = transit.initMarker(markers[counter].coords[0], markers[counter].title);
                var coordList = markers[counter].coords.slice(1);

                markerSet.push({
                    marker: markerObj,
                    coordSet: coordList
                });
            }
            return markerSet;
        },

        overlayKml : function (kmlUrl, map) {
            var kmlOptions = {
                map: map
            };
            var kmlLayer = new google.maps.KmlLayer(kmlUrl, kmlOptions);
        },

        routeParser : function (xmlUrl) {
            $.ajax({
                url: xmlUrl,
                dataType: 'xml',
                success : function (data) {
                    var xmlNode = $('Document', data);
                    var lines = {};
                    var points = {};

                    xmlNode.find('Placemark > LineString > coordinates').each(function () {
                        var simPts = transit.trim($(this).text()).split(' ');
                        var grpPts = new Array();
                        var routeName = $(this).closest('Placemark').find('name').text().toLowerCase();
                        for (eachPt in simPts) {
                            var xy = transit.strip(simPts[eachPt]).split(',');
                            grpPts.push({ x: xy[0], y: xy[1] });
                        }
                        lines[routeName] = grpPts;
                    });

                    xmlNode.find('Point').each(function () {
                        var parentTag = $(this).closest('Placemark');

                        var xy = transit.strip(parentTag.find('Point').text()).split(',');
                        points[parentTag.find('name').text().toLowerCase()] = { x: xy[0], y: xy[1] };
                    });

                    return {
                        "lines": lines,
                        "points": points
                    };
                },
                error : function (data) {
                    console.log('Error');
                }
            });
        },

        vehicleParser : function (jsonUrl) {
            $.getJSON(jsonUrl).success(function (data) {
                return {
                    "timezone": data.timezone,
                    "vehicles": data.vehicles
                };
            });
        },

        schedule : function (vehicleObj, routes, timezone) {
            var vehicleArrivals = {};
            var vehicleDepartures = {};
            var vehicleTravelTimes = [];
            var vehicleRoute = vehicleObj.route;
            var stopsObj = vehicleObj.stops;
            var noOfStops = vehicleObj.stops.length;
            var firstStop = stopsObj[0];
            var firstStopName = firstStop.name.toLowerCase();
            var startTime = transit.parseTime(firstStop.departure, firstStop.day, timezone);
            var vehicleStopCoords = {};
            var points = routes.points;

            vehicleDepartures[startTime] = firstStopName;
            vehicleTravelTimes.push(startTime);
            vehicleStopCoords[firstStopName] = points[firstStopName];

            for (var eachStop = 1; eachStop < noOfStops - 1; eachStop++) {
                var temp = stopsObj[eachStop];
                var tempName = temp.name.toLowerCase();
                var currArrivalTime = transit.parseTime(temp.arrival, temp.day, timezone) - startTime;
                var currDepartureTime = transit.parseTime(temp.departure, temp.day, timezone) - startTime;
                vehicleArrivals[currArrivalTime] = tempName;
                vehicleDepartures[currDepartureTime] = tempName;
                vehicleTravelTimes.push(currArrivalTime, currDepartureTime);
                vehicleStopCoords[tempName] = points[tempName];
            }

            var lastStop = stopsObj[noOfStops];
            var lastStopName = lastStop.name.toLowerCase();
            var endTime = transit.parseTime(lastStop.arrival, lastStop.day, timezone) - startTime;
            vehicleArrivals[endTime] = lastStopName;
            vehicleTravelTimes.push(endTime);
            vehicleStopCoords[lastStopName] = points[lastStopName];
            var line = transit.pointsBetweenStops(routes.lines[vehicleRoute], points[firstStopName],
                                                  points[lastStopName]);
            var percentStopDists = transit.hashOfPercentDists(line);

            return {
                "name": vehicleObj.name,
                "info": vehicleObj.info,
                "route": line,
                "stops": vehicleStopCoords,
                "ppoints": percentStopDists,
                "days": lastStop.day - firstStop.day,
                "arrivals": vehicleArrivals,
                "departures": vehicleDepartures,
                "traveltimes": vehicleTravelTimes
            };
        },

        strip : function (string) {
            return string.replace(/\s+/g, '').replace(/\n/g, '');
        },

        trim : function (string) {
            return string.replace(/^\s+|\s+$/g, '');
        },

        // Reimplemented from http://www.movable-type.co.uk/scripts/latlong.html
        haversine : function (sourceCoords, targetCoords) {
            var R = 6371; // km

            /** Converts numeric degrees to radians */
            if (typeof(Number.prototype.toRad) === "undefined") {
              Number.prototype.toRad = function() {
                return this * Math.PI / 180;
              }
            }

            var dLat = (targetCoords.x - sourceCoords.x).toRad();
            var dLon = (targetCoords.y - sourceCoords.y).toRad();
            var lat1 = sourceCoords.x.toRad();
            var lat2 = targetCoords.x.toRad();

            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            var d = R * c;
            return d;
        },

        linearDist : function (sourceCoords, targetCoords) {
            return Math.sqrt(Math.pow((targetCoords.x - sourceCoords.x), 2),
                             Math.pow((targetCoords.y - sourceCoords.y), 2));
        },

        percentDist : function (sourceCoords, targetCoords, percentage) {
            var newCoords = {};
            var ratio = percentage / 100;
            newCoords.x = (1 - ratio) * sourceCoords.x + ratio * targetCoords.x;
            newCoords.y = (1 - ratio) * sourceCoords.y + ratio * targetCoords.y;
            return newCoords;
        },

        percentInRange : function (lower, upper, target) {
            return ((target - lower) / (upper - lower)) * 100;
        },

        pointsBetweenStops: function (route, start, end) {
            return route.slice(route.indexOf(start), route.indexOf(end) + 1);
        },

        hashOfPercentDists : function (points) {
            var start = points[0];
            var routeLength = points.length;
            var end = points[routeLength - 1];
            var distanceHash = {
                0 : points[0]
            };
            var distances = new Array();
            var totalDist = 0;

            for (var x = 0; x < routeLength - 1; x++) {
                totalDist += transit.haversine(points[x], points[x + 1]);
                distances.push(totalDist);
            }

            for (var x = 0; x < distances.length; x++) {
                distanceHash[transit.percentInRange(0, totalDist, distances[x])] = points[x + 1];
            }

            return distanceHash;
        },

        // Variant of binary search that returns enclosing elements of searchElement in array.
        enclosure : function (searchElement) {
            'use strict';

            var minIndex = 0;
            var maxIndex = this.length - 1;
            var currentIndex;
            var currentElement;

            while (minIndex <= maxIndex) {
                currentIndex = (minIndex + maxIndex) / 2 | 0;
                currentElement = this[currentIndex];

                if (currentElement < searchElement) {
                    minIndex = currentIndex + 1;
                }
                else if (currentElement > searchElement) {
                    maxIndex = currentIndex - 1;
                }
                else {
                    return currentIndex;
                }
            }

            if (searchElement > this[currentIndex]) {
                if (typeof this[currentIndex + 1] != 'undefined')
                    return [currentIndex, currentIndex + 1];
                else
                    return currentIndex;
            } else {
                if (typeof this[currentIndex - 1] !=  'undefined')
                    return [currentIndex - 1, currentIndex];
                else
                    return 0;
            }
        },

        parseTime : function (timeString, day, timezone) {
            var hms = timeString.split(':');
            hms = hms.map(function (x) { return parseInt(x, 10); });

            if (hms.length < 3)
                hms[2] = 0;

            return hms[0] * 3600 + hms[1] * 60 + hms[2] + (day - 1) * 86400 + timezone * 60;
        },

        currTime : function () {
            var t = new Date();
            return t.getHours().toString() + ":" +
                   t.getMinutes().toString() + ":" +
                   t.getSeconds().toString();
        },

        estimateCurrentPosition : function (vehicleObj, timezone) {
            var arrivals = vehicleObj.arrivals;
            var departures = vehicleObj.departures;
            var travelTimes = vehicleObj.traveltimes;
            var noOfDays = vehicleObj.days;
            var ppoints = vehicleObj.ppoints;

            var route = vehicleObj.route;
            var stops = vehicleObj.stops;
            var percentDists = transit.hashOfPercentDists(stops);
            var positions = new Array();

            var currPos = {
                "stationaryAt": "",
                "departureTime": 0,
                "leaving": "",
                "approaching": "",
                "leftTime": 0,
                "approachTime": 0,
                "currentCoords": null
            };

            var time = transit.currTime();

            for (var i = 1; i <= noOfDays + 1; i++) {
                var range = transit.enclosure.call(travelTimes, transit.parseTime(time, i, timezone));

                if (range[0] % 2 == 0 && range.length == 2) {
                    currPos.leftTime = travelTimes[range[0]];
                    currPos.approachTime = travelTimes[range[1]];
                    var leavingStop = departures[currPos.leftTime];
                    var approachingStop = arrivals[currPos.approachTime];
                    currPos.leaving = leavingStop;
                    currPos.approaching = approachingStop;

                    var timePercent = transit.percentInRange(travelTimes[range[0]], travelTimes[range[1]],
                                                             transit.parseTime(currTime, i, timezone));
                    var timeRange = transit.enclosure.call(ppoints, timePercent);
                    var percent = transit.percentInRange(ppoints[timeRange[0]],
                                                         ppoints[timeRange[1]], timePercentArray[i]);
                    currPos.currentCoords = transit.percentDist(ppoints[timeRange[0]],
                                                                ppoints[timeRange[1]], percent);
                } else {
                    if (typeof range[1] != 'undefined' && range[0] != 0) {
                        var currentStop = travelTimes[range[1]];
                        currPos.stationaryAt = departures[currentStop];
                        currPos.departureTime = currentStop;
                        currPos.currentCoords = stops[currentStop];
                    }
                }

                positions.push(currPos);
            }

            return positions;
        },

        mouseOverInfo : function (name, info, stationary, departTime, l, a, lT, aT) {
            var base = "Vehicle: " + name + "\n" + info + "\n"
            if (stationary) {
                return base + "\n" + "At: " + stationary + "\n" + "Departs: " + departTime;
            } else {
                return base +
                       "Leaving: " + l + "\n" + " (" + lT + ")" +
                       "Approaching: " + a + " (" + aT + ")";
            }
        },

        main : function (localKmlFile, remoteKmlFile, jsonFile) {
            var map = transit.initMap();
            transit.overlayKml(remoteKmlFile, map);
            var routeObj = transit.routeParser(localKmlFile);
            var routeLines = routeObj.lines;
            var routePoints = routeObj.points;
            var vehicleObj = transit.vehicleParser(jsonFile);
            var timezone = vehicleObj.timezone;
            var vehicles = vehicleObj.vehicles;
            var noOfVehicles = vehicleList.length;

            for (var count = 0; count < noOfVehicles; count++) {
                vehicles[count] = transit.schedule(vehicles[count], routeObj, timezone);
            }

            for (var count = 0; count < noOfVehicles; count++) {
                var vehicle = vehicles[count];

                if (typeof vehicle.markers == 'undefined') {
                    var vehicle.markers = new Array();
                } else {
                    for (var i = 0; i < vehicle.markers.length; i++) {
                        vehicle.markers[i].setMap(null);
                    }
                }

                var currPositions = transit.estimateCurrentPosition(vehicle, timezone);

                for (var i = 0; i < currPositions.length; i++) {
                    var currPosition = currPositions[0];

                    if (!currPosition.currentCoords) continue;

                    var mouseOverInfo = transit.mouseOverInfo(vehicle.name, vehicle.info,
                                                              currPosition.stationaryAt,
                                                              currPosition.departureTime,
                                                              currPosition.leaving,
                                                              currPosition.approaching,
                                                              currPosition.leftTime,
                                                              currPosition.approachTime);
                    var currMarker = transit.initMarker(currCoords[i], mouseOverInfo, map);
                    currMarker.setMap(map);
                    vehicle.markers[i] = currMarker;
                }
            }
        },

        initialize : function (localKmlFile, remoteKmlFile, jsonFile) {
            google.maps.event.addDomListener(window, 'load',
                    function () {
                        transit.main(localKmlFile, remoteKmlFile, jsonFile);
                    });
        }
    };
})();
<commit_msg>Fixing improper return<commit_after>var transit = (function () {
    return {
        initMap : function () {
            var mapDet = {
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };

            var map = new google.maps.Map(document.getElementById('transitMap'), mapDet);
            return map;
        },

        initMarker : function (coords, mouseoverText, map) {
            mouseoverText = (typeof mouseoverText == 'undefined') ? 'Marker' : mouseoverText;
            var markerPos = new google.maps.LatLng(coords[0], coords[1]);

            var markerIcon = {
                path: google.maps.SymbolPath.CIRCLE,
                fillColor: 'red',
                fillOpacity: 0.8,
                scale: 5
            };

            var mouseoverInfo = new google.maps.InfoWindow({
                content: mouseoverText,
                map: map
            });

            var marker = {
                position: markerPos,
                title: mouseoverText,
                icon: markerIcon
            };

            marker = new google.maps.Marker(marker);

            google.maps.event.addListener(marker, 'mouseover', function() {
            	mouseoverInfo.open(map, this);
            });

            google.maps.event.addListener(marker, 'mouseout', function() {
            	mouseoverInfo.close();
            });

            return marker;
        },

        moveMarkers : function (markerSet, interval, map) {
            interval = (typeof interval == 'undefined') ? 1000 : interval;
            var transition = setInterval(
                    function () {
                        for (var counter = 0; counter < markerSet.length; counter++) {
                            for (var i = 0; i < markerSet.length; i++) {
                                var curPos = new google.maps.LatLng(markerSet[i].coordSet[counter][0],
                                    markerSet[i].coordSet[counter][1]);
                                markerSet[i].marker.position = curPos;
                                markerSet[i].marker.setMap(map);
                            }
                        }
                    },
                    interval);
        },

        constructMarkerSet : function (markers) {
            var markerSet = new Array();
            for (var counter = 0; counter < markers.length; counter++) {
                var markerObj = transit.initMarker(markers[counter].coords[0], markers[counter].title);
                var coordList = markers[counter].coords.slice(1);

                markerSet.push({
                    marker: markerObj,
                    coordSet: coordList
                });
            }
            return markerSet;
        },

        overlayKml : function (kmlUrl, map) {
            var kmlOptions = {
                map: map
            };
            var kmlLayer = new google.maps.KmlLayer(kmlUrl, kmlOptions);
        },

        routeParser : function (xmlUrl) {
            var lines = {};
            var points = {};

            $.ajax({
                url: xmlUrl,
                dataType: 'xml',
                success : function (data) {
                    var xmlNode = $('Document', data);

                    xmlNode.find('Placemark > LineString > coordinates').each(function () {
                        var simPts = transit.trim($(this).text()).split(' ');
                        var grpPts = new Array();
                        var routeName = $(this).closest('Placemark').find('name').text().toLowerCase();
                        for (eachPt in simPts) {
                            var xy = transit.strip(simPts[eachPt]).split(',');
                            grpPts.push({ x: xy[0], y: xy[1] });
                        }
                        lines[routeName] = grpPts;
                    });

                    xmlNode.find('Point').each(function () {
                        var parentTag = $(this).closest('Placemark');

                        var xy = transit.strip(parentTag.find('Point').text()).split(',');
                        points[parentTag.find('name').text().toLowerCase()] = { x: xy[0], y: xy[1] };
                    });
                },
                error : function (data) {
                    console.log('Error');
                }
            });

            return {
                "lines": lines,
                "points": points
            };
        },

        vehicleParser : function (jsonUrl) {
            $.getJSON(jsonUrl).success(function (data) {
                return {
                    "timezone": data.timezone,
                    "vehicles": data.vehicles
                };
            });
        },

        schedule : function (vehicleObj, routes, timezone) {
            var vehicleArrivals = {};
            var vehicleDepartures = {};
            var vehicleTravelTimes = [];
            var vehicleRoute = vehicleObj.route;
            var stopsObj = vehicleObj.stops;
            var noOfStops = vehicleObj.stops.length;
            var firstStop = stopsObj[0];
            var firstStopName = firstStop.name.toLowerCase();
            var startTime = transit.parseTime(firstStop.departure, firstStop.day, timezone);
            var vehicleStopCoords = {};
            var points = routes.points;

            vehicleDepartures[startTime] = firstStopName;
            vehicleTravelTimes.push(startTime);
            vehicleStopCoords[firstStopName] = points[firstStopName];

            for (var eachStop = 1; eachStop < noOfStops - 1; eachStop++) {
                var temp = stopsObj[eachStop];
                var tempName = temp.name.toLowerCase();
                var currArrivalTime = transit.parseTime(temp.arrival, temp.day, timezone) - startTime;
                var currDepartureTime = transit.parseTime(temp.departure, temp.day, timezone) - startTime;
                vehicleArrivals[currArrivalTime] = tempName;
                vehicleDepartures[currDepartureTime] = tempName;
                vehicleTravelTimes.push(currArrivalTime, currDepartureTime);
                vehicleStopCoords[tempName] = points[tempName];
            }

            var lastStop = stopsObj[noOfStops];
            var lastStopName = lastStop.name.toLowerCase();
            var endTime = transit.parseTime(lastStop.arrival, lastStop.day, timezone) - startTime;
            vehicleArrivals[endTime] = lastStopName;
            vehicleTravelTimes.push(endTime);
            vehicleStopCoords[lastStopName] = points[lastStopName];
            var line = transit.pointsBetweenStops(routes.lines[vehicleRoute], points[firstStopName],
                                                  points[lastStopName]);
            var percentStopDists = transit.hashOfPercentDists(line);

            return {
                "name": vehicleObj.name,
                "info": vehicleObj.info,
                "route": line,
                "stops": vehicleStopCoords,
                "ppoints": percentStopDists,
                "days": lastStop.day - firstStop.day,
                "arrivals": vehicleArrivals,
                "departures": vehicleDepartures,
                "traveltimes": vehicleTravelTimes
            };
        },

        strip : function (string) {
            return string.replace(/\s+/g, '').replace(/\n/g, '');
        },

        trim : function (string) {
            return string.replace(/^\s+|\s+$/g, '');
        },

        // Reimplemented from http://www.movable-type.co.uk/scripts/latlong.html
        haversine : function (sourceCoords, targetCoords) {
            var R = 6371; // km

            /** Converts numeric degrees to radians */
            if (typeof(Number.prototype.toRad) === "undefined") {
              Number.prototype.toRad = function() {
                return this * Math.PI / 180;
              }
            }

            var dLat = (targetCoords.x - sourceCoords.x).toRad();
            var dLon = (targetCoords.y - sourceCoords.y).toRad();
            var lat1 = sourceCoords.x.toRad();
            var lat2 = targetCoords.x.toRad();

            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            var d = R * c;
            return d;
        },

        linearDist : function (sourceCoords, targetCoords) {
            return Math.sqrt(Math.pow((targetCoords.x - sourceCoords.x), 2),
                             Math.pow((targetCoords.y - sourceCoords.y), 2));
        },

        percentDist : function (sourceCoords, targetCoords, percentage) {
            var newCoords = {};
            var ratio = percentage / 100;
            newCoords.x = (1 - ratio) * sourceCoords.x + ratio * targetCoords.x;
            newCoords.y = (1 - ratio) * sourceCoords.y + ratio * targetCoords.y;
            return newCoords;
        },

        percentInRange : function (lower, upper, target) {
            return ((target - lower) / (upper - lower)) * 100;
        },

        pointsBetweenStops: function (route, start, end) {
            return route.slice(route.indexOf(start), route.indexOf(end) + 1);
        },

        hashOfPercentDists : function (points) {
            var start = points[0];
            var routeLength = points.length;
            var end = points[routeLength - 1];
            var distanceHash = {
                0 : points[0]
            };
            var distances = new Array();
            var totalDist = 0;

            for (var x = 0; x < routeLength - 1; x++) {
                totalDist += transit.haversine(points[x], points[x + 1]);
                distances.push(totalDist);
            }

            for (var x = 0; x < distances.length; x++) {
                distanceHash[transit.percentInRange(0, totalDist, distances[x])] = points[x + 1];
            }

            return distanceHash;
        },

        // Variant of binary search that returns enclosing elements of searchElement in array.
        enclosure : function (searchElement) {
            'use strict';

            var minIndex = 0;
            var maxIndex = this.length - 1;
            var currentIndex;
            var currentElement;

            while (minIndex <= maxIndex) {
                currentIndex = (minIndex + maxIndex) / 2 | 0;
                currentElement = this[currentIndex];

                if (currentElement < searchElement) {
                    minIndex = currentIndex + 1;
                }
                else if (currentElement > searchElement) {
                    maxIndex = currentIndex - 1;
                }
                else {
                    return currentIndex;
                }
            }

            if (searchElement > this[currentIndex]) {
                if (typeof this[currentIndex + 1] != 'undefined')
                    return [currentIndex, currentIndex + 1];
                else
                    return currentIndex;
            } else {
                if (typeof this[currentIndex - 1] !=  'undefined')
                    return [currentIndex - 1, currentIndex];
                else
                    return 0;
            }
        },

        parseTime : function (timeString, day, timezone) {
            var hms = timeString.split(':');
            hms = hms.map(function (x) { return parseInt(x, 10); });

            if (hms.length < 3)
                hms[2] = 0;

            return hms[0] * 3600 + hms[1] * 60 + hms[2] + (day - 1) * 86400 + timezone * 60;
        },

        currTime : function () {
            var t = new Date();
            return t.getHours().toString() + ":" +
                   t.getMinutes().toString() + ":" +
                   t.getSeconds().toString();
        },

        estimateCurrentPosition : function (vehicleObj, timezone) {
            var arrivals = vehicleObj.arrivals;
            var departures = vehicleObj.departures;
            var travelTimes = vehicleObj.traveltimes;
            var noOfDays = vehicleObj.days;
            var ppoints = vehicleObj.ppoints;

            var route = vehicleObj.route;
            var stops = vehicleObj.stops;
            var percentDists = transit.hashOfPercentDists(stops);
            var positions = new Array();

            var currPos = {
                "stationaryAt": "",
                "departureTime": 0,
                "leaving": "",
                "approaching": "",
                "leftTime": 0,
                "approachTime": 0,
                "currentCoords": null
            };

            var time = transit.currTime();

            for (var i = 1; i <= noOfDays + 1; i++) {
                var range = transit.enclosure.call(travelTimes, transit.parseTime(time, i, timezone));

                if (range[0] % 2 == 0 && range.length == 2) {
                    currPos.leftTime = travelTimes[range[0]];
                    currPos.approachTime = travelTimes[range[1]];
                    var leavingStop = departures[currPos.leftTime];
                    var approachingStop = arrivals[currPos.approachTime];
                    currPos.leaving = leavingStop;
                    currPos.approaching = approachingStop;

                    var timePercent = transit.percentInRange(travelTimes[range[0]], travelTimes[range[1]],
                                                             transit.parseTime(currTime, i, timezone));
                    var timeRange = transit.enclosure.call(ppoints, timePercent);
                    var percent = transit.percentInRange(ppoints[timeRange[0]],
                                                         ppoints[timeRange[1]], timePercentArray[i]);
                    currPos.currentCoords = transit.percentDist(ppoints[timeRange[0]],
                                                                ppoints[timeRange[1]], percent);
                } else {
                    if (typeof range[1] != 'undefined' && range[0] != 0) {
                        var currentStop = travelTimes[range[1]];
                        currPos.stationaryAt = departures[currentStop];
                        currPos.departureTime = currentStop;
                        currPos.currentCoords = stops[currentStop];
                    }
                }

                positions.push(currPos);
            }

            return positions;
        },

        mouseOverInfo : function (name, info, stationary, departTime, l, a, lT, aT) {
            var base = "Vehicle: " + name + "\n" + info + "\n"
            if (stationary) {
                return base + "\n" + "At: " + stationary + "\n" + "Departs: " + departTime;
            } else {
                return base +
                       "Leaving: " + l + "\n" + " (" + lT + ")" +
                       "Approaching: " + a + " (" + aT + ")";
            }
        },

        main : function (localKmlFile, remoteKmlFile, jsonFile) {
            var map = transit.initMap();
            transit.overlayKml(remoteKmlFile, map);
            var routeObj = transit.routeParser(localKmlFile);
            var routeLines = routeObj.lines;
            var routePoints = routeObj.points;
            var vehicleObj = transit.vehicleParser(jsonFile);
            var timezone = vehicleObj.timezone;
            var vehicles = vehicleObj.vehicles;
            var noOfVehicles = vehicleList.length;

            for (var count = 0; count < noOfVehicles; count++) {
                vehicles[count] = transit.schedule(vehicles[count], routeObj, timezone);
            }

            for (var count = 0; count < noOfVehicles; count++) {
                var vehicle = vehicles[count];

                if (typeof vehicle.markers == "undefined") {
                    vehicle.markers = new Array();
                } else {
                    for (var i = 0; i < vehicle.markers.length; i++) {
                        vehicle.markers[i].setMap(null);
                    }
                }

                var currPositions = transit.estimateCurrentPosition(vehicle, timezone);

                for (var i = 0; i < currPositions.length; i++) {
                    var currPosition = currPositions[0];

                    if (!currPosition.currentCoords) continue;

                    var mouseOverInfo = transit.mouseOverInfo(vehicle.name, vehicle.info,
                                                              currPosition.stationaryAt,
                                                              currPosition.departureTime,
                                                              currPosition.leaving,
                                                              currPosition.approaching,
                                                              currPosition.leftTime,
                                                              currPosition.approachTime);
                    var currMarker = transit.initMarker(currCoords[i], mouseOverInfo, map);
                    currMarker.setMap(map);
                    vehicle.markers[i] = currMarker;
                }
            }
        },

        initialize : function (localKmlFile, remoteKmlFile, jsonFile) {
            google.maps.event.addDomListener(window, 'load',
                    function () {
                        transit.main(localKmlFile, remoteKmlFile, jsonFile);
                    });
        }
    };
})();
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

module.exports.finderForProperty = function(field, props) {
    if (props === undefined) {
        props = {};
    }

    var isFindOne = props['findOne'] === true;
    var caseInsensitive = props['caseInsensitive'] === true;

    return function(value, cb) {
        var lookupHash = {};
        lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

        if(isFindOne) {
            this.findOne(lookupHash, cb);
        } else {
            this.find(lookupHash, cb);
        }
    };
};

module.exports.pushOntoProperty = function(field, props) {
  if (props === undefined) {
    props = {};
  }

  var isFindOne = props['findOne'] === true;
  var caseInsensitive = props['caseInsensitive'] === true;

  return function(value, cb) {
    var lookupHash = {};
    lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

    if (isFindOne) {
      //
    } else {
      //
    }
  }
}
<commit_msg>Revert "SYNTAX ERROR SORRY EVERYONE"<commit_after>var mongoose = require('mongoose');

module.exports.finderForProperty = function(field, props) {
    if (props === undefined) {
        props = {};
    }

    var isFindOne = props['findOne'] === true;
    var caseInsensitive = props['caseInsensitive'] === true;

    return function(value, cb) {
        var lookupHash = {};
        lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

        if(isFindOne) {
            this.findOne(lookupHash, cb);
        } else {
            this.find(lookupHash, cb);
        }
    };
};

module.exports.pushOntoProperty = function(field, props) {
  if (props === undefined) {
    props = {};
  }

  var isFindOne = props['findOne'] === true;
  var caseInsensitive = props['caseInsensitive'] === true;

  return function(value, cb) {
    var lookupHash = {};
    lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

    if (isFindOne) {
      this.
    } else {
      this.
    }
  }
}
<|endoftext|>


<commit_before>"use strict"

const remote = require('remote')
const shell = remote.require('shell')
const MMailbox = require('./models/MMailbox')
const mailboxActions = require('./actions/mailbox')

class Mailboxes {
	/***************************************************************************/
	// Lifecycle
	/***************************************************************************/
	constructor(app) {
		this.app = app
	}

	/***************************************************************************/
	// Active state
	/***************************************************************************/

	/**
	* Updates the active mailbox
	* @param mailboxId: the id of the mailbox to make active
	*/
	updateActive(mailboxId) {
		$('#mailboxes .mailbox.active:not([data-id="' + mailboxId + '"])').removeClass('active')
		$('#mailboxes .mailbox[data-id="' + mailboxId + '"]').addClass('active')
	}

	/***************************************************************************/
	// Rendering
	/***************************************************************************/

	/**
	* Renders a webview
	* @param mailbox: the mailbox to render
	* @return the element
	*/
	renderWebview(mailbox) {
		// Generate source
		var src
		switch(mailbox.type) {
			case 'ginbox': src='https://inbox.google.com'; break;
			case 'gmail': src='https://mail.google.com'; break;
		}

		// Create dom
		let webview = $([
			'<webview',
				'class="mailbox"',
				'data-id="' + mailbox.id + '"',
				'src="' + src + '"',
				'partition="persist:' + mailbox.id + '"',
				'preload="./js/sniff/clicklisten"',
				'></webview>'
			].join(' ')).get(0)

		// Bind events
		webview.addEventListener('dom-ready', () => {
			webview.insertCSS('.gb_9a { visibility: hidden !important; }')
		})
		webview.addEventListener('ipc-message', (event) => {
			if (event.channel.type === 'page-click') {
				this.app.procResyncMailboxUnread(mailbox.id)
			}
		});
		webview.addEventListener('new-window', (event) => {
			shell.openExternal(event.url)
		})

		return webview
	}

	/**
	* Renders the welcome view
	*/
	renderWelcome() {
		const welcome = $([
			'
',
				'
Add your first mailbox
',
				'
',
					'To get started you need to add a mailbox. You can add your Gmail or Google Inbox account.',
					'
',
					'To add more mailboxes later on just tap the plus icon in the toolbar on the left',
				'
',
				'
',
				'
',
				'<button type="button" class="btn inbox">Add Inbox Mailbox</button>',
				'<button type="button" class="btn gmail">Add Gmail Mailbox</button>',
			'
'
		].join('\n'))
		welcome.find('.btn.inbox').on('click', (evt) => {
			evt.preventDefault();
			mailboxActions.addInboxMailbox(this.app)
		})
		welcome.find('.btn.gmail').on('click', (evt) => {
			evt.preventDefault();
			mailboxActions.addGmailMailbox(this.app)
		})
		return welcome
	}

	/**
	* Render the webviews based on the database
	*/
	render() {
		let container = $('#mailboxes')
		let mailboxIds = new Set(MMailbox.ids())

		// Delete un-needed webviews
		container.find('.mailbox').each((_index, elem) => {
			if (!mailboxIds.has($(elem).attr('data-id'))) {
				$(elem).remove()
			}
		})

		// Add new webviews
		MMailbox.all().forEach((mailbox, index) => {
			if (container.find('.mailbox[data-id="' + mailbox.id + '"]').length === 0) {
				container.append(this.renderWebview(mailbox))
			}
		})

		if (mailboxIds.size === 0) {
			if (container.find('.welcome').length === 0) {
				container.append(this.renderWelcome())
			}
		} else {
			container.find('.welcome').remove();
		}
	}
}

module.exports = Mailboxes<commit_msg>Update gmail url to not redirect to inbox. #7<commit_after>"use strict"

const remote = require('remote')
const shell = remote.require('shell')
const MMailbox = require('./models/MMailbox')
const mailboxActions = require('./actions/mailbox')

class Mailboxes {
	/***************************************************************************/
	// Lifecycle
	/***************************************************************************/
	constructor(app) {
		this.app = app
	}

	/***************************************************************************/
	// Active state
	/***************************************************************************/

	/**
	* Updates the active mailbox
	* @param mailboxId: the id of the mailbox to make active
	*/
	updateActive(mailboxId) {
		$('#mailboxes .mailbox.active:not([data-id="' + mailboxId + '"])').removeClass('active')
		$('#mailboxes .mailbox[data-id="' + mailboxId + '"]').addClass('active')
	}

	/***************************************************************************/
	// Rendering
	/***************************************************************************/

	/**
	* Renders a webview
	* @param mailbox: the mailbox to render
	* @return the element
	*/
	renderWebview(mailbox) {
		// Generate source
		var src
		switch(mailbox.type) {
			case 'ginbox': src='https://inbox.google.com'; break;
			case 'gmail': src='https://mail.google.com?ibxr=0'; break;
		}

		// Create dom
		let webview = $([
			'<webview',
				'class="mailbox"',
				'data-id="' + mailbox.id + '"',
				'src="' + src + '"',
				'partition="persist:' + mailbox.id + '"',
				'preload="./js/sniff/clicklisten"',
				'></webview>'
			].join(' ')).get(0)

		// Bind events
		webview.addEventListener('dom-ready', () => {
			webview.insertCSS('.gb_9a { visibility: hidden !important; }')
		})
		webview.addEventListener('ipc-message', (event) => {
			if (event.channel.type === 'page-click') {
				this.app.procResyncMailboxUnread(mailbox.id)
			}
		});
		webview.addEventListener('new-window', (event) => {
			shell.openExternal(event.url)
		})

		return webview
	}

	/**
	* Renders the welcome view
	*/
	renderWelcome() {
		const welcome = $([
			'
',
				'
Add your first mailbox
',
				'
',
					'To get started you need to add a mailbox. You can add your Gmail or Google Inbox account.',
					'
',
					'To add more mailboxes later on just tap the plus icon in the toolbar on the left',
				'
',
				'
',
				'
',
				'<button type="button" class="btn inbox">Add Inbox Mailbox</button>',
				'<button type="button" class="btn gmail">Add Gmail Mailbox</button>',
			'
'
		].join('\n'))
		welcome.find('.btn.inbox').on('click', (evt) => {
			evt.preventDefault();
			mailboxActions.addInboxMailbox(this.app)
		})
		welcome.find('.btn.gmail').on('click', (evt) => {
			evt.preventDefault();
			mailboxActions.addGmailMailbox(this.app)
		})
		return welcome
	}

	/**
	* Render the webviews based on the database
	*/
	render() {
		let container = $('#mailboxes')
		let mailboxIds = new Set(MMailbox.ids())

		// Delete un-needed webviews
		container.find('.mailbox').each((_index, elem) => {
			if (!mailboxIds.has($(elem).attr('data-id'))) {
				$(elem).remove()
			}
		})

		// Add new webviews
		MMailbox.all().forEach((mailbox, index) => {
			if (container.find('.mailbox[data-id="' + mailbox.id + '"]').length === 0) {
				container.append(this.renderWebview(mailbox))
			}
		})

		if (mailboxIds.size === 0) {
			if (container.find('.welcome').length === 0) {
				container.append(this.renderWelcome())
			}
		} else {
			container.find('.welcome').remove();
		}
	}
}

module.exports = Mailboxes<|endoftext|>


<commit_before>#!/bin/env node

// --- prelude ---

var spawn = require('child_process').spawn,
    map   = require('async').map,
    series= require('async').series,
    foldl = require('async').foldl,
    path  = require('path')

Array.prototype.unique=
function unique() {
  return this.reduce(function(p,c) {
    if(p.indexOf(c)<0) p.push(c);
    return p;
  },[]);
}

// --- scheduling ---

function group_by_depth(addrs,cb) {
  function batch(xs,n){
    var t,r=[];
    while((t=xs.splice(0,n)).length)
      r.push(t);
    return r;
  }

  var d={};
  map(Object.keys(addrs),
      function(v,ret) {var k=v.length;if(v=='0') k=0; if(!d[k]) {d[k]=[v];} else {d[k].push(v);} ret();},
      function() {
        var g={};
        map(Object.keys(d),
            function(v,ret)  {g[v]=batch(d[v],7);ret();},
            function(err,res){cb(g,addrs);}
           );
      }
     );
}

/*
  cmd   [in]        Root command to run. Each argument should be in a list.
                    Example: ['./render','in','out']
  cb    [callback]  Called with a object that uses the addresses as keys.
                    The values are objects with the addreses' parent, and
                    the holds for the job.
*/
function addresses(cmd,cb) {
  cmd.push("--print-addresses");
  var p=spawn(cmd[0],cmd.slice(1),{cwd:process.cwd()});
  var buf="";
  p.stdout.on('data',function(data) {buf+=data});
  p.stderr.on('data',function(data) {console.log("stderr: "+data);});
  p.on('close',function(code) {
    var d={};
    map(buf.split('\n')
           .filter(function(e){return e;}),
        function(v,ret){d[v]={parent:Math.floor(v/10),holds:[]}; ret();},
        function(err,result) {d['0'].parent=null; group_by_depth(d,cb);}
    );
  });
}

/*
  xs    array               f will be mapped serially over the elements of xs
  f     function(x,ret)     f is an async function that will call ret(err,value)
                            when done.
  ret   function(err,value) A callback that will be called when the entire series
                            is finished.

  NOTE: I'm not strictly sure this was necessary.  foldl might have worked fine.
        This gaurantees the approach since it uses series.
*/
function foreach(xs,f,ret) {
  map(xs,
      function(x,r)    { r(null,function(cb) {f(x,cb);}); },
      function(err,fs) { series(fs,ret);}
     );
}

/*
  batches is an object:  {depth:   [ [ addr, addr,... ],...]}
  jobs is an object:     {address: { parent: addr, holds: [jid,...] }}
  submit is a function:  submit(addresses,holds,cb) --> jid (via callback)
                         It assembles the qsub call and returns the job id of the submited job.

                         addresses is [addr,...]
                         holds     is [jid,...]
                         cb        is function(err,jid)
                                      where
                                        err is not null when there is an error
                                        jid is the job id of the submitted job (see qsub)
  cb is a function:      It is called with no arguments when exec is done.

  exec submits job batches in reverse order of detpth (from highest depth to lowest),
  and adds the job id's to each corresponding parent address as a hold.
*/
function exec(batches,jobs,submit,cb) {
  var depths=Object.keys(batches).sort(function(a,b) {return b-a;});
  foreach(depths,
          function(d,retd) {
            console.log({depth:d});
            foreach(batches[d],
                    function(addrs,retb) {
                      console.log({batch:addrs});
                      foldl(addrs,[],           //1 : aggregate existing holds for addresses
                          function(holds,addr,ret) {
                            ret(null,holds.concat(jobs[addr].holds))
                          },
                          function(err,holds) { //2 : submit jobs
                            enqueue(function(){
                              submit(addrs,holds.unique(),function(err,jid){
                                var i,p;        //3 : update parents (if any)
                                for(i=0;i<addrs.length;++i)
                                  if((p=jobs[addrs[i]].parent)!=null)
                                    jobs[p].holds.push(jid);
                                retb();
                              });
                            });
                          }
                      );
                    },
                    retd);
          },
          function() {stopQstatPolling(); cb();}
         );
}

// --- throttle ---
// gate based on number of qstat'd jobs
// b.c. of the way scheduling is serialized here, there's only 1 job in the internal queue at any time.

var queue=[]
function enqueue(cb) { queue.push(cb); }
function popn(n) {
  for(i=0;i<Math.min(n,queue.length);++i)
    if(cb=queue.shift())
      cb();
}
var poll=setInterval(function(){
  var thresh=1000;
  var buf="";
  var p=spawn(path.join(__dirname,'my-job-count.sh'));
  p.stdout.on('data',function(data) {buf+=data});
  p.stderr.on('data',function(data) { console.log('ERROR: '+data);});
  p.on('close',function(code) {
    console.log("Check queue: Size: "+queue.length+" (thresh: "+thresh+") - Jobs: "+buf);
    if(buf<thresh) {popn(thresh-buf);}
  });
},100); // check queue once every 5 seconds
function stopQstatPolling() {clearTimeout(poll);}

// --- job submission ---

function qsubopts(name,log,nbatch,addrs,holds) {
  var holdopt='';
  var batchcmd=[path.join(__dirname,'worker.js '),addrs.join(',')].concat(process.argv.slice(2)).join(' ');
  console.log("QSUBOPTS: holds: "+holds);
  if(holds.length)
    holdopt='-hold_jid '+holds.join(',');
  return ('-terse -V -N '+name+' -j y -o '+log+' -b y -cwd -pe batch '+nbatch+' -l gpu_nodes=true '+holdopt).trim().split(' ').concat([batchcmd]);
}

function qsub(addrs,holds,ret) {
  QSUB(qsubopts('clackn-render','log2/',7,addrs,holds),function(r) {console.log(r); ret(null,r);});
}

var njobs=0;
function QSUB(opts,on_jid) {
  var buf='';
  function done() {njobs--; on_jid(parseInt(buf));}
  console.log('['+njobs+']: '+opts);
  var j=spawn('qsub',opts);
  j.on('exit',done);
  j.stderr.on('data',function(data) {console.log('qsub stderr: '+data.toString());});
  j.stdout.on('data',function(data) {buf+=data.toString();});
  njobs++;
}

// --- MAIN ---

// callback needs to be function(batches,jobs)
addresses(process.argv.slice(2),function(batches,jobs) {
  exec(batches,jobs,qsub,function() {console.log('DONE');});
});

// --- test/debug ---

var mock_jid=0;
function mock(addrs,holds,ret){
  setTimeout(function() {
    console.log([mock_jid,{addrs:addrs,holds:holds}]);
    ret(null,mock_jid++);
  },100);
}
function mockqsub(addrs,holds,ret) {
  setTimeout(function(){
    console.log('qsub '+qsubopts('mbm-render','log2/',7,addrs,holds));
    ret(null,mock_jid++);
  },100);
}
<commit_msg>[render/dispatch] dated log files for cluster jobs<commit_after>#!/bin/env node

// --- prelude ---

var spawn = require('child_process').spawn,
    map   = require('async').map,
    series= require('async').series,
    foldl = require('async').foldl,
    path  = require('path')

Array.prototype.unique=
function unique() {
  return this.reduce(function(p,c) {
    if(p.indexOf(c)<0) p.push(c);
    return p;
  },[]);
}

// --- scheduling ---

function group_by_depth(addrs,cb) {
  function batch(xs,n){
    var t,r=[];
    while((t=xs.splice(0,n)).length)
      r.push(t);
    return r;
  }

  var d={};
  map(Object.keys(addrs),
      function(v,ret) {var k=v.length;if(v=='0') k=0; if(!d[k]) {d[k]=[v];} else {d[k].push(v);} ret();},
      function() {
        var g={};
        map(Object.keys(d),
            function(v,ret)  {g[v]=batch(d[v],7);ret();},
            function(err,res){cb(g,addrs);}
           );
      }
     );
}

/*
  cmd   [in]        Root command to run. Each argument should be in a list.
                    Example: ['./render','in','out']
  cb    [callback]  Called with a object that uses the addresses as keys.
                    The values are objects with the addreses' parent, and
                    the holds for the job.
*/
function addresses(cmd,cb) {
  cmd.push("--print-addresses");
  var p=spawn(cmd[0],cmd.slice(1),{cwd:process.cwd()});
  var buf="";
  p.stdout.on('data',function(data) {buf+=data});
  p.stderr.on('data',function(data) {console.log("stderr: "+data);});
  p.on('close',function(code) {
    var d={};
    map(buf.split('\n')
           .filter(function(e){return e;}),
        function(v,ret){d[v]={parent:Math.floor(v/10),holds:[]}; ret();},
        function(err,result) {d['0'].parent=null; group_by_depth(d,cb);}
    );
  });
}

/*
  xs    array               f will be mapped serially over the elements of xs
  f     function(x,ret)     f is an async function that will call ret(err,value)
                            when done.
  ret   function(err,value) A callback that will be called when the entire series
                            is finished.

  NOTE: I'm not strictly sure this was necessary.  foldl might have worked fine.
        This gaurantees the approach since it uses series.
*/
function foreach(xs,f,ret) {
  map(xs,
      function(x,r)    { r(null,function(cb) {f(x,cb);}); },
      function(err,fs) { series(fs,ret);}
     );
}

/*
  batches is an object:  {depth:   [ [ addr, addr,... ],...]}
  jobs is an object:     {address: { parent: addr, holds: [jid,...] }}
  submit is a function:  submit(addresses,holds,cb) --> jid (via callback)
                         It assembles the qsub call and returns the job id of the submited job.

                         addresses is [addr,...]
                         holds     is [jid,...]
                         cb        is function(err,jid)
                                      where
                                        err is not null when there is an error
                                        jid is the job id of the submitted job (see qsub)
  cb is a function:      It is called with no arguments when exec is done.

  exec submits job batches in reverse order of detpth (from highest depth to lowest),
  and adds the job id's to each corresponding parent address as a hold.
*/
function exec(batches,jobs,submit,cb) {
  var depths=Object.keys(batches).sort(function(a,b) {return b-a;});
  foreach(depths,
          function(d,retd) {
            console.log({depth:d});
            foreach(batches[d],
                    function(addrs,retb) {
                      console.log({batch:addrs});
                      foldl(addrs,[],           //1 : aggregate existing holds for addresses
                          function(holds,addr,ret) {
                            ret(null,holds.concat(jobs[addr].holds))
                          },
                          function(err,holds) { //2 : submit jobs
                            enqueue(function(){
                              submit(addrs,holds.unique(),function(err,jid){
                                var i,p;        //3 : update parents (if any)
                                for(i=0;i<addrs.length;++i)
                                  if((p=jobs[addrs[i]].parent)!=null)
                                    jobs[p].holds.push(jid);
                                retb();
                              });
                            });
                          }
                      );
                    },
                    retd);
          },
          function() {stopQstatPolling(); cb();}
         );
}

// --- throttle ---
// gate based on number of qstat'd jobs
// b.c. of the way scheduling is serialized here, there's only 1 job in the internal queue at any time.

var queue=[]
function enqueue(cb) { queue.push(cb); }
function popn(n) {
  for(i=0;i<Math.min(n,queue.length);++i)
    if(cb=queue.shift())
      cb();
}
var poll=setInterval(function(){
  var thresh=1000;
  var buf="";
  var p=spawn(path.join(__dirname,'my-job-count.sh'));
  p.stdout.on('data',function(data) {buf+=data});
  p.stderr.on('data',function(data) { console.log('ERROR: '+data);});
  p.on('close',function(code) {
    console.log("Check queue: Size: "+queue.length+" (thresh: "+thresh+") - Jobs: "+buf);
    if(buf<thresh) {popn(thresh-buf);}
  });
},100); // check queue once every 5 seconds
function stopQstatPolling() {clearTimeout(poll);}

// --- job submission ---

function qsubopts(name,log,nbatch,addrs,holds) {
  var holdopt='';
  var batchcmd=[path.join(__dirname,'worker.js '),addrs.join(',')].concat(process.argv.slice(2)).join(' ');
  console.log("QSUBOPTS: holds: "+holds);
  if(holds.length)
    holdopt='-hold_jid '+holds.join(',');
  return ('-terse -V -N '+name+' -j y -o '+log+' -b y -cwd -pe batch '+nbatch+' -l gpu_nodes=true '+holdopt).trim().split(' ').concat([batchcmd]);
}

function qsub(addrs,holds,ret) {
  var d=new Date()
  var log=path.join('log', d.toDateString().split(' ').reverse().join('-')+'.txt');
  QSUB(qsubopts('mbm-render',log,7,addrs,holds),function(r) {console.log(r); ret(null,r);});
}

var njobs=0;
function QSUB(opts,on_jid) {
  var buf='';
  function done() {njobs--; on_jid(parseInt(buf));}
  console.log('['+njobs+']: '+opts);
  var j=spawn('qsub',opts);
  j.on('exit',done);
  j.stderr.on('data',function(data) {console.log('qsub stderr: '+data.toString());});
  j.stdout.on('data',function(data) {buf+=data.toString();});
  njobs++;
}

// --- MAIN ---

// callback needs to be function(batches,jobs)
addresses(process.argv.slice(2),function(batches,jobs) {
  exec(batches,jobs,qsub,function() {console.log('DONE');});
});

// --- test/debug ---

var mock_jid=0;
function mock(addrs,holds,ret){
  setTimeout(function() {
    console.log([mock_jid,{addrs:addrs,holds:holds}]);
    ret(null,mock_jid++);
  },100);
}
function mockqsub(addrs,holds,ret) {
  setTimeout(function(){
    console.log('qsub '+qsubopts('mbm-render','log2/',7,addrs,holds));
    ret(null,mock_jid++);
  },100);
}
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var pgb = require('../pg-binding');
var common = require('../common.js');

/**
 * @api {get} /relationships Request all relationships
 * @apiName GetRelationships
 * @apiGroup Relationships
 *
 * @apiParam (Optional query string parameters) {String} [fields] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_by] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_dir=ASC] Options: ASC or DESC
 * @apiParam (Optional query string parameters) {Number} [limit] integer of records to return
 * @apiParam (Optional query string parameters) {Boolean} [returnGeometry=false] integer of records to return
 *
 * @apiSuccess {Object} response A feature collection with zero to many features
 * @apiSuccess {String} response.type "Feature Collection"
 * @apiSuccess {Object[]} response.features An array of feature objects
 * @apiSuccess {String} response.features.type "Feature"
 * @apiSuccess {Object} response.features.geometry GeoJSON geometry object
 * @apiSuccess {Object} response.features.properties GeoJSON feature's properties
 * @apiSuccess {Number} response.features.properties.relationship_id relationship id
 * @apiSuccess {String} response.features.properties.relationship_type relationship type
 * @apiSuccess {String} response.features.properties.spatial_source spatial source
 * @apiSuccess {Number} response.features.properties.parcel_id relationship's parcel id
 * @apiSuccess {Number} response.features.properties.party_id relationship's party id
 * @apiSuccess {String} response.features.properties.first_name first name of creator
 * @apiSuccess {String} response.features.properties.last_name last_name of creator
 * @apiSuccess {String} response.features.properties.time_created Time stamp of creation
 *
 * @apiExample {curl} Example usage:
 *     curl -i http://localhost/relationships
 *
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          47.867583,
          -122.164306
        ]
      },
      "properties": {
        "relationship_id": 1,
        "relationship_type": "Own",
        "parcel_id": 1,
        "spatial_source": "survey_grade_gps",
        "party_id": 1,
        "first_name": "Daniel",
        "last_name": "Baah",
        "time_created": "2015-08-12T03:46:01.673153+00:00"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          47.670367,
          -122.387855
        ]
      },
      "properties": {
        "relationship_id": 2,
        "relationship_type": "Own",
        "parcel_id": 2,
        "spatial_source": "survey_grade_gps",
        "party_id": 2,
        "first_name": "Sarah",
        "last_name": "Bindman",
        "time_created": "2015-08-12T03:46:01.673153+00:00"
      }
    }
  ]
}
 */
router.get('', common.parseQueryOptions, function(req, res, next) {

    common.tableColumnQuery("show_relationships")
        .then(function(response){

            var sql = common.featureCollectionSQL("show_relationships", req.modifiers);

            return pgb.queryDeferred(sql,{paramValues: [req.params.id]});
        })
        .then(function(result){

            res.status(200).json(result[0].response);

        })
        .catch(function(err){
            next(err);
        })
        .done();

});

/**
 * @api {get} /relationships/:id Request one relationship
 * @apiName GetRelationship
 * @apiGroup Relationships
 *
 * @apiParam {Number} id relationship's unique ID.
 *
 * @apiParam (Optional query string parameters) {String} [fields] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_by] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_dir=ASC] Options: ASC or DESC
 * @apiParam (Optional query string parameters) {Number} [limit] integer of records to return
 * @apiParam (Optional query string parameters) {Boolean} [returnGeometry=false] integer of records to return
 *
 * @apiSuccess {Object} response A feature collection with zero to many features
 * @apiSuccess {String} response.type "Feature Collection"
 * @apiSuccess {Object[]} response.features An array of feature objects
 * @apiSuccess {String} response.features.type "Feature"
 * @apiSuccess {Object} response.features.geometry GeoJSON geometry object
 * @apiSuccess {Object} response.features.properties GeoJSON feature's properties
 * @apiSuccess {Number} response.features.properties.relationship_id relationship id
 * @apiSuccess {String} response.features.properties.relationship_type relationship type
 * @apiSuccess {String} response.features.properties.spatial_source spatial source
 * @apiSuccess {Number} response.features.properties.parcel_id relationship's parcel id
 * @apiSuccess {Number} response.features.properties.party_id relationship's party id
 * @apiSuccess {String} response.features.properties.first_name first name of creator
 * @apiSuccess {String} response.features.properties.last_name last_name of creator
 * @apiSuccess {String} response.features.properties.time_created Time stamp of creation
 *
 * @apiExample {curl} Example usage:
 *     curl -i http://localhost/relationships
 *
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *
 *     {
          "type": "FeatureCollection",
          "features": [
            {
              "type": "Feature",
              "geometry": {
                "type": "Point",
                "coordinates": [
                  47.867583,
                  -122.164306
                ]
              },
              "properties": {
                "relationship_id": 1,
                "relationship_type": "Own",
                "parcel_id": 1,
                "spatial_source": "survey_grade_gps",
                "party_id": 1,
                "first_name": "Daniel",
                "last_name": "Baah",
                "time_created": "2015-08-12T03:46:01.673153+00:00"
              }
            }
          ]
        }
 */
router.get('/:id', common.parseQueryOptions, function(req, res, next) {

    common.tableColumnQuery("show_relationships")
        .then(function(response){

            var sql = common.featureCollectionSQL("show_relationships",  req.queryModifiers, "WHERE relationship_id = $1");

            return pgb.queryDeferred(sql,{paramValues: [req.params.id]});
        })
        .then(function(result){

            res.status(200).json(result[0].response);

        })
        .catch(function(err){
            next(err);
        })
        .done();
});

module.exports = router;<commit_msg>Add relationship history route.<commit_after>var express = require('express');
var router = express.Router();
var pgb = require('../pg-binding');
var common = require('../common.js');

/**
 * @api {get} /relationships Request all relationships
 * @apiName GetRelationships
 * @apiGroup Relationships
 *
 * @apiParam (Optional query string parameters) {String} [fields] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_by] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_dir=ASC] Options: ASC or DESC
 * @apiParam (Optional query string parameters) {Number} [limit] integer of records to return
 * @apiParam (Optional query string parameters) {Boolean} [returnGeometry=false] integer of records to return
 *
 * @apiSuccess {Object} response A feature collection with zero to many features
 * @apiSuccess {String} response.type "Feature Collection"
 * @apiSuccess {Object[]} response.features An array of feature objects
 * @apiSuccess {String} response.features.type "Feature"
 * @apiSuccess {Object} response.features.geometry GeoJSON geometry object
 * @apiSuccess {Object} response.features.properties GeoJSON feature's properties
 * @apiSuccess {Number} response.features.properties.relationship_id relationship id
 * @apiSuccess {String} response.features.properties.relationship_type relationship type
 * @apiSuccess {String} response.features.properties.spatial_source spatial source
 * @apiSuccess {Number} response.features.properties.parcel_id relationship's parcel id
 * @apiSuccess {Number} response.features.properties.party_id relationship's party id
 * @apiSuccess {String} response.features.properties.first_name first name of creator
 * @apiSuccess {String} response.features.properties.last_name last_name of creator
 * @apiSuccess {String} response.features.properties.time_created Time stamp of creation
 *
 * @apiExample {curl} Example usage:
 *     curl -i http://localhost/relationships
 *
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          47.867583,
          -122.164306
        ]
      },
      "properties": {
        "relationship_id": 1,
        "relationship_type": "Own",
        "parcel_id": 1,
        "spatial_source": "survey_grade_gps",
        "party_id": 1,
        "first_name": "Daniel",
        "last_name": "Baah",
        "time_created": "2015-08-12T03:46:01.673153+00:00"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          47.670367,
          -122.387855
        ]
      },
      "properties": {
        "relationship_id": 2,
        "relationship_type": "Own",
        "parcel_id": 2,
        "spatial_source": "survey_grade_gps",
        "party_id": 2,
        "first_name": "Sarah",
        "last_name": "Bindman",
        "time_created": "2015-08-12T03:46:01.673153+00:00"
      }
    }
  ]
}
 */
router.get('', common.parseQueryOptions, function(req, res, next) {

    common.tableColumnQuery("show_relationships")
        .then(function(response){

            var sql = common.featureCollectionSQL("show_relationships", req.modifiers);

            return pgb.queryDeferred(sql,{paramValues: [req.params.id]});
        })
        .then(function(result){

            res.status(200).json(result[0].response);

        })
        .catch(function(err){
            next(err);
        })
        .done();

});

/**
 * @api {get} /relationships/:id Request one relationship
 * @apiName GetRelationship
 * @apiGroup Relationships
 *
 * @apiParam {Number} id relationship's unique ID.
 *
 * @apiParam (Optional query string parameters) {String} [fields] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_by] Options: id, spatial_source, user_id, time_created, time_updated
 * @apiParam (Optional query string parameters) {String} [sort_dir=ASC] Options: ASC or DESC
 * @apiParam (Optional query string parameters) {Number} [limit] integer of records to return
 * @apiParam (Optional query string parameters) {Boolean} [returnGeometry=false] integer of records to return
 *
 * @apiSuccess {Object} response A feature collection with zero to many features
 * @apiSuccess {String} response.type "Feature Collection"
 * @apiSuccess {Object[]} response.features An array of feature objects
 * @apiSuccess {String} response.features.type "Feature"
 * @apiSuccess {Object} response.features.geometry GeoJSON geometry object
 * @apiSuccess {Object} response.features.properties GeoJSON feature's properties
 * @apiSuccess {Number} response.features.properties.relationship_id relationship id
 * @apiSuccess {String} response.features.properties.relationship_type relationship type
 * @apiSuccess {String} response.features.properties.spatial_source spatial source
 * @apiSuccess {Number} response.features.properties.parcel_id relationship's parcel id
 * @apiSuccess {Number} response.features.properties.party_id relationship's party id
 * @apiSuccess {String} response.features.properties.first_name first name of creator
 * @apiSuccess {String} response.features.properties.last_name last_name of creator
 * @apiSuccess {String} response.features.properties.time_created Time stamp of creation
 *
 * @apiExample {curl} Example usage:
 *     curl -i http://localhost/relationships
 *
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *
 *     {
          "type": "FeatureCollection",
          "features": [
            {
              "type": "Feature",
              "geometry": {
                "type": "Point",
                "coordinates": [
                  47.867583,
                  -122.164306
                ]
              },
              "properties": {
                "relationship_id": 1,
                "relationship_type": "Own",
                "parcel_id": 1,
                "spatial_source": "survey_grade_gps",
                "party_id": 1,
                "first_name": "Daniel",
                "last_name": "Baah",
                "time_created": "2015-08-12T03:46:01.673153+00:00"
              }
            }
          ]
        }
 */
router.get('/:id', common.parseQueryOptions, function(req, res, next) {

    common.tableColumnQuery("show_relationships")
        .then(function(response){

            var sql = common.featureCollectionSQL("show_relationships",  req.queryModifiers, "WHERE relationship_id = $1");

            return pgb.queryDeferred(sql,{paramValues: [req.params.id]});
        })
        .then(function(result){

            res.status(200).json(result[0].response);

        })
        .catch(function(err){
            next(err);
        })
        .done();
});

router.get('/:id', common.parseQueryOptions, function(req, res, next) {

    common.tableColumnQuery("relationship_history")
        .then(function(response){

            var sql = common.featureCollectionSQL("relationship_history",  req.queryModifiers, "WHERE id = $1");

            return pgb.queryDeferred(sql,{paramValues: [req.params.id]});
        })
        .then(function(result){

            res.status(200).json(result[0].response);

        })
        .catch(function(err){
            next(err);
        })
        .done();
});
module.exports = router;<|endoftext|>