<commit_before>import {Button} from 'widget/button'
import {Form} from 'widget/form/form'
import {Input} from 'widget/input'
import {ScrollableList} from 'widget/list'
import {Subject, delay} from 'rxjs'
import {compose} from 'compose'
import {connect} from 'store'
import {isMobile} from 'widget/userAgent'
import {selectFrom} from 'stateUtils'
import {simplifyString} from 'string'
import {splitString} from 'string'
import AutoFocus from 'widget/autoFocus'
import FloatingBox from 'widget/floatingBox'
import Keybinding from 'widget/keybinding'
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import styles from './combo.module.css'
import withSubscriptions from 'subscription'

const SELECTION_DELAY_MS = 350

const mapStateToProps = state => ({
    dimensions: selectFrom(state, 'dimensions') || []
})

class _Combo extends React.Component {
    inputContainer = React.createRef()
    input = React.createRef()
    list = React.createRef()
    select$ = new Subject()
    state = {
        showOptions: false,
        filter: '',
        filteredOptions: [],
        flattenedOptions: [],
        selectedOption: null,
        selected: false,
        focused: false
    }

    constructor() {
        super()
        this.onBlur = this.onBlur.bind(this)
        this.handleBlur = this.handleBlur.bind(this)
    }

    isActive() {
        const {busyMessage, disabled} = this.props
        return !(disabled || busyMessage)
    }

    render() {
        const {errorMessage, busyMessage, standalone, disabled, className, label, labelButtons, tooltip, tooltipPlacement, onCancel} = this.props
        const {showOptions} = this.state
        const onClick = e => {
            if (this.isActive()) {
                return standalone
                    ? onCancel && onCancel(e)
                    : showOptions
                        ? this.hideOptions()
                        : this.showOptions()
            }
        }
        return (
            <Form.FieldSet
                className={[styles.container, className].join(' ')}
                label={label}
                labelButtons={labelButtons}
                tooltip={tooltip}
                tooltipPlacement={tooltipPlacement}
                disabled={disabled}
                errorMessage={errorMessage}
                busyMessage={busyMessage}>
                <div
                    ref={this.inputContainer}
                    onClick={onClick}>
                    {this.renderInput()}
                </div>
                {showOptions ? this.renderOptions() : null}
            </Form.FieldSet>
        )
    }

    renderInput() {
        const {placeholder, autoFocus, standalone, readOnly, inputClassName, additionalButtons = []} = this.props
        const {focused, filter, selectedOption, showOptions} = this.state
        const showOptionsKeyBinding = showOptions ? undefined : () => this.showOptions()
        const keymap = {
            ArrowUp: showOptionsKeyBinding,
            ArrowDown: showOptionsKeyBinding,
            ArrowLeft: showOptionsKeyBinding,
            ArrowRight: showOptionsKeyBinding,
            Home: showOptionsKeyBinding,
            End: showOptionsKeyBinding
        }
        return (
            <Keybinding
                disabled={!this.isActive() || !focused}
                keymap={keymap}>
                <AutoFocus element={this.input.current} focusEnabled={autoFocus}>
                    <Input
                        ref={this.input}
                        className={[
                            styles.input,
                            standalone ? styles.standalone : null,
                            selectedOption && !showOptions ? styles.fakePlaceholder : null,
                            inputClassName
                        ].join(' ')}
                        value={filter}
                        placeholder={selectedOption && !standalone ? selectedOption.label : placeholder}
                        disabled={!this.isActive()}
                        readOnly={readOnly || isMobile()}
                        buttons={[
                            this.renderClearButton(),
                            this.renderToggleOptionsButton()
                        ]}
                        additionalButtons={additionalButtons}
                        onChange={e => this.setFilter(e.target.value)}
                        onFocus={() => this.setState({focused: true})}
                        onBlur={this.onBlur}
                    />
                </AutoFocus>
            </Keybinding>
        )
    }

    onBlur(e) {
        const {onBlur} = this.props
        const {showOptions} = this.state
        if (showOptions) {
            this.input.current.focus()
        } else {
            onBlur && onBlur(e)
            this.setState({focused: false})
        }
    }

    renderClearButton() {
        const {allowClear} = this.props
        const {selectedOption} = this.state
        return allowClear && selectedOption
            ? (
                <Button
                    key='clear'
                    chromeless
                    shape='none'
                    air='none'
                    icon='times'
                    iconAttributes={{
                        fixedWidth: true
                    }}
                    tabIndex={-1}
                    onClick={() => this.select$.next()}
                />
            )
            : null
    }

    renderToggleOptionsButton() {
        const {placement} = this.props
        const {showOptions} = this.state
        const icon = {
            below: 'chevron-down',
            above: 'chevron-up'
        }
        return (
            <Button
                key='toggle'
                chromeless
                shape='none'
                air='none'
                icon={icon[placement]}
                iconAttributes={{
                    fixedWidth: true,
                    flip: showOptions ? 'vertical' : null
                }}
                tabIndex={-1}
                disabled={!this.isActive()}
                onClick={() => showOptions
                    ? this.hideOptions()
                    : this.showOptions()
                }
            />
        )
    }

    renderOptions() {
        const {placement, optionsClassName, optionTooltipPlacement, alignment} = this.props
        const {flattenedOptions, selectedOption, selected} = this.state
        return (
            <FloatingBox
                element={this.inputContainer.current}
                placement={placement}
                alignment='fit'
                onBlur={this.handleBlur}>
                <ScrollableList
                    ref={this.list}
                    air='more'
                    className={optionsClassName || styles.options}
                    options={flattenedOptions}
                    selectedOption={selectedOption}
                    onSelect={option => this.select$.next(option)}
                    onCancel={() => this.resetFilter()}
                    autoCenter={!selected}
                    tooltipPlacement={optionTooltipPlacement}
                    autoHighlight
                    keyboard
                    alignment={alignment}
                />
            </FloatingBox>
        )
    }

    showOptions() {
        this.setState({showOptions: true})
    }

    hideOptions() {
        this.setState({showOptions: false})
    }

    setFilter(filter = '') {
        const {standalone} = this.props
        this.setState({
            showOptions: !!filter || standalone,
            filter
        }, this.updateOptions)
    }

    resetFilter() {
        const {onCancel, standalone} = this.props
        const {filter} = this.state
        if (standalone && onCancel && !filter) {
            onCancel()
        } else {
            this.setFilter()
        }
    }

    setSelectedOption(selectedOption) {
        this.updateState({
            selectedOption,
            selected: !!selectedOption
        })
    }

    updateState(state, callback) {
        const updatedState = (prevState, state) =>
            _.isEqual(_.pick(prevState, _.keys(state)), state) ? null : state
        this.setState(
            prevState =>
                updatedState(prevState, _.isFunction(state) ? state(prevState) : state),
            callback
        )
    }

    handleSelect() {
        const {onChange, addSubscription} = this.props
        addSubscription(
            this.select$.subscribe(
                option => {
                    this.setSelectedOption(option)
                    onChange && onChange(option)
                }
            ),
            this.select$.pipe(
                delay(SELECTION_DELAY_MS)
            ).subscribe(
                () => this.setState({selected: false}, this.setFilter)
            )
        )
    }

    handleBlur(e) {
        const {onCancel} = this.props
        const isInputClick = e => this.inputContainer.current && this.inputContainer.current.contains(e.target)
        const isListClick = e => this.list.current && this.list.current.contains && this.list.current.contains(e.target)
        if (!isInputClick(e) && !isListClick(e)) {
            this.setFilter()
            onCancel && onCancel(e)
        }
    }

    componentDidMount() {
        this.setFilter()
        this.handleSelect()
    }

    shouldComponentUpdate() {
        return !this.state.selected
    }

    componentDidUpdate(prevProps) {
        const {value} = this.props
        const {value: prevValue} = prevProps
        this.updateOptions(value !== prevValue && value)
    }

    matcher(filter) {
        // match beginning of multiple words in any order (e.g. both "u k" and "k u" match "United Kingdom")
        const parts = splitString(simplifyString(filter))
            .map(part => part ? `(?=.*${(part)})` : '')
        return RegExp(`^${parts.join('')}.*$`, 'i')
    }

    updateOptions(inputChanged) {
        const {options} = this.props
        const {filter} = this.state
        const matcher = this.matcher(filter)
        const filterGroup = group => {
            const filtered = {...group, options: getFilteredOptions(group.options)}
            return filtered.options.length ? filtered : null
        }
        const getFilteredOptions = options =>
            _.compact(
                options.map(option =>
                    option.options
                        ? filterGroup(option)
                        : matcher.test(simplifyString(option.searchableText || option.label))
                            ? option
                            : null
                )
            )
        const getFlattenedOptions = options =>
            _.flatten(
                options.map(option =>
                    option.options
                        ? [{..._.omit(option, 'options'), group: true}, ...option.options]
                        : option
                )
            )
        const filteredOptions = getFilteredOptions(options)
        const flattenedOptions = getFlattenedOptions(filteredOptions)

        const getInputOption = () => {
            const {value} = this.props
            return flattenedOptions && flattenedOptions.find(option => !option.group && option.value === value)
        }

        const getSelectedOption = selectedOption => {
            const validatedSelectedOption = selectedOption && flattenedOptions.find(option => option.value === selectedOption.value)
            return inputChanged
                ? getInputOption()
                : validatedSelectedOption || getInputOption()
        }

        this.updateState(prevState =>
            ({
                filteredOptions,
                flattenedOptions,
                selectedOption: getSelectedOption.bind(this)(prevState.selectedOption)
            }))
    }
}

export const Combo = compose(
    _Combo,
    withSubscriptions(),
    connect(mapStateToProps)
)

Combo.propTypes = {
    options: PropTypes.any.isRequired,
    additionalButtons: PropTypes.arrayOf(PropTypes.node),
    alignment: PropTypes.oneOf(['left', 'center', 'right', 'fit']),
    allowClear: PropTypes.any,
    autoFocus: PropTypes.any,
    busyMessage: PropTypes.any,
    className: PropTypes.string,
    disabled: PropTypes.any,
    errorMessage: PropTypes.any,
    inputClassName: PropTypes.string,
    keyboard: PropTypes.any,
    label: PropTypes.any,
    labelButtons: PropTypes.any,
    optionsClassName: PropTypes.string,
    optionTooltipPlacement: PropTypes.string,
    placeholder: PropTypes.string,
    placement: PropTypes.oneOf(['above', 'below']),
    readOnly: PropTypes.any,
    standalone: PropTypes.any,
    tooltip: PropTypes.string,
    tooltipPlacement: PropTypes.string,
    value: PropTypes.any,
    onBlur: PropTypes.func,
    onCancel: PropTypes.func,
    onChange: PropTypes.func
}

Combo.defaultProps = {
    alignment: 'left',
    placement: 'below',
    tooltipPlacement: 'top'
}
<commit_msg>Fixed Combo onBlur.<commit_after>import {Button} from 'widget/button'
import {Form} from 'widget/form/form'
import {Input} from 'widget/input'
import {ScrollableList} from 'widget/list'
import {Subject, delay} from 'rxjs'
import {compose} from 'compose'
import {connect} from 'store'
import {isMobile} from 'widget/userAgent'
import {selectFrom} from 'stateUtils'
import {simplifyString} from 'string'
import {splitString} from 'string'
import AutoFocus from 'widget/autoFocus'
import FloatingBox from 'widget/floatingBox'
import Keybinding from 'widget/keybinding'
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import styles from './combo.module.css'
import withSubscriptions from 'subscription'

const SELECTION_DELAY_MS = 350

const mapStateToProps = state => ({
    dimensions: selectFrom(state, 'dimensions') || []
})

class _Combo extends React.Component {
    inputContainer = React.createRef()
    input = React.createRef()
    list = React.createRef()
    select$ = new Subject()
    state = {
        showOptions: false,
        filter: '',
        filteredOptions: [],
        flattenedOptions: [],
        selectedOption: null,
        selected: false,
        focused: false
    }

    constructor() {
        super()
        this.onBlur = this.onBlur.bind(this)
        this.handleBlur = this.handleBlur.bind(this)
    }

    isActive() {
        const {busyMessage, disabled} = this.props
        return !(disabled || busyMessage)
    }

    render() {
        const {errorMessage, busyMessage, standalone, disabled, className, label, labelButtons, tooltip, tooltipPlacement, onCancel} = this.props
        const {showOptions} = this.state
        const onClick = e => {
            if (this.isActive()) {
                return standalone
                    ? onCancel && onCancel(e)
                    : showOptions
                        ? this.hideOptions()
                        : this.showOptions()
            }
        }
        return (
            <Form.FieldSet
                className={[styles.container, className].join(' ')}
                label={label}
                labelButtons={labelButtons}
                tooltip={tooltip}
                tooltipPlacement={tooltipPlacement}
                disabled={disabled}
                errorMessage={errorMessage}
                busyMessage={busyMessage}>
                <div
                    ref={this.inputContainer}
                    onClick={onClick}>
                    {this.renderInput()}
                </div>
                {showOptions ? this.renderOptions() : null}
            </Form.FieldSet>
        )
    }

    renderInput() {
        const {placeholder, autoFocus, standalone, readOnly, inputClassName, additionalButtons = []} = this.props
        const {focused, filter, selectedOption, showOptions} = this.state
        const showOptionsKeyBinding = showOptions ? undefined : () => this.showOptions()
        const keymap = {
            ArrowUp: showOptionsKeyBinding,
            ArrowDown: showOptionsKeyBinding,
            ArrowLeft: showOptionsKeyBinding,
            ArrowRight: showOptionsKeyBinding,
            Home: showOptionsKeyBinding,
            End: showOptionsKeyBinding
        }
        return (
            <Keybinding
                disabled={!this.isActive() || !focused}
                keymap={keymap}>
                <AutoFocus element={this.input.current} focusEnabled={autoFocus}>
                    <Input
                        ref={this.input}
                        className={[
                            styles.input,
                            standalone ? styles.standalone : null,
                            selectedOption && !showOptions ? styles.fakePlaceholder : null,
                            inputClassName
                        ].join(' ')}
                        value={filter}
                        placeholder={selectedOption && !standalone ? selectedOption.label : placeholder}
                        disabled={!this.isActive()}
                        readOnly={readOnly || isMobile()}
                        buttons={[
                            this.renderClearButton(),
                            this.renderToggleOptionsButton()
                        ]}
                        additionalButtons={additionalButtons}
                        onChange={e => this.setFilter(e.target.value)}
                        onFocus={() => this.setState({focused: true})}
                        onBlur={this.onBlur}
                    />
                </AutoFocus>
            </Keybinding>
        )
    }

    onBlur(e) {
        const {onBlur} = this.props
        const {showOptions} = this.state
        if (showOptions) {
            this.input.current && this.input.current.focus()
        } else {
            onBlur && onBlur(e)
            this.setState({focused: false})
        }
    }

    renderClearButton() {
        const {allowClear} = this.props
        const {selectedOption} = this.state
        return allowClear && selectedOption
            ? (
                <Button
                    key='clear'
                    chromeless
                    shape='none'
                    air='none'
                    icon='times'
                    iconAttributes={{
                        fixedWidth: true
                    }}
                    tabIndex={-1}
                    onClick={() => this.select$.next()}
                />
            )
            : null
    }

    renderToggleOptionsButton() {
        const {placement} = this.props
        const {showOptions} = this.state
        const icon = {
            below: 'chevron-down',
            above: 'chevron-up'
        }
        return (
            <Button
                key='toggle'
                chromeless
                shape='none'
                air='none'
                icon={icon[placement]}
                iconAttributes={{
                    fixedWidth: true,
                    flip: showOptions ? 'vertical' : null
                }}
                tabIndex={-1}
                disabled={!this.isActive()}
                onClick={() => showOptions
                    ? this.hideOptions()
                    : this.showOptions()
                }
            />
        )
    }

    renderOptions() {
        const {placement, optionsClassName, optionTooltipPlacement, alignment} = this.props
        const {flattenedOptions, selectedOption, selected} = this.state
        return (
            <FloatingBox
                element={this.inputContainer.current}
                placement={placement}
                alignment='fit'
                onBlur={this.handleBlur}>
                <ScrollableList
                    ref={this.list}
                    air='more'
                    className={optionsClassName || styles.options}
                    options={flattenedOptions}
                    selectedOption={selectedOption}
                    onSelect={option => this.select$.next(option)}
                    onCancel={() => this.resetFilter()}
                    autoCenter={!selected}
                    tooltipPlacement={optionTooltipPlacement}
                    autoHighlight
                    keyboard
                    alignment={alignment}
                />
            </FloatingBox>
        )
    }

    showOptions() {
        this.setState({showOptions: true})
    }

    hideOptions() {
        this.setState({showOptions: false})
    }

    setFilter(filter = '') {
        const {standalone} = this.props
        this.setState({
            showOptions: !!filter || standalone,
            filter
        }, this.updateOptions)
    }

    resetFilter() {
        const {onCancel, standalone} = this.props
        const {filter} = this.state
        if (standalone && onCancel && !filter) {
            onCancel()
        } else {
            this.setFilter()
        }
    }

    setSelectedOption(selectedOption) {
        this.updateState({
            selectedOption,
            selected: !!selectedOption
        })
    }

    updateState(state, callback) {
        const updatedState = (prevState, state) =>
            _.isEqual(_.pick(prevState, _.keys(state)), state) ? null : state
        this.setState(
            prevState =>
                updatedState(prevState, _.isFunction(state) ? state(prevState) : state),
            callback
        )
    }

    handleSelect() {
        const {onChange, addSubscription} = this.props
        addSubscription(
            this.select$.subscribe(
                option => {
                    this.setSelectedOption(option)
                    onChange && onChange(option)
                }
            ),
            this.select$.pipe(
                delay(SELECTION_DELAY_MS)
            ).subscribe(
                () => this.setState({selected: false}, this.setFilter)
            )
        )
    }

    handleBlur(e) {
        const {onCancel} = this.props
        const isInputClick = e => this.inputContainer.current && this.inputContainer.current.contains(e.target)
        const isListClick = e => this.list.current && this.list.current.contains && this.list.current.contains(e.target)
        if (!isInputClick(e) && !isListClick(e)) {
            this.setFilter()
            onCancel && onCancel(e)
        }
    }

    componentDidMount() {
        this.setFilter()
        this.handleSelect()
    }

    shouldComponentUpdate() {
        return !this.state.selected
    }

    componentDidUpdate(prevProps) {
        const {value} = this.props
        const {value: prevValue} = prevProps
        this.updateOptions(value !== prevValue && value)
    }

    matcher(filter) {
        // match beginning of multiple words in any order (e.g. both "u k" and "k u" match "United Kingdom")
        const parts = splitString(simplifyString(filter))
            .map(part => part ? `(?=.*${(part)})` : '')
        return RegExp(`^${parts.join('')}.*$`, 'i')
    }

    updateOptions(inputChanged) {
        const {options} = this.props
        const {filter} = this.state
        const matcher = this.matcher(filter)
        const filterGroup = group => {
            const filtered = {...group, options: getFilteredOptions(group.options)}
            return filtered.options.length ? filtered : null
        }
        const getFilteredOptions = options =>
            _.compact(
                options.map(option =>
                    option.options
                        ? filterGroup(option)
                        : matcher.test(simplifyString(option.searchableText || option.label))
                            ? option
                            : null
                )
            )
        const getFlattenedOptions = options =>
            _.flatten(
                options.map(option =>
                    option.options
                        ? [{..._.omit(option, 'options'), group: true}, ...option.options]
                        : option
                )
            )
        const filteredOptions = getFilteredOptions(options)
        const flattenedOptions = getFlattenedOptions(filteredOptions)

        const getInputOption = () => {
            const {value} = this.props
            return flattenedOptions && flattenedOptions.find(option => !option.group && option.value === value)
        }

        const getSelectedOption = selectedOption => {
            const validatedSelectedOption = selectedOption && flattenedOptions.find(option => option.value === selectedOption.value)
            return inputChanged
                ? getInputOption()
                : validatedSelectedOption || getInputOption()
        }

        this.updateState(prevState =>
            ({
                filteredOptions,
                flattenedOptions,
                selectedOption: getSelectedOption.bind(this)(prevState.selectedOption)
            }))
    }
}

export const Combo = compose(
    _Combo,
    withSubscriptions(),
    connect(mapStateToProps)
)

Combo.propTypes = {
    options: PropTypes.any.isRequired,
    additionalButtons: PropTypes.arrayOf(PropTypes.node),
    alignment: PropTypes.oneOf(['left', 'center', 'right', 'fit']),
    allowClear: PropTypes.any,
    autoFocus: PropTypes.any,
    busyMessage: PropTypes.any,
    className: PropTypes.string,
    disabled: PropTypes.any,
    errorMessage: PropTypes.any,
    inputClassName: PropTypes.string,
    keyboard: PropTypes.any,
    label: PropTypes.any,
    labelButtons: PropTypes.any,
    optionsClassName: PropTypes.string,
    optionTooltipPlacement: PropTypes.string,
    placeholder: PropTypes.string,
    placement: PropTypes.oneOf(['above', 'below']),
    readOnly: PropTypes.any,
    standalone: PropTypes.any,
    tooltip: PropTypes.string,
    tooltipPlacement: PropTypes.string,
    value: PropTypes.any,
    onBlur: PropTypes.func,
    onCancel: PropTypes.func,
    onChange: PropTypes.func
}

Combo.defaultProps = {
    alignment: 'left',
    placement: 'below',
    tooltipPlacement: 'top'
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

define(function(require) {
  /*
    DEPENDENCIES
   */

//  require("foundation.tab");
  var BaseFormPanel = require("utils/form-panels/form-panel");
  var Sunstone = require("sunstone");
  var Locale = require("utils/locale");
  var Tips = require("utils/tips");
  var OpenNebulaAction = require('opennebula/action');
  var OpenNebulaServiceTemplate = require("opennebula/servicetemplate");
  var OpenNebulaTemplate = require("opennebula/template");
  var Notifier = require("utils/notifier");
  var WizardFields = require("utils/wizard-fields");
  var UserInputs = require("utils/user-inputs");
  var Config = require("sunstone-config");
  var TemplateUtils = require("utils/template-utils");
  var ServiceUtils = require("utils/service-utils");

  /*
    TEMPLATES
   */

  var TemplateHTML = require("hbs!./instantiate/html");

  /*
    CONSTANTS
   */

  var FORM_PANEL_ID = require("./instantiate/formPanelId");
  var TAB_ID = require("../tabId");
  var vm_group = "VM_GROUP";
  var classButton = 'small button leases right radius';

  /*
    CONSTRUCTOR
   */

  function FormPanel() {
    this.formPanelId = FORM_PANEL_ID;
    this.tabId = TAB_ID;
    this.display_vmgroups = false;
    this.actions = {
      "instantiate": {
        "title": Locale.tr("Instantiate Service Template"),
        "buttonText": Locale.tr("Instantiate"),
        "resetButton": false
      }
    };

    BaseFormPanel.call(this);
  }

  FormPanel.FORM_PANEL_ID = FORM_PANEL_ID;
  FormPanel.prototype = Object.create(BaseFormPanel.prototype);
  FormPanel.prototype.constructor = FormPanel;
  FormPanel.prototype.setTemplateId = _setTemplateId;
  FormPanel.prototype.htmlWizard = _html;
  FormPanel.prototype.submitWizard = _submitWizard;
  FormPanel.prototype.onShow = _onShow;
  FormPanel.prototype.setup = _setup;

  return FormPanel;

  /*
    FUNCTION DEFINITIONS
   */

  function _html() {
    var values_hbs = {
      'formPanelId': this.formPanelId,
      'userInputsHTML': UserInputs.html(),
    };
    if(config && config.system_config && config.system_config.leases){
      values_hbs.userInputsCharters = $("<div/>").append(
        $("<div/>",{style:"display:inline-block;clear:both;width:100%"}).append(
          $("<button />", {class: classButton, id:"addCharters"}).append(
            $("<i/>", {class: 'fa fa-clock'})
          )
        ).add(
          $("<table/>", {class: "service-charters"})
        )
      ).prop('outerHTML');
    }
    return TemplateHTML(values_hbs);
  }

  function _setup(context) {
    var that = this;
    Tips.setup(context);
    return false;
  }

  function _onShow(context) {
    $(".service-charters", context).empty();
    if(config && config.system_config && config.system_config.leases ){
      function remove_leases(){
        $(".remove-lease", context).off("click").on("click", function(e){
          e.preventDefault();
          e.stopPropagation();
          $(this).closest("tr").remove();
        });
      }
      remove_leases();
      $("#addCharters",context).off("click").on("click", function(e){
        e.preventDefault();
        e.stopPropagation();
        var leases = config.system_config.leases;
        var confLeasesKeys = Object.keys(leases);
        var last = 0;
        confLeasesKeys.forEach(function(schedAction){
          if(leases[schedAction] && leases[schedAction].time){
            var schedActionTime = parseInt(leases[schedAction].time,10);
            last = last + schedActionTime;
            var time = (last>=0? "+":"-")+last;
            $(".service-charters", context).append(
              $("<tr/>").attr("data-time", time).attr("data-action", schedAction).append(
                $("<td/>").text(schedAction).add(
                  $("<td/>").text(time)
                ).add(
                  $("<td>").append(
                    $("<button/>", {class: "remove-lease"}).append(
                      $("<i>",{class: "fas fa-trash-alt"})
                    )
                  )
                )
              )
            );
            remove_leases();
          }
        });
      });
    }

    var vmgroups = OpenNebulaAction.cache(vm_group)
    if(!vmgroups){
      Sunstone.runAction("VMGroup.list");
    }
  }

  function _setTemplateId(context, templateId) {
    var that = this;
    this.templateId = templateId;
    this.service_template_json = {};
    OpenNebulaServiceTemplate.show({
      data : {
        id: templateId
      },
      timeout: true,
      success: function (request, template_json){
        that.service_template_json = template_json;
        $(".name", context).text(template_json.DOCUMENT.NAME);
        $("#instantiate_service_user_inputs", context).empty();
        UserInputs.serviceTemplateInsert(
          $("#instantiate_service_user_inputs", context),
          template_json, {
            select_networks: true
          });
        n_roles = template_json.DOCUMENT.TEMPLATE.BODY.roles.length;
        n_roles_done = 0;
        var total_cost = 0;
        var vmgroup_title = Locale.tr("Associate VM Group to Service");
        var setDisplayTitle = function(value=""){
          vmgroup_title = value
        }
        $.each(template_json.DOCUMENT.TEMPLATE.BODY.roles, function(index, role){
          var div_id = "user_input_role_"+index;
          if(vmgroup_title && that.display_vmgroups){
            $("#instantiate_service_role_user_inputs", context).empty().prepend(
              $("<legend/>").text(vmgroup_title)
            );
            setDisplayTitle("");
          }
          $("#instantiate_service_role_user_inputs", context).append(
            $("<div/>",{id:div_id, class:"large-6 columns"})
          );
          OpenNebulaTemplate.show({
            data : {
              id: role.vm_template,
              extended: true
            },
            timeout: true,
            success: function (request, vm_template_json){
              that.vm_template_json = vm_template_json;
              $("#"+div_id, context).empty();
              //if (role.vm_template_contents){
                roleTemplate = TemplateUtils.stringToTemplate(role.vm_template_contents);
                if(roleTemplate && roleTemplate.APPEND){
                  var append = roleTemplate.APPEND.split(",");
                  $.each(append, function(key, value){
                    if (!that.vm_template_json.VMTEMPLATE.TEMPLATE[value]){
                      that.vm_template_json.VMTEMPLATE.TEMPLATE[value] = roleTemplate[value];
                    } else {
                      if (!Array.isArray(that.vm_template_json.VMTEMPLATE.TEMPLATE[value])){
                        that.vm_template_json.VMTEMPLATE.TEMPLATE[value] = [that.vm_template_json.VMTEMPLATE.TEMPLATE[value]];
                      }
                      if (Array.isArray(roleTemplate[value])){
                        $.each(roleTemplate[value], function(rkey, rvalue){
                          that.vm_template_json.VMTEMPLATE.TEMPLATE[value].push(rvalue);
                        });
                      } else {
                        that.vm_template_json.VMTEMPLATE.TEMPLATE[value].push(roleTemplate[value]);
                      }
                    }
                    delete roleTemplate[value];
                  });
                  delete roleTemplate.APPEND;
                }
                $.extend(true, that.vm_template_json.VMTEMPLATE.TEMPLATE, roleTemplate);
              //}
              if (vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"] && vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_UNIT_COST"] && vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_UNIT_COST"] === "GB") {
                vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"] = vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"]*1024;
              }
              if (vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"]) {
                vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"] = vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"]*1024;
              }
              var cost = OpenNebulaTemplate.cost(that.vm_template_json);
              if (cost !== 0 && Config.isFeatureEnabled("showback")) {
                total_cost += (cost * role.cardinality);

                $(".total_cost_div", context).show();
                $(".total_cost_div .cost_value", context).text((total_cost).toFixed(4));
              }

              var opts = {
                text_header: Locale.tr("Role") + " " + role.name
              };
              //vmgroups
              if(that.display_vmgroups){
                var vmgroups = OpenNebulaAction.cache(vm_group);
                if(vmgroups && vmgroups.data){
                  opts.vmgroups = vmgroups.data;
                  opts.role = role.name;
                  if(!Array.isArray(vmgroups.data)){
                    opts.vmgroups = [vmgroups.data];
                  }
                }
              }

              UserInputs.vmTemplateInsert(
                $("#"+div_id, context),
                vm_template_json,
                opts
              );
              n_roles_done += 1;
              if(n_roles_done === n_roles){
                Sunstone.enableFormPanelSubmit();
              }
            },
            error: function(request,error_json, container){
              Notifier.onError(request,error_json, container);
              $("#instantiate_vm_user_inputs", context).empty();
            }
          });
        });
      },
      error: function(request,error_json, container){
        Notifier.onError(request,error_json, container);
        $("#instantiate_service_user_inputs", context).empty();
      }
    });
  }

  //submit
  function _submitWizard(context) {
    var that = this;
    var service_name = $("#service_name",context).val();
    var n_times = $("#service_n_times",context).val();
    var n_times_int=1;
    if (n_times.length){
      n_times_int=parseInt(n_times,10);
    }

    var extra_info = ServiceUtils.getExtraInfo(context);

    if(
      that &&
      that.service_template_json &&
      that.service_template_json.DOCUMENT &&
      that.service_template_json.DOCUMENT.TEMPLATE &&
      that.service_template_json.DOCUMENT.TEMPLATE.BODY &&
      that.service_template_json.DOCUMENT.TEMPLATE.BODY.roles
    ){
      var charters = "";
      if(config && config.system_config && config.system_config.leases ){
        $(".service-charters", context).find("tr").each(function(index){
          var time = $(this).attr("data-time");
          var action = $(this).attr("data-action");
          if(time.length>0 && action.length>0){
            charters += TemplateUtils.templateToString({SCHED_ACTION:{ACTION:action, TIME: time, ID: index.toString()}});
          }
        });
      }
      $.each(that.service_template_json.DOCUMENT.TEMPLATE.BODY.roles, function(index, role){
        var temp_role = {};
        $.extend( temp_role, role);
        var div_id = "user_input_role_"+index;
        var tmp_json = {};
        var stringCustomValues = TemplateUtils.templateToString(extra_info.merge_template.custom_attrs_values);
        $.extend( tmp_json, WizardFields.retrieve($("#"+div_id, context)) );
        role.user_inputs_values = tmp_json;
        if (stringCustomValues) {
          (temp_role.vm_template_contents)
            ? temp_role.vm_template_contents += stringCustomValues
            : temp_role.vm_template_contents = stringCustomValues;
        }
        $("#instantiate_service_role_user_inputs").find("select").each(function(key, vm_group){
          var element = $(vm_group);
          rolevm_group = element.attr("data-role");
          vm_group_value = element.children("option:selected").val();
          if(rolevm_group && role.name && rolevm_group === role.name && vm_group_value){
            if(temp_role.vm_template_contents === undefined){
              temp_role.vm_template_contents = "";
            }
            temp_role.vm_template_contents += TemplateUtils.templateToString({VMGROUP:{ROLE:role.name,VMGROUP_ID:vm_group_value}});
          }
        });

        temp_role.vm_template_contents += charters;
        extra_info.merge_template.roles.push(temp_role);
      });
      charters = "";
    }
    if (!service_name.length){ //empty name
      for (var i=0; i< n_times_int; i++){
        Sunstone.runAction("ServiceTemplate.instantiate", that.templateId, extra_info);
      }
    } else {
      if (service_name.indexOf("%i") === -1){//no wildcard, all with the same name
        extra_info["merge_template"]["name"] = service_name;
        for (var i=0; i< n_times_int; i++){
          Sunstone.runAction(
              "ServiceTemplate.instantiate",
              that.templateId, extra_info);
        }
      } else { //wildcard present: replace wildcard
        for (var i=0; i< n_times_int; i++){
          extra_info["merge_template"]["name"] = service_name.replace(/%i/gi,i);
          Sunstone.runAction(
              "ServiceTemplate.instantiate",
              that.templateId, extra_info);
        }
      }
    }
    return false;
  }
});
<commit_msg>M #~: fix vm_template_content undefined (#88)<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

define(function(require) {
  /*
    DEPENDENCIES
   */

//  require("foundation.tab");
  var BaseFormPanel = require("utils/form-panels/form-panel");
  var Sunstone = require("sunstone");
  var Locale = require("utils/locale");
  var Tips = require("utils/tips");
  var OpenNebulaAction = require('opennebula/action');
  var OpenNebulaServiceTemplate = require("opennebula/servicetemplate");
  var OpenNebulaTemplate = require("opennebula/template");
  var Notifier = require("utils/notifier");
  var WizardFields = require("utils/wizard-fields");
  var UserInputs = require("utils/user-inputs");
  var Config = require("sunstone-config");
  var TemplateUtils = require("utils/template-utils");
  var ServiceUtils = require("utils/service-utils");

  /*
    TEMPLATES
   */

  var TemplateHTML = require("hbs!./instantiate/html");

  /*
    CONSTANTS
   */

  var FORM_PANEL_ID = require("./instantiate/formPanelId");
  var TAB_ID = require("../tabId");
  var vm_group = "VM_GROUP";
  var classButton = 'small button leases right radius';

  /*
    CONSTRUCTOR
   */

  function FormPanel() {
    this.formPanelId = FORM_PANEL_ID;
    this.tabId = TAB_ID;
    this.display_vmgroups = false;
    this.actions = {
      "instantiate": {
        "title": Locale.tr("Instantiate Service Template"),
        "buttonText": Locale.tr("Instantiate"),
        "resetButton": false
      }
    };

    BaseFormPanel.call(this);
  }

  FormPanel.FORM_PANEL_ID = FORM_PANEL_ID;
  FormPanel.prototype = Object.create(BaseFormPanel.prototype);
  FormPanel.prototype.constructor = FormPanel;
  FormPanel.prototype.setTemplateId = _setTemplateId;
  FormPanel.prototype.htmlWizard = _html;
  FormPanel.prototype.submitWizard = _submitWizard;
  FormPanel.prototype.onShow = _onShow;
  FormPanel.prototype.setup = _setup;

  return FormPanel;

  /*
    FUNCTION DEFINITIONS
   */

  function _html() {
    var values_hbs = {
      'formPanelId': this.formPanelId,
      'userInputsHTML': UserInputs.html(),
    };
    if(config && config.system_config && config.system_config.leases){
      values_hbs.userInputsCharters = $("<div/>").append(
        $("<div/>",{style:"display:inline-block;clear:both;width:100%"}).append(
          $("<button />", {class: classButton, id:"addCharters"}).append(
            $("<i/>", {class: 'fa fa-clock'})
          )
        ).add(
          $("<table/>", {class: "service-charters"})
        )
      ).prop('outerHTML');
    }
    return TemplateHTML(values_hbs);
  }

  function _setup(context) {
    var that = this;
    Tips.setup(context);
    return false;
  }

  function _onShow(context) {
    $(".service-charters", context).empty();
    if(config && config.system_config && config.system_config.leases ){
      function remove_leases(){
        $(".remove-lease", context).off("click").on("click", function(e){
          e.preventDefault();
          e.stopPropagation();
          $(this).closest("tr").remove();
        });
      }
      remove_leases();
      $("#addCharters",context).off("click").on("click", function(e){
        e.preventDefault();
        e.stopPropagation();
        var leases = config.system_config.leases;
        var confLeasesKeys = Object.keys(leases);
        var last = 0;
        confLeasesKeys.forEach(function(schedAction){
          if(leases[schedAction] && leases[schedAction].time){
            var schedActionTime = parseInt(leases[schedAction].time,10);
            last = last + schedActionTime;
            var time = (last>=0? "+":"-")+last;
            $(".service-charters", context).append(
              $("<tr/>").attr("data-time", time).attr("data-action", schedAction).append(
                $("<td/>").text(schedAction).add(
                  $("<td/>").text(time)
                ).add(
                  $("<td>").append(
                    $("<button/>", {class: "remove-lease"}).append(
                      $("<i>",{class: "fas fa-trash-alt"})
                    )
                  )
                )
              )
            );
            remove_leases();
          }
        });
      });
    }

    var vmgroups = OpenNebulaAction.cache(vm_group)
    if(!vmgroups){
      Sunstone.runAction("VMGroup.list");
    }
  }

  function _setTemplateId(context, templateId) {
    var that = this;
    this.templateId = templateId;
    this.service_template_json = {};
    OpenNebulaServiceTemplate.show({
      data : {
        id: templateId
      },
      timeout: true,
      success: function (request, template_json){
        that.service_template_json = template_json;
        $(".name", context).text(template_json.DOCUMENT.NAME);
        $("#instantiate_service_user_inputs", context).empty();
        UserInputs.serviceTemplateInsert(
          $("#instantiate_service_user_inputs", context),
          template_json, {
            select_networks: true
          });
        n_roles = template_json.DOCUMENT.TEMPLATE.BODY.roles.length;
        n_roles_done = 0;
        var total_cost = 0;
        var vmgroup_title = Locale.tr("Associate VM Group to Service");
        var setDisplayTitle = function(value=""){
          vmgroup_title = value
        }
        $.each(template_json.DOCUMENT.TEMPLATE.BODY.roles, function(index, role){
          var div_id = "user_input_role_"+index;
          if(vmgroup_title && that.display_vmgroups){
            $("#instantiate_service_role_user_inputs", context).empty().prepend(
              $("<legend/>").text(vmgroup_title)
            );
            setDisplayTitle("");
          }
          $("#instantiate_service_role_user_inputs", context).append(
            $("<div/>",{id:div_id, class:"large-6 columns"})
          );
          OpenNebulaTemplate.show({
            data : {
              id: role.vm_template,
              extended: true
            },
            timeout: true,
            success: function (request, vm_template_json){
              that.vm_template_json = vm_template_json;
              $("#"+div_id, context).empty();
              //if (role.vm_template_contents){
                roleTemplate = TemplateUtils.stringToTemplate(role.vm_template_contents);
                if(roleTemplate && roleTemplate.APPEND){
                  var append = roleTemplate.APPEND.split(",");
                  $.each(append, function(key, value){
                    if (!that.vm_template_json.VMTEMPLATE.TEMPLATE[value]){
                      that.vm_template_json.VMTEMPLATE.TEMPLATE[value] = roleTemplate[value];
                    } else {
                      if (!Array.isArray(that.vm_template_json.VMTEMPLATE.TEMPLATE[value])){
                        that.vm_template_json.VMTEMPLATE.TEMPLATE[value] = [that.vm_template_json.VMTEMPLATE.TEMPLATE[value]];
                      }
                      if (Array.isArray(roleTemplate[value])){
                        $.each(roleTemplate[value], function(rkey, rvalue){
                          that.vm_template_json.VMTEMPLATE.TEMPLATE[value].push(rvalue);
                        });
                      } else {
                        that.vm_template_json.VMTEMPLATE.TEMPLATE[value].push(roleTemplate[value]);
                      }
                    }
                    delete roleTemplate[value];
                  });
                  delete roleTemplate.APPEND;
                }
                $.extend(true, that.vm_template_json.VMTEMPLATE.TEMPLATE, roleTemplate);
              //}
              if (vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"] && vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_UNIT_COST"] && vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_UNIT_COST"] === "GB") {
                vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"] = vm_template_json.VMTEMPLATE.TEMPLATE["MEMORY_COST"]*1024;
              }
              if (vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"]) {
                vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"] = vm_template_json.VMTEMPLATE.TEMPLATE["DISK_COST"]*1024;
              }
              var cost = OpenNebulaTemplate.cost(that.vm_template_json);
              if (cost !== 0 && Config.isFeatureEnabled("showback")) {
                total_cost += (cost * role.cardinality);

                $(".total_cost_div", context).show();
                $(".total_cost_div .cost_value", context).text((total_cost).toFixed(4));
              }

              var opts = {
                text_header: Locale.tr("Role") + " " + role.name
              };
              //vmgroups
              if(that.display_vmgroups){
                var vmgroups = OpenNebulaAction.cache(vm_group);
                if(vmgroups && vmgroups.data){
                  opts.vmgroups = vmgroups.data;
                  opts.role = role.name;
                  if(!Array.isArray(vmgroups.data)){
                    opts.vmgroups = [vmgroups.data];
                  }
                }
              }

              UserInputs.vmTemplateInsert(
                $("#"+div_id, context),
                vm_template_json,
                opts
              );
              n_roles_done += 1;
              if(n_roles_done === n_roles){
                Sunstone.enableFormPanelSubmit();
              }
            },
            error: function(request,error_json, container){
              Notifier.onError(request,error_json, container);
              $("#instantiate_vm_user_inputs", context).empty();
            }
          });
        });
      },
      error: function(request,error_json, container){
        Notifier.onError(request,error_json, container);
        $("#instantiate_service_user_inputs", context).empty();
      }
    });
  }

  //submit
  function _submitWizard(context) {
    var that = this;
    var service_name = $("#service_name",context).val();
    var n_times = $("#service_n_times",context).val();
    var n_times_int=1;
    if (n_times.length){
      n_times_int=parseInt(n_times,10);
    }

    var extra_info = ServiceUtils.getExtraInfo(context);

    if(
      that &&
      that.service_template_json &&
      that.service_template_json.DOCUMENT &&
      that.service_template_json.DOCUMENT.TEMPLATE &&
      that.service_template_json.DOCUMENT.TEMPLATE.BODY &&
      that.service_template_json.DOCUMENT.TEMPLATE.BODY.roles
    ){
      var charters = "";
      if(config && config.system_config && config.system_config.leases ){
        $(".service-charters", context).find("tr").each(function(index){
          var time = $(this).attr("data-time");
          var action = $(this).attr("data-action");
          if(time.length>0 && action.length>0){
            charters += TemplateUtils.templateToString({SCHED_ACTION:{ACTION:action, TIME: time, ID: index.toString()}});
          }
        });
      }
      $.each(that.service_template_json.DOCUMENT.TEMPLATE.BODY.roles, function(index, role){
        var temp_role = {};
        $.extend( temp_role, role);
        var div_id = "user_input_role_"+index;
        var tmp_json = {};
        var stringCustomValues = TemplateUtils.templateToString(extra_info.merge_template.custom_attrs_values);
        $.extend( tmp_json, WizardFields.retrieve($("#"+div_id, context)) );
        role.user_inputs_values = tmp_json;
        if (stringCustomValues) {
          (temp_role.vm_template_contents)
            ? temp_role.vm_template_contents += stringCustomValues
            : temp_role.vm_template_contents = stringCustomValues;
        }
        $("#instantiate_service_role_user_inputs").find("select").each(function(key, vm_group){
          var element = $(vm_group);
          rolevm_group = element.attr("data-role");
          vm_group_value = element.children("option:selected").val();
          if(rolevm_group && role.name && rolevm_group === role.name && vm_group_value){
            if(temp_role.vm_template_contents === undefined){
              temp_role.vm_template_contents = "";
            }
            temp_role.vm_template_contents += TemplateUtils.templateToString({VMGROUP:{ROLE:role.name,VMGROUP_ID:vm_group_value}});
          }
        });
        if(charters.length){
          if(temp_role.vm_template_contents !== undefined){
            temp_role.vm_template_contents += charters;
          }else{
            temp_role.vm_template_contents = charters;
          }
        }
        extra_info.merge_template.roles.push(temp_role);
      });
      charters = "";
    }
    if (!service_name.length){ //empty name
      for (var i=0; i< n_times_int; i++){
        Sunstone.runAction("ServiceTemplate.instantiate", that.templateId, extra_info);
      }
    } else {
      if (service_name.indexOf("%i") === -1){//no wildcard, all with the same name
        extra_info["merge_template"]["name"] = service_name;
        for (var i=0; i< n_times_int; i++){
          Sunstone.runAction(
              "ServiceTemplate.instantiate",
              that.templateId, extra_info);
        }
      } else { //wildcard present: replace wildcard
        for (var i=0; i< n_times_int; i++){
          extra_info["merge_template"]["name"] = service_name.replace(/%i/gi,i);
          Sunstone.runAction(
              "ServiceTemplate.instantiate",
              that.templateId, extra_info);
        }
      }
    }
    return false;
  }
});
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

class FlagDependencyExportsPlugin {

	apply(compiler) {
		compiler.plugin("compilation", (compilation) => {
			compilation.plugin("finish-modules", (modules) => {

				const dependencies = {};

				let module;
				let moduleWithExports;
				const queue = modules.filter((m) => !m.providedExports);
				for(let i = 0; i < queue.length; i++) {
					module = queue[i];

					if(module.providedExports !== true) {
						moduleWithExports = false;
						processDependenciesBlock(module);
						if(!moduleWithExports) {
							module.providedExports = true;
							notifyDependencies();
						}
					}
				}

				function processDependenciesBlock(depBlock) {
					depBlock.dependencies.forEach((dep) => processDependency(dep));
					depBlock.variables.forEach((variable) => {
						variable.dependencies.forEach((dep) => processDependency(dep));
					});
					depBlock.blocks.forEach((block) => processDependenciesBlock(block));
				}

				function processDependency(dep, usedExports) {
					const exportDesc = dep.getExports && dep.getExports();
					if(!exportDesc) return;
					moduleWithExports = true;
					const exports = exportDesc.exports;
					const exportDeps = exportDesc.dependencies;
					if(exportDeps) {
						exportDeps.forEach((dep) => {
							const depIdent = dep.identifier();
							let array = dependencies["$" + depIdent];
							if(!array) array = dependencies["$" + depIdent] = [];
							if(array.indexOf(module) < 0)
								array.push(module);
						});
					}
					let changed = false;
					if(module.providedExports !== true) {
						if(exports === true) {
							module.providedExports = true;
							changed = true;
						} else if(Array.isArray(exports)) {
							if(Array.isArray(module.providedExports)) {
								changed = addToSet(module.providedExports, exports);
							} else {
								module.providedExports = exports.slice();
								changed = true;
							}
						}
					}
					if(changed) {
						notifyDependencies();
					}
				}

				function notifyDependencies() {
					const deps = dependencies["$" + module.identifier()];
					if(deps) {
						deps.forEach((dep) => queue.push(dep));
					}
				}
			});

			function addToSet(a, b) {
				let changed = false;
				b.forEach((item) => {
					if(a.indexOf(item) < 0) {
						a.push(item);
						changed = true;
					}
				});
				return changed;
			}
		});
	}
}

module.exports = FlagDependencyExportsPlugin;
<commit_msg>minor improvements<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

class FlagDependencyExportsPlugin {

	apply(compiler) {
		compiler.plugin("compilation", (compilation) => {
			compilation.plugin("finish-modules", (modules) => {

				const dependencies = Object.create(null);

				let module;
				let moduleWithExports;
				const queue = modules.filter((m) => !m.providedExports);
				for(let i = 0; i < queue.length; i++) {
					module = queue[i];

					if(module.providedExports !== true) {
						moduleWithExports = false;
						processDependenciesBlock(module);
						if(!moduleWithExports) {
							module.providedExports = true;
							notifyDependencies();
						}
					}
				}

				function processDependenciesBlock(depBlock) {
					depBlock.dependencies.forEach((dep) => processDependency(dep));
					depBlock.variables.forEach((variable) => {
						variable.dependencies.forEach((dep) => processDependency(dep));
					});
					depBlock.blocks.forEach(processDependenciesBlock);
				}

				function processDependency(dep, usedExports) {
					const exportDesc = dep.getExports && dep.getExports();
					if(!exportDesc) return;
					moduleWithExports = true;
					const exports = exportDesc.exports;
					const exportDeps = exportDesc.dependencies;
					if(exportDeps) {
						exportDeps.forEach((dep) => {
							const depIdent = dep.identifier();
							let array = dependencies[depIdent];
							if(!array) array = dependencies[depIdent] = [];
							if(array.indexOf(module) < 0)
								array.push(module);
						});
					}
					let changed = false;
					if(module.providedExports !== true) {
						if(exports === true) {
							module.providedExports = true;
							changed = true;
						} else if(Array.isArray(exports)) {
							if(Array.isArray(module.providedExports)) {
								changed = addToSet(module.providedExports, exports);
							} else {
								module.providedExports = exports.slice();
								changed = true;
							}
						}
					}
					if(changed) {
						notifyDependencies();
					}
				}

				function notifyDependencies() {
					const deps = dependencies[module.identifier()];
					if(deps) {
						deps.forEach((dep) => queue.push(dep));
					}
				}
			});

			function addToSet(a, b) {
				let changed = false;
				b.forEach((item) => {
					if(a.indexOf(item) < 0) {
						a.push(item);
						changed = true;
					}
				});
				return changed;
			}
		});
	}
}

module.exports = FlagDependencyExportsPlugin;
<|endoftext|>


<commit_before>// @flow

import React, { Component, createRef } from 'react';
import { connect } from 'react-redux';
import { string, arrayOf, func, object } from 'prop-types';
import { Button, Checkbox, Dropdown } from 'antd';

import { getColumns, getVisibleColumns } from '../../reducers/mappings';
import { setVisibleColumns } from '../../actions/mappings';
import colors from '../theme/colors';

const { Group } = Checkbox;

type Props = {
	columns: string[],
	visibleColumns: string[],
	setVisibleColumns: (string[]) => void,
};

type State = {
	showDropdown: boolean,
};
class ShowHideColumns extends Component<Props, State> {
	showHideDropdownNode = createRef();

	state = {
		showDropdown: false,
	};

	componentDidMount() {
		document.addEventListener(
			'mousedown',
			this.handleDropdownOutsideClick,
			false,
		);
	}

	componentWillUnmount() {
		document.removeEventListener(
			'mousedown',
			this.handleDropdownOutsideClick,
			false,
		);
	}

	handleDropdownOutsideClick = (e: object) => {
		if (
			this.showHideDropdownNode &&
			this.showHideDropdownNode.current &&
			this.showHideDropdownNode.current.contains &&
			this.showHideDropdownNode.current.contains(e.target)
		) {
			return;
		}

		if (e.target.id === 'show-hide-button') {
			return;
		}

		this.setState({
			showDropdown: false,
		});
	};

	handleSelectAll = e => {
		const { checked } = e.target;
		let visibleColumns;
		if (checked) {
			visibleColumns = this.props.columns;
		} else {
			visibleColumns = [];
		}
		this.props.setVisibleColumns(visibleColumns);
	};

	handleVisibleColumnsChange = visibleColumns => {
		this.props.setVisibleColumns(visibleColumns);
	};

	toggleDropDown = () => {
		this.setState(prevState => ({
			showDropdown: !prevState.showDropdown,
		}));
	};

	render() {
		const { columns: allColumns, visibleColumns } = this.props;
		const { showDropdown } = this.state;

		return (
			<Dropdown
				ref={this.showHideDropdownNode}
				overlay={
					<div
						css={{
							background: colors.white,
							borderRadius: 4,
							padding: 10,
							boxShadow: '0 1px 6px rgba(0, 0, 0, .2)',
							maxHeight: '75vh',
							overflowY: 'auto',
						}}
						ref={this.showHideDropdownNode}
					>
						<Checkbox
							checked={
								visibleColumns.length === allColumns.length
							}
							indeterminate={
								visibleColumns.length < allColumns.length &&
								visibleColumns.length
							}
							css={{
								marginBottom: 5,
								fontWeight: 'bold',
							}}
							onChange={this.handleSelectAll}
						>
							Select All
						</Checkbox>
						<Group
							options={allColumns}
							css={{
								display: 'grid',
								gridGap: 5,
							}}
							value={visibleColumns}
							onChange={this.handleVisibleColumnsChange}
						/>
					</div>
				}
				visible={showDropdown}
				trigger={['click']}
				onClick={this.toggleDropDown}
			>
				<Button
					css={{ marginLeft: '5px' }}
					id="show-hide-button"
					icon="setting"
				/>
			</Dropdown>
		);
	}
}

const mapStateToProps = state => ({
	columns: getColumns(state),
	visibleColumns: getVisibleColumns(state),
});

const mapDispatchToProps = {
	setVisibleColumns,
};

ShowHideColumns.propTypes = {
	columns: arrayOf(string).isRequired,
	visibleColumns: arrayOf(string).isRequired,
	setVisibleColumns: func.isRequired,
};

export default connect(
	mapStateToProps,
	mapDispatchToProps,
)(ShowHideColumns);
<commit_msg>Set visiblity of meta columns first in table<commit_after>// @flow

import React, { Component, createRef } from 'react';
import { connect } from 'react-redux';
import { string, arrayOf, func, object } from 'prop-types';
import { Button, Checkbox, Dropdown } from 'antd';

import { getColumns, getVisibleColumns } from '../../reducers/mappings';
import { setVisibleColumns } from '../../actions/mappings';
import colors from '../theme/colors';

const { Group } = Checkbox;

type Props = {
	columns: string[],
	visibleColumns: string[],
	setVisibleColumns: (string[]) => void,
};

type State = {
	showDropdown: boolean,
};
class ShowHideColumns extends Component<Props, State> {
	showHideDropdownNode = createRef();

	state = {
		showDropdown: false,
	};

	componentDidMount() {
		document.addEventListener(
			'mousedown',
			this.handleDropdownOutsideClick,
			false,
		);
	}

	componentWillUnmount() {
		document.removeEventListener(
			'mousedown',
			this.handleDropdownOutsideClick,
			false,
		);
	}

	handleDropdownOutsideClick = (e: object) => {
		if (
			this.showHideDropdownNode &&
			this.showHideDropdownNode.current &&
			this.showHideDropdownNode.current.contains &&
			this.showHideDropdownNode.current.contains(e.target)
		) {
			return;
		}

		if (e.target.id === 'show-hide-button') {
			return;
		}

		this.setState({
			showDropdown: false,
		});
	};

	handleSelectAll = e => {
		const { checked } = e.target;
		let visibleColumns;
		if (checked) {
			visibleColumns = this.props.columns;
		} else {
			visibleColumns = [];
		}
		this.props.setVisibleColumns(visibleColumns);
	};

	handleVisibleColumnsChange = visibleColumns => {
		const metaIndex = visibleColumns.indexOf('_index');
		const metaType = visibleColumns.indexOf('_type');
		let currentVissibleColums = visibleColumns;

		// to append meta fields at the beginning of array
		if (metaType > -1) {
			currentVissibleColums.splice(metaType, 1);
			currentVissibleColums = ['_type', ...currentVissibleColums];
		}

		if (metaIndex > -1) {
			currentVissibleColums.splice(metaIndex, 1);
			currentVissibleColums = ['_index', ...currentVissibleColums];
		}

		this.props.setVisibleColumns(currentVissibleColums);
	};

	toggleDropDown = () => {
		this.setState(prevState => ({
			showDropdown: !prevState.showDropdown,
		}));
	};

	render() {
		const { columns: allColumns, visibleColumns } = this.props;
		const { showDropdown } = this.state;

		return (
			<Dropdown
				ref={this.showHideDropdownNode}
				overlay={
					<div
						css={{
							background: colors.white,
							borderRadius: 4,
							padding: 10,
							boxShadow: '0 1px 6px rgba(0, 0, 0, .2)',
							maxHeight: '75vh',
							overflowY: 'auto',
						}}
						ref={this.showHideDropdownNode}
					>
						<Checkbox
							checked={
								visibleColumns.length === allColumns.length
							}
							indeterminate={
								visibleColumns.length < allColumns.length &&
								visibleColumns.length
							}
							css={{
								marginBottom: 5,
								fontWeight: 'bold',
							}}
							onChange={this.handleSelectAll}
						>
							Select All
						</Checkbox>
						<Group
							options={allColumns}
							css={{
								display: 'grid',
								gridGap: 5,
							}}
							value={visibleColumns}
							onChange={this.handleVisibleColumnsChange}
						/>
					</div>
				}
				visible={showDropdown}
				trigger={['click']}
				onClick={this.toggleDropDown}
			>
				<Button
					css={{ marginLeft: '5px' }}
					id="show-hide-button"
					icon="setting"
				/>
			</Dropdown>
		);
	}
}

const mapStateToProps = state => ({
	columns: getColumns(state),
	visibleColumns: getVisibleColumns(state),
});

const mapDispatchToProps = {
	setVisibleColumns,
};

ShowHideColumns.propTypes = {
	columns: arrayOf(string).isRequired,
	visibleColumns: arrayOf(string).isRequired,
	setVisibleColumns: func.isRequired,
};

export default connect(
	mapStateToProps,
	mapDispatchToProps,
)(ShowHideColumns);
<|endoftext|>


<commit_before>var assert   = require('assert');
var events   = require('events');
var inherits = require('util').inherits;
var hostname = require('os').hostname();
const e2p    = require('event-to-promise');

/* riemann uses Google Protocol Buffers
   as its wire transfer protocol. */
var Serializer = require('./serializer');

/* riemann communicates over UDP and TCP.
   UDP is way faster for sending events,
   we favor that whenever possible. */
var Socket = require('./socket');

var MAX_UDP_BUFFER_SIZE = 16384;
function _sendMessage(contents, transport) {
  var self = this;
  return function() {
    // all events are wrapped in the Message type.
    var message = Serializer.serializeMessage(contents);

    // if an explict transport is specified via code,
    // at definition of this message, we trust it.
    if (transport) {
      transport.send(message);

    // if we're sending a message that is larger than the max buffer
    // size of UDP, we should switch over to TCP.
    } else if (message.length >= MAX_UDP_BUFFER_SIZE) {
      self.tcp.send(message);

    // utilize whatever transport this message is applied to,
    // by caller.
    } else {
      this.send(message);
    }
    return e2p.multi(self, [ 'data', 'sent' ]).then(([ param, e ]) => param);
  };
}

/* some friendly defaults for event,
   in case they went missing. */
function _defaultValues(payload) {
  if (!payload.host)  { payload.host = hostname; }
  if (!payload.time)  { payload.time = new Date().getTime()/1000; }
  if (typeof payload.metric !== "undefined" && payload.metric !== null) {
    payload.metric_f = payload.metric;
    delete payload.metric;
  }
  return payload;
}

/* sets up a client connection to a Riemann server.
   options supports the following:
    - host (eg; my.riemannserver.biz)
    - port (eg; 5555 -- default) */
function Client(options, onConnect) {
  events.EventEmitter.call(this);
  if (!options) { options = {}; }
  options.host = options.host ? options.host : '127.0.0.1';
  options.port = options.port ? Number(options.port) : 5555;

  if (onConnect) { this.once('connect', onConnect); }

  var self = this;

  this.tcp = new Socket.tcpSocket(options);
  this.udp = new Socket.udpSocket(options);

  // monitor both close events, and proxy
  // it as a single disconnect event.
  var _closeAcks = 0;
  var monitorClose = function() {
    ++_closeAcks;
    return function() {
      if (--_closeAcks === 0) { self.emit('disconnect'); }
    };
  };
  this.tcp.socket.on('close', monitorClose());
  this.udp.socket.on('close', monitorClose());

  // proxy the TCP connect event.
  this.tcp.socket.on('connect', function() { self.emit('connect'); });

  // proxy the UDP sent event.
  this.udp.socket.on('sent', function() { self.emit('sent'); });

  // proxy errors from TCP and UDP
  this.tcp.socket.on('error', function(error) { self.emit('error', error); });
  this.udp.socket.on('error', function(error) { self.emit('error', error); });

  this.tcp.onMessage(function(message) {
    self.emit('data', Serializer.deserializeMessage(message));
  });

}

inherits(Client, events.EventEmitter);
exports.Client = Client;

/* Submits an Event to the server.
   takes a key/value object of valid
   Event protocol buffer values. */
Client.prototype.Event = function(event) {
  event = _defaultValues(event);
  return _sendMessage.call(this, { events: [event] });
};

/* Submits a State to the server.
   takes a key/value object of valid
   State protocol buffer values. */
Client.prototype.State = function(state) {
  state = _defaultValues(state);
  return _sendMessage.call(this, { states: [state] });
};

/* Submits a Query to the server.
  takes a key/value object of valid
  Query protocol buffer values. */
Client.prototype.Query = function(query) {
  return _sendMessage.call(this, { query: query }, this.tcp);
};

/* sends a payload to Riemann. Exepects any valid payload type
   (eg: Event, State, Query...) and an optional (requested, not guaranteed)
   transport (TCP or UDP). */
Client.prototype.send = function(payload, transport) {
  if (transport) {
    assert(transport === this.tcp || transport === this.udp, 'invalid transport provided.');
  } else {
    transport = this.udp;
  }
  return payload.apply(transport);
};

/* disconnects our client */
Client.prototype.disconnect = function(onDisconnect) {
  if (this.tcp) { this.tcp.socket.end(); }
  if (this.udp) { this.udp.socket.close(); }

  if (onDisconnect) {
    this.once('disconnect', onDisconnect);
  }
  else {
    const tcpEnded = e2p(this.tcp.socket, 'close');
    const udpClosed = e2p(this.udp.socket, 'close');
    return Promise.all([ tcpEnded, udpClosed ])
      .then(() => undefined); // discard resolution parameters if any
  }
};
<commit_msg>'unhandled rejection error' threat is eliminated (#29)<commit_after>var assert   = require('assert');
var events   = require('events');
var inherits = require('util').inherits;
var hostname = require('os').hostname();
const e2p    = require('event-to-promise');

/* riemann uses Google Protocol Buffers
   as its wire transfer protocol. */
var Serializer = require('./serializer');

/* riemann communicates over UDP and TCP.
   UDP is way faster for sending events,
   we favor that whenever possible. */
var Socket = require('./socket');

var MAX_UDP_BUFFER_SIZE = 16384;
function _sendMessage(contents, transport) {
  var self = this;
  return function() {
    // all events are wrapped in the Message type.
    var message = Serializer.serializeMessage(contents);

    // if an explict transport is specified via code,
    // at definition of this message, we trust it.
    if (transport) {
      transport.send(message);

    // if we're sending a message that is larger than the max buffer
    // size of UDP, we should switch over to TCP.
    } else if (message.length >= MAX_UDP_BUFFER_SIZE) {
      self.tcp.send(message);

    // utilize whatever transport this message is applied to,
    // by caller.
    } else {
      this.send(message);
    }

    if (self.returnPromise) {
      return e2p.multi(self, [ 'data', 'sent' ]).then(([ param, e ]) => param);
    }
  };
}

/* some friendly defaults for event,
   in case they went missing. */
function _defaultValues(payload) {
  if (!payload.host)  { payload.host = hostname; }
  if (!payload.time)  { payload.time = new Date().getTime()/1000; }
  if (typeof payload.metric !== "undefined" && payload.metric !== null) {
    payload.metric_f = payload.metric;
    delete payload.metric;
  }
  return payload;
}

/* sets up a client connection to a Riemann server.
   options supports the following:
    - host (eg; my.riemannserver.biz)
    - port (eg; 5555 -- default) */
function Client(options, onConnect) {
  events.EventEmitter.call(this);
  if (!options) { options = {}; }
  options.host = options.host ? options.host : '127.0.0.1';
  options.port = options.port ? Number(options.port) : 5555;

  if (onConnect) { this.once('connect', onConnect); }

  var self = this;

  this.tcp = new Socket.tcpSocket(options);
  this.udp = new Socket.udpSocket(options);
  this.returnPromise = options.returnPromise;

  // monitor both close events, and proxy
  // it as a single disconnect event.
  var _closeAcks = 0;
  var monitorClose = function() {
    ++_closeAcks;
    return function() {
      if (--_closeAcks === 0) { self.emit('disconnect'); }
    };
  };
  this.tcp.socket.on('close', monitorClose());
  this.udp.socket.on('close', monitorClose());

  // proxy the TCP connect event.
  this.tcp.socket.on('connect', function() { self.emit('connect'); });

  // proxy the UDP sent event.
  this.udp.socket.on('sent', function() { self.emit('sent'); });

  // proxy errors from TCP and UDP
  this.tcp.socket.on('error', function(error) { self.emit('error', error); });
  this.udp.socket.on('error', function(error) { self.emit('error', error); });

  this.tcp.onMessage(function(message) {
    self.emit('data', Serializer.deserializeMessage(message));
  });

}

inherits(Client, events.EventEmitter);
exports.Client = Client;

/* Submits an Event to the server.
   takes a key/value object of valid
   Event protocol buffer values. */
Client.prototype.Event = function(event) {
  event = _defaultValues(event);
  return _sendMessage.call(this, { events: [event] });
};

/* Submits a State to the server.
   takes a key/value object of valid
   State protocol buffer values. */
Client.prototype.State = function(state) {
  state = _defaultValues(state);
  return _sendMessage.call(this, { states: [state] });
};

/* Submits a Query to the server.
  takes a key/value object of valid
  Query protocol buffer values. */
Client.prototype.Query = function(query) {
  return _sendMessage.call(this, { query: query }, this.tcp);
};

/* sends a payload to Riemann. Exepects any valid payload type
   (eg: Event, State, Query...) and an optional (requested, not guaranteed)
   transport (TCP or UDP). */
Client.prototype.send = function(payload, transport) {
  if (transport) {
    assert(transport === this.tcp || transport === this.udp, 'invalid transport provided.');
  } else {
    transport = this.udp;
  }
  return payload.apply(transport);
};

/* disconnects our client */
Client.prototype.disconnect = function(onDisconnect) {
  if (this.tcp) { this.tcp.socket.end(); }
  if (this.udp) { this.udp.socket.close(); }
  if (this.returnPromise) {
    const tcpEnded = e2p(this.tcp.socket, 'close');
    const udpClosed = e2p(this.udp.socket, 'close');
    return Promise.all([ tcpEnded, udpClosed ])
      .then(() => undefined); // discard resolution parameters if any
  }
  else if (onDisconnect) {
    this.once('disconnect', onDisconnect);
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import UtilsNew from "../../../core/utilsNew.js";

export default class ClinicalInterpretationVariantReview extends LitElement {

    constructor() {
        super();

        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object,
            },
            variant: {
                type: Object,
            },
            mode: {
                type: String, // Values: form, modal
            },
        };
    }

    _init() {
        this._prefix = UtilsNew.randomString(8);
        this.updateParams = {};
        this.mode = "form";
        this.variant = {};
        this._config = this.getDefaultconfig();
        this.newCommentsAdded = false;
    }

    update(changedProperties) {
        if (changedProperties.has("variant")) {
            this.variantObserver();
        }

        if (changedProperties.has("mode")) {
            this.modeObserver();
        }

        super.update(changedProperties);
    }

    variantObserver() {
        this.variant = this.variant || {}; // Prevent undefined variant review
        this.newCommentsAdded = false;
    }

    modeObserver() {
        this._config = this.getDefaultconfig();
    }

    onCommentChange(e) {
        this.commentsUpdate = e.detail;

        if (this.commentsUpdate?.newComments?.length > 0) {
            // Josemi 20220719 Note: added fix to append new comments to the variant instead of replacing
            // the saved comment with the new comment
            if (!this.newCommentsAdded) {
                if (!this.variant.comments) {
                    this.variant.comments = [];
                }
                this.variant.comments.push(this.commentsUpdate.newComments[0]);
                this.newCommentsAdded = true;
            } else {
                this.variant.comments[this.variant.comments.length - 1] = this.commentsUpdate.newComments[0];
            }
        }

        this.dispatchEvent(new CustomEvent("variantChange", {
            detail: {
                value: this.variant,
                update: this.updateParams
            },
        }));
    }

    onSaveFieldChange(e) {
        switch (e.detail.param) {
            case "status":
            case "discussion":
                if (e.detail.value !== null) {
                    this.variant[e.detail.param] = e.detail.value;
                    this.updateParams[e.detail.param] = e.detail.value;
                } else {
                    delete this.updateParams[e.detail.param];
                }
                break;
        }

        if (this.commentsUpdate?.newComments?.length > 0) {
            this.variant.comments = this.commentsUpdate.newComments;
        }

        this.dispatchEvent(new CustomEvent("variantChange", {
            detail: {
                value: this.variant,
                update: this.updateParams
            },
        }));
    }

    render() {
        return html`
            <data-form
                .data="${this.variant}"
                .config="${this._config}"
                @fieldChange="${e => this.onSaveFieldChange(e)}">
            </data-form>
        `;
    }

    getDefaultconfig() {
        const sections = [
            {
                elements: [
                    {
                        title: "Status",
                        field: "status",
                        type: "select",
                        allowedValues: [
                            "NOT_REVIEWED",
                            "REVIEW_REQUESTED",
                            "REVIEWED",
                            "DISCARDED",
                            "REPORTED"
                        ],
                    },
                    {
                        title: "Discussion",
                        field: "discussion",
                        type: "input-text",
                        display: {
                            placeholder: "Add a discussion",
                            rows: 5,
                        },
                    },
                    {
                        title: "Comments",
                        field: "comments",
                        type: "custom",
                        display: {
                            // Josemi 20220719 NOTE: comments field has been removed from the comment-editor properties to allow
                            // saving more than one comment to the variant
                            render: comments => html`

                                    ${(this.variant?.comments || []).map(comment => html`

                                                    ${comment.author || "-"} - ${UtilsNew.dateFormatter(comment.date)}

${comment.message || "-"}

Tags: ${(comment.tags || []).join(" ") || "-"}

                                    `)}

                                <clinical-analysis-comment-editor
                                    .comments="${[]}"
                                    @commentChange="${e => this.onCommentChange(e)}">
                                </clinical-analysis-comment-editor>
                            `,
                        },
                    },
                ]
            }
        ];

        if (this.mode === "modal") {
            return {
                title: "Edit",
                icon: "fas fa-edit",
                type: "modal",
                display: {
                    // style: "margin: 25px 50px 0px 0px",
                    titleWidth: 3,
                    defaultValue: "",
                    defaultLayout: "horizontal",
                    buttonClearText: "Cancel",
                    buttonOkText: "Save",
                },
                sections: sections,
            };
        } else {
            return {
                title: "Save",
                // icon: "fas fa-save",
                display: {
                    style: "padding:16px;",
                    titleWidth: 3,
                    titleAlign: "right",
                    defaultValue: "",
                    defaultLayout: "horizontal",
                    titleVisible: false,
                    buttonsVisible: false,
                },
                sections: sections,
            };
        }
    }

}

customElements.define("clinical-interpretation-variant-review", ClinicalInterpretationVariantReview);

<commit_msg>wc: minor style improvements in comments of variant review #TASK-1541<commit_after>/*
 * Copyright 2015-2016 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import UtilsNew from "../../../core/utilsNew.js";

export default class ClinicalInterpretationVariantReview extends LitElement {

    constructor() {
        super();

        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object,
            },
            variant: {
                type: Object,
            },
            mode: {
                type: String, // Values: form, modal
            },
        };
    }

    _init() {
        this._prefix = UtilsNew.randomString(8);
        this.updateParams = {};
        this.mode = "form";
        this.variant = {};
        this._config = this.getDefaultconfig();
        this.newCommentsAdded = false;
    }

    update(changedProperties) {
        if (changedProperties.has("variant")) {
            this.variantObserver();
        }

        if (changedProperties.has("mode")) {
            this.modeObserver();
        }

        super.update(changedProperties);
    }

    variantObserver() {
        this.variant = this.variant || {}; // Prevent undefined variant review
        this.newCommentsAdded = false;
    }

    modeObserver() {
        this._config = this.getDefaultconfig();
    }

    onCommentChange(e) {
        this.commentsUpdate = e.detail;

        if (this.commentsUpdate?.newComments?.length > 0) {
            // Josemi 20220719 Note: added fix to append new comments to the variant instead of replacing
            // the saved comment with the new comment
            if (!this.newCommentsAdded) {
                if (!this.variant.comments) {
                    this.variant.comments = [];
                }
                this.variant.comments.push(this.commentsUpdate.newComments[0]);
                this.newCommentsAdded = true;
            } else {
                this.variant.comments[this.variant.comments.length - 1] = this.commentsUpdate.newComments[0];
            }
        }

        this.dispatchEvent(new CustomEvent("variantChange", {
            detail: {
                value: this.variant,
                update: this.updateParams
            },
        }));
    }

    onSaveFieldChange(e) {
        switch (e.detail.param) {
            case "status":
            case "discussion":
                if (e.detail.value !== null) {
                    this.variant[e.detail.param] = e.detail.value;
                    this.updateParams[e.detail.param] = e.detail.value;
                } else {
                    delete this.updateParams[e.detail.param];
                }
                break;
        }

        if (this.commentsUpdate?.newComments?.length > 0) {
            this.variant.comments = this.commentsUpdate.newComments;
        }

        this.dispatchEvent(new CustomEvent("variantChange", {
            detail: {
                value: this.variant,
                update: this.updateParams
            },
        }));
    }

    render() {
        return html`
            <data-form
                .data="${this.variant}"
                .config="${this._config}"
                @fieldChange="${e => this.onSaveFieldChange(e)}">
            </data-form>
        `;
    }

    getDefaultconfig() {
        const sections = [
            {
                elements: [
                    {
                        title: "Status",
                        field: "status",
                        type: "select",
                        allowedValues: [
                            "NOT_REVIEWED",
                            "REVIEW_REQUESTED",
                            "REVIEWED",
                            "DISCARDED",
                            "REPORTED"
                        ],
                    },
                    {
                        title: "Discussion",
                        field: "discussion",
                        type: "input-text",
                        display: {
                            placeholder: "Add a discussion",
                            rows: 5,
                        },
                    },
                    {
                        title: "Comments",
                        field: "comments",
                        type: "custom",
                        display: {
                            // Josemi 20220719 NOTE: comments field has been removed from the comment-editor properties to allow
                            // saving more than one comment to the variant
                            render: comments => html`

                                    ${(comments || []).map(comment => html`

                                                    ${comment.author || "-"} - ${UtilsNew.dateFormatter(comment.date)}

${comment.message || "-"}

Tags: ${(comment.tags || []).join(" ") || "-"}

                                    `)}

                                <clinical-analysis-comment-editor
                                    .comments="${[]}"
                                    @commentChange="${e => this.onCommentChange(e)}">
                                </clinical-analysis-comment-editor>
                            `,
                        },
                    },
                ]
            }
        ];

        if (this.mode === "modal") {
            return {
                title: "Edit",
                icon: "fas fa-edit",
                type: "modal",
                display: {
                    // style: "margin: 25px 50px 0px 0px",
                    titleWidth: 3,
                    defaultValue: "",
                    defaultLayout: "horizontal",
                    buttonClearText: "Cancel",
                    buttonOkText: "Save",
                },
                sections: sections,
            };
        } else {
            return {
                title: "Save",
                // icon: "fas fa-save",
                display: {
                    style: "padding:16px;",
                    titleWidth: 3,
                    titleAlign: "right",
                    defaultValue: "",
                    defaultLayout: "horizontal",
                    titleVisible: false,
                    buttonsVisible: false,
                },
                sections: sections,
            };
        }
    }

}

customElements.define("clinical-interpretation-variant-review", ClinicalInterpretationVariantReview);

<|endoftext|>


<commit_before>import DeveloperError from "../Core/DeveloperError.js";

/**
 * A light source. This type describes an interface and is not intended to be instantiated directly. Together, color and intensity produce a high-dynamic-range light color. intensity can also be used individually to dim or brighten the light without changing the hue.
 *
 * @alias Light
 * @constructor
 *
 * @see DirectionalLight
 * @see SunLight
 */
function Light() {}

Object.defineProperties(Light.prototype, {
  /**
   * The color of the light.
   * @memberof Light.prototype
   * @type {Color}
   */
  color: {
    get: DeveloperError.throwInstantiationError,
  },

  /**
   * The intensity controls the strength of the light.  intensity  has a minimum value of 0.0 and no maximum value.
   * @memberof Light.prototype
   * @type {Number}
   */
  intensity: {
    get: DeveloperError.throwInstantiationError,
  },
});

export default Light;
<commit_msg>removed whitespace from intensity doc<commit_after>import DeveloperError from "../Core/DeveloperError.js";

/**
 * A light source. This type describes an interface and is not intended to be instantiated directly. Together, color and intensity produce a high-dynamic-range light color. intensity can also be used individually to dim or brighten the light without changing the hue.
 *
 * @alias Light
 * @constructor
 *
 * @see DirectionalLight
 * @see SunLight
 */
function Light() {}

Object.defineProperties(Light.prototype, {
  /**
   * The color of the light.
   * @memberof Light.prototype
   * @type {Color}
   */
  color: {
    get: DeveloperError.throwInstantiationError,
  },

  /**
   * The intensity controls the strength of the light. intensity has a minimum value of 0.0 and no maximum value.
   * @memberof Light.prototype
   * @type {Number}
   */
  intensity: {
    get: DeveloperError.throwInstantiationError,
  },
});

export default Light;
<|endoftext|>