<commit_before>/* eslint-disable max-len */

export {
    meta,
    isDestroyed,
    guid,
    mixin,
    merge,
    clone,
    isEqual,
    Class,
} from './core/Core.js';
export { default as sortByProperties } from './core/sortByProperties.js';
import './core/Array.js';
import './core/Date.js';
import './core/Number.js';
import './core/Object.js';
import './core/RegExp.js';
import './core/String.js';

export { Binding, bind, bindTwoWay } from './foundation/Binding.js';
export { default as BoundProps } from './foundation/BoundProps.js';
export { default as ComputedProps } from './foundation/ComputedProps.js';
export { default as Enumerable } from './foundation/Enumerable.js';
export { default as Event } from './foundation/Event.js';
export { default as EventTarget } from './foundation/EventTarget.js';
export { default as getFromPath } from './foundation/getFromPath.js';
export { default as Heap } from './foundation/Heap.js';
export { default as MutableEnumerable } from './foundation/MutableEnumerable.js';
export { default as Object } from './foundation/Object.js';
export { default as ObservableArray } from './foundation/ObservableArray.js';
export { default as ObservableProps } from './foundation/ObservableProps.js';
export { default as ObservableRange } from './foundation/ObservableRange.js';
import * as RunLoop from './foundation/RunLoop.js';
export { RunLoop };
import * as Transform from './foundation/Transform.js';
export { Transform };
import Promise from './foundation/Promise.js';
// Replace the global Promise with our RunLoop-enabled Promise
window.Promise = Promise;
// … as well as as `O.Promise`.
export { Promise };

export { default as Color } from './color/Color.js';

export { default as AnimatableView } from './animation/AnimatableView.js';
export { default as Animation } from './animation/Animation.js';
export { default as Easing } from './animation/Easing.js';
export { default as StyleAnimation } from './animation/StyleAnimation.js';

import * as keyboardShortcuts from './application/keyboardShortcuts.js';
export { keyboardShortcuts };
export { default as formatKeyForPlatform } from './application/formatKeyForPlatform.js';
export { default as toPlatformKey } from './application/toPlatformKey.js';
export { default as GlobalKeyboardShortcuts } from './application/GlobalKeyboardShortcuts.js';
export { default as Router } from './application/Router.js';
export { default as ThemeManager } from './application/ThemeManager.js';
export { default as WindowController } from './application/WindowController.js';

export { default as RecordArray } from './datastore/query/RecordArray.js';
export { default as Query } from './datastore/query/Query.js';
export { default as LocalQuery } from './datastore/query/LocalQuery.js';
export { default as WindowedQuery } from './datastore/query/WindowedQuery.js';
export { default as AttributeErrors } from './datastore/record/AttributeErrors.js';
import Record from './datastore/record/Record.js';
export { Record };
import { attr, RecordAttribute } from './datastore/record/attr.js';
Record.attr = attr;
export { RecordAttribute };
import * as Status from './datastore/record/Status.js';
export { Status };
import { toMany, ToManyAttribute } from './datastore/record/toMany.js';
Record.toMany = toMany;
export { ToManyAttribute };
import { toOne, ToOneAttribute } from './datastore/record/toOne.js';
Record.toOne = toOne;
export { ToOneAttribute };
export { default as RecordResult } from './datastore/record/RecordResult.js';
export { default as ValidationError } from './datastore/record/ValidationError.js';
export { default as AggregateSource } from './datastore/source/AggregateSource.js';
export { default as Source } from './datastore/source/Source.js';
export { default as MemoryManager } from './datastore/store/MemoryManager.js';
export { default as NestedStore } from './datastore/store/NestedStore.js';
export { default as Store } from './datastore/store/Store.js';
export { default as StoreUndoManager } from './datastore/store/StoreUndoManager.js';
export { default as UndoManager } from './datastore/store/UndoManager.js';

import * as DOMEvent from './dom/DOMEvent.js';
export { DOMEvent };
import * as Element from './dom/Element.js';
export { Element };
import * as Stylesheet from './dom/Stylesheet.js';
export { Stylesheet };

export { default as Drag } from './drag-drop/Drag.js';
export { default as DragController } from './drag-drop/DragController.js';
export { default as DragDataSource } from './drag-drop/DragDataSource.js';
import * as DragEffect from './drag-drop/DragEffect.js';
export { DragEffect };
export { default as Draggable } from './drag-drop/Draggable.js';
export { default as DropTarget } from './drag-drop/DropTarget.js';

export { default as EventSource } from './io/EventSource.js';
export { default as HttpRequest } from './io/HttpRequest.js';
export { default as IOQueue } from './io/IOQueue.js';
export { default as XHR } from './io/XHR.js';

export { default as Locale } from './localisation/Locale.js';
import * as i18n from './localisation/i18n.js';
export { i18n };
export { loc } from './localisation/i18n.js';
import './localisation/RelativeDate.js';

import * as parse from './parser/DateParser.js';
export { parse };
import Parse, {
    define,
    optional,
    not,
    repeat,
    sequence,
    firstMatch,
    longestMatch,
} from './parser/Parse.js';
Object.assign(Parse, {
    define,
    optional,
    not,
    repeat,
    sequence,
    firstMatch,
    longestMatch,
});
export { Parse };

export { default as OptionsController } from './selection/OptionsController.js';
export { default as SelectionController } from './selection/SelectionController.js';
export { default as SingleSelectionController } from './selection/SingleSelectionController.js';

export { default as LocalStorage } from './storage/LocalStorage.js';

export { default as TimeZone } from './timezones/TimeZone.js';

export { default as Gesture } from './touch/Gesture.js';
export { default as GestureManager } from './touch/GestureManager.js';
export { default as Hold } from './touch/Hold.js';
export { default as Tap } from './touch/Tap.js';

import * as UA from './ua/UA.js';
export { UA };

export { default as RootView } from './views/RootView.js';
export { default as View } from './views/View.js';
export {
    default as activeViews,
    getViewFromNode,
} from './views/activeViews.js';
export { default as ViewEventsController } from './views/ViewEventsController.js';
export { default as ListItemView } from './views/collections/ListItemView.js';
export { default as ListKBFocusView } from './views/collections/ListKBFocusView.js';
export { default as ListView } from './views/collections/ListView.js';
export { default as ProgressiveListView } from './views/collections/ProgressiveListView.js';
export { default as OptionsListView } from './views/collections/OptionsListView.js';
export {
    default as SwitchView,
    when,
    unless,
    choose,
} from './views/collections/SwitchView.js';
export { default as ToolbarView } from './views/collections/ToolbarView.js';
export { default as TrueVisibleRect } from './views/collections/TrueVisibleRect.js';
export { default as ScrollView } from './views/containers/ScrollView.js';
export { default as SplitDividerView } from './views/containers/SplitDividerView.js';
export { default as SplitViewController } from './views/containers/SplitViewController.js';
export { default as AbstractControlView } from './views/controls/AbstractControlView.js';
export { default as ButtonView } from './views/controls/ButtonView.js';
export { default as CheckboxView } from './views/controls/CheckboxView.js';
export { default as ClearSearchButtonView } from './views/controls/ClearSearchButtonView.js';
export { default as FileButtonView } from './views/controls/FileButtonView.js';
export { default as LabelView } from './views/controls/LabelView.js';
export { default as KeyDownController } from './views/controls/KeyDownController.js';
export {
    ShortcutView,
    ShortcutOverlayView,
} from './views/controls/ShortcutOverlayView.js';
export { default as MenuOptionView } from './views/menu/MenuOptionView.js';
export { default as MenuFilterView } from './views/menu/MenuFilterView.js';
export { default as MenuButtonView } from './views/menu/MenuButtonView.js';
export { default as MenuView } from './views/menu/MenuView.js';
export { default as RadioView } from './views/controls/RadioView.js';
export { default as RichTextView } from './views/controls/RichTextView.js';
export { default as SearchTextView } from './views/controls/SearchTextView.js';
export { default as SelectView } from './views/controls/SelectView.js';
export { default as TextView } from './views/controls/TextView.js';
export { default as ModalEventHandler } from './views/panels/ModalEventHandler.js';
export { default as PopOverView } from './views/panels/PopOverView.js';
<commit_msg>Remove redundant lint directive<commit_after>export {
    meta,
    isDestroyed,
    guid,
    mixin,
    merge,
    clone,
    isEqual,
    Class,
} from './core/Core.js';
export { default as sortByProperties } from './core/sortByProperties.js';
import './core/Array.js';
import './core/Date.js';
import './core/Number.js';
import './core/Object.js';
import './core/RegExp.js';
import './core/String.js';

export { Binding, bind, bindTwoWay } from './foundation/Binding.js';
export { default as BoundProps } from './foundation/BoundProps.js';
export { default as ComputedProps } from './foundation/ComputedProps.js';
export { default as Enumerable } from './foundation/Enumerable.js';
export { default as Event } from './foundation/Event.js';
export { default as EventTarget } from './foundation/EventTarget.js';
export { default as getFromPath } from './foundation/getFromPath.js';
export { default as Heap } from './foundation/Heap.js';
export { default as MutableEnumerable } from './foundation/MutableEnumerable.js';
export { default as Object } from './foundation/Object.js';
export { default as ObservableArray } from './foundation/ObservableArray.js';
export { default as ObservableProps } from './foundation/ObservableProps.js';
export { default as ObservableRange } from './foundation/ObservableRange.js';
import * as RunLoop from './foundation/RunLoop.js';
export { RunLoop };
import * as Transform from './foundation/Transform.js';
export { Transform };
import Promise from './foundation/Promise.js';
// Replace the global Promise with our RunLoop-enabled Promise
window.Promise = Promise;
// … as well as as `O.Promise`.
export { Promise };

export { default as Color } from './color/Color.js';

export { default as AnimatableView } from './animation/AnimatableView.js';
export { default as Animation } from './animation/Animation.js';
export { default as Easing } from './animation/Easing.js';
export { default as StyleAnimation } from './animation/StyleAnimation.js';

import * as keyboardShortcuts from './application/keyboardShortcuts.js';
export { keyboardShortcuts };
export { default as formatKeyForPlatform } from './application/formatKeyForPlatform.js';
export { default as toPlatformKey } from './application/toPlatformKey.js';
export { default as GlobalKeyboardShortcuts } from './application/GlobalKeyboardShortcuts.js';
export { default as Router } from './application/Router.js';
export { default as ThemeManager } from './application/ThemeManager.js';
export { default as WindowController } from './application/WindowController.js';

export { default as RecordArray } from './datastore/query/RecordArray.js';
export { default as Query } from './datastore/query/Query.js';
export { default as LocalQuery } from './datastore/query/LocalQuery.js';
export { default as WindowedQuery } from './datastore/query/WindowedQuery.js';
export { default as AttributeErrors } from './datastore/record/AttributeErrors.js';
import Record from './datastore/record/Record.js';
export { Record };
import { attr, RecordAttribute } from './datastore/record/attr.js';
Record.attr = attr;
export { RecordAttribute };
import * as Status from './datastore/record/Status.js';
export { Status };
import { toMany, ToManyAttribute } from './datastore/record/toMany.js';
Record.toMany = toMany;
export { ToManyAttribute };
import { toOne, ToOneAttribute } from './datastore/record/toOne.js';
Record.toOne = toOne;
export { ToOneAttribute };
export { default as RecordResult } from './datastore/record/RecordResult.js';
export { default as ValidationError } from './datastore/record/ValidationError.js';
export { default as AggregateSource } from './datastore/source/AggregateSource.js';
export { default as Source } from './datastore/source/Source.js';
export { default as MemoryManager } from './datastore/store/MemoryManager.js';
export { default as NestedStore } from './datastore/store/NestedStore.js';
export { default as Store } from './datastore/store/Store.js';
export { default as StoreUndoManager } from './datastore/store/StoreUndoManager.js';
export { default as UndoManager } from './datastore/store/UndoManager.js';

import * as DOMEvent from './dom/DOMEvent.js';
export { DOMEvent };
import * as Element from './dom/Element.js';
export { Element };
import * as Stylesheet from './dom/Stylesheet.js';
export { Stylesheet };

export { default as Drag } from './drag-drop/Drag.js';
export { default as DragController } from './drag-drop/DragController.js';
export { default as DragDataSource } from './drag-drop/DragDataSource.js';
import * as DragEffect from './drag-drop/DragEffect.js';
export { DragEffect };
export { default as Draggable } from './drag-drop/Draggable.js';
export { default as DropTarget } from './drag-drop/DropTarget.js';

export { default as EventSource } from './io/EventSource.js';
export { default as HttpRequest } from './io/HttpRequest.js';
export { default as IOQueue } from './io/IOQueue.js';
export { default as XHR } from './io/XHR.js';

export { default as Locale } from './localisation/Locale.js';
import * as i18n from './localisation/i18n.js';
export { i18n };
export { loc } from './localisation/i18n.js';
import './localisation/RelativeDate.js';

import * as parse from './parser/DateParser.js';
export { parse };
import Parse, {
    define,
    optional,
    not,
    repeat,
    sequence,
    firstMatch,
    longestMatch,
} from './parser/Parse.js';
Object.assign(Parse, {
    define,
    optional,
    not,
    repeat,
    sequence,
    firstMatch,
    longestMatch,
});
export { Parse };

export { default as OptionsController } from './selection/OptionsController.js';
export { default as SelectionController } from './selection/SelectionController.js';
export { default as SingleSelectionController } from './selection/SingleSelectionController.js';

export { default as LocalStorage } from './storage/LocalStorage.js';

export { default as TimeZone } from './timezones/TimeZone.js';

export { default as Gesture } from './touch/Gesture.js';
export { default as GestureManager } from './touch/GestureManager.js';
export { default as Hold } from './touch/Hold.js';
export { default as Tap } from './touch/Tap.js';

import * as UA from './ua/UA.js';
export { UA };

export { default as RootView } from './views/RootView.js';
export { default as View } from './views/View.js';
export {
    default as activeViews,
    getViewFromNode,
} from './views/activeViews.js';
export { default as ViewEventsController } from './views/ViewEventsController.js';
export { default as ListItemView } from './views/collections/ListItemView.js';
export { default as ListKBFocusView } from './views/collections/ListKBFocusView.js';
export { default as ListView } from './views/collections/ListView.js';
export { default as ProgressiveListView } from './views/collections/ProgressiveListView.js';
export { default as OptionsListView } from './views/collections/OptionsListView.js';
export {
    default as SwitchView,
    when,
    unless,
    choose,
} from './views/collections/SwitchView.js';
export { default as ToolbarView } from './views/collections/ToolbarView.js';
export { default as TrueVisibleRect } from './views/collections/TrueVisibleRect.js';
export { default as ScrollView } from './views/containers/ScrollView.js';
export { default as SplitDividerView } from './views/containers/SplitDividerView.js';
export { default as SplitViewController } from './views/containers/SplitViewController.js';
export { default as AbstractControlView } from './views/controls/AbstractControlView.js';
export { default as ButtonView } from './views/controls/ButtonView.js';
export { default as CheckboxView } from './views/controls/CheckboxView.js';
export { default as ClearSearchButtonView } from './views/controls/ClearSearchButtonView.js';
export { default as FileButtonView } from './views/controls/FileButtonView.js';
export { default as LabelView } from './views/controls/LabelView.js';
export { default as KeyDownController } from './views/controls/KeyDownController.js';
export {
    ShortcutView,
    ShortcutOverlayView,
} from './views/controls/ShortcutOverlayView.js';
export { default as MenuOptionView } from './views/menu/MenuOptionView.js';
export { default as MenuFilterView } from './views/menu/MenuFilterView.js';
export { default as MenuButtonView } from './views/menu/MenuButtonView.js';
export { default as MenuView } from './views/menu/MenuView.js';
export { default as RadioView } from './views/controls/RadioView.js';
export { default as RichTextView } from './views/controls/RichTextView.js';
export { default as SearchTextView } from './views/controls/SearchTextView.js';
export { default as SelectView } from './views/controls/SelectView.js';
export { default as TextView } from './views/controls/TextView.js';
export { default as ModalEventHandler } from './views/panels/ModalEventHandler.js';
export { default as PopOverView } from './views/panels/PopOverView.js';
<|endoftext|>


<commit_before>/**
 * Particle constructor.
 * Creates a particle that can have behaviours that are applied on each time step within the system.
 * @constructor
 * @param {Number} mass Mass of the particle.
 * @param {Number} opt_radius Optional radius value for the particle. Defaults to the same value as the mass.
 * @param {Boolean} opt_fixed Optional flag specifying whether or not the particle is fixed within the system. Behaviours are not applied to fixed particles. Defaults to false.
 * @param {Number} opt_dimensions Optional number of component dimension that the particle should have. Defaults to NDP.DIMENSIONS.
 */
NDP.Particle = function(mass, opt_radius, opt_fixed, opt_dimensions) {

  // Validate mass parameter.
  if (!NDP.isNumber(mass)) throw 'Particle: mass must be a Number ['+mass+']';
};

/**
 * Adds a behaviour to the particle.
 * @param {Behaviour} behaviour Behaviour to add to the particle.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.addBehaviour = function(behaviour) {
};

/**
 * Removes a behaviour from the particle.
 * @param {Behaviour} behaviour Behaviour to remove from the particle.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.removeBehaviour = function(behaviour) {
};

/**
 * Applies registered behaviours to the particle and updates its vectors.
 * @param {Number} delta Time delta since last integration.
 * @param {Number} index Index of the particle within the system.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.update = function(delta, index) {
};
<commit_msg>:gb: Added parameter type checking to constructor<commit_after>/**
 * Particle constructor.
 * Creates a particle that can have behaviours that are applied on each time step within the system.
 * @constructor
 * @param {Number} mass Mass of the particle.
 * @param {Number} opt_radius Optional radius value for the particle. Defaults to the same value as the mass.
 * @param {Boolean} opt_fixed Optional flag specifying whether or not the particle is fixed within the system. Behaviours are not applied to fixed particles. Defaults to false.
 * @param {Number} opt_dimensions Optional number of component dimension that the particle should have. Defaults to NDP.DIMENSIONS.
 */
NDP.Particle = function(mass, opt_radius, opt_fixed, opt_dimensions) {

  // Validate mass parameter.
  if (!NDP.isNumber(mass)) throw 'Particle: mass must be a Number ['+mass+']';

  // Set particle properties.
  this.mass = mass;
  this.radius = NDP.isNumber(opt_radius) ? opt_radius : mass;
  this.fixed = NDP.isBoolean(opt_fixed) ? opt_fixed : false;

  // Create and set dimensions property.
  Object.defineProperty(this, 'dimensions', {
    value: NDP.isNumber(opt_dimensions) ? opt_dimensions : NDP.DIMENSIONS
  });
};

/**
 * Adds a behaviour to the particle.
 * @param {Behaviour} behaviour Behaviour to add to the particle.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.addBehaviour = function(behaviour) {
};

/**
 * Removes a behaviour from the particle.
 * @param {Behaviour} behaviour Behaviour to remove from the particle.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.removeBehaviour = function(behaviour) {
};

/**
 * Applies registered behaviours to the particle and updates its vectors.
 * @param {Number} delta Time delta since last integration.
 * @param {Number} index Index of the particle within the system.
 * @return {Particle} Particle instance for chaining.
 */
NDP.Particle.prototype.update = function(delta, index) {
};
<|endoftext|>


<commit_before>let os = require('os');
let ipcRenderer = require('electron').ipcRenderer;
let delayOnExecution = 50; // in milliseconds

document.addEventListener('keyup', handleGlobalKeyPress);

let vue = new Vue({
    el: '#vue-root',
    data: {
        userInput: '',
        searchResults: [],
        autoFocus: true,
        stylesheetPath: os.platform() === 'win32' ? './styles/css/windows.css' : './styles/css/mac.css'
    },
    methods: {
        handleKeyPress: (event) => {
            if (event.key === 'Enter') {
                handleEnterPress();
            }
            if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
                event.preventDefault();
                let direction = event.key === 'ArrowDown' ? 'next' : 'prev';
                handleChangeActive(direction);
            }
        }
    },
    watch: {
        userInput: (val) => {
            ipcRenderer.send('get-search', val);
        }
    }
});

ipcRenderer.on('get-search-response', (event, arg) => {
    let searchResults = arg;
    let idIndex = 0;

    searchResults.forEach((s) => {
        s.id = `search-result-item-${idIndex}`;
        s.active = false;
        idIndex++;
    });

    if (searchResults.length > 0) {
        searchResults[0].active = true;
    }

    vue.searchResults = searchResults;
});

function handleChangeActive(direction) {
    let next;

    for (let i = 0; i < vue.searchResults.length; i++) {
        if (vue.searchResults[i].active) {
            next = direction === 'next' ? i + 1 : i - 1;
        }
    }

    vue.searchResults.forEach((s) => {
        s.active = false;
    });

    if (next < 0) {
        next = vue.searchResults.length - 1;
    }
    else if (next >= vue.searchResults.length) {
        next = 0;
    }

    vue.searchResults[next].active = true;
    scrollIntoView(vue.searchResults[next]);
}

function scrollIntoView(searchResult) {
    el = document.getElementById(searchResult.id);
    el.scrollIntoView();
}

function handleEnterPress() {
    let activeSearchResults = vue.searchResults.filter((s) => {
        return s.active;
    });

    if (activeSearchResults.length > 0) {
        resetUserInput();
        setTimeout(() => {
            execute(activeSearchResults[0].executionArgument)
        }, delayOnExecution);
    }
}

function execute(executionArgument) {
    ipcRenderer.send('execute', executionArgument);
}

function resetUserInput() {
    vue.userInput = '';
}

function handleGlobalKeyPress(event) {
    console.log(event);
    if (event.key === 'F6' || (event.key === 'l' && event.ctrlKey)) {
        focusOnInput();
    }
}

function focusOnInput() {
    document.getElementById('user-input').focus();
}<commit_msg>Removed useless console.log()<commit_after>let os = require('os');
let ipcRenderer = require('electron').ipcRenderer;
let delayOnExecution = 50; // in milliseconds

document.addEventListener('keyup', handleGlobalKeyPress);

let vue = new Vue({
    el: '#vue-root',
    data: {
        userInput: '',
        searchResults: [],
        autoFocus: true,
        stylesheetPath: os.platform() === 'win32' ? './styles/css/windows.css' : './styles/css/mac.css'
    },
    methods: {
        handleKeyPress: (event) => {
            if (event.key === 'Enter') {
                handleEnterPress();
            }
            if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
                event.preventDefault();
                let direction = event.key === 'ArrowDown' ? 'next' : 'prev';
                handleChangeActive(direction);
            }
        }
    },
    watch: {
        userInput: (val) => {
            ipcRenderer.send('get-search', val);
        }
    }
});

ipcRenderer.on('get-search-response', (event, arg) => {
    let searchResults = arg;
    let idIndex = 0;

    searchResults.forEach((s) => {
        s.id = `search-result-item-${idIndex}`;
        s.active = false;
        idIndex++;
    });

    if (searchResults.length > 0) {
        searchResults[0].active = true;
    }

    vue.searchResults = searchResults;
});

function handleChangeActive(direction) {
    let next;

    for (let i = 0; i < vue.searchResults.length; i++) {
        if (vue.searchResults[i].active) {
            next = direction === 'next' ? i + 1 : i - 1;
        }
    }

    vue.searchResults.forEach((s) => {
        s.active = false;
    });

    if (next < 0) {
        next = vue.searchResults.length - 1;
    }
    else if (next >= vue.searchResults.length) {
        next = 0;
    }

    vue.searchResults[next].active = true;
    scrollIntoView(vue.searchResults[next]);
}

function scrollIntoView(searchResult) {
    el = document.getElementById(searchResult.id);
    el.scrollIntoView();
}

function handleEnterPress() {
    let activeSearchResults = vue.searchResults.filter((s) => {
        return s.active;
    });

    if (activeSearchResults.length > 0) {
        resetUserInput();
        setTimeout(() => {
            execute(activeSearchResults[0].executionArgument)
        }, delayOnExecution);
    }
}

function execute(executionArgument) {
    ipcRenderer.send('execute', executionArgument);
}

function resetUserInput() {
    vue.userInput = '';
}

function handleGlobalKeyPress(event) {
    if (event.key === 'F6' || (event.key === 'l' && event.ctrlKey)) {
        focusOnInput();
    }
}

function focusOnInput() {
    document.getElementById('user-input').focus();
}<|endoftext|>


<commit_before>import React, { Fragment } from 'react';
import DownCaretIcon from 'react-icons/lib/fa/caret-down';
import {
  find,
  isEmpty,
  maxBy,
  reject,
} from 'lodash';
import { Row, Column } from '@ncigdc/uikit/Flex';
import Button from '@ncigdc/uikit/Button';
import { Tooltip } from '@ncigdc/uikit/Tooltip';
import { visualizingButton, zDepth1 } from '@ncigdc/theme/mixins';
import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
import Dropdown from '@ncigdc/uikit/Dropdown';
import DropdownItem from '@ncigdc/uikit/DropdownItem';
import Hidden from '@ncigdc/components/Hidden';
import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
import wrapSvg from '@ncigdc/utils/wrapSvg';
import {
  BarChartIcon,
  BoxPlot,
  CloseIcon,
  SpinnerIcon,
  SurvivalIcon,
} from '@ncigdc/theme/icons';
import termCapitaliser from '@ncigdc/utils/customisation';
import {
  humanify,
} from '@ncigdc/utils/string';
import {
  removeClinicalAnalysisVariable,
  updateClinicalAnalysisVariable,
} from '@ncigdc/dux/analysis';
import Loader from '@ncigdc/uikit/Loaders/Loader';
import { MAXIMUM_CURVES, MINIMUM_CASES } from '@ncigdc/utils/survivalplot';

import ActionsDropdown from './components/ActionsDropdown';
import ClinicalBoxPlot from './components/ClinicalBoxPlot';
import ClinicalHistogram from './components/ClinicalHistogram';
import ClinicalSurvivalPlot from './components/ClinicalSurvivalPlot';

import {
  colorsArray,
  getBoxTableData,
  getCardFilters,
  getHeadings,
  styles,
} from './helpers';

const vizButtons = {
  box: {
    action: updateClinicalAnalysisVariable,
    icon: <BoxPlot style={styles.chartIcon} />,
    title: 'Box/QQ Plot',
  },
  delete: {
    action: removeClinicalAnalysisVariable,
    icon: <CloseIcon style={styles.chartIcon} />,
    title: 'Remove Card',
  },
  histogram: {
    action: updateClinicalAnalysisVariable,
    icon: <BarChartIcon style={styles.chartIcon} />,
    title: 'Histogram',
  },
  survival: {
    action: updateClinicalAnalysisVariable,
    icon: <SurvivalIcon style={styles.chartIcon} />,
    title: 'Survival Plot',
  },
};

const getTableData = ({
  active_chart,
  displayData = [],
  fieldName,
  selectedBins,
  selectedSurvivalBins,
  selectedSurvivalLoadingIds,
  setSelectedBins,
  theme,
  updateSelectedSurvivalBins,
}) => displayData.map(bin => {
  const isSelected = find(selectedBins, { key: bin.displayName });
  const indexSurvival = selectedSurvivalBins.indexOf(bin.displayName);
  const isSurvivalLoading = selectedSurvivalLoadingIds.indexOf(bin.displayName) >= 0;
  const isSelectedForSurvival = indexSurvival >= 0;
  const isSurvivalFull = selectedSurvivalBins.length === MAXIMUM_CURVES;

  return {

    ...bin,
    select: (
      <input
        aria-label={`${fieldName} ${bin.displayName}`}
        checked={isSelected}
        disabled={bin.doc_count === 0}
        id={`${fieldName}-${bin.key}`}
        onChange={() => {
          if (isSelected) {
            setSelectedBins(
              reject(selectedBins, r => r.key === bin.key)
            );
          } else {
            setSelectedBins(selectedBins.concat(bin));
          }
        }}
        style={{
          marginLeft: 3,
          pointerEvents: 'initial',
        }}
        type="checkbox"
        value={bin.key}
        />
    ),
    ...active_chart === 'survival' && {
      survival: (
        <Tooltip
          Component={
            bin.key === '_missing' || bin.chart_doc_count < MINIMUM_CASES
              ? 'Not enough data'
              : isSelectedForSurvival
                ? `Click icon to remove "${bin.displayName}"`
                : isSurvivalFull
                  ? `Maximum plots (${MAXIMUM_CURVES}) reached`
                  : `Click icon to plot "${bin.displayName}"`
          }
          >
          <Button
            disabled={
              bin.key === '_missing' ||
              bin.chart_doc_count < MINIMUM_CASES ||
              (isSurvivalFull && !isSelectedForSurvival)
            }
            onClick={() => {
              updateSelectedSurvivalBins(displayData, bin);
            }}
            style={{
              backgroundColor: isSelectedForSurvival
                ? colorsArray[indexSurvival]
                : theme.greyScale3,
              color: 'white',
              margin: '0 auto',
              opacity:
                bin.key === '_missing' ||
                  bin.chart_doc_count < MINIMUM_CASES ||
                  (isSurvivalFull && !isSelectedForSurvival)
                    ? '0.33'
                    : '1',
              padding: '2px 3px',
              position: 'static',
            }}
            >
            {isSurvivalLoading ? <SpinnerIcon /> : <SurvivalIcon />}
            <Hidden>add to survival plot</Hidden>
          </Button>
        </Tooltip>
      ),
    },
  };
});

const ClinicalVariableCard = ({
  boxPlotValues,
  currentAnalysis,
  dataBuckets,
  dataDimension,
  dispatch,
  displayData,
  fieldName,
  filters,
  id,
  isLoading,
  openCustomBinModal,
  overallSurvivalData,
  plots,
  qqData,
  resetBins,
  binsAreCustom,
  selectedBins,
  selectedSurvivalBins = [],
  selectedSurvivalData,
  selectedSurvivalLoadingIds,
  setId,
  setQQData,
  setQQDataIsSet,
  setSelectedBins,
  style = {},
  survivalDataLoading,
  theme,
  totalDocs,
  updateSelectedSurvivalBins,
  variable,
  wrapperId,
}) => {
  const tableData = variable.active_chart === 'box'
    ? getBoxTableData(boxPlotValues)
    : getTableData({
      active_chart: variable.active_chart,
      displayData,
      fieldName,
      selectedBins,
      selectedSurvivalBins,
      selectedSurvivalLoadingIds,
      setSelectedBins,
      theme,
      updateSelectedSurvivalBins,
    });

  const histogramData =
    variable.active_chart === 'histogram'
      ? tableData.map(tableRow => ({
        fullLabel: tableRow.displayName,
        label: tableRow.displayName,
        tooltip: `${tableRow.displayName}: ${
            tableRow.chart_doc_count.toLocaleString()} (${
            (((tableRow.chart_doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`,
        value: variable.active_calculation === 'number'
            ? tableRow.chart_doc_count
            : (tableRow.chart_doc_count / totalDocs) * 100,
      }))
      : [];

  const maxKeyNameLength = (
    maxBy(histogramData
      .map(d => d.fullLabel), (item) => item.length) || ''
  ).length;

  const tsvSubstring = fieldName.replace(/\./g, '-');
  const cardFilters = getCardFilters(
    variable.plotTypes, selectedBins, fieldName, filters
  );
  const disabledCharts = plotType => isEmpty(tableData) &&
    plotType !== 'delete';

  return (
    <Column
      className="clinical-analysis-card"
      style={{
        ...zDepth1,
        height: 560,
        justifyContent: 'space-between',
        margin: '0 1rem 1rem',
        padding: '0.5rem 1rem 1rem',
        ...style,
      }}
      >
      <Row
        id={wrapperId}
        style={{
          alignItems: 'center',
          justifyContent: 'space-between',
          margin: '5px 0 10px',
        }}
        >

          {humanify({ term: termCapitaliser(fieldName).split('__').pop() })}

        <Row>
          {plots.concat('delete')
            .map(plotType => (
              <Tooltip Component={vizButtons[plotType].title} key={plotType}>
                <Button
                  className={`chart-button-${plotType}`}
                  disabled={disabledCharts(plotType)}
                  onClick={() => {
                    dispatch(
                      vizButtons[plotType].action({
                        fieldName,
                        id,
                        variable: {
                          active_chart: plotType,
                        },
                      })
                    );
                  }}
                  style={{
                    ...(disabledCharts(plotType)
                      ? {}
                      : plotType === variable.active_chart
                        ? styles.activeButton(theme)
                        : styles.common(theme)),
                    margin: 2,
                  }}
                  >
                  <Hidden>{vizButtons[plotType].title}</Hidden>
                  {vizButtons[plotType].icon}
                </Button>
              </Tooltip>
            ))}
        </Row>
      </Row>
      {isLoading
        ? <Loader />
        : isEmpty(tableData)
          ? (
            <Row
              id={`${wrapperId}-container`}
              style={{
                alignItems: 'center',
                flex: 1,
                justifyContent: 'center',
              }}
              >

No data for this field

            </Row>
          )
          : (
            <Fragment>
              <Column id={`${wrapperId}-container`}>
                {['histogram'].includes(variable.active_chart) && (
                  <Row className="no-print" style={{ paddingLeft: 10 }}>
                    <form style={{ width: '100%' }}>
                      <label
                        htmlFor={`variable-percentage-radio-${fieldName}`}
                        style={{
                          fontSize: '1.2rem',
                          marginRight: 10,
                        }}
                        >
                        <input
                          aria-label="Percentage of cases"
                          checked={variable.active_calculation === 'percentage'}
                          id={`variable-percentage-radio-${fieldName}`}
                          onChange={() => dispatch(updateClinicalAnalysisVariable({
                            fieldName,
                            id,
                            variable: {
                              active_calculation: 'percentage'
                            },
                          }))}
                          style={{ marginRight: 5 }}
                          type="radio"
                          value="percentage"
                          />
                      % of Cases

                      <label
                        htmlFor={`variable-number-radio-${fieldName}`}
                        style={{ fontSize: '1.2rem' }}
                        >
                        <input
                          aria-label="Number of cases"
                          checked={variable.active_calculation === 'number'}
                          id={`variable-number-radio-${fieldName}`}
                          onChange={() => dispatch(updateClinicalAnalysisVariable({
                            fieldName,
                            id,
                            variable: {
                              active_calculation: 'number',
                            },
                          }))}
                          style={{ marginRight: 5 }}
                          type="radio"
                          value="number"
                          />
                      # of Cases

                      <DownloadVisualizationButton
                        data={histogramData.map(d => ({
                          label: d.fullLabel,
                          value: d.value,
                        }))}
                        key="download"
                        noText
                        onClick={(e) => {
                          e.preventDefault();
                        }}
                        slug={`${fieldName}-bar-chart`}
                        style={{
                          float: 'right',
                          marginRight: 2,
                        }}
                        svg={() => wrapSvg({
                          bottomBuffer: maxKeyNameLength * 3,
                          rightBuffer: maxKeyNameLength * 2,
                          selector: `#${wrapperId}-container .test-bar-chart svg`,
                          title: humanify({ term: fieldName }),
                        })}
                        tooltipHTML="Download image or data"
                        />
                    </form>
                  </Row>
                )}

                {variable.active_chart === 'histogram' && (
                  <ClinicalHistogram
                    active_calculation={variable.active_calculation}
                    histogramData={histogramData}
                    histogramStyles={styles.histogram}
                    theme={theme}
                    type={variable.type}
                    />
                )}

                {variable.active_chart === 'survival' && (
                (variable.isSurvivalCustom &&
                  selectedSurvivalBins.length === 0 &&
                  !variable.showOverallSurvival)
                  ? (
                    <Row
                      id={`${wrapperId}-container`}
                      style={{
                        alignItems: 'center',
                        flex: 1,
                        justifyContent: 'center',
                      }}
                      >
                      No matching categories
                    </Row>
                  )
                  : (
                    <ClinicalSurvivalPlot
                      plotType={selectedSurvivalBins.length === 0 ||
                        variable.showOverallSurvival
                        ? 'clinicalOverall'
                        : 'categorical'}
                      survivalData={selectedSurvivalBins.length === 0 ||
                        variable.showOverallSurvival
                        ? overallSurvivalData
                        : selectedSurvivalData}
                      survivalDataLoading={survivalDataLoading}
                      />
                    )
                )}

                {variable.active_chart === 'box' && (
                  <ClinicalBoxPlot
                    boxPlotValues={boxPlotValues}
                    cardFilters={cardFilters}
                    dataBuckets={dataBuckets}
                    fieldName={fieldName}
                    qqData={qqData}
                    setId={setId}
                    setQQData={setQQData}
                    setQQDataIsSet={setQQDataIsSet}
                    theme={theme}
                    totalDocs={totalDocs}
                    type={variable.type}
                    wrapperId={wrapperId}
                    />
                )}
              </Column>

              <Column>
                <Row
                  style={{
                    justifyContent: 'space-between',
                    margin: '5px 0',
                  }}
                  >
                  <ActionsDropdown
                    active_chart={variable.active_chart}
                    cardFilters={cardFilters}
                    currentAnalysis={currentAnalysis}
                    dispatch={dispatch}
                    selectedBins={selectedBins}
                    styles={styles}
                    theme={theme}
                    totalDocs={totalDocs}
                    tsvSubstring={tsvSubstring}
                    />

                  {variable.active_chart === 'box' || (
                    <Dropdown
                      button={(
                        <Button
                          rightIcon={<DownCaretIcon />}
                          style={{
                            ...visualizingButton,
                            padding: '0 12px',
                          }}
                          >
                        Customize Bins
                        </Button>
                      )}
                      dropdownStyle={{ right: 0 }}
                      >
                      <DropdownItem
                        onClick={openCustomBinModal}
                        style={styles.actionMenuItem}
                        >
                      Edit Bins
                      </DropdownItem>
                      <DropdownItem
                        onClick={resetBins}
                        style={{
                          ...styles.actionMenuItem,
                          ...binsAreCustom || variable.isSurvivalCustom
                          ? {}
                          : styles.actionMenuItemDisabled(theme),
                        }}
                        >
                      Reset to Default
                      </DropdownItem>
                    </Dropdown>
                  )}
                </Row>

                <EntityPageHorizontalTable
                  data={tableData.map(tableRow => ({

                    ...tableRow,
                    key: tableRow.displayName,
                  }))}
                  headings={getHeadings(
                    variable.active_chart,
                    dataDimension,
                    fieldName + (binsAreCustom
                    ? ' (User defined bins applied)'
                    : ''),
                  )}
                  tableContainerStyle={{ height: 175 }}
                  tableId={`analysis-${tsvSubstring}-table`}
                  />
              </Column>
            </Fragment>
          )}
    </Column>
  );
};

export default ClinicalVariableCard;
<commit_msg>[PRTL-2845] avoid boxplot and qq for “years of” (#2712)<commit_after>import React, { Fragment } from 'react';
import DownCaretIcon from 'react-icons/lib/fa/caret-down';
import {
  find,
  isEmpty,
  maxBy,
  reject,
} from 'lodash';
import { Row, Column } from '@ncigdc/uikit/Flex';
import Button from '@ncigdc/uikit/Button';
import { Tooltip } from '@ncigdc/uikit/Tooltip';
import { visualizingButton, zDepth1 } from '@ncigdc/theme/mixins';
import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
import Dropdown from '@ncigdc/uikit/Dropdown';
import DropdownItem from '@ncigdc/uikit/DropdownItem';
import Hidden from '@ncigdc/components/Hidden';
import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
import wrapSvg from '@ncigdc/utils/wrapSvg';
import {
  BarChartIcon,
  BoxPlot,
  CloseIcon,
  SpinnerIcon,
  SurvivalIcon,
} from '@ncigdc/theme/icons';
import termCapitaliser from '@ncigdc/utils/customisation';
import {
  humanify,
} from '@ncigdc/utils/string';
import {
  removeClinicalAnalysisVariable,
  updateClinicalAnalysisVariable,
} from '@ncigdc/dux/analysis';
import Loader from '@ncigdc/uikit/Loaders/Loader';
import { MAXIMUM_CURVES, MINIMUM_CASES } from '@ncigdc/utils/survivalplot';

import ActionsDropdown from './components/ActionsDropdown';
import ClinicalBoxPlot from './components/ClinicalBoxPlot';
import ClinicalHistogram from './components/ClinicalHistogram';
import ClinicalSurvivalPlot from './components/ClinicalSurvivalPlot';

import {
  colorsArray,
  getBoxTableData,
  getCardFilters,
  getHeadings,
  styles,
} from './helpers';

const vizButtons = {
  box: {
    action: updateClinicalAnalysisVariable,
    icon: <BoxPlot style={styles.chartIcon} />,
    title: 'Box/QQ Plot',
  },
  delete: {
    action: removeClinicalAnalysisVariable,
    icon: <CloseIcon style={styles.chartIcon} />,
    title: 'Remove Card',
  },
  histogram: {
    action: updateClinicalAnalysisVariable,
    icon: <BarChartIcon style={styles.chartIcon} />,
    title: 'Histogram',
  },
  survival: {
    action: updateClinicalAnalysisVariable,
    icon: <SurvivalIcon style={styles.chartIcon} />,
    title: 'Survival Plot',
  },
};

const getTableData = ({
  active_chart,
  displayData = [],
  fieldName,
  selectedBins,
  selectedSurvivalBins,
  selectedSurvivalLoadingIds,
  setSelectedBins,
  theme,
  updateSelectedSurvivalBins,
}) => displayData.map(bin => {
  const isSelected = find(selectedBins, { key: bin.displayName });
  const indexSurvival = selectedSurvivalBins.indexOf(bin.displayName);
  const isSurvivalLoading = selectedSurvivalLoadingIds.indexOf(bin.displayName) >= 0;
  const isSelectedForSurvival = indexSurvival >= 0;
  const isSurvivalFull = selectedSurvivalBins.length === MAXIMUM_CURVES;

  return {

    ...bin,
    select: (
      <input
        aria-label={`${fieldName} ${bin.displayName}`}
        checked={isSelected}
        disabled={bin.doc_count === 0}
        id={`${fieldName}-${bin.key}`}
        onChange={() => {
          if (isSelected) {
            setSelectedBins(
              reject(selectedBins, r => r.key === bin.key)
            );
          } else {
            setSelectedBins(selectedBins.concat(bin));
          }
        }}
        style={{
          marginLeft: 3,
          pointerEvents: 'initial',
        }}
        type="checkbox"
        value={bin.key}
        />
    ),
    ...active_chart === 'survival' && {
      survival: (
        <Tooltip
          Component={
            bin.key === '_missing' || bin.chart_doc_count < MINIMUM_CASES
              ? 'Not enough data'
              : isSelectedForSurvival
                ? `Click icon to remove "${bin.displayName}"`
                : isSurvivalFull
                  ? `Maximum plots (${MAXIMUM_CURVES}) reached`
                  : `Click icon to plot "${bin.displayName}"`
          }
          >
          <Button
            disabled={
              bin.key === '_missing' ||
              bin.chart_doc_count < MINIMUM_CASES ||
              (isSurvivalFull && !isSelectedForSurvival)
            }
            onClick={() => {
              updateSelectedSurvivalBins(displayData, bin);
            }}
            style={{
              backgroundColor: isSelectedForSurvival
                ? colorsArray[indexSurvival]
                : theme.greyScale3,
              color: 'white',
              margin: '0 auto',
              opacity:
                bin.key === '_missing' ||
                  bin.chart_doc_count < MINIMUM_CASES ||
                  (isSurvivalFull && !isSelectedForSurvival)
                    ? '0.33'
                    : '1',
              padding: '2px 3px',
              position: 'static',
            }}
            >
            {isSurvivalLoading ? <SpinnerIcon /> : <SurvivalIcon />}
            <Hidden>add to survival plot</Hidden>
          </Button>
        </Tooltip>
      ),
    },
  };
});

const ClinicalVariableCard = ({
  boxPlotValues,
  currentAnalysis,
  dataBuckets,
  dataDimension,
  dispatch,
  displayData,
  fieldName,
  filters,
  id,
  isLoading,
  openCustomBinModal,
  overallSurvivalData,
  plots,
  qqData,
  resetBins,
  binsAreCustom,
  selectedBins,
  selectedSurvivalBins = [],
  selectedSurvivalData,
  selectedSurvivalLoadingIds,
  setId,
  setQQData,
  setQQDataIsSet,
  setSelectedBins,
  style = {},
  survivalDataLoading,
  theme,
  totalDocs,
  updateSelectedSurvivalBins,
  variable,
  wrapperId,
}) => {
  const tableData = variable.active_chart === 'box'
    ? getBoxTableData(boxPlotValues)
    : getTableData({
      active_chart: variable.active_chart,
      displayData,
      fieldName,
      selectedBins,
      selectedSurvivalBins,
      selectedSurvivalLoadingIds,
      setSelectedBins,
      theme,
      updateSelectedSurvivalBins,
    });

  const histogramData =
    variable.active_chart === 'histogram'
      ? tableData.map(tableRow => ({
        fullLabel: tableRow.displayName,
        label: tableRow.displayName,
        tooltip: `${tableRow.displayName}: ${
            tableRow.chart_doc_count.toLocaleString()} (${
            (((tableRow.chart_doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`,
        value: variable.active_calculation === 'number'
            ? tableRow.chart_doc_count
            : (tableRow.chart_doc_count / totalDocs) * 100,
      }))
      : [];

  const maxKeyNameLength = (
    maxBy(histogramData
      .map(d => d.fullLabel), (item) => item.length) || ''
  ).length;

  const tsvSubstring = fieldName.replace(/\./g, '-');
  const cardFilters = getCardFilters(
    variable.plotTypes, selectedBins, fieldName, filters
  );
  const disabledCharts = plotType => isEmpty(tableData) &&
    plotType !== 'delete';

  return (
    <Column
      className="clinical-analysis-card"
      style={{
        ...zDepth1,
        height: 560,
        justifyContent: 'space-between',
        margin: '0 1rem 1rem',
        padding: '0.5rem 1rem 1rem',
        ...style,
      }}
      >
      <Row
        id={wrapperId}
        style={{
          alignItems: 'center',
          justifyContent: 'space-between',
          margin: '5px 0 10px',
        }}
        >

          {humanify({ term: termCapitaliser(fieldName).split('__').pop() })}

        <Row>
          {plots.concat('delete')
            .reduce((buttons, plotType) => ([
              'demographic.year_of_birth',
              'demographic.year_of_death',
              'diagnoses.year_of_diagnosis',
              'exposures.tobacco_smoking_onset_year',
              'exposures.tobacco_smoking_quit_year',
            ].includes(fieldName) && plotType === 'box'
                ? buttons // avoid boxplot+qq for those fields
                : buttons.concat(
                  <Tooltip Component={vizButtons[plotType].title} key={plotType}>
                    <Button
                      className={`chart-button-${plotType}`}
                      disabled={disabledCharts(plotType)}
                      onClick={() => {
                        dispatch(
                          vizButtons[plotType].action({
                            fieldName,
                            id,
                            variable: {
                              active_chart: plotType,
                            },
                          })
                        );
                      }}
                      style={{
                        ...(disabledCharts(plotType)
                          ? {}
                          : plotType === variable.active_chart
                            ? styles.activeButton(theme)
                            : styles.common(theme)),
                        margin: 2,
                      }}
                      >
                      <Hidden>{vizButtons[plotType].title}</Hidden>
                      {vizButtons[plotType].icon}
                    </Button>
                  </Tooltip>
                )), [])}
        </Row>
      </Row>
      {isLoading
        ? <Loader />
        : isEmpty(tableData)
          ? (
            <Row
              id={`${wrapperId}-container`}
              style={{
                alignItems: 'center',
                flex: 1,
                justifyContent: 'center',
              }}
              >

No data for this field

            </Row>
          )
          : (
            <Fragment>
              <Column id={`${wrapperId}-container`}>
                {['histogram'].includes(variable.active_chart) && (
                  <Row className="no-print" style={{ paddingLeft: 10 }}>
                    <form style={{ width: '100%' }}>
                      <label
                        htmlFor={`variable-percentage-radio-${fieldName}`}
                        style={{
                          fontSize: '1.2rem',
                          marginRight: 10,
                        }}
                        >
                        <input
                          aria-label="Percentage of cases"
                          checked={variable.active_calculation === 'percentage'}
                          id={`variable-percentage-radio-${fieldName}`}
                          onChange={() => dispatch(updateClinicalAnalysisVariable({
                            fieldName,
                            id,
                            variable: {
                              active_calculation: 'percentage',
                            },
                          }))}
                          style={{ marginRight: 5 }}
                          type="radio"
                          value="percentage"
                          />
                      % of Cases

                      <label
                        htmlFor={`variable-number-radio-${fieldName}`}
                        style={{ fontSize: '1.2rem' }}
                        >
                        <input
                          aria-label="Number of cases"
                          checked={variable.active_calculation === 'number'}
                          id={`variable-number-radio-${fieldName}`}
                          onChange={() => dispatch(updateClinicalAnalysisVariable({
                            fieldName,
                            id,
                            variable: {
                              active_calculation: 'number',
                            },
                          }))}
                          style={{ marginRight: 5 }}
                          type="radio"
                          value="number"
                          />
                      # of Cases

                      <DownloadVisualizationButton
                        data={histogramData.map(d => ({
                          label: d.fullLabel,
                          value: d.value,
                        }))}
                        key="download"
                        noText
                        onClick={(e) => {
                          e.preventDefault();
                        }}
                        slug={`${fieldName}-bar-chart`}
                        style={{
                          float: 'right',
                          marginRight: 2,
                        }}
                        svg={() => wrapSvg({
                          bottomBuffer: maxKeyNameLength * 3,
                          rightBuffer: maxKeyNameLength * 2,
                          selector: `#${wrapperId}-container .test-bar-chart svg`,
                          title: humanify({ term: fieldName }),
                        })}
                        tooltipHTML="Download image or data"
                        />
                    </form>
                  </Row>
                )}

                {variable.active_chart === 'histogram' && (
                  <ClinicalHistogram
                    active_calculation={variable.active_calculation}
                    histogramData={histogramData}
                    histogramStyles={styles.histogram}
                    theme={theme}
                    type={variable.type}
                    />
                )}

                {variable.active_chart === 'survival' && (
                (variable.isSurvivalCustom &&
                  selectedSurvivalBins.length === 0 &&
                  !variable.showOverallSurvival)
                  ? (
                    <Row
                      id={`${wrapperId}-container`}
                      style={{
                        alignItems: 'center',
                        flex: 1,
                        justifyContent: 'center',
                      }}
                      >
                      No matching categories
                    </Row>
                  )
                  : (
                    <ClinicalSurvivalPlot
                      plotType={selectedSurvivalBins.length === 0 ||
                        variable.showOverallSurvival
                        ? 'clinicalOverall'
                        : 'categorical'}
                      survivalData={selectedSurvivalBins.length === 0 ||
                        variable.showOverallSurvival
                        ? overallSurvivalData
                        : selectedSurvivalData}
                      survivalDataLoading={survivalDataLoading}
                      />
                    )
                )}

                {variable.active_chart === 'box' && (
                  <ClinicalBoxPlot
                    boxPlotValues={boxPlotValues}
                    cardFilters={cardFilters}
                    dataBuckets={dataBuckets}
                    fieldName={fieldName}
                    qqData={qqData}
                    setId={setId}
                    setQQData={setQQData}
                    setQQDataIsSet={setQQDataIsSet}
                    theme={theme}
                    totalDocs={totalDocs}
                    type={variable.type}
                    wrapperId={wrapperId}
                    />
                )}
              </Column>

              <Column>
                <Row
                  style={{
                    justifyContent: 'space-between',
                    margin: '5px 0',
                  }}
                  >
                  <ActionsDropdown
                    active_chart={variable.active_chart}
                    cardFilters={cardFilters}
                    currentAnalysis={currentAnalysis}
                    dispatch={dispatch}
                    selectedBins={selectedBins}
                    styles={styles}
                    theme={theme}
                    totalDocs={totalDocs}
                    tsvSubstring={tsvSubstring}
                    />

                  {variable.active_chart === 'box' || (
                    <Dropdown
                      button={(
                        <Button
                          rightIcon={<DownCaretIcon />}
                          style={{
                            ...visualizingButton,
                            padding: '0 12px',
                          }}
                          >
                        Customize Bins
                        </Button>
                      )}
                      dropdownStyle={{ right: 0 }}
                      >
                      <DropdownItem
                        onClick={openCustomBinModal}
                        style={styles.actionMenuItem}
                        >
                      Edit Bins
                      </DropdownItem>
                      <DropdownItem
                        onClick={resetBins}
                        style={{
                          ...styles.actionMenuItem,
                          ...binsAreCustom || variable.isSurvivalCustom
                          ? {}
                          : styles.actionMenuItemDisabled(theme),
                        }}
                        >
                      Reset to Default
                      </DropdownItem>
                    </Dropdown>
                  )}
                </Row>

                <EntityPageHorizontalTable
                  data={tableData.map(tableRow => ({

                    ...tableRow,
                    key: tableRow.displayName,
                  }))}
                  headings={getHeadings(
                    variable.active_chart,
                    dataDimension,
                    fieldName + (binsAreCustom
                    ? ' (User defined bins applied)'
                    : ''),
                  )}
                  tableContainerStyle={{ height: 175 }}
                  tableId={`analysis-${tsvSubstring}-table`}
                  />
              </Column>
            </Fragment>
          )}
    </Column>
  );
};

export default ClinicalVariableCard;
<|endoftext|>


<commit_before>var fs = require('fs');
var exec = require('child_process').exec;

var Q = require('q');

var TMP_SOURCE_FILE = '/tmp/node-csharp.cs';
var TMP_EXE_FILE = '/tmp/node-csharp.exe';

var CSharpFromString = function(program) {
    fs.writeFileSync(TMP_SOURCE_FILE, program);
    return CSharpFromFile(TMP_SOURCE_FILE);
};

var CSharpFromFile = function (programPath) {
    return Q.Promise(function (resolve, reject) {
        var mcsCommand = 'mcs -o ' + TMP_EXE_FILE + ' ' + programPath;
        var monoCommand = 'mono ' + TMP_EXE_FILE;

        exec(mcsCommand, function (error) {
            if (error) {
                reject(error);
                return;
            }

            exec(monoCommand, function (error, stdout, stderr) {
                if (error) {
                    reject(error);
                    return;
                }

                resolve(stdout);
            });
        });
    });
};

module.exports = {
    fromString: CSharpFromString,
    fromFile: CSharpFromFile
};<commit_msg>added way to pass input stream<commit_after>var fs = require('fs');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;

var Q = require('q');

var TMP_SOURCE_FILE = '/tmp/node-csharp.cs';
var TMP_EXE_FILE = '/tmp/node-csharp.exe';

var CSharpFromString = function(program, options) {
    fs.writeFileSync(TMP_SOURCE_FILE, program);
    return CSharpFromFile(TMP_SOURCE_FILE, options);
};

var CSharpFromFile = function (programPath, options) {
    return Q.Promise(function (resolve, reject) {
        var mcsCommand = 'mcs -o ' + TMP_EXE_FILE + ' ' + programPath;
        var monoCommand = 'mono';

        exec(mcsCommand, function (error) {
            if (error) {
                reject(error);
                return;
            }

            var spawnedProgram = spawn(monoCommand, [TMP_EXE_FILE]);
            if (options.stdin) {
                options.stdin.pipe(spawnedProgram.stdin);
            }

            var output = '';

            spawnedProgram.stdout.on('data', function(chunk) {
                output += chunk.toString();
            });

            spawnedProgram.on('exit', function(code) {
                if (code !== 0) {
                    reject(error + output);
                    return;
                }

                resolve(output);
            });
        });
    });
};

module.exports = {
    fromString: CSharpFromString,
    fromFile: CSharpFromFile
};<|endoftext|>


<commit_before>describe('speechService', () => {
  let speechService;

  beforeEach(angular.mock.module('app'));
  beforeEach(inject((_speechService_) => {
    speechService = _speechService_;
  }));

  it('exists', () => {
    expect(speechService).toBeDefined();
  });
});

<commit_msg>Add speech service unit tests<commit_after>import SpeechService from './speech.service';

describe('Speech service', () => {
  let speechService;

  describe('Class', () => {
    afterEach(() => {
      localStorage.clear();
    });

    it('inits with audio setting if no localStored setting', () => {
      let s = new SpeechService();
      expect(s.setting).toBe('audio');
    });

    it('inits setting with localStored audioVoice if present', () => {
      localStorage.audioVoice = 'voice';
      let s = new SpeechService();
      expect(s.setting).toBe('voice');
    });

    it('inits with interval of 1 if no localStored setting', () => {
      let s = new SpeechService();
      expect(s.interval).toBe(1);
    });

    it('inits with localStored intervalsetting if present', () => {
      localStorage.interval = 5;
      let s = new SpeechService();
      expect(s.interval).toBe(5);
    });
  });

  beforeEach(angular.mock.module('app'));
  beforeEach(inject((_speechService_) => {
    speechService = _speechService_;
  }));

  it('exists', () => {
    expect(speechService).toBeDefined();
  });

  describe('getTimePhrase()', () => {
    it('returns the correct phrase of seconds only', () => {
      expect(speechService.getTimePhrase(50)).toBe(50);
      expect(speechService.getTimePhrase(30)).toBe(30);
      expect(speechService.getTimePhrase(2)).toBe(2);
    });

    it('returns the correct phrase of minute(s) only', () => {
      expect(speechService.getTimePhrase(60)).toBe('1 minute');
      expect(speechService.getTimePhrase(120)).toBe('2 minutes');
      expect(speechService.getTimePhrase(1200)).toBe('20 minutes');
    });

    it('returns correct phrase of minutes and seconds', () => {
      expect(speechService.getTimePhrase(65)).toBe('1:05');
      expect(speechService.getTimePhrase(235)).toBe('3:55');
    });
  });
});

<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, FormattedNumber, injectIntl, defineMessages } from 'react-intl';
import Relay from 'react-relay/classic';
import { Link } from 'react-router';
import Button from '@material-ui/core/Button';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Can from '../Can';
import { withPusher, pusherShape } from '../../pusher';
import CreateProject from '../project/CreateProject';
import TeamRoute from '../../relay/TeamRoute';
import RelayContainer from '../../relay/RelayContainer';

import { units } from '../../styles/js/shared';

const messages = defineMessages({
  addProject: {
    id: 'projects.addProject',
    defaultMessage: 'Add list',
  },
  dismiss: {
    id: 'projects.dismiss',
    defaultMessage: 'Dismiss',
  },
});

const useProjectLinkStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
    whiteSpace: 'nowrap',

    '&>.title': {
      flex: '1 1 auto',
      overflow: 'hidden',
      textOverflow: 'ellipsis',
      paddingRight: theme.spacing(1),
    },

    '&>.count': {
      flex: '0 0 auto',
    },
  },
}));

const ProjectListItemText = ({
  title, count, teamSlug, projectDbid,
}) => {
  const linkClasses = useProjectLinkStyles();

  // Not using <ListItemSecondaryAction> because it absorbs events, so clicking
  // the count wouldn't follow the link

  const to = projectDbid ? `/${teamSlug}/project/${projectDbid}` : `/${teamSlug}/all-items`;

  return (
    <ListItemText>
      <Typography
        component={Link}
        to={to}
        className={projectDbid ? 'project-list__link' : 'project-list__link-all'}
        variant="inherit"
        classes={linkClasses}
      >
        {title}
        <FormattedNumber value={count} />
      </Typography>
    </ListItemText>
  );
};

// TODO Fix a11y issues
/* eslint jsx-a11y/click-events-have-key-events: 0 */
class DrawerProjectsComponent extends Component {
  state = {
    showCreateProject: false,
  };

  componentDidMount() {
    this.subscribe();
  }

  componentWillUpdate(nextProps) {
    if (this.props.team && this.props.team.dbid !== nextProps.team.dbid) {
      this.unsubscribe();
    }
  }

  componentDidUpdate(prevProps) {
    if (this.props.team && this.props.team.dbid !== prevProps.team.dbid) {
      this.subscribe();
    }
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  subscribe() {
    const { pusher, clientSessionId, team } = this.props;
    if (pusher && team) {
      pusher.subscribe(team.pusher_channel).bind('media_updated', 'Projects', (data, run) => {
        if (clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `projects-drawer-${team.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
      pusher.subscribe(team.pusher_channel).bind('project_updated', 'Projects', (data, run) => {
        if (clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `projects-drawer-${team.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
    }
  }

  unsubscribe() {
    const { pusher, team } = this.props;
    if (pusher && team) {
      pusher.unsubscribe(team.pusher_channel, 'media_updated', 'Projects');
      pusher.unsubscribe(team.pusher_channel, 'project_updated', 'Projects');
    }
  }

  toggleShowCreateProject = () => {
    this.setState({ showCreateProject: !this.state.showCreateProject });
  };

  render() {
    const { team } = this.props;

    return (

        <List dense>
          <ListItem>
            <ProjectListItemText
              teamSlug={team.slug}
              projectDbid={null}
              title={<FormattedMessage id="projects.allClaims" defaultMessage="All items" />}
              count={team.medias_count}
            />
          </ListItem>
          {team.projects.edges
            .map(({ node }) => node) // make copy of Array
            .sort((a, b) => a.title.localeCompare(b.title)) // mutate _copy_ of Array
            .map(({ dbid, title, medias_count }) => (
              <ListItem key={dbid}>
                <ProjectListItemText
                  teamSlug={team.slug}
                  projectDbid={dbid}
                  title={title}
                  count={medias_count}
                />
              </ListItem>
            ))
          }
        </List>
        <Can permissions={team.permissions} permission="create Project">
          <Tooltip
            title={this.props.intl.formatMessage(this.props.showAddProj ?
              messages.dismiss : messages.addProject)}
          >
            <Button
              onClick={this.toggleShowCreateProject}
              className="drawer__create-project-button"
              style={{
                fontSize: '12px',
                marginLeft: '5px',
                paddingLeft: '10px',
                paddingRight: '10px',
              }}
            >
              <FormattedMessage id="projects.newList" defaultMessage="+ New list" />
            </Button>
          </Tooltip>
        </Can>
        { this.state.showCreateProject ?
          <div style={{ width: '100%', padding: `0 ${units(2)}` }}>
            <CreateProject
              className="project-list__input"
              team={team}
              onCreate={this.toggleShowCreateProject}
              onBlur={this.toggleShowCreateProject}
              autoFocus
            />

          : null }

    );
  }
}

DrawerProjectsComponent.propTypes = {
  pusher: pusherShape.isRequired,
  clientSessionId: PropTypes.string.isRequired,
};

const ConnectedDrawerProjectsComponent = withPusher(injectIntl(DrawerProjectsComponent));

const DrawerProjectsContainer = Relay.createContainer(ConnectedDrawerProjectsComponent, {
  fragments: {
    team: () => Relay.QL`
      fragment on Team {
        id,
        dbid,
        slug,
        permissions,
        medias_count,
        pusher_channel,
        projects(first: 10000) {
          edges {
            node {
              title,
              dbid,
              id,
              search_id,
              medias_count,
            }
          }
        }
      }
    `,
  },
});

const DrawerProjects = (props) => {
  const route = new TeamRoute({ teamSlug: props.team });
  return (
    <RelayContainer
      Component={DrawerProjectsContainer}
      route={route}
      forceFetch
      renderFetched={data =>
        <DrawerProjectsContainer {...data} />}
    />
  );
};

export default DrawerProjects;
<commit_msg>Project list: fix CSS so descenders aren't cropped<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, FormattedNumber, injectIntl, defineMessages } from 'react-intl';
import Relay from 'react-relay/classic';
import { Link } from 'react-router';
import Button from '@material-ui/core/Button';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Tooltip from '@material-ui/core/Tooltip';
import { makeStyles } from '@material-ui/core/styles';
import Can from '../Can';
import { withPusher, pusherShape } from '../../pusher';
import CreateProject from '../project/CreateProject';
import TeamRoute from '../../relay/TeamRoute';
import RelayContainer from '../../relay/RelayContainer';

import { units } from '../../styles/js/shared';

const messages = defineMessages({
  addProject: {
    id: 'projects.addProject',
    defaultMessage: 'Add list',
  },
  dismiss: {
    id: 'projects.dismiss',
    defaultMessage: 'Dismiss',
  },
});

const useProjectLinkStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
    whiteSpace: 'nowrap',
  },

  title: {
    display: 'block',
    flex: '1 1 auto',
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    paddingRight: theme.spacing(1),
  },

  count: {
    display: 'block',
    flex: '0 0 auto',
  },
}));

const ProjectListItemText = ({
  title, count, teamSlug, projectDbid,
}) => {
  const linkClasses = useProjectLinkStyles();

  // Not using <ListItemSecondaryAction> because it absorbs events, so clicking
  // the count wouldn't follow the link

  const to = projectDbid ? `/${teamSlug}/project/${projectDbid}` : `/${teamSlug}/all-items`;

  return (
    <ListItemText>
      <Link
        to={to}
        className={`${linkClasses.root} ${projectDbid ? 'project-list__link' : 'project-list__link-all'}`}
      >
        {title}
        <FormattedNumber value={count} />
      </Link>
    </ListItemText>
  );
};

// TODO Fix a11y issues
/* eslint jsx-a11y/click-events-have-key-events: 0 */
class DrawerProjectsComponent extends Component {
  state = {
    showCreateProject: false,
  };

  componentDidMount() {
    this.subscribe();
  }

  componentWillUpdate(nextProps) {
    if (this.props.team && this.props.team.dbid !== nextProps.team.dbid) {
      this.unsubscribe();
    }
  }

  componentDidUpdate(prevProps) {
    if (this.props.team && this.props.team.dbid !== prevProps.team.dbid) {
      this.subscribe();
    }
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  subscribe() {
    const { pusher, clientSessionId, team } = this.props;
    if (pusher && team) {
      pusher.subscribe(team.pusher_channel).bind('media_updated', 'Projects', (data, run) => {
        if (clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `projects-drawer-${team.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
      pusher.subscribe(team.pusher_channel).bind('project_updated', 'Projects', (data, run) => {
        if (clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `projects-drawer-${team.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
    }
  }

  unsubscribe() {
    const { pusher, team } = this.props;
    if (pusher && team) {
      pusher.unsubscribe(team.pusher_channel, 'media_updated', 'Projects');
      pusher.unsubscribe(team.pusher_channel, 'project_updated', 'Projects');
    }
  }

  toggleShowCreateProject = () => {
    this.setState({ showCreateProject: !this.state.showCreateProject });
  };

  render() {
    const { team } = this.props;

    return (

        <List dense>
          <ListItem>
            <ProjectListItemText
              teamSlug={team.slug}
              projectDbid={null}
              title={<FormattedMessage id="projects.allClaims" defaultMessage="All items" />}
              count={team.medias_count}
            />
          </ListItem>
          {team.projects.edges
            .map(({ node }) => node) // make copy of Array
            .sort((a, b) => a.title.localeCompare(b.title)) // mutate _copy_ of Array
            .map(({ dbid, title, medias_count }) => (
              <ListItem key={dbid}>
                <ProjectListItemText
                  teamSlug={team.slug}
                  projectDbid={dbid}
                  title={title}
                  count={medias_count}
                />
              </ListItem>
            ))
          }
        </List>
        <Can permissions={team.permissions} permission="create Project">
          <Tooltip
            title={this.props.intl.formatMessage(this.props.showAddProj ?
              messages.dismiss : messages.addProject)}
          >
            <Button
              onClick={this.toggleShowCreateProject}
              className="drawer__create-project-button"
              style={{
                fontSize: '12px',
                marginLeft: '5px',
                paddingLeft: '10px',
                paddingRight: '10px',
              }}
            >
              <FormattedMessage id="projects.newList" defaultMessage="+ New list" />
            </Button>
          </Tooltip>
        </Can>
        { this.state.showCreateProject ?
          <div style={{ width: '100%', padding: `0 ${units(2)}` }}>
            <CreateProject
              className="project-list__input"
              team={team}
              onCreate={this.toggleShowCreateProject}
              onBlur={this.toggleShowCreateProject}
              autoFocus
            />

          : null }

    );
  }
}

DrawerProjectsComponent.propTypes = {
  pusher: pusherShape.isRequired,
  clientSessionId: PropTypes.string.isRequired,
};

const ConnectedDrawerProjectsComponent = withPusher(injectIntl(DrawerProjectsComponent));

const DrawerProjectsContainer = Relay.createContainer(ConnectedDrawerProjectsComponent, {
  fragments: {
    team: () => Relay.QL`
      fragment on Team {
        id,
        dbid,
        slug,
        permissions,
        medias_count,
        pusher_channel,
        projects(first: 10000) {
          edges {
            node {
              title,
              dbid,
              id,
              search_id,
              medias_count,
            }
          }
        }
      }
    `,
  },
});

const DrawerProjects = (props) => {
  const route = new TeamRoute({ teamSlug: props.team });
  return (
    <RelayContainer
      Component={DrawerProjectsContainer}
      route={route}
      forceFetch
      renderFetched={data =>
        <DrawerProjectsContainer {...data} />}
    />
  );
};

export default DrawerProjects;
<|endoftext|>