<commit_before>(function (root, factory) {
	if ( typeof define === 'function' && define.amd ) {
		define('houdini', factory(root));
	} else if ( typeof exports === 'object' ) {
		module.exports = factory(root);
	} else {
		root.houdini = factory(root);
	}
})(this, function (root) {

	'use strict';

	//
	// Variables
	//

	var exports = {}; // Object for public APIs
	var supports = !!document.querySelector && !!root.addEventListener; // Feature test
	var settings; // Script settings
	var toggles; // Toggle nodes list
	var eventListeners = []; //Listeners array

	// Default settings
	var defaults = {
		toggleActiveClass: 'active',
		contentActiveClass: 'active',
		initClass: 'js-houdini',
		callbackBefore: function () {},
		callbackAfter: function () {}
	};

	//
	// Methods
	//

	/**
	 * A simple forEach() implementation for Arrays, Objects and NodeLists
	 * @private
	 * @param {Array|Object|NodeList} collection Collection of items to iterate
	 * @param {Function} callback Callback function for each iteration
	 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
	 */
	var forEach = function (collection, callback, scope) {
		if (Object.prototype.toString.call(collection) === '[object Object]') {
			for (var prop in collection) {
				if (Object.prototype.hasOwnProperty.call(collection, prop)) {
					callback.call(scope, collection[prop], prop, collection);
				}
			}
		} else {
			for (var i = 0, len = collection.length; i < len; i++) {
				callback.call(scope, collection[i], i, collection);
			}
		}
	};

	/**
	 * Merge defaults with user options
	 * @private
	 * @param {Object} defaults Default settings
	 * @param {Object} options User options
	 * @returns {Object} Merged values of defaults and options
	 */
	var extend = function ( defaults, options ) {
		var extended = {};
		forEach(defaults, function (value, prop) {
			extended[prop] = defaults[prop];
		});
		forEach(options, function (value, prop) {
			extended[prop] = options[prop];
		});
		return extended;
	};

	/**
	 * Stop YouTube, Vimeo, and HTML5 videos from playing when leaving the slide
	 * @private
	 * @param  {Element} content The content container the video is in
	 * @param  {String} activeClass The class asigned to expanded content areas
	 */
	var stopVideos = function ( content, activeClass ) {
		if ( !content.classList.contains( activeClass ) ) {
			var iframe = content.querySelector( 'iframe');
			var video = content.querySelector( 'video' );
			if ( iframe ) {
				var iframeSrc = iframe.src;
				iframe.src = iframeSrc;
			}
			if ( video ) {
				video.pause();
			}
		}
	};

	/**
	 * Close all content areas in an expand/collapse group
	 * @private
	 * @param  {Element} toggle The element that toggled the expand or collapse
	 * @param  {Object} settings
	 */
	var closeCollapseGroup = function ( toggle, settings ) {
		if ( !toggle.classList.contains( settings.toggleActiveClass ) && toggle.hasAttribute('data-group') ) {

			// Get all toggles in the group
			var groupName = toggle.getAttribute('data-group');
			var group = document.querySelectorAll('[data-group="' + groupName + '"]');

			// Deactivate each toggle and it's content area
			forEach(group, function (item) {
				var content = document.querySelector( item.getAttribute('data-collapse') );
				item.classList.remove( settings.toggleActiveClass );
				content.classList.remove( settings.contentActiveClass );
			});

		}
	};

	/**
	 * Toggle the collapse/expand widget
	 * @public
	 * @param  {Element} toggle The element that toggled the expand or collapse
	 * @param  {String} contentID The ID of the content area to expand or collapse
	 * @param  {Object} options
	 * @param  {Event} event
	 */
	exports.toggleContent = function (toggle, contentID, options, event) {

		var settings = extend( settings || defaults, options || {} );  // Merge user options with defaults
		var content = document.querySelector(contentID); // Get content area

		// If toggle is a link, prevent default click event
		if ( toggle && toggle.tagName.toLowerCase() === 'a' && event ) {
			event.preventDefault();
		}

		settings.callbackBefore( toggle, contentID ); // Run callbacks before toggling content

		// Toggle collapse element
		closeCollapseGroup(toggle, settings); // Close collapse group items
		toggle.classList.toggle( settings.toggleActiveClass );// Change text on collapse toggle
		content.classList.toggle( settings.contentActiveClass ); // Collapse or expand content area
		stopVideos( content, settings.contentActiveClass ); // If content area is closed, stop playing any videos

		settings.callbackAfter( toggle, contentID ); // Run callbacks after toggling content

	};

	/**
	 * Destroy the current initialization.
	 * @public
	 */
	exports.destroy = function () {
		if ( !settings ) return;
		document.documentElement.classList.remove( settings.initClass );
		if ( toggles ) {
			forEach( toggles, function ( toggle, index ) {
				toggle.removeEventListener( 'click', eventListeners[index], false );
			});
			eventListeners = [];
		}
		settings = null;
		toggles = null;
	};

	/**
	 * Initialize Houdini
	 * @public
	 * @param {Object} options User settings
	 */
	exports.init = function ( options ) {

		// feature test
		if ( !supports ) return;

		// Destroy any existing initializations
		exports.destroy();

		// Selectors and variables
		settings = extend( defaults, options || {} ); // Merge user options with defaults

		// Add class to HTML element to activate conditional CSS
		document.documentElement.classList.add( settings.initClass );

		// Whenever a toggle is clicked, run the expand/collapse function
		toggles = document.querySelectorAll('[data-collapse]'); // Get all collapse toggles
		forEach(toggles, function (toggle, index) {
			eventListeners[index] = exports.toggleContent.bind( null, toggle, toggle.getAttribute('data-collapse'), settings );
			toggle.addEventListener('click', eventListeners[index], false);
		});

	};

	//
	// Public APIs
	//

	return exports;

});<commit_msg>Combined variables<commit_after>(function (root, factory) {
	if ( typeof define === 'function' && define.amd ) {
		define('houdini', factory(root));
	} else if ( typeof exports === 'object' ) {
		module.exports = factory(root);
	} else {
		root.houdini = factory(root);
	}
})(this, function (root) {

	'use strict';

	//
	// Variables
	//

	var exports = {}; // Object for public APIs
	var supports = !!document.querySelector && !!root.addEventListener; // Feature test
	var eventListeners = []; //Listeners array
	var settings, toggles;

	// Default settings
	var defaults = {
		toggleActiveClass: 'active',
		contentActiveClass: 'active',
		initClass: 'js-houdini',
		callbackBefore: function () {},
		callbackAfter: function () {}
	};

	//
	// Methods
	//

	/**
	 * A simple forEach() implementation for Arrays, Objects and NodeLists
	 * @private
	 * @param {Array|Object|NodeList} collection Collection of items to iterate
	 * @param {Function} callback Callback function for each iteration
	 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
	 */
	var forEach = function (collection, callback, scope) {
		if (Object.prototype.toString.call(collection) === '[object Object]') {
			for (var prop in collection) {
				if (Object.prototype.hasOwnProperty.call(collection, prop)) {
					callback.call(scope, collection[prop], prop, collection);
				}
			}
		} else {
			for (var i = 0, len = collection.length; i < len; i++) {
				callback.call(scope, collection[i], i, collection);
			}
		}
	};

	/**
	 * Merge defaults with user options
	 * @private
	 * @param {Object} defaults Default settings
	 * @param {Object} options User options
	 * @returns {Object} Merged values of defaults and options
	 */
	var extend = function ( defaults, options ) {
		var extended = {};
		forEach(defaults, function (value, prop) {
			extended[prop] = defaults[prop];
		});
		forEach(options, function (value, prop) {
			extended[prop] = options[prop];
		});
		return extended;
	};

	/**
	 * Stop YouTube, Vimeo, and HTML5 videos from playing when leaving the slide
	 * @private
	 * @param  {Element} content The content container the video is in
	 * @param  {String} activeClass The class asigned to expanded content areas
	 */
	var stopVideos = function ( content, activeClass ) {
		if ( !content.classList.contains( activeClass ) ) {
			var iframe = content.querySelector( 'iframe');
			var video = content.querySelector( 'video' );
			if ( iframe ) {
				var iframeSrc = iframe.src;
				iframe.src = iframeSrc;
			}
			if ( video ) {
				video.pause();
			}
		}
	};

	/**
	 * Close all content areas in an expand/collapse group
	 * @private
	 * @param  {Element} toggle The element that toggled the expand or collapse
	 * @param  {Object} settings
	 */
	var closeCollapseGroup = function ( toggle, settings ) {
		if ( !toggle.classList.contains( settings.toggleActiveClass ) && toggle.hasAttribute('data-group') ) {

			// Get all toggles in the group
			var groupName = toggle.getAttribute('data-group');
			var group = document.querySelectorAll('[data-group="' + groupName + '"]');

			// Deactivate each toggle and it's content area
			forEach(group, function (item) {
				var content = document.querySelector( item.getAttribute('data-collapse') );
				item.classList.remove( settings.toggleActiveClass );
				content.classList.remove( settings.contentActiveClass );
			});

		}
	};

	/**
	 * Toggle the collapse/expand widget
	 * @public
	 * @param  {Element} toggle The element that toggled the expand or collapse
	 * @param  {String} contentID The ID of the content area to expand or collapse
	 * @param  {Object} options
	 * @param  {Event} event
	 */
	exports.toggleContent = function (toggle, contentID, options, event) {

		var settings = extend( settings || defaults, options || {} );  // Merge user options with defaults
		var content = document.querySelector(contentID); // Get content area

		// If toggle is a link, prevent default click event
		if ( toggle && toggle.tagName.toLowerCase() === 'a' && event ) {
			event.preventDefault();
		}

		settings.callbackBefore( toggle, contentID ); // Run callbacks before toggling content

		// Toggle collapse element
		closeCollapseGroup(toggle, settings); // Close collapse group items
		toggle.classList.toggle( settings.toggleActiveClass );// Change text on collapse toggle
		content.classList.toggle( settings.contentActiveClass ); // Collapse or expand content area
		stopVideos( content, settings.contentActiveClass ); // If content area is closed, stop playing any videos

		settings.callbackAfter( toggle, contentID ); // Run callbacks after toggling content

	};

	/**
	 * Destroy the current initialization.
	 * @public
	 */
	exports.destroy = function () {
		if ( !settings ) return;
		document.documentElement.classList.remove( settings.initClass );
		if ( toggles ) {
			forEach( toggles, function ( toggle, index ) {
				toggle.removeEventListener( 'click', eventListeners[index], false );
			});
			eventListeners = [];
		}
		settings = null;
		toggles = null;
	};

	/**
	 * Initialize Houdini
	 * @public
	 * @param {Object} options User settings
	 */
	exports.init = function ( options ) {

		// feature test
		if ( !supports ) return;

		// Destroy any existing initializations
		exports.destroy();

		// Selectors and variables
		settings = extend( defaults, options || {} ); // Merge user options with defaults

		// Add class to HTML element to activate conditional CSS
		document.documentElement.classList.add( settings.initClass );

		// Whenever a toggle is clicked, run the expand/collapse function
		toggles = document.querySelectorAll('[data-collapse]'); // Get all collapse toggles
		forEach(toggles, function (toggle, index) {
			eventListeners[index] = exports.toggleContent.bind( null, toggle, toggle.getAttribute('data-collapse'), settings );
			toggle.addEventListener('click', eventListeners[index], false);
		});

	};

	//
	// Public APIs
	//

	return exports;

});<|endoftext|>


<commit_before>import uniq from 'lodash/uniq';

const initialState = {};

const documentChildrenResults = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'FETCH_CHILD_DOCS_REQUEST':
      return {
        ...state,
        [payload.id]: { ...state[payload.id], isFetching: true },
      };
    case 'FETCH_CHILD_DOCS_SUCCESS':
      return {
        ...state,
        [payload.id]: payload.data,
        isFetching: false,
      };
    case 'FETCH_CHILD_DOCS_NEXT_REQUEST':
      return {
        ...state,
        [payload.id]: { ...state[payload.id], isFetchingNext: true },
      }
    case 'FETCH_CHILD_DOCS_NEXT_SUCCESS':
      return {
        ...state,
        [payload.id]: {
          ...mergeResults(state[payload.id] || {}, payload.data),
          isFetchingNext: false,
        },
      }
    default:
      return state;
  }
};

function mergeResults(oldResult, newResult) {
  return {
    next: newResult.next,
    total: newResult.total,
    // uniq should not be necessary, but included just in case.
    results: uniq((oldResult ? oldResult.results : []).concat(newResult.results)),
  };
}

export default documentChildrenResults;
<commit_msg>Remove spurious isFetching: false<commit_after>import uniq from 'lodash/uniq';

const initialState = {};

const documentChildrenResults = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'FETCH_CHILD_DOCS_REQUEST':
      return {
        ...state,
        [payload.id]: { ...state[payload.id], isFetching: true },
      };
    case 'FETCH_CHILD_DOCS_SUCCESS':
      return {
        ...state,
        [payload.id]: payload.data,
      };
    case 'FETCH_CHILD_DOCS_NEXT_REQUEST':
      return {
        ...state,
        [payload.id]: { ...state[payload.id], isFetchingNext: true },
      }
    case 'FETCH_CHILD_DOCS_NEXT_SUCCESS':
      return {
        ...state,
        [payload.id]: {
          ...mergeResults(state[payload.id] || {}, payload.data),
          isFetchingNext: false,
        },
      }
    default:
      return state;
  }
};

function mergeResults(oldResult, newResult) {
  return {
    next: newResult.next,
    total: newResult.total,
    // uniq should not be necessary, but included just in case.
    results: uniq((oldResult ? oldResult.results : []).concat(newResult.results)),
  };
}

export default documentChildrenResults;
<|endoftext|>


<commit_before>import _ from 'lodash'
import {shiftDate} from 'shared/query/helpers'
import {map, reduce, forEach, concat, clone} from 'fast.js'

const groupByMap = (results, responseIndex, groupByColumns) => {
  if (_.isEmpty(results)) {
    return []
  }
  const firstColumns = _.get(results, [0, 'series', 0, 'columns'])
  const accum = [
    {
      responseIndex,
      series: [
        {
          columns: [
            firstColumns[0],
            ...groupByColumns,
            ...firstColumns.slice(1),
          ],
          groupByColumns,
          name: _.get(results, [0, 'series', 0, 'name'], ''),
          values: [],
        },
      ],
    },
  ]

  const seriesArray = _.get(results, [0, 'series'])
  seriesArray.forEach(s => {
    const prevValues = accum[0].series[0].values
    const tagsToAdd = groupByColumns.map(gb => s.tags[gb])
    const newValues = s.values.map(v => [v[0], ...tagsToAdd, ...v.slice(1)])
    accum[0].series[0].values = [...prevValues, ...newValues]
  })
  return accum
}

const constructResults = (raw, groupBys) => {
  return _.flatten(
    map(raw, (response, index) => {
      const results = _.get(response, 'response.results', [])

      const successfulResults = _.filter(results, r => _.isNil(r.error))

      if (!_.isEmpty(groupBys[index])) {
        return groupByMap(successfulResults, index, groupBys[index])
      }
      return map(successfulResults, r => ({...r, responseIndex: index}))
    })
  )
}

const constructSerieses = results => {
  return reduce(
    results,
    (acc, {series = [], responseIndex}) => {
      return [
        ...acc,
        ...map(series, (item, index) => ({
          ...item,
          responseIndex,
          seriesIndex: index,
        })),
      ]
    },
    []
  )
}

const constructCells = serieses => {
  let cellIndex = 0
  let labels = []
  const seriesLabels = []
  const cells = {
    label: [],
    value: [],
    time: [],
    seriesIndex: [],
    responseIndex: [],
  }
  forEach(
    serieses,
    (
      {
        name: measurement,
        columns,
        groupByColumns,
        values = [],
        seriesIndex,
        responseIndex,
        tags = {},
      },
      ind
    ) => {
      const rows = map(values, vals => ({vals}))

      const tagSet = map(Object.keys(tags), tag => `[${tag}=${tags[tag]}]`)
        .sort()
        .join('')

      const unsortedLabels = map(columns.slice(1), (field, i) => ({
        label:
          groupByColumns && i <= groupByColumns.length - 1
            ? `${field}`
            : `${measurement}.${field}${tagSet}`,
        responseIndex,
        seriesIndex,
      }))
      seriesLabels[ind] = unsortedLabels
      labels = concat(labels, unsortedLabels)

      forEach(rows, ({vals}) => {
        const [time, ...rowValues] = vals
        forEach(rowValues, (value, i) => {
          cells.label[cellIndex] = unsortedLabels[i].label
          cells.value[cellIndex] = value
          cells.time[cellIndex] = time
          cells.seriesIndex[cellIndex] = seriesIndex
          cells.responseIndex[cellIndex] = responseIndex
          cellIndex++ // eslint-disable-line no-plusplus
        })
      })
    }
  )
  const sortedLabels = _.sortBy(labels, 'label')
  return {cells, sortedLabels, seriesLabels}
}

const insertGroupByValues = (
  serieses,
  groupBys,
  seriesLabels,
  labelsToValueIndex,
  sortedLabels
) => {
  const dashArray = Array(sortedLabels.length).fill('-')
  const timeSeries = []
  let existingRowIndex
  forEach(serieses, (s, sind) => {
    if (!_.isEmpty(groupBys[s.responseIndex])) {
      forEach(s.values, vs => {
        timeSeries.push({time: vs[0], values: clone(dashArray)})
        existingRowIndex = timeSeries.length - 1
        forEach(vs.slice(1), (v, i) => {
          const label = seriesLabels[sind][i].label
          timeSeries[existingRowIndex].values[
            labelsToValueIndex[label + s.responseIndex + s.seriesIndex]
          ] = v
        })
      })
    }
  })

  return timeSeries
}

const constructTimeSeries = (
  serieses,
  cells,
  sortedLabels,
  groupBys,
  seriesLabels
) => {
  const nullArray = Array(sortedLabels.length).fill(null)

  const labelsToValueIndex = reduce(
    sortedLabels,
    (acc, {label, responseIndex, seriesIndex}, i) => {
      // adding series index prevents overwriting of two distinct labels that have the same field and measurements
      acc[label + responseIndex + seriesIndex] = i
      return acc
    },
    {}
  )

  const tsMemo = {}

  const timeSeries = insertGroupByValues(
    serieses,
    groupBys,
    seriesLabels,
    labelsToValueIndex,
    sortedLabels
  )

  let existingRowIndex

  for (let i = 0; i < _.get(cells, ['value', 'length'], 0); i++) {
    let time
    time = cells.time[i]
    const value = cells.value[i]
    const label = cells.label[i]
    const seriesIndex = cells.seriesIndex[i]
    const responseIndex = cells.responseIndex[i]

    if (!_.isEmpty(groupBys[cells.responseIndex[i]])) {
      // we've already inserted GroupByValues
      continue
    }

    if (label.includes('_shifted__')) {
      const [, quantity, duration] = label.split('__')
      time = +shiftDate(time, quantity, duration).format('x')
    }

    existingRowIndex = tsMemo[time]

    if (existingRowIndex === undefined) {
      timeSeries.push({
        time,
        values: clone(nullArray),
      })

      existingRowIndex = timeSeries.length - 1
      tsMemo[time] = existingRowIndex
    }

    timeSeries[existingRowIndex].values[
      labelsToValueIndex[label + responseIndex + seriesIndex]
    ] = value
  }

  return _.sortBy(timeSeries, 'time')
}

export const groupByTimeSeriesTransform = (raw, groupBys) => {
  if (!groupBys) {
    groupBys = Array(raw.length).fill([])
  }

  const results = constructResults(raw, groupBys)
  const serieses = constructSerieses(results)

  const {cells, sortedLabels, seriesLabels} = constructCells(serieses)

  const sortedTimeSeries = constructTimeSeries(
    serieses,
    cells,
    sortedLabels,
    groupBys,
    seriesLabels
  )

  return {
    sortedLabels,
    sortedTimeSeries,
  }
}
<commit_msg>Use series[x].tags instead of groupby.tags from the queryAST for groupby queries<commit_after>import _ from 'lodash'
import {shiftDate} from 'shared/query/helpers'
import {map, reduce, forEach, concat, clone} from 'fast.js'

const groupByMap = (results, responseIndex, groupByTagsObject) => {
  if (_.isEmpty(results)) {
    return []
  }

  const groupByKeys = _.keys(groupByTagsObject)
  const firstColumns = _.get(results, [0, 'series', 0, 'columns'])
  const accumulatedSeries = [
    {
      responseIndex,
      series: [
        {
          columns: [firstColumns[0], ...groupByKeys, ...firstColumns.slice(1)],
          groupByKeys,
          name: _.get(results, [0, 'series', 0, 'name'], ''),
          values: [],
        },
      ],
    },
  ]

  const seriesArray = _.get(results, [0, 'series'])
  seriesArray.forEach(s => {
    const prevValues = accumulatedSeries[0].series[0].values
    const tagsToAdd = groupByKeys.map(gb => s.tags[gb])
    const newValues = s.values.map(v => [v[0], ...tagsToAdd, ...v.slice(1)])
    accumulatedSeries[0].series[0].values = [...prevValues, ...newValues]
  })
  return accumulatedSeries
}

const constructResults = (raw, groupBys) => {
  return _.flatten(
    map(raw, (response, index) => {
      const results = _.get(response, 'response.results', [])

      const successfulResults = _.filter(results, r => _.isNil(r.error))
      const resultsHaveGroupBys = _.get(
        results,
        ['0', 'series', '0', 'tags'],
        false
      )
      if (resultsHaveGroupBys) {
        return groupByMap(successfulResults, index, resultsHaveGroupBys)
      }
      return map(successfulResults, r => ({...r, responseIndex: index}))
    })
  )
}

const constructSerieses = results => {
  return reduce(
    results,
    (acc, {series = [], responseIndex}) => {
      return [
        ...acc,
        ...map(series, (item, index) => ({
          ...item,
          responseIndex,
          seriesIndex: index,
        })),
      ]
    },
    []
  )
}

const constructCells = serieses => {
  let cellIndex = 0
  let labels = []
  const seriesLabels = []
  const cells = {
    label: [],
    value: [],
    time: [],
    seriesIndex: [],
    responseIndex: [],
  }
  forEach(
    serieses,
    (
      {
        name: measurement,
        columns,
        groupByKeys,
        values = [],
        seriesIndex,
        responseIndex,
        tags = {},
      },
      ind
    ) => {
      const rows = map(values, vals => ({vals}))

      const tagSet = map(Object.keys(tags), tag => `[${tag}=${tags[tag]}]`)
        .sort()
        .join('')

      const unsortedLabels = map(columns.slice(1), (field, i) => ({
        label:
          groupByKeys && i <= groupByKeys.length - 1
            ? `${field}`
            : `${measurement}.${field}${tagSet}`,
        responseIndex,
        seriesIndex,
      }))
      seriesLabels[ind] = unsortedLabels
      labels = concat(labels, unsortedLabels)

      forEach(rows, ({vals}) => {
        const [time, ...rowValues] = vals
        forEach(rowValues, (value, i) => {
          cells.label[cellIndex] = unsortedLabels[i].label
          cells.value[cellIndex] = value
          cells.time[cellIndex] = time
          cells.seriesIndex[cellIndex] = seriesIndex
          cells.responseIndex[cellIndex] = responseIndex
          cellIndex++ // eslint-disable-line no-plusplus
        })
      })
    }
  )
  const sortedLabels = _.sortBy(labels, 'label')
  return {cells, sortedLabels, seriesLabels}
}

const insertGroupByValues = (
  serieses,
  groupBys,
  seriesLabels,
  labelsToValueIndex,
  sortedLabels
) => {
  const dashArray = Array(sortedLabels.length).fill('-')
  const timeSeries = []
  let existingRowIndex
  forEach(serieses, (s, sind) => {
    if (!_.isEmpty(groupBys[s.responseIndex])) {
      forEach(s.values, vs => {
        timeSeries.push({time: vs[0], values: clone(dashArray)})
        existingRowIndex = timeSeries.length - 1
        forEach(vs.slice(1), (v, i) => {
          const label = seriesLabels[sind][i].label
          timeSeries[existingRowIndex].values[
            labelsToValueIndex[label + s.responseIndex + s.seriesIndex]
          ] = v
        })
      })
    }
  })

  return timeSeries
}

const constructTimeSeries = (
  serieses,
  cells,
  sortedLabels,
  groupBys,
  seriesLabels
) => {
  const nullArray = Array(sortedLabels.length).fill(null)

  const labelsToValueIndex = reduce(
    sortedLabels,
    (acc, {label, responseIndex, seriesIndex}, i) => {
      // adding series index prevents overwriting of two distinct labels that have the same field and measurements
      acc[label + responseIndex + seriesIndex] = i
      return acc
    },
    {}
  )

  const tsMemo = {}

  const timeSeries = insertGroupByValues(
    serieses,
    groupBys,
    seriesLabels,
    labelsToValueIndex,
    sortedLabels
  )

  let existingRowIndex

  for (let i = 0; i < _.get(cells, ['value', 'length'], 0); i++) {
    let time
    time = cells.time[i]
    const value = cells.value[i]
    const label = cells.label[i]
    const seriesIndex = cells.seriesIndex[i]
    const responseIndex = cells.responseIndex[i]

    if (!_.isEmpty(groupBys[cells.responseIndex[i]])) {
      // we've already inserted GroupByValues
      continue
    }

    if (label.includes('_shifted__')) {
      const [, quantity, duration] = label.split('__')
      time = +shiftDate(time, quantity, duration).format('x')
    }

    existingRowIndex = tsMemo[time]

    if (existingRowIndex === undefined) {
      timeSeries.push({
        time,
        values: clone(nullArray),
      })

      existingRowIndex = timeSeries.length - 1
      tsMemo[time] = existingRowIndex
    }

    timeSeries[existingRowIndex].values[
      labelsToValueIndex[label + responseIndex + seriesIndex]
    ] = value
  }

  return _.sortBy(timeSeries, 'time')
}

export const groupByTimeSeriesTransform = (raw, groupBys) => {
  if (!groupBys) {
    groupBys = Array(raw.length).fill([])
  }

  const results = constructResults(raw, groupBys)
  const serieses = constructSerieses(results)

  const {cells, sortedLabels, seriesLabels} = constructCells(serieses)

  const sortedTimeSeries = constructTimeSeries(
    serieses,
    cells,
    sortedLabels,
    groupBys,
    seriesLabels
  )

  return {
    sortedLabels,
    sortedTimeSeries,
  }
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';

addTest(testSuiteName.MICROPHONE, testCaseName.AUDIOCAPTURE, function(test) {
  var micTest = new MicTest(test);
  micTest.run();
});

function MicTest(test) {
  this.test = test;
  this.inputChannelCount = 6;
  this.outputChannelCount = 2;
  // Buffer size set to 0 to let Chrome choose based on the platform.
  this.bufferSize = 0;
  // Turning off echoCancellation constraint enables stereo input.
  this.constraints = {
    audio: {
      optional: [
        {echoCancellation: false}
      ]
    }
  };

  this.collectSeconds = 2.0;
  // At least one LSB 16-bit data (compare is on absolute value).
  this.silentThreshold = 1.0 / 32767;
  this.lowVolumeThreshold = -60;
  // Data must be identical within one LSB 16-bit to be identified as mono.
  this.monoDetectThreshold = 1.0 / 65536;
  // Number of consequtive clipThreshold level samples that indicate clipping.
  this.clipCountThreshold = 6;
  this.clipThreshold = 1.0;

  // Populated with audio as a 3-dimensional array:
  //   collectedAudio[channels][buffers][samples]
  this.collectedAudio = [];
  this.collectedSampleCount = 0;
  for (var i = 0; i < this.inputChannelCount; ++i) {
    this.collectedAudio[i] = [];
  }
}

MicTest.prototype = {
  run: function() {
    if (typeof audioContext === 'undefined') {
      this.test.reportError('WebAudio is not supported, test cannot run.');
      this.test.done();
    } else {
      doGetUserMedia(this.constraints, this.gotStream.bind(this));
    }
  },

  gotStream: function(stream) {
    if (!this.checkAudioTracks(stream)) {
      this.test.done();
      return;
    }
    this.createAudioBuffer(stream);
  },

  checkAudioTracks: function(stream) {
    this.stream = stream;
    var audioTracks = stream.getAudioTracks();
    if (audioTracks.length < 1) {
      this.test.reportError('No audio track in returned stream.');
      return false;
    }
    this.test.reportSuccess('Audio track created using device=' +
        audioTracks[0].label);
    return true;
  },

  createAudioBuffer: function() {
    this.audioSource = audioContext.createMediaStreamSource(this.stream);
    this.scriptNode = audioContext.createScriptProcessor(this.bufferSize,
        this.inputChannelCount, this.outputChannelCount);
    this.audioSource.connect(this.scriptNode);
    this.scriptNode.connect(audioContext.destination);
    this.scriptNode.onaudioprocess = this.collectAudio.bind(this);
    this.stopCollectingAudio = setTimeoutWithProgressBar(
        this.onStopCollectingAudio.bind(this), 5000);
  },

  collectAudio: function(event) {
    // Simple silence detection: check first and last sample of each channel in
    // the buffer. If both are below a threshold, the buffer is considered
    // silent.
    var sampleCount = event.inputBuffer.length;
    var allSilent = true;
    for (var c = 0; c < event.inputBuffer.numberOfChannels; c++) {
      var data = event.inputBuffer.getChannelData(c);
      var first = Math.abs(data[0]);
      var last = Math.abs(data[sampleCount - 1]);
      var newBuffer;
      if (first > this.silentThreshold || last > this.silentThreshold) {
        // Non-silent buffers are copied for analysis. Note that the silent
        // detection will likely cause the stored stream to contain discontinu-
        // ities, but that is ok for our needs here (just looking at levels).
        newBuffer = new Float32Array(sampleCount);
        newBuffer.set(data);
        allSilent = false;
      } else {
        // Silent buffers are not copied, but we store empty buffers so that the
        // analysis doesn't have to care.
        newBuffer = new Float32Array();
      }
      this.collectedAudio[c].push(newBuffer);
    }
    if (!allSilent) {
      this.collectedSampleCount += sampleCount;
      if ((this.collectedSampleCount / event.inputBuffer.sampleRate) >=
          this.collectSeconds) {
        this.stopCollectingAudio();
      }
    }
  },

  onStopCollectingAudio: function() {
    this.stream.getAudioTracks()[0].stop();
    this.audioSource.disconnect(this.scriptNode);
    this.scriptNode.disconnect(audioContext.destination);
    this.analyzeAudio(this.collectedAudio);
    this.test.done();
  },

  analyzeAudio: function(channels) {
    var activeChannels = [];
    for (var c = 0; c < channels.length; c++) {
      if (this.channelStats(c, channels[c])) {
        activeChannels.push(c);
      }
    }
    if (activeChannels.length === 0) {
      this.test.reportError('No active input channels detected. Microphone ' +
          'is most likely muted or broken, please check if muted in the ' +
          'sound settings or physically on the device. Then rerun the test.');
    } else {
      this.test.reportSuccess('Active audio input channels: ' +
          activeChannels.length);
    }
    if (activeChannels.length === 2) {
      this.detectMono(channels[activeChannels[0]], channels[activeChannels[1]]);
    }
  },

  channelStats: function(channelNumber, buffers) {
    var maxPeak = 0.0;
    var maxRms = 0.0;
    var clipCount = 0;
    var maxClipCount = 0;
    for (var j = 0; j < buffers.length; j++) {
      var samples = buffers[j];
      if (samples.length > 0) {
        var s = 0;
        var rms = 0.0;
        for (var i = 0; i < samples.length; i++) {
          s = Math.abs(samples[i]);
          maxPeak = Math.max(maxPeak, s);
          rms += s * s;
          if (maxPeak >= this.clipThreshold) {
            clipCount++;
            maxClipCount = Math.max(maxClipCount, clipCount);
          } else {
            clipCount = 0;
          }
        }
        // RMS is calculated over each buffer, meaning the integration time will
        // be different depending on sample rate and buffer size. In practise
        // this should be a small problem.
        rms = Math.sqrt(rms / samples.length);
        maxRms = Math.max(maxRms, rms);
      }
    }

    if (maxPeak > this.silentThreshold) {
      var dBPeak = this.dBFS(maxPeak);
      var dBRms = this.dBFS(maxRms);
      this.test.reportInfo('Channel ' + channelNumber + ' levels: ' +
                 dBPeak.toFixed(1) + ' dB (peak), ' +
                 dBRms.toFixed(1) + ' dB (RMS)');
      if (dBRms < this.lowVolumeThreshold) {
        this.test.reportError('Microphone input level is low, increase input ' +
                    'volume or move closer to the microphone.');
      }
      if (maxClipCount > this.clipCountThreshold) {
        this.test.reportWarning('Clipping detected! Microphone input level ' +
            'is high. Decrease input volume or move away from the microphone.');
      }
      return true;
    } else {
      return false;
    }
  },

  detectMono: function(buffersL, buffersR) {
    var diffSamples = 0;
    for (var j = 0; j < buffersL.length; j++) {
      var l = buffersL[j];
      var r = buffersR[j];
      if (l.length === r.length) {
        var d = 0.0;
        for (var i = 0; i < l.length; i++) {
          d = Math.abs(l[i] - r[i]);
          if (d > this.monoDetectThreshold) {
            diffSamples++;
          }
        }
      } else {
        diffSamples++;
      }
    }
    if (diffSamples > 0) {
      this.test.reportInfo('Stereo microphone detected.');
    } else {
      this.test.reportInfo('Mono microphone detected.');
    }
  },

  dBFS: function(gain) {
    var dB = 20 * Math.log(gain) / Math.log(10);
    // Use Math.round to display up to one decimal place.
    return Math.round(dB * 10) / 10;
  },
};
<commit_msg>Fix comments<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';

addTest(testSuiteName.MICROPHONE, testCaseName.AUDIOCAPTURE, function(test) {
  var micTest = new MicTest(test);
  micTest.run();
});

function MicTest(test) {
  this.test = test;
  this.inputChannelCount = 6;
  this.outputChannelCount = 2;
  // Buffer size set to 0 to let Chrome choose based on the platform.
  this.bufferSize = 0;
  // Turning off echoCancellation constraint enables stereo input.
  this.constraints = {
    audio: {
      optional: [
        {echoCancellation: false}
      ]
    }
  };

  this.collectSeconds = 2.0;
  // At least one LSB 16-bit data (compare is on absolute value).
  this.silentThreshold = 1.0 / 32767;
  this.lowVolumeThreshold = -60;
  // Data must be identical within one LSB 16-bit to be identified as mono.
  this.monoDetectThreshold = 1.0 / 65536;
  // Number of consequtive clipThreshold level samples that indicate clipping.
  this.clipCountThreshold = 6;
  this.clipThreshold = 1.0;

  // Populated with audio as a 3-dimensional array:
  //   collectedAudio[channels][buffers][samples]
  this.collectedAudio = [];
  this.collectedSampleCount = 0;
  for (var i = 0; i < this.inputChannelCount; ++i) {
    this.collectedAudio[i] = [];
  }
}

MicTest.prototype = {
  run: function() {
    if (typeof audioContext === 'undefined') {
      this.test.reportError('WebAudio is not supported, test cannot run.');
      this.test.done();
    } else {
      doGetUserMedia(this.constraints, this.gotStream.bind(this));
    }
  },

  gotStream: function(stream) {
    if (!this.checkAudioTracks(stream)) {
      this.test.done();
      return;
    }
    this.createAudioBuffer(stream);
  },

  checkAudioTracks: function(stream) {
    this.stream = stream;
    var audioTracks = stream.getAudioTracks();
    if (audioTracks.length < 1) {
      this.test.reportError('No audio track in returned stream.');
      return false;
    }
    this.test.reportSuccess('Audio track created using device=' +
        audioTracks[0].label);
    return true;
  },

  createAudioBuffer: function() {
    this.audioSource = audioContext.createMediaStreamSource(this.stream);
    this.scriptNode = audioContext.createScriptProcessor(this.bufferSize,
        this.inputChannelCount, this.outputChannelCount);
    this.audioSource.connect(this.scriptNode);
    this.scriptNode.connect(audioContext.destination);
    this.scriptNode.onaudioprocess = this.collectAudio.bind(this);
    this.stopCollectingAudio = setTimeoutWithProgressBar(
        this.onStopCollectingAudio.bind(this), 5000);
  },

  collectAudio: function(event) {
    // Simple silence detection: check first and last sample of each channel in
    // the buffer. If both are below a threshold, the buffer is considered
    // silent.
    var sampleCount = event.inputBuffer.length;
    var allSilent = true;
    for (var c = 0; c < event.inputBuffer.numberOfChannels; c++) {
      var data = event.inputBuffer.getChannelData(c);
      var first = Math.abs(data[0]);
      var last = Math.abs(data[sampleCount - 1]);
      var newBuffer;
      if (first > this.silentThreshold || last > this.silentThreshold) {
        // Non-silent buffers are copied for analysis. Note that the silent
        // detection will likely cause the stored stream to contain discontinu-
        // ities, but that is ok for our needs here (just looking at levels).
        newBuffer = new Float32Array(sampleCount);
        newBuffer.set(data);
        allSilent = false;
      } else {
        // Silent buffers are not copied, but we store empty buffers so that the
        // analysis doesn't have to care.
        newBuffer = new Float32Array();
      }
      this.collectedAudio[c].push(newBuffer);
    }
    if (!allSilent) {
      this.collectedSampleCount += sampleCount;
      if ((this.collectedSampleCount / event.inputBuffer.sampleRate) >=
          this.collectSeconds) {
        this.stopCollectingAudio();
      }
    }
  },

  onStopCollectingAudio: function() {
    this.stream.getAudioTracks()[0].stop();
    this.audioSource.disconnect(this.scriptNode);
    this.scriptNode.disconnect(audioContext.destination);
    this.analyzeAudio(this.collectedAudio);
    this.test.done();
  },

  analyzeAudio: function(channels) {
    var activeChannels = [];
    for (var c = 0; c < channels.length; c++) {
      if (this.channelStats(c, channels[c])) {
        activeChannels.push(c);
      }
    }
    if (activeChannels.length === 0) {
      this.test.reportError('No active input channels detected. Microphone ' +
          'is most likely muted or broken, please check if muted in the ' +
          'sound settings or physically on the device. Then rerun the test.');
    } else {
      this.test.reportSuccess('Active audio input channels: ' +
          activeChannels.length);
    }
    if (activeChannels.length === 2) {
      this.detectMono(channels[activeChannels[0]], channels[activeChannels[1]]);
    }
  },

  channelStats: function(channelNumber, buffers) {
    var maxPeak = 0.0;
    var maxRms = 0.0;
    var clipCount = 0;
    var maxClipCount = 0;
    for (var j = 0; j < buffers.length; j++) {
      var samples = buffers[j];
      if (samples.length > 0) {
        var s = 0;
        var rms = 0.0;
        for (var i = 0; i < samples.length; i++) {
          s = Math.abs(samples[i]);
          maxPeak = Math.max(maxPeak, s);
          rms += s * s;
          if (maxPeak >= this.clipThreshold) {
            clipCount++;
            maxClipCount = Math.max(maxClipCount, clipCount);
          } else {
            clipCount = 0;
          }
        }
        // RMS is calculated over each buffer, meaning the integration time will
        // be different depending on sample rate and buffer size. In practise
        // this should be a small problem.
        rms = Math.sqrt(rms / samples.length);
        maxRms = Math.max(maxRms, rms);
      }
    }

    if (maxPeak > this.silentThreshold) {
      var dBPeak = this.dBFS(maxPeak);
      var dBRms = this.dBFS(maxRms);
      this.test.reportInfo('Channel ' + channelNumber + ' levels: ' +
          dBPeak.toFixed(1) + ' dB (peak), ' + dBRms.toFixed(1) + ' dB (RMS)');
      if (dBRms < this.lowVolumeThreshold) {
        this.test.reportError('Microphone input level is low, increase input ' +
            'volume or move closer to the microphone.');
      }
      if (maxClipCount > this.clipCountThreshold) {
        this.test.reportWarning('Clipping detected! Microphone input level ' +
            'is high. Decrease input volume or move away from the microphone.');
      }
      return true;
    } else {
      return false;
    }
  },

  detectMono: function(buffersL, buffersR) {
    var diffSamples = 0;
    for (var j = 0; j < buffersL.length; j++) {
      var l = buffersL[j];
      var r = buffersR[j];
      if (l.length === r.length) {
        var d = 0.0;
        for (var i = 0; i < l.length; i++) {
          d = Math.abs(l[i] - r[i]);
          if (d > this.monoDetectThreshold) {
            diffSamples++;
          }
        }
      } else {
        diffSamples++;
      }
    }
    if (diffSamples > 0) {
      this.test.reportInfo('Stereo microphone detected.');
    } else {
      this.test.reportInfo('Mono microphone detected.');
    }
  },

  dBFS: function(gain) {
    var dB = 20 * Math.log(gain) / Math.log(10);
    // Use Math.round to display up to one decimal place.
    return Math.round(dB * 10) / 10;
  },
};
<|endoftext|>


<commit_before> /**
	* Project: Modular
	* Name: modular.css V 1.4
	* Author: Corbin "Spr0xy" Matschull
	* License: MIT
	*
	*
	*
	* MASSIVE TODO: Rewrite this entire file, it's clunky, big and buggy!
 **/
  /* jQuery checker for propagation from the top of the page.

  jQuery.fn.topper = function(prop){
  'use strict';
    var v = parseInt(this.css(prop),10);
    return isNaN(v) ? 0 : v;
  };	*/

/**
* NavBurger controller
**/
 $('.navburger').click(function(){
    $('.navburger').toggleClass('open');
    $('.nav-links').toggleClass('open');
  });

	/**
	*
	* Modal controller
	*
	**/
	$('.popup-button').click(function(){
			var down = false;
				if(!$('body').hasClass('modal--open') && down == false){
					$('body').addClass('modal--open');
					$('.main-modal').toggleClass('modal-open');
					$('.popup').toggleClass('popped');
					down = true;
					}else{
					throw new Error('!Cannot animate modal!');
				}
		$('.popup').click(function(){
			if($('body').hasClass('modal--open') && down == true){
				$('body').removeClass('modal--open');
				$('.main-modal').toggleClass('modal-open');
				$('.popup').toggleClass('popped');
				down = false;
			}
		});
});

/**
* Warning bar controller
**/
$('.button-warning').click(function(){
	var animated = false;
		if(!$('body').hasClass('bar--open') && animated == false){
				$('body').addClass('bar--open');
				$('.warning-bar').toggleClass('open', true);
				animated = true;
		}else{
			throw new Error('Cannot animate warning bar element');
		}
		$('.warning-bar-toggle').click(function(){
			if($('body').hasClass('bar--open') && animated == true){
				$('body').removeClass('bar--open');
				$('.warning-bar').toggleClass('open', false);
				animated = false;
			}
		});
});

/**
* Default bar controller
**/
$('.button-default').click(function(){
	var animated = false;
		if(!$('body').hasClass('bar--open') && animated == false){
				$('body').addClass('bar--open');
				$('.default-bar').toggleClass('open', true);
				animated = true;
		}else{
			throw new Error('Cannot animate warning bar element');
		}
		$('.default-bar-toggle').click(function(){
			if($('body').hasClass('bar--open') && animated == true){
				$('body').removeClass('bar--open');
				$('.default-bar').toggleClass('open', false);
				animated = false;
			}
		});
});

/**
* Toaster Controller
*
* TODO: Write better size and location linter
**/
$(document).ready(function(){
	var animated = false,
		 toast               = $('#toast'),
		 toasterMain	   = $('#toastContainer'),
		 containerLeft   = $(toasterMain).hasClass('bottomLeft'),
		 containerRight = $(toasterMain).hasClass('bottomRight')
	;

	if(containerLeft && !containerRight && !animated){
			$(toast).click(function(){
			$(toasterMain).removeClass('toaster-cold').addClass('toaster-active');
			animated = true;
			setTimeout(function(){
				$(toasterMain).addClass('toaster-cold').removeClass('toaster-active').dequeue(); }, 4000);
				animated = false;
			});
		}else if(containerRight && !containerLeft && !animated){
			$(toasterMain).addClass('i-am-toast-right');
			$(toasterMain).removeClass('i-am-toast');

			$(toast).click(function(){
			$(toasterMain).animate({ 'left' : '81vw' });
			});
		}else{
			throw new Error('Error in passing toast function to DOM');
		}
});
<commit_msg>Removed TODO. Completed.<commit_after> /**
	* Project: Modular
	* Name: modular.css V 1.4
	* Author: Corbin "Spr0xy" Matschull
	* License: MIT
 **/
  /* jQuery checker for propagation from the top of the page.

  jQuery.fn.topper = function(prop){
  'use strict';
    var v = parseInt(this.css(prop),10);
    return isNaN(v) ? 0 : v;
  };	*/

/**
* NavBurger controller
**/
 $('.navburger').click(function(){
    $('.navburger').toggleClass('open');
    $('.nav-links').toggleClass('open');
  });

	/**
	*
	* Modal controller
	*
	**/
	$('.popup-button').click(function(){
			var down = false;
				if(!$('body').hasClass('modal--open') && down == false){
					$('body').addClass('modal--open');
					$('.main-modal').toggleClass('modal-open');
					$('.popup').toggleClass('popped');
					down = true;
					}else{
					throw new Error('!Cannot animate modal!');
				}
		$('.popup').click(function(){
			if($('body').hasClass('modal--open') && down == true){
				$('body').removeClass('modal--open');
				$('.main-modal').toggleClass('modal-open');
				$('.popup').toggleClass('popped');
				down = false;
			}
		});
});

/**
* Warning bar controller
**/
$('.button-warning').click(function(){
	var animated = false;
		if(!$('body').hasClass('bar--open') && animated == false){
				$('body').addClass('bar--open');
				$('.warning-bar').toggleClass('open', true);
				animated = true;
		}else{
			throw new Error('Cannot animate warning bar element');
		}
		$('.warning-bar-toggle').click(function(){
			if($('body').hasClass('bar--open') && animated == true){
				$('body').removeClass('bar--open');
				$('.warning-bar').toggleClass('open', false);
				animated = false;
			}
		});
});

/**
* Default bar controller
**/
$('.button-default').click(function(){
	var animated = false;
		if(!$('body').hasClass('bar--open') && animated == false){
				$('body').addClass('bar--open');
				$('.default-bar').toggleClass('open', true);
				animated = true;
		}else{
			throw new Error('Cannot animate warning bar element');
		}
		$('.default-bar-toggle').click(function(){
			if($('body').hasClass('bar--open') && animated == true){
				$('body').removeClass('bar--open');
				$('.default-bar').toggleClass('open', false);
				animated = false;
			}
		});
});

/**
* Toaster Controller
*
* TODO: Write better size and location linter
**/
$(document).ready(function(){
	var animated = false,
		 toast               = $('#toast'),
		 toasterMain	   = $('#toastContainer'),
		 containerLeft   = $(toasterMain).hasClass('bottomLeft'),
		 containerRight = $(toasterMain).hasClass('bottomRight')
	;

	if(containerLeft && !containerRight && !animated){
			$(toast).click(function(){
			$(toasterMain).removeClass('toaster-cold').addClass('toaster-active');
			animated = true;
			setTimeout(function(){
				$(toasterMain).addClass('toaster-cold').removeClass('toaster-active').dequeue(); }, 4000);
				animated = false;
			});
		}else if(containerRight && !containerLeft && !animated){
			$(toasterMain).addClass('i-am-toast-right');
			$(toasterMain).removeClass('i-am-toast');

			$(toast).click(function(){
			$(toasterMain).animate({ 'left' : '81vw' });
			});
		}else{
			throw new Error('Error in passing toast function to DOM');
		}
});
<|endoftext|>


<commit_before>/*
 *  Proxy (ES6 draft)
 */

/*===
proxy existence
Proxy exists: true function
Proxy.length: 2
Proxy.name: Proxy
Proxy desc: writable=true enumerable=false configurable=true
Proxy.revocable exists: true function
Proxy.revocable.length: 2
Proxy.revocable.name: revocable
Proxy.revocable desc: writable=true enumerable=false configurable=true
===*/

function proxyExistenceTest() {
    var pd;

    print('Proxy exists:', 'Proxy' in this, typeof this.Proxy);
    print('Proxy.length:', this.Proxy.length);
    print('Proxy.name:', this.Proxy.name);

    pd = Object.getOwnPropertyDescriptor(this, 'Proxy');
    if (pd) {
        print('Proxy desc:', 'writable=' + pd.writable, 'enumerable=' + pd.enumerable,
              'configurable=' + pd.configurable);
    }

    print('Proxy.revocable exists:', 'revocable' in this.Proxy, typeof this.Proxy.revocable);
    print('Proxy.revocable.length:', this.Proxy.revocable.length);
    print('Proxy.revocable.name:', this.Proxy.revocable.name);

    pd = Object.getOwnPropertyDescriptor(this.Proxy, 'revocable');
    if (pd) {
        print('Proxy.revocable desc:', 'writable=' + pd.writable, 'enumerable=' + pd.enumerable,
              'configurable=' + pd.configurable);
    }

    // FIXME: lengths and names
}

print('proxy existence');

try {
    proxyExistenceTest();
} catch (e) {
    print(e);
}

/*===
subset proxy
object
[object Object]
counts: get=0 set=0 del=0
handler.get: true true string foo true
get foo: fake-for-key-foo
handler.get: true true string bar true
get bar: fake-for-key-bar
handler.get: true true string 1000 true
get 1000 (string): fake-for-key-1000
handler.get: true true number 1000 true
get 1000 (number): 2000
counts: get=4 set=0 del=0
handler.get: true true string special true
get special: specialValue
handler.get: true true string special true
get special: uncaughtValue
target.special: uncaughtValue
counts: get=6 set=0 del=0
handler.set: true true string foo number 123 true
handler.set: true true string bar number 234 true
handler.set: true true string quux number 345 true
handler.set: true true number 123 string foo true
handler.set: true true string rejectSet1 string reject true
handler.set: true true string rejectSet2 string reject true
counts: get=6 set=6 del=0
target.foo: 123
target.bar: 234
target.quux: 345
target[123]: foo
target.rejectSet1: undefined
target.rejectSet2: undefined
counts: get=6 set=6 del=0
target.foo: undefined
target.bar: 234
counts: get=6 set=6 del=0
handler.deleteProperty: true true string rejectSet1
handler.deleteProperty: true true string rejectSet2
handler.deleteProperty: true true string foo
handler.deleteProperty: true true string 1234
counts: get=6 set=6 del=4
target.rejectSet1: reject1
target.rejectSet2: reject2
target.foo undefined
target[1234] undefined
===*/

function subsetProxyTest() {
    var getCount = 0;
    var setCount = 0;
    var deleteCount = 0;
    var target = { foo: 123, '1000': 'thousand', special: 'specialValue' };
    var handler = {};
    var proxy = new Proxy(target, handler);

    function printCounts() {
        print('counts:', 'get=' + getCount, 'set=' + setCount, 'del=' + deleteCount);
    }

    print(typeof proxy);
    print(Object.prototype.toString.call(proxy));  // XXX: now class is 'Object'

    // handler 'get' hook
    handler.get = function(targ, key, receiver) {
        print('handler.get:', this === handler, targ === target, typeof key, key, receiver === proxy);
        getCount++;
        if (typeof key === 'number') {
            return 2 * (+key);
        } else if (key === 'special') {
            return targ.special;
        } else {
            return 'fake-for-key-' + key;
        }
    };

    // Get tests
    printCounts();
    print('get foo:', proxy.foo);
    print('get bar:', proxy.bar);
    print('get 1000 (string):', proxy['1000']);
    print('get 1000 (number):', proxy[1000]);
    printCounts();

    // without a 'set' hook, writes go through
    print('get special:', proxy.special);
    proxy.special = 'uncaughtValue';  // goes into 'target'
    print('get special:', proxy.special);
    print('target.special:', target.special);

    // handler 'set' hook
    handler.set = function(targ, key, val, receiver) {
        print('handler.set:', this === handler, targ === target, typeof key, key, typeof val, val, receiver === proxy);
        setCount++;
        if (key === 'rejectSet1') {
            // false return code prevents target object from being modified
            return false;
        }
        if (key === 'rejectSet2') {
            // same for any 'falsy' value
            return 0;
        }
        return true;
    };

    // Set tests
    printCounts();
    proxy.foo = 123;
    proxy.bar = 234;
    proxy.quux = 345;
    proxy[123] = 'foo';
    proxy.rejectSet1 = 'reject';
    proxy.rejectSet2 = 'reject';
    printCounts();
    print('target.foo:', target.foo);
    print('target.bar:', target.bar);
    print('target.quux:', target.quux);
    print('target[123]:', target[123]);
    print('target.rejectSet1:', target.rejectSet1);
    print('target.rejectSet2:', target.rejectSet2);

    // without a 'deleteProperty' hook, deletes go through
    printCounts();
    delete proxy.foo;
    print('target.foo:', target.foo);
    print('target.bar:', target.bar);
    printCounts();

    // handler 'deleteProperty' hook
    handler.deleteProperty = function(targ, key) {
        print('handler.deleteProperty:', this === handler, targ === target, typeof key, key);
        deleteCount++;
        if (key === 'rejectSet1') {
            // false return code prevents target object from being modified
            return false;
        }
        if (key === 'rejectSet2') {
            // same for any 'falsy' value
            return 0;
        }
        return true;
    };

    target.rejectSet1 = 'reject1';
    target.rejectSet2 = 'reject2';
    target.foo = 123;
    target[1234] = 4321;
    delete proxy.rejectSet1;
    delete proxy.rejectSet2;
    delete proxy.foo;  // allowed
    delete proxy[1234];  // allowed
    printCounts();
    print('target.rejectSet1:', target.rejectSet1);
    print('target.rejectSet2:', target.rejectSet2);
    print('target.foo', target.foo);
    print('target[1234]', target[1234]);
}

print('subset proxy');

try {
    subsetProxyTest();
} catch (e) {
    print(e);
}

/*===
recursive proxies
TypeError
TypeError
===*/

/* Currently Duktape doesn't allow a proxy as either a handler or a target.
 * This makes it easier to implement because there is no arbitrary depth C
 * recursion when doing proxy lookups.
 */

function proxyHandlerTest() {
    var target = { foo: 123 };
    var handler = new Proxy({}, {});
    var proxy = new Proxy(target, handler);
}

function proxyTargetTest() {
    var target = new Proxy({}, {});
    var handler = {};
    var proxy = new Proxy(target, handler);
}

print('recursive proxies');

try {
    proxyHandlerTest();
} catch (e) {
    print(e.name);
}

try {
    proxyTargetTest();
} catch (e) {
    print(e.name);
}

/*===
getter handler
handler.get: true true foo true
proxy.foo: dummy-value
===*/

/* A getter as a handler property.  No reason why this wouldn't work but
 * test just in case.
 */

function getterHandlerTest() {
    var target = { foo: 123 };
    var handler = {};
    var proxy = new Proxy(target, handler);

    Object.defineProperty(handler, 'get', {
        enumerable: true,
        configurable: true,
        get: function () {
            return function (targ, key, receiver) {
                print('handler.get:', this === handler, targ === target, key, receiver === proxy);
                return 'dummy-value';
            };
        }
    });

    print('proxy.foo:', proxy.foo);
}

print('getter handler');

try {
    getterHandlerTest();
} catch (e) {
    print(e);
}

/*===
non-callable handler
TypeError
===*/

/* A non-callable handler property.  This is not checked during proxy creation
 * and should cause a TypeError.
 */

function nonCallableHandlerTest() {
    var target = { foo: 123 };
    var handler = { get: 'dummy' };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);
}

print('non-callable handler');

try {
    nonCallableHandlerTest();
} catch (e) {
    print(e.name);
}

/*===
throwing handler
handler.get: about to throw
URIError: fake error
===*/

/* Handler function throws.  Nothing special here. */

function throwingHandlerTest() {
    var target = { foo: 123 };
    var handler = {
        get: function() {
            print('handler.get: about to throw');
            throw new URIError('fake error');
        }
    };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);

}

print('throwing handler');

try {
    throwingHandlerTest();
} catch (e) {
    print(e);
}

/*===
proxy revocation
handler.get: true true foo true
proxy.foo: dummy-value
===*/

/* Revoked proxy. */

function proxyRevocationTest() {
    var target = { foo: 123 };
    var handler = {
        get: function(targ, key, receiver) {
            print('handler.get:', this === handler, targ === target, key, receiver === proxy);
            return 'dummy-value';
        }
    };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);

    // FIXME: unimplemented
}

print('proxy revocation');

try {
    proxyRevocationTest();
} catch (e) {
    print(e.name);
}
<commit_msg>proxy deleteProperty test fix<commit_after>/*
 *  Proxy (ES6 draft)
 */

/*===
proxy existence
Proxy exists: true function
Proxy.length: 2
Proxy.name: Proxy
Proxy desc: writable=true enumerable=false configurable=true
Proxy.revocable exists: true function
Proxy.revocable.length: 2
Proxy.revocable.name: revocable
Proxy.revocable desc: writable=true enumerable=false configurable=true
===*/

function proxyExistenceTest() {
    var pd;

    print('Proxy exists:', 'Proxy' in this, typeof this.Proxy);
    print('Proxy.length:', this.Proxy.length);
    print('Proxy.name:', this.Proxy.name);

    pd = Object.getOwnPropertyDescriptor(this, 'Proxy');
    if (pd) {
        print('Proxy desc:', 'writable=' + pd.writable, 'enumerable=' + pd.enumerable,
              'configurable=' + pd.configurable);
    }

    print('Proxy.revocable exists:', 'revocable' in this.Proxy, typeof this.Proxy.revocable);
    print('Proxy.revocable.length:', this.Proxy.revocable.length);
    print('Proxy.revocable.name:', this.Proxy.revocable.name);

    pd = Object.getOwnPropertyDescriptor(this.Proxy, 'revocable');
    if (pd) {
        print('Proxy.revocable desc:', 'writable=' + pd.writable, 'enumerable=' + pd.enumerable,
              'configurable=' + pd.configurable);
    }

    // FIXME: lengths and names
}

print('proxy existence');

try {
    proxyExistenceTest();
} catch (e) {
    print(e);
}

/*===
subset proxy
object
[object Object]
counts: get=0 set=0 del=0
handler.get: true true string foo true
get foo: fake-for-key-foo
handler.get: true true string bar true
get bar: fake-for-key-bar
handler.get: true true string 1000 true
get 1000 (string): fake-for-key-1000
handler.get: true true number 1000 true
get 1000 (number): 2000
counts: get=4 set=0 del=0
handler.get: true true string special true
get special: specialValue
handler.get: true true string special true
get special: uncaughtValue
target.special: uncaughtValue
counts: get=6 set=0 del=0
handler.set: true true string foo number 123 true
handler.set: true true string bar number 234 true
handler.set: true true string quux number 345 true
handler.set: true true number 123 string foo true
handler.set: true true string rejectSet1 string reject true
handler.set: true true string rejectSet2 string reject true
counts: get=6 set=6 del=0
target.foo: 123
target.bar: 234
target.quux: 345
target[123]: foo
target.rejectSet1: undefined
target.rejectSet2: undefined
counts: get=6 set=6 del=0
target.foo: undefined
target.bar: 234
counts: get=6 set=6 del=0
handler.deleteProperty: true true string rejectSet1
handler.deleteProperty: true true string rejectSet2
handler.deleteProperty: true true string foo
handler.deleteProperty: true true number 1234
counts: get=6 set=6 del=4
target.rejectSet1: reject1
target.rejectSet2: reject2
target.foo undefined
target[1234] undefined
===*/

function subsetProxyTest() {
    var getCount = 0;
    var setCount = 0;
    var deleteCount = 0;
    var target = { foo: 123, '1000': 'thousand', special: 'specialValue' };
    var handler = {};
    var proxy = new Proxy(target, handler);

    function printCounts() {
        print('counts:', 'get=' + getCount, 'set=' + setCount, 'del=' + deleteCount);
    }

    print(typeof proxy);
    print(Object.prototype.toString.call(proxy));  // XXX: now class is 'Object'

    // handler 'get' hook
    handler.get = function(targ, key, receiver) {
        print('handler.get:', this === handler, targ === target, typeof key, key, receiver === proxy);
        getCount++;
        if (typeof key === 'number') {
            return 2 * (+key);
        } else if (key === 'special') {
            return targ.special;
        } else {
            return 'fake-for-key-' + key;
        }
    };

    // Get tests
    printCounts();
    print('get foo:', proxy.foo);
    print('get bar:', proxy.bar);
    print('get 1000 (string):', proxy['1000']);
    print('get 1000 (number):', proxy[1000]);
    printCounts();

    // without a 'set' hook, writes go through
    print('get special:', proxy.special);
    proxy.special = 'uncaughtValue';  // goes into 'target'
    print('get special:', proxy.special);
    print('target.special:', target.special);

    // handler 'set' hook
    handler.set = function(targ, key, val, receiver) {
        print('handler.set:', this === handler, targ === target, typeof key, key, typeof val, val, receiver === proxy);
        setCount++;
        if (key === 'rejectSet1') {
            // false return code prevents target object from being modified
            return false;
        }
        if (key === 'rejectSet2') {
            // same for any 'falsy' value
            return 0;
        }
        return true;
    };

    // Set tests
    printCounts();
    proxy.foo = 123;
    proxy.bar = 234;
    proxy.quux = 345;
    proxy[123] = 'foo';
    proxy.rejectSet1 = 'reject';
    proxy.rejectSet2 = 'reject';
    printCounts();
    print('target.foo:', target.foo);
    print('target.bar:', target.bar);
    print('target.quux:', target.quux);
    print('target[123]:', target[123]);
    print('target.rejectSet1:', target.rejectSet1);
    print('target.rejectSet2:', target.rejectSet2);

    // without a 'deleteProperty' hook, deletes go through
    printCounts();
    delete proxy.foo;
    print('target.foo:', target.foo);
    print('target.bar:', target.bar);
    printCounts();

    // handler 'deleteProperty' hook
    handler.deleteProperty = function(targ, key) {
        print('handler.deleteProperty:', this === handler, targ === target, typeof key, key);
        deleteCount++;
        if (key === 'rejectSet1') {
            // false return code prevents target object from being modified
            return false;
        }
        if (key === 'rejectSet2') {
            // same for any 'falsy' value
            return 0;
        }
        return true;
    };

    target.rejectSet1 = 'reject1';
    target.rejectSet2 = 'reject2';
    target.foo = 123;
    target[1234] = 4321;
    delete proxy.rejectSet1;
    delete proxy.rejectSet2;
    delete proxy.foo;  // allowed
    delete proxy[1234];  // allowed
    printCounts();
    print('target.rejectSet1:', target.rejectSet1);
    print('target.rejectSet2:', target.rejectSet2);
    print('target.foo', target.foo);
    print('target[1234]', target[1234]);
}

print('subset proxy');

try {
    subsetProxyTest();
} catch (e) {
    print(e);
}

/*===
recursive proxies
TypeError
TypeError
===*/

/* Currently Duktape doesn't allow a proxy as either a handler or a target.
 * This makes it easier to implement because there is no arbitrary depth C
 * recursion when doing proxy lookups.
 */

function proxyHandlerTest() {
    var target = { foo: 123 };
    var handler = new Proxy({}, {});
    var proxy = new Proxy(target, handler);
}

function proxyTargetTest() {
    var target = new Proxy({}, {});
    var handler = {};
    var proxy = new Proxy(target, handler);
}

print('recursive proxies');

try {
    proxyHandlerTest();
} catch (e) {
    print(e.name);
}

try {
    proxyTargetTest();
} catch (e) {
    print(e.name);
}

/*===
getter handler
handler.get: true true foo true
proxy.foo: dummy-value
===*/

/* A getter as a handler property.  No reason why this wouldn't work but
 * test just in case.
 */

function getterHandlerTest() {
    var target = { foo: 123 };
    var handler = {};
    var proxy = new Proxy(target, handler);

    Object.defineProperty(handler, 'get', {
        enumerable: true,
        configurable: true,
        get: function () {
            return function (targ, key, receiver) {
                print('handler.get:', this === handler, targ === target, key, receiver === proxy);
                return 'dummy-value';
            };
        }
    });

    print('proxy.foo:', proxy.foo);
}

print('getter handler');

try {
    getterHandlerTest();
} catch (e) {
    print(e);
}

/*===
non-callable handler
TypeError
===*/

/* A non-callable handler property.  This is not checked during proxy creation
 * and should cause a TypeError.
 */

function nonCallableHandlerTest() {
    var target = { foo: 123 };
    var handler = { get: 'dummy' };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);
}

print('non-callable handler');

try {
    nonCallableHandlerTest();
} catch (e) {
    print(e.name);
}

/*===
throwing handler
handler.get: about to throw
URIError: fake error
===*/

/* Handler function throws.  Nothing special here. */

function throwingHandlerTest() {
    var target = { foo: 123 };
    var handler = {
        get: function() {
            print('handler.get: about to throw');
            throw new URIError('fake error');
        }
    };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);

}

print('throwing handler');

try {
    throwingHandlerTest();
} catch (e) {
    print(e);
}

/*===
proxy revocation
handler.get: true true foo true
proxy.foo: dummy-value
===*/

/* Revoked proxy. */

function proxyRevocationTest() {
    var target = { foo: 123 };
    var handler = {
        get: function(targ, key, receiver) {
            print('handler.get:', this === handler, targ === target, key, receiver === proxy);
            return 'dummy-value';
        }
    };
    var proxy = new Proxy(target, handler);
    print('proxy.foo:', proxy.foo);

    // FIXME: unimplemented
}

print('proxy revocation');

try {
    proxyRevocationTest();
} catch (e) {
    print(e.name);
}
<|endoftext|>


<commit_before>/**
 * These options can be overridden by passing an object to the ``mhtEditor``
 * function, e.g.:
 *
 *     mhtEditor({
 *       div: 'myDivId',
 *       clientId: '<google-client-id>',
 *       templateId: "0AppSVxABhnltdEhzQjQ4MlpOaldjTmZLclQxQWFTOUE",
 *       spreadsheetsCorsProxy: "https://localhost:5000"
 *     })
 */
module.exports = {
  /** Default template ID to use for creating new spreadsheets */
  templateId: "0AppSVxABhnltdEhzQjQ4MlpOaldjTmZLclQxQWFTOUE",
  /** Default TimelineJS template */
  redirectParam: "oauth2callback",
  /** URL parameter for timeline IDs */
  timelineParam: "timeline",
  /** URL parameter for edit mode */
  editParam: "edit",
  /** Proxy to use for adding CORS headers to Google Spreadsheets API responses. */
  spreadsheetsCorsProxy: "https://gspreadsheetscorsproxy.herokuapp.com"
};
<commit_msg>Fix comment for redirectParam<commit_after>/**
 * These options can be overridden by passing an object to the ``mhtEditor``
 * function, e.g.:
 *
 *     mhtEditor({
 *       div: 'myDivId',
 *       clientId: '<google-client-id>',
 *       templateId: "0AppSVxABhnltdEhzQjQ4MlpOaldjTmZLclQxQWFTOUE",
 *       spreadsheetsCorsProxy: "https://localhost:5000"
 *     })
 */
module.exports = {
  /** Default template ID to use for creating new spreadsheets */
  templateId: "0AppSVxABhnltdEhzQjQ4MlpOaldjTmZLclQxQWFTOUE",
  /** URL parameter for oauth popups */
  redirectParam: "oauth2callback",
  /** URL parameter for timeline IDs */
  timelineParam: "timeline",
  /** URL parameter for edit mode */
  editParam: "edit",
  /** Proxy to use for adding CORS headers to Google Spreadsheets API responses. */
  spreadsheetsCorsProxy: "https://gspreadsheetscorsproxy.herokuapp.com"
};
<|endoftext|>


<commit_before>/**
 * Runtime environment for Jarlang
 * Provides all of the neccessary environment functions to allow Jarlang to work as intended
 * such as our process queue
 */
const jrts = (function (secondsPerTick) {
    'use strict';

    // Return a shuffled copy of an array, this is used so that the processes queue
    // is mapped over randomly each tick
    function shuffle(input) {
        let array = [...input];

        let count = array.length;
        let randomnumber;
        let temp;
        while (count) {
            randomnumber = Math.random() * count-- | 0;
            temp = array[count];
            array[count] = array[randomnumber];
            array[randomnumber] = temp;
        }

        return array;
    }

    // Spawn a new process which registers itself with the processes queue
    function spawnProcess(lambda, pidGenerator) {
        return new Process(lambda, pidGenerator).pid;
    }

    function jsToErlang(variable) {
        let erlVar = null;
        switch (typeof variable) {
            case "number":
                if (Number.isInteger(variable)) {
                    erlVar = new Int(variable);
                } else {
                    erlVar = new Float(variable);
                }
                break;
            case "string":
                erlVar = new List(...(variable.split("").map(char => new Int(char.charCodeAt(0)))));
                break;
            case "object":
                if (Array.isArray(variable)) {
                    erlVar = new List(...(variable.map(jsToErlang)));
                } else if (variable.constructor.name === "Uint8Array") {
                    erlVar = new BitString(...variable);
                } else if (variable.constructor.name === "Object") {
                    erlVar = new Map(variable);
                } else {
                    erlVar = variable;
                }
                break;
            default: erlVar = variable;
        }

        return erlVar;
    }

    function erlangToJs(variable) {
        let jsVar = null;
        switch (variable !== undefined ? variable.constructor.name : undefined) {
            case "Atom":
                jsVar = variable.value;
                break;
            case "BitString":
                jsVar = variable.value;
                break;
            case "Fun":
                jsVar = variable.value;
                break;
            case "List":
                jsVar = List.isString(variable) ?
                    [...variable].map(charCode => String.fromCharCode(charCode)).join("") :
                    [...variable];
                break;
            case "Map":
                jsVar = variable.value;
                break;
            case "Int":
            case "Float":
                jsVar = Number(variable.getValue().valueOf());
                break;
            case "Pid":
                jsVar = variable.value;
                break;
            case "Port":
                jsVar = variable.value;
                break;
            case "Process":
                jsVar = variable;
                break;
            case "Reference":
                jsVar = variable.value;
                break;
            case "Tuple":
                jsVar = [...variable];
                break;
            case "Unbound":
                jsVar = undefined;
                break;
            default: jsVar = variable;
        }

        return jsVar;
    }

    // Set up timer to iterate through processes
    const eventLoop = setInterval(function () {
        shuffle(exports.processes).map(process => {
            if (process.lambdas.length) {
                process.currentLambda = process.lambdas.shift();
                process.value = process.currentLambda();
                process.currentLambda = null;
                process.lastRun = Date.now();
            }
        });
    }, secondsPerTick !== undefined ? secondsPerTick : 1);

    // const garbageCollection = setInterval(function() {
    //     for (let pid in jrts.pids) {
    //         if (Date.now() - Process.getProcess(pid).lastRun > 1000/* * 60 * 2*/) {
    //             delete jrts.processes[jrts.pids[pid]];
    //             delete jrts.pids[pid];
    //             console.log("deleted process: ", pid);
    //         }
    //     }
    // }, 5000);

    const exports = {
        processes: [],
        pids: {},
        atoms: {},
        timer: eventLoop,
        spawn: spawnProcess,
        jsToErlang: jsToErlang,
        erlangToJs: erlangToJs,
    };

    return exports;
})();
<commit_msg>boolean -> atom edge case fix<commit_after>/**
 * Runtime environment for Jarlang
 * Provides all of the neccessary environment functions to allow Jarlang to work as intended
 * such as our process queue
 */
const jrts = (function (secondsPerTick) {
    'use strict';

    // Return a shuffled copy of an array, this is used so that the processes queue
    // is mapped over randomly each tick
    function shuffle(input) {
        let array = [...input];

        let count = array.length;
        let randomnumber;
        let temp;
        while (count) {
            randomnumber = Math.random() * count-- | 0;
            temp = array[count];
            array[count] = array[randomnumber];
            array[randomnumber] = temp;
        }

        return array;
    }

    // Spawn a new process which registers itself with the processes queue
    function spawnProcess(lambda, pidGenerator) {
        return new Process(lambda, pidGenerator).pid;
    }

    function jsToErlang(variable) {
        let erlVar = null;
        switch (typeof variable) {
            case "number":
                if (Number.isInteger(variable)) {
                    erlVar = new Int(variable);
                } else {
                    erlVar = new Float(variable);
                }
                break;
            case "string":
                erlVar = new List(...(variable.split("").map(char => new Int(char.charCodeAt(0)))));
                break;
            case "boolean":
                erlVar = new Atom(variable.toString());
                break;
            case "object":
                if (Array.isArray(variable)) {
                    erlVar = new List(...(variable.map(jsToErlang)));
                } else if (variable.constructor.name === "Uint8Array") {
                    erlVar = new BitString(...variable);
                } else if (variable.constructor.name === "Object") {
                    erlVar = new Map(variable);
                } else {
                    erlVar = variable;
                }
                break;
            default: erlVar = variable;
        }

        return erlVar;
    }

    function erlangToJs(variable) {
        let jsVar = null;
        switch (variable !== undefined ? variable.constructor.name : undefined) {
            case "Atom":
                jsVar = variable.value;
                break;
            case "BitString":
                jsVar = variable.value;
                break;
            case "Fun":
                jsVar = variable.value;
                break;
            case "List":
                jsVar = List.isString(variable) ?
                    [...variable].map(charCode => String.fromCharCode(charCode)).join("") :
                    [...variable];
                break;
            case "Map":
                jsVar = variable.value;
                break;
            case "Int":
            case "Float":
                jsVar = Number(variable.getValue().valueOf());
                break;
            case "Pid":
                jsVar = variable.value;
                break;
            case "Port":
                jsVar = variable.value;
                break;
            case "Process":
                jsVar = variable;
                break;
            case "Reference":
                jsVar = variable.value;
                break;
            case "Tuple":
                jsVar = [...variable];
                break;
            case "Unbound":
                jsVar = undefined;
                break;
            default: jsVar = variable;
        }

        return jsVar;
    }

    // Set up timer to iterate through processes
    const eventLoop = setInterval(function () {
        shuffle(exports.processes).map(process => {
            if (process.lambdas.length) {
                process.currentLambda = process.lambdas.shift();
                process.value = process.currentLambda();
                process.currentLambda = null;
                process.lastRun = Date.now();
            }
        });
    }, secondsPerTick !== undefined ? secondsPerTick : 1);

    // const garbageCollection = setInterval(function() {
    //     for (let pid in jrts.pids) {
    //         if (Date.now() - Process.getProcess(pid).lastRun > 1000/* * 60 * 2*/) {
    //             delete jrts.processes[jrts.pids[pid]];
    //             delete jrts.pids[pid];
    //             console.log("deleted process: ", pid);
    //         }
    //     }
    // }, 5000);

    const exports = {
        processes: [],
        pids: {},
        atoms: {},
        timer: eventLoop,
        spawn: spawnProcess,
        jsToErlang: jsToErlang,
        erlangToJs: erlangToJs,
    };

    return exports;
})();
<|endoftext|>