<commit_before>/**
 * @file CascaderSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import CascaderList from '../CascaderList';
import Theme from '../Theme';
import Tip from '../Tip';

import SelectMode from '../_statics/SelectMode';
import HorizontalDirection from '../_statics/HorizontalDirection';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import CascaderCalculation from '../_vendors/CascaderCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class CascaderSelect extends Component {

    static SelectMode = SelectMode;
    static ExpandDirection = HorizontalDirection;
    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            popupVisible: false,
            path: props.selectMode === SelectMode.SINGLE_SELECT ?
                CascaderCalculation.calPath(props.value, props.data, props) : undefined
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.dropdown && this.refs.dropdown.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.dropdown && this.refs.dropdown.resetPosition();
    };

    /**
     * public
     */
    openPopup = () => {
        this.refs.dropdown && this.refs.dropdown.openPopup();
    };

    /**
     * public
     */
    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    getTriggerValue = (props = this.props) => {

        const {data, selectMode, placeholder, triggerRenderer, renderer, displayField, valueField} = props,
            {value, path} = this.state,
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value, path) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        if (isMultiSelect) {
            return value.length > 0 ?
                value.length + ' selected'
                :
                placeholder;
        }

        let result = [];

        function addNode(node, i) {
            result.push(
                renderer ?

                        {renderer(node, path && path.slice(0, i + 1))}

                    :
                    Util.getTextByDisplayField(node, displayField, valueField)
            );
        }

        if (path) {
            for (let i = 0, len = path.length; i < len; i++) {

                i > 0 && result.push(

                );

                addNode(path[i].node, i);

            }
        }

        return result;

    };

    pathChangeHandler = () => {
        this.refs.dropdown.resetPopupPosition();
    };

    nodeSelectHandler = (node, path) => {

        const {onNodeSelect} = this.props;
        onNodeSelect && onNodeSelect(node, path);

        if (this.props.selectMode !== SelectMode.SINGLE_SELECT) {
            return;
        }

        this.setState({
            path
        });

    };

    changeHandler = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    };

    popupClosedHandler = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, listWidth, data, renderer,
                selectTheme, selectMode, expandDirection, valueField, displayField, descriptionField, triggerRenderer,
                isSelectRecursive, allowCollapse, onNodeDeselect, popupChildren, onNodeClick,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, popupVisible} = this.state,

            wrapperClassName = classNames('cascader-select', {
                [className]: className
            }),

            selectTriggerClassName = classNames({
                activated: popupVisible,
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),
            selectPopupClassName = classNames('cascader-select-popup', {
                [popupClassName]: popupClassName
            });

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={selectTriggerClassName}
                          popupClassName={selectPopupClassName}
                          popupTheme={popupTheme}
                          autoPopupWidth={false}
                          triggerValue={this.getTriggerValue()}
                          onClosePopup={this.popupClosedHandler}>

 Event.wheelHandler(e, this.props)}>

                        <CascaderList className="cascader-select-list"
                                      theme={popupTheme}
                                      selectTheme={selectTheme}
                                      expandDirection={expandDirection}
                                      listWidth={listWidth}
                                      selectMode={selectMode}
                                      data={data}
                                      value={value}
                                      valueField={valueField}
                                      displayField={displayField}
                                      descriptionField={descriptionField}
                                      isSelectRecursive={isSelectRecursive}
                                      allowCollapse={allowCollapse}
                                      collapsedIconCls={collapsedIconCls}
                                      expandedIconCls={expandedIconCls}
                                      radioUncheckedIconCls={radioUncheckedIconCls}
                                      radioCheckedIconCls={radioCheckedIconCls}
                                      checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                      checkboxCheckedIconCls={checkboxCheckedIconCls}
                                      checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                      renderer={renderer}
                                      onNodeClick={onNodeClick}
                                      onNodeSelect={this.nodeSelectHandler}
                                      onNodeDeselect={onNodeDeselect}
                                      onChange={this.changeHandler}
                                      onPathChange={this.pathChangeHandler}/>

                    {popupChildren}

                </Dropdown>

        );

    }
}

CascaderSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    listWidth: PropTypes.number,

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The direction of expansion.
     */
    expandDirection: PropTypes.oneOf(Util.enumerateValue(HorizontalDirection)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),
    triggerValue: PropTypes.string,
    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    })),

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    popupChildren: PropTypes.any,

    renderer: PropTypes.func,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the popup is closed.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

CascaderSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,
    listWidth: 200,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,
    expandDirection: HorizontalDirection.RIGHT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    isLoading: false,
    tipPosition: Position.BOTTOM,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: false,

    shouldPreventContainerScroll: true,
    isSelectRecursive: true,
    allowCollapse: true

};

export default CascaderSelect;<commit_msg>update CascaderSelect<commit_after>/**
 * @file CascaderSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import CascaderList from '../CascaderList';
import Theme from '../Theme';
import Tip from '../Tip';

import SelectMode from '../_statics/SelectMode';
import HorizontalDirection from '../_statics/HorizontalDirection';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import CascaderCalculation from '../_vendors/CascaderCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class CascaderSelect extends Component {

    static SelectMode = SelectMode;
    static ExpandDirection = HorizontalDirection;
    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            popupVisible: false,
            path: props.selectMode === SelectMode.SINGLE_SELECT ?
                CascaderCalculation.calPath(props.value, props.data, props) : undefined
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.dropdown && this.refs.dropdown.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.dropdown && this.refs.dropdown.resetPosition();
    };

    /**
     * public
     */
    openPopup = () => {
        this.refs.dropdown && this.refs.dropdown.openPopup();
    };

    /**
     * public
     */
    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    getTriggerValue = (props = this.props) => {

        const {data, selectMode, placeholder, triggerRenderer, renderer, displayField, valueField} = props,
            {value, path} = this.state,
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value, path) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        if (isMultiSelect) {
            return value.length > 0 ?
                value.length + ' selected'
                :
                placeholder;
        }

        let result = [];

        function addNode(node, i) {
            result.push(
                renderer ?

                        {renderer(node, path && path.slice(0, i + 1))}

                    :
                    Util.getTextByDisplayField(node, displayField, valueField)
            );
        }

        if (path) {
            for (let i = 0, len = path.length; i < len; i++) {

                i > 0 && result.push(

                );

                addNode(path[i].node, i);

            }
        }

        return result;

    };

    pathChangeHandler = () => {
        this.refs.dropdown.resetPopupPosition();
    };

    nodeSelectHandler = (node, path) => {

        const {onNodeSelect} = this.props;
        onNodeSelect && onNodeSelect(node, path);

        if (this.props.selectMode !== SelectMode.SINGLE_SELECT) {
            return;
        }

        this.setState({
            path
        });

    };

    changeHandler = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    };

    popupClosedHandler = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, listWidth, data, renderer,
                selectTheme, selectMode, expandDirection, valueField, displayField, descriptionField, triggerRenderer,
                isSelectRecursive, allowCollapse, onNodeDeselect, popupChildren, onNodeClick,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, popupVisible} = this.state,

            wrapperClassName = classNames('cascader-select', {
                [className]: className
            }),

            selectTriggerClassName = classNames({
                activated: popupVisible,
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),
            selectPopupClassName = classNames('cascader-select-popup', {
                [popupClassName]: popupClassName
            });

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={selectTriggerClassName}
                          popupClassName={selectPopupClassName}
                          popupTheme={popupTheme}
                          autoPopupWidth={false}
                          triggerValue={this.getTriggerValue()}
                          onClosePopup={this.popupClosedHandler}>

 Event.wheelHandler(e, this.props)}>

                        <CascaderList className="cascader-select-list"
                                      theme={popupTheme}
                                      selectTheme={selectTheme}
                                      expandDirection={expandDirection}
                                      listWidth={listWidth}
                                      selectMode={selectMode}
                                      data={data}
                                      value={value}
                                      valueField={valueField}
                                      displayField={displayField}
                                      descriptionField={descriptionField}
                                      isSelectRecursive={isSelectRecursive}
                                      allowCollapse={allowCollapse}
                                      collapsedIconCls={collapsedIconCls}
                                      expandedIconCls={expandedIconCls}
                                      radioUncheckedIconCls={radioUncheckedIconCls}
                                      radioCheckedIconCls={radioCheckedIconCls}
                                      checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                      checkboxCheckedIconCls={checkboxCheckedIconCls}
                                      checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                      renderer={renderer}
                                      onNodeClick={onNodeClick}
                                      onNodeSelect={this.nodeSelectHandler}
                                      onNodeDeselect={onNodeDeselect}
                                      onChange={this.changeHandler}
                                      onPathChange={this.pathChangeHandler}/>

                    {popupChildren}

                </Dropdown>

        );

    }
}

CascaderSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    listWidth: PropTypes.number,

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The direction of expansion.
     */
    expandDirection: PropTypes.oneOf(Util.enumerateValue(HorizontalDirection)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),
    triggerValue: PropTypes.string,
    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    })),

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    popupChildren: PropTypes.any,

    resetPopPositionWait: PropTypes.number,

    renderer: PropTypes.func,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the popup is closed.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

CascaderSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,
    listWidth: 200,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,
    expandDirection: HorizontalDirection.RIGHT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    isLoading: false,
    tipPosition: Position.BOTTOM,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: false,

    shouldPreventContainerScroll: true,
    isSelectRecursive: true,
    allowCollapse: true,

    resetPopPositionWait: 250

};

export default CascaderSelect;
<|endoftext|>


<commit_before>"use strict";

var _ = require("underscore");
var util = require("substance-util");

var util = require("substance-util");
var errors = util.errors;
var ContainerError = errors.define("ContainerError");

// The container must be much more view oriented as the actual visualized components depend very much on the
// used renderers.

var __id__ = 0;

var Container = function(document, name, surfaceProvider) {
  this.document = document;
  this.name = name;
  this.__id__ = __id__++;

  var viewNode = this.document.nodes[name];
  if (viewNode instanceof Container) {
    // HACK fixing an issue with creating a container for transactions
    viewNode = {
      type: "container",
      id: viewNode.id,
      nodes: viewNode.nodes
    }
    // throw new ContainerError("ViewNode is already wrapped as Container: " + name);
  }
  // TODO: get rid of 'view' as node type... instead use 'container'
  if (!viewNode || !viewNode.nodes) {
    throw new ContainerError("Illegal argument: no view with name " + name);
  }

  // TODO: rename this.view to this.node, which is less confusing
  this.__viewNode = viewNode;
  this.__components = null;
  this.__children = null;
  this.__updater = null;

  this.surfaceProvider = surfaceProvider || new Container.DefaultNodeSurfaceProvider(document);
  this.rebuild();

  this.listenTo(this.document, "operation:applied", this.update);
};

Container.Prototype = function() {

  _.extend(this, util.Events);

  this.rebuild = function() {
    // console.log("Container.rebuild", this.name);
    var __components = [];
    var __children = {};
    var __updater = [];

    var rootNodes = this.__viewNode.nodes;

    // TODO: we have a problem with doc-simulation here.
    // Nodes are duplicated for simulation. Not so the references in the components.
    for (var i = 0; i < rootNodes.length; i++) {
      var id = rootNodes[i];
      var nodeSurface = this.surfaceProvider.getNodeSurface(id);
      if (!nodeSurface) {
        throw new ContainerError("Aaaaah! no surface available for node " + id);
      }

      if (nodeSurface.update) {
        __updater.push(nodeSurface.update.bind(nodeSurface));
      }

      var components = nodeSurface.components;
      if (!components) {
        throw new ContainerError("Node Surface did not provide components: " + nodeSurface.node.type);
      }
      __children[id] = [];
      for (var j = 0; j < components.length; j++) {
        var component = components[j].clone();
        component.surface.detachView();
        component.pos = __components.length;
        component.rootPos = i;
        __children[id].push(component);
        __components.push(component);
      }
    }
    this.__components = __components;
    this.__children = __children;
    this.__updater = __updater;
  };

  this.getComponents = function() {
    if (!this.__components) {
      this.rebuild();
    }
    return this.__components;
  };

  this.lookup = function(path) {
    var components = this.getComponents();
    for (var i = 0; i < components.length; i++) {
      var component = components[i];
      // a node surface can register an alias for a component
      if (component.alias && _.isEqual(component.alias, path)) {
        return component;
      }
      if (_.isEqual(component.path, path)) {
        return component;
      }
    }

    console.error("Could not find a view component for path " + JSON.stringify(path));

    return null;
  };

  this.getNodes = function(idsOnly) {
    var nodeIds = this.__viewNode.nodes;
    if (idsOnly) {
      return _.clone(nodeIds);
    }
    else {
      var result = [];
      for (var i = 0; i < nodeIds.length; i++) {
        result.push(this.document.get(nodeIds[i]));
      }
      return result;
    }
  };

  this.update = function(op) {
    var path = op.path;
    var needRebuild = (!this.__components || path[0] === this.__viewNode.id);

    // Note: we let the NodeSurfaces in invalidate the container
    // TODO: this could be done more efficiently.
    // This strategy means that every container iterates through all
    // surfaces on *each* graph operation.
    // One way to solve this efficiently would be to add an invalidate()
    // that runs with a timeout=0.
    // This however comes with extra listeners and hard to control order of
    // observer calls.
    if (!needRebuild) {
      for (var i = 0; i < this.__updater.length; i++) {
        if (this.__updater[i](op)) {
          needRebuild = true;
          break;
        }
      }
    }
    if (needRebuild) this.rebuild();
  };

  this.getLength = function(pos) {
    var components = this.getComponents();
    if (pos === undefined) {
      return components.length;
    } else {
      return components[pos].length;
    }
  };

  this.getRootNodeFromPos = function(pos) {
    if (!this.__components) this.rebuild();
    return this.__components[pos].root;
  };

  this.getNodePos = function(pos) {
    if (!this.__components) this.rebuild();
    var id = this.__components[pos].root.id;
    return this.__viewNode.nodes.indexOf(id);
  };

  // This is used to find the containing node of a reference target.
  // E.g., an annotation might point to ['caption_2', 'content'] which is actually
  // contained by the 'figure_1' on the top-level.
  this.lookupRootNode = function(path) {
    var components = this.getComponents();
    for (var i = 0; i < components.length; i++) {
      var component = components[i];
      if ( (component.alias && _.isEqual(path, component.alias)) || _.isEqual(path, component.path) ) {
        return component.root;
      }
    }
    console.error("Could not find a root node for the given path:" + path);
    return null;
  };

  this.getComponent = function(pos) {
    var components = this.getComponents();
    return components[pos];
  };

  this.last = function() {
    var components = this.getComponents();
    return components[components.length-1];
  };

  this.getNodeComponents = function(nodeId) {
    var result = this.__children[nodeId];
    if (!result) {
      throw new ContainerError("Node is not in this container:"+nodeId);
    }
    return result;
  };

  this.dispose = function() {
    console.error("Typically we do not want this, as a container is bound to the life-time of a document.")
    this.stopListening();
  };

  // Creates a container for a given document
  // --------
  // This named constructor is used to create Container instance with the
  // same setup (name, surface provider, etc.) for a another document instance.
  // This is particularly used for creating manipulation sessions.
  //
  this.createContainer = function(doc) {
    // HACK: I want to tie the Containers to the document directly
    // i.e., to make sure that this exact instance is used, not one created by document.get('content').
    // However this seems overengineered... Do we really need the ability to control the node-surface factory?
    // If not, the whole would be obsolete.
    var container = new Container(doc, this.name, this.surfaceProvider.createCopy(doc));
    doc.nodes[this.name] = container;
    return container;
  };

  // Returns the first position after a given node.
  // if the node is the last in the container it will set
  this.after = function(nodeId) {
    var comps = this.getNodeComponents(nodeId);
    var lastComp = comps[comps.length-1];
    if (this.__components.length - 1 === lastComp.pos) {
      return [lastComp.pos, lastComp.length];
    } else {
      return [lastComp.pos+1, 0];
    }
  };

  this.first = function(nodeId) {
    var comps = this.getNodeComponents(nodeId);
    return [comps[0].pos, 0];
  };

  this.getLastCoor = function() {
    var lastComp = this.__components[this.__components.length-1];
    return [lastComp.pos, lastComp.length];
  };

  this.toJSON = function() {
    return this.__viewNode;
  };
};

Container.prototype = _.extend(new Container.Prototype(), util.Events.Listener);

Object.defineProperties(Container.prototype, {
  "id": {
    get: function() { return this.__viewNode.id; }
  },
  "type": {
    get: function() { return this.__viewNode.type; }
  },
  "nodes": {
    get: function() { return this.__viewNode.nodes; },
    set: function(val) { this.__viewNode.nodes = val; }
  }
});

Container.DefaultNodeSurfaceProvider = require("./node_surface_provider");
Container.ContainerError = ContainerError;

module.exports = Container;
<commit_msg>Bugfix in Container.<commit_after>"use strict";

var _ = require("underscore");
var util = require("substance-util");

var util = require("substance-util");
var errors = util.errors;
var ContainerError = errors.define("ContainerError");

// The container must be much more view oriented as the actual visualized components depend very much on the
// used renderers.

var __id__ = 0;

var Container = function(document, name, surfaceProvider) {
  this.document = document;
  this.name = name;
  this.__id__ = __id__++;

  var viewNode = this.document.nodes[name];
  if (viewNode instanceof Container) {
    // HACK fixing an issue with creating a container for transactions
    viewNode = {
      type: "container",
      id: viewNode.id,
      nodes: viewNode.nodes
    }
    // throw new ContainerError("ViewNode is already wrapped as Container: " + name);
  }
  // TODO: get rid of 'view' as node type... instead use 'container'
  if (!viewNode || !viewNode.nodes) {
    throw new ContainerError("Illegal argument: no view with name " + name);
  }

  // TODO: rename this.view to this.node, which is less confusing
  this.__viewNode = viewNode;
  this.__components = null;
  this.__children = null;
  this.__updater = null;

  this.surfaceProvider = surfaceProvider || new Container.DefaultNodeSurfaceProvider(document);
  this.rebuild();

  this.listenTo(this.document, "operation:applied", this.update);
};

Container.Prototype = function() {

  _.extend(this, util.Events);

  this.rebuild = function() {
    // console.log("Container.rebuild", this.name);
    var __components = [];
    var __children = {};
    var __updater = [];

    var rootNodes = this.__viewNode.nodes;

    // TODO: we have a problem with doc-simulation here.
    // Nodes are duplicated for simulation. Not so the references in the components.
    for (var i = 0; i < rootNodes.length; i++) {
      var id = rootNodes[i];
      var nodeSurface = this.surfaceProvider.getNodeSurface(id);
      if (!nodeSurface) {
        throw new ContainerError("Aaaaah! no surface available for node " + id);
      }

      if (nodeSurface.update) {
        __updater.push(nodeSurface.update.bind(nodeSurface));
      }

      var components = nodeSurface.components;
      if (!components) {
        throw new ContainerError("Node Surface did not provide components: " + nodeSurface.node.type);
      }
      __children[id] = [];
      for (var j = 0; j < components.length; j++) {
        var component = components[j].clone();
        component.surface.detachView();
        component.pos = __components.length;
        component.rootPos = i;
        __children[id].push(component);
        __components.push(component);
      }
    }
    this.__components = __components;
    this.__children = __children;
    this.__updater = __updater;
  };

  this.getComponents = function() {
    if (!this.__components) {
      this.rebuild();
    }
    return this.__components;
  };

  this.lookup = function(path) {
    var components = this.getComponents();
    for (var i = 0; i < components.length; i++) {
      var component = components[i];
      // a node surface can register an alias for a component
      if (component.alias && _.isEqual(component.alias, path)) {
        return component;
      }
      if (_.isEqual(component.path, path)) {
        return component;
      }
    }

    console.error("Could not find a view component for path " + JSON.stringify(path));

    return null;
  };

  this.getNodes = function(idsOnly) {
    var nodeIds = this.__viewNode.nodes;
    if (idsOnly) {
      return _.clone(nodeIds);
    }
    else {
      var result = [];
      for (var i = 0; i < nodeIds.length; i++) {
        result.push(this.document.get(nodeIds[i]));
      }
      return result;
    }
  };

  this.update = function(op) {
    var path = op.path;
    var needRebuild = (!this.__components || path[0] === this.__viewNode.id);

    // Note: we let the NodeSurfaces in invalidate the container
    // TODO: this could be done more efficiently.
    // This strategy means that every container iterates through all
    // surfaces on *each* graph operation.
    // One way to solve this efficiently would be to add an invalidate()
    // that runs with a timeout=0.
    // This however comes with extra listeners and hard to control order of
    // observer calls.
    if (!needRebuild) {
      for (var i = 0; i < this.__updater.length; i++) {
        if (this.__updater[i](op)) {
          needRebuild = true;
          break;
        }
      }
    }
    if (needRebuild) this.rebuild();
  };

  this.getLength = function(pos) {
    var components = this.getComponents();
    if (pos === undefined) {
      return components.length;
    } else {
      return components[pos].length;
    }
  };

  this.getRootNodeFromPos = function(pos) {
    if (!this.__components) this.rebuild();
    return this.__components[pos].root;
  };

  this.getNodePos = function(pos) {
    if (!this.__components) this.rebuild();
    var id = this.__components[pos].root.id;
    return this.__viewNode.nodes.indexOf(id);
  };

  // This is used to find the containing node of a reference target.
  // E.g., an annotation might point to ['caption_2', 'content'] which is actually
  // contained by the 'figure_1' on the top-level.
  this.lookupRootNode = function(path) {
    var components = this.getComponents();
    for (var i = 0; i < components.length; i++) {
      var component = components[i];
      if ( (component.alias && _.isEqual(path, component.alias)) || _.isEqual(path, component.path) ) {
        return component.root;
      }
    }
    console.error("Could not find a root node for the given path:" + path);
    return null;
  };

  this.getComponent = function(pos) {
    var components = this.getComponents();
    return components[pos];
  };

  this.last = function() {
    var components = this.getComponents();
    return components[components.length-1];
  };

  this.getNodeComponents = function(nodeId) {
    var result = this.__children[nodeId];
    if (!result) {
      throw new ContainerError("Node is not in this container:"+nodeId);
    }
    return result;
  };

  this.dispose = function() {
    console.error("Typically we do not want this, as a container is bound to the life-time of a document.")
    this.stopListening();
  };

  // Creates a container for a given document
  // --------
  // This named constructor is used to create Container instance with the
  // same setup (name, surface provider, etc.) for a another document instance.
  // This is particularly used for creating manipulation sessions.
  //
  this.createContainer = function(doc) {
    // HACK: I want to tie the Containers to the document directly
    // i.e., to make sure that this exact instance is used, not one created by document.get('content').
    // However this seems overengineered... Do we really need the ability to control the node-surface factory?
    // If not, the whole would be obsolete.
    var container = new Container(doc, this.name, this.surfaceProvider.createCopy(doc));
    doc.nodes[this.name] = container;
    return container;
  };

  // Returns the first position after a given node.
  // if the node is the last in the container it will set
  this.after = function(nodeId) {
    var comps = this.getNodeComponents(nodeId);
    var lastComp = comps[comps.length-1];
    if (this.__components.length - 1 === lastComp.pos) {
      return [lastComp.pos, lastComp.length];
    } else {
      return [lastComp.pos+1, 0];
    }
  };

  this.first = function(nodeId) {
    var comps = this.getNodeComponents(nodeId);
    return [comps[0].pos, 0];
  };

  this.getLastCoor = function() {
    var lastComp = this.__components[this.__components.length-1];
    return [lastComp.pos, lastComp.length];
  };

  this.toJSON = function() {
    return util.clone(this.__viewNode);
  };
};

Container.prototype = _.extend(new Container.Prototype(), util.Events.Listener);

Object.defineProperties(Container.prototype, {
  "id": {
    get: function() { return this.__viewNode.id; }
  },
  "type": {
    get: function() { return this.__viewNode.type; }
  },
  "nodes": {
    get: function() { return this.__viewNode.nodes; },
    set: function(val) { this.__viewNode.nodes = val; }
  }
});

Container.DefaultNodeSurfaceProvider = require("./node_surface_provider");
Container.ContainerError = ContainerError;

module.exports = Container;
<|endoftext|>


<commit_before>/*
 * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)
 *
 * 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 StandardDialect from './standard/StandardDialect';

import {flatten}                  from '@ultraq/array-utils';
import {merge}                    from '@ultraq/object-utils';
import fs                         from 'fs';
import {jsdom, serializeDocument} from 'jsdom';

const DEFAULT_CONFIGURATION = {
	dialects: [
		new StandardDialect()
	]
};
const XML_NAMESPACE_ATTRIBUTE = 'xmlns:th';

/**
 * A highly-configurable class responsible for processing the Thymeleaf
 * directives found in HTML documents and fragments.
 *
 * @author Emanuel Rabina
 */
export default class TemplateEngine {

	/**
	 * Constructor, set up a new template engine instance.
	 *
	 * @param {Array} dialects
	 *   List of dialects to use in the new template engine.  Defaults to just the
	 *   standard dialect.
	 */
	constructor({ dialects } = DEFAULT_CONFIGURATION) {

		// Create the engine's instance of processors from all of the configured
		// dialect processors
		this.processors = flatten(dialects.map(dialect => dialect.processors));
	}

	/**
	 * Process a DOM element.
	 *
	 * @param {Element} element
	 * @param {Object} context
	 * @return {Boolean} Whether or not the parent node needs reprocessing.
	 */
	processNode(element, context) {

		// TODO: Standardize this data attribute somewhere.  Shared const?
		// element.dataset not yet implemented in JSDOM (https://github.com/tmpvar/jsdom/issues/961),
		// so until then we're getting data- attributes the old-fashioned way.
		let localVariables = JSON.parse(element.getAttribute('data-thymeleaf-local-variables'));
		element.removeAttribute('data-thymeleaf-local-variables');
		let localContext = merge({}, context, localVariables);

		// Process the current element, store whether or not reprocessing of the
		// parent needs to happen before moving on to this element's children.
		let requireReprocessing = this.processors
			.map(processor => {
				let attribute = `${processor.prefix}:${processor.name}`;
				if (!element.hasAttribute(attribute)) {
					attribute = `data-${processor.prefix}-${processor.name}`;
					if (!element.hasAttribute(attribute)) {
						return;
					}
				}
				let attributeValue = element.getAttribute(attribute);
				return processor.process(element, attribute, attributeValue, localContext);
			})
			.reduce((accumulator, processorResult) => accumulator || processorResult, false);

		if (requireReprocessing) {
			return true;
		}

		// Process this element's children
		let reprocess;
		do {
			reprocess = false;
			for (let child of element.children) {
				reprocess = this.processNode(child, localContext);
				if (reprocess) {
					break;
				}
			}
		}
		while (reprocess);
	}

	/**
	 * Process the Thymeleaf template data, returning the processed template.
	 *
	 * @param {String} template
	 * @param {Object} context
	 * @return {Promise}
	 *   A promise resolved with the processed template, or rejected with an error
	 *   message.
	 */
	process(template, context) {

		return new Promise((resolve, reject) => {
			try {
				let document = jsdom(template, {
					features: {
						FetchExternalResources: false,
						ProcessExternalResources: false
					}
				});

				let htmlElement = document.documentElement;
				this.processNode(htmlElement, context);

				// TODO: Special case, remove the xmlns:th namespace from the document.
				//       This should be handled like in main Thymeleaf where it's just
				//       another processor that runs on the document.
				if (htmlElement.hasAttribute(XML_NAMESPACE_ATTRIBUTE)) {
					htmlElement.removeAttribute(XML_NAMESPACE_ATTRIBUTE);
				}

				let documentAsString = serializeDocument(document);
				resolve(documentAsString);
			}
			catch (exception) {
				reject(exception);
			}
		});
	}

	/**
	 * Process the Thymeleaf template at the given path, returning a promise of the
	 * processed template.
	 *
	 * @param {String} filePath
	 * @param {Object} context
	 * @return {Promise}
	 *   A promise resolved with the processed template, or rejected with an error
	 *   message.
	 */
	processFile(filePath, context) {

		return new Promise((resolve, reject) => {
			fs.readFile(filePath, (error, data) => {
				if (error) {
					reject(new Error(error));
				}
				else {
					resolve(this.process(data, context));
				}
			});
		});
	}
}
<commit_msg>Default context value<commit_after>/*
 * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)
 *
 * 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 StandardDialect from './standard/StandardDialect';

import {flatten}                  from '@ultraq/array-utils';
import {merge}                    from '@ultraq/object-utils';
import fs                         from 'fs';
import {jsdom, serializeDocument} from 'jsdom';

const DEFAULT_CONFIGURATION = {
	dialects: [
		new StandardDialect()
	]
};
const XML_NAMESPACE_ATTRIBUTE = 'xmlns:th';

/**
 * A highly-configurable class responsible for processing the Thymeleaf
 * directives found in HTML documents and fragments.
 *
 * @author Emanuel Rabina
 */
export default class TemplateEngine {

	/**
	 * Constructor, set up a new template engine instance.
	 *
	 * @param {Array} dialects
	 *   List of dialects to use in the new template engine.  Defaults to just the
	 *   standard dialect.
	 */
	constructor({ dialects } = DEFAULT_CONFIGURATION) {

		// Create the engine's instance of processors from all of the configured
		// dialect processors
		this.processors = flatten(dialects.map(dialect => dialect.processors));
	}

	/**
	 * Process a DOM element.
	 *
	 * @private
	 * @param {Element} element
	 * @param {Object} [context={}]
	 * @return {Boolean} Whether or not the parent node needs reprocessing.
	 */
	processNode(element, context = {}) {

		// TODO: Standardize this data attribute somewhere.  Shared const?
		// element.dataset not yet implemented in JSDOM (https://github.com/tmpvar/jsdom/issues/961),
		// so until then we're getting data- attributes the old-fashioned way.
		let localVariables = JSON.parse(element.getAttribute('data-thymeleaf-local-variables'));
		element.removeAttribute('data-thymeleaf-local-variables');
		let localContext = merge({}, context, localVariables);

		// Process the current element, store whether or not reprocessing of the
		// parent needs to happen before moving on to this element's children.
		let requireReprocessing = this.processors
			.map(processor => {
				let attribute = `${processor.prefix}:${processor.name}`;
				if (!element.hasAttribute(attribute)) {
					attribute = `data-${processor.prefix}-${processor.name}`;
					if (!element.hasAttribute(attribute)) {
						return;
					}
				}
				let attributeValue = element.getAttribute(attribute);
				return processor.process(element, attribute, attributeValue, localContext);
			})
			.reduce((accumulator, processorResult) => accumulator || processorResult, false);

		if (requireReprocessing) {
			return true;
		}

		// Process this element's children
		let reprocess;
		do {
			reprocess = false;
			for (let child of element.children) {
				reprocess = this.processNode(child, localContext);
				if (reprocess) {
					break;
				}
			}
		}
		while (reprocess);
	}

	/**
	 * Process the Thymeleaf template data, returning the processed template.
	 *
	 * @param {String} template
	 * @param {Object} [context={}]
	 * @return {Promise}
	 *   A promise resolved with the processed template, or rejected with an error
	 *   message.
	 */
	process(template, context = {}) {

		return new Promise((resolve, reject) => {
			try {
				let document = jsdom(template, {
					features: {
						FetchExternalResources: false,
						ProcessExternalResources: false
					}
				});

				let htmlElement = document.documentElement;
				this.processNode(htmlElement, context);

				// TODO: Special case, remove the xmlns:th namespace from the document.
				//       This should be handled like in main Thymeleaf where it's just
				//       another processor that runs on the document.
				if (htmlElement.hasAttribute(XML_NAMESPACE_ATTRIBUTE)) {
					htmlElement.removeAttribute(XML_NAMESPACE_ATTRIBUTE);
				}

				let documentAsString = serializeDocument(document);
				resolve(documentAsString);
			}
			catch (exception) {
				reject(exception);
			}
		});
	}

	/**
	 * Process the Thymeleaf template at the given path, returning a promise of the
	 * processed template.
	 *
	 * @param {String} filePath
	 * @param {Object} [context={}]
	 * @return {Promise}
	 *   A promise resolved with the processed template, or rejected with an error
	 *   message.
	 */
	processFile(filePath, context = {}) {

		return new Promise((resolve, reject) => {
			fs.readFile(filePath, (error, data) => {
				if (error) {
					reject(new Error(error));
				}
				else {
					resolve(this.process(data, context));
				}
			});
		});
	}
}
<|endoftext|>


<commit_before>/*
 *  MainScene.js
 *  2014/06/19
 *  @auther minimo
 *  This Program is MIT license.
 *
 */

tm.define("shotgun.MainScene", {
    superClass: tm.app.Scene,

    //マルチタッチ補助クラス
    touches: null,
    touchID: -1,

    //タッチ情報
    startX: 0,
    startY: 0,
    moveX: 0,
    moveY: 0,
    beforeX: 0,
    beforeY: 0,

    //デッキ
    deck: null,

    shuffled: false,
    startTime: 0,

    //ゲーム内情報
    start: false,   //ゲームスタートフラグ
    score: 0,       //スコア
    life: 6,        //ライフ
    pick: true,     //カードピック可能フラグ
    count: 10,      //カード選択カウントダウン用
    level: 0,       //ゲームレベル

    //再生中BGM
    bgm: null,

    //経過時間
    time: 0,
    absTime: 0,

    init: function() {
        this.superInit();
        this.background = "rgba(0, 0, 0, 0.0)";

        //バックグラウンド
        this.bg = tm.display.Sprite("greenback", SC_W*2, SC_H*2).addChildTo(this);

        //マルチタッチ初期化
        this.touches = tm.input.TouchesEx(this);

        //レイヤー準備
        this.lowerLayer = tm.app.Object2D().addChildTo(this);
        this.mainLayer = tm.app.Object2D().addChildTo(this);
        this.upperLayer = tm.app.Object2D().addChildTo(this);

        //スコア表示
        var that = this;
        var lb = this.scoreLabel = tm.display.OutlineLabel("SCORE:", 50).addChildTo(this);
        lb.setPosition(8, 64);
        lb.fontFamily = "'azuki'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 1;
        lb.update = function() {
            this.text = "SCORE:"+that.score;
        }

        //ライフ表示
        var lb = this.lifeLabel = tm.display.OutlineLabel("LIFE:", 50).addChildTo(this);
        lb.setPosition(SC_W*0.7, 64);
        lb.fontFamily = "'azuki'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 1;
        lb.update = function() {
            var life = that.life < 0 ? 0 : that.life;
            this.text = "LIFE:"+life;
        }

        //目隠し
        this.mask = tm.display.Sprite("blackback", SC_W*2, SC_H*2).addChildTo(this);
        this.mask.tweener.clear().fadeOut(100);

        //カードデッキ
        this.deck = shotgun.CardDeck().addChildTo(this.mainLayer);

        //BGM
        this.bgm = tm.asset.AssetManager.get("bgm").clone();
        this.bgm.loop = true;
        this.bgm.play();

        //スタートアップ
        var lb = this.scoreLabel = tm.display.OutlineLabel("READY", 100).addChildTo(this.upperLayer);
        lb.setPosition(SC_W/2, SC_H/2);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 2;
        lb.tweener.clear().wait(500).fadeOut(500);
        lb.tweener.call(function(){
            that.deck.startup();
            that.deck.shuffle();
            that.start = true;
        });

        //カウントダウン表示
        var lb = this.countDown = tm.display.OutlineLabel("5", 300).addChildTo(this.upperLayer);
        lb.setPosition(SC_W/2, SC_H/2);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 3;
        lb.beforeCount = 9;
        lb.alpha = 0.8;
        lb.update = function() {
            if (that.count < 6) {
                this.visible = true;
                this.text = ""+that.count;
            } else {
                this.visible = false;
            }
            if (this.beforeCount == that.count) {
                this.alpha -= 0.05;
                if (this.alpha < 0)this.alpha = 0;
            } else {
                this.alpha = 0.8;
            }
            this.beforeCount = that.count;
        }
    },

    update: function() {
        if (!this.start) return;

        var level = Math.sqrt(this.absTime*0.0001)+1;
        var interval = 41-~~(level*10);
        if (interval < 20) interval = 20;
        if (this.time % interval == 0) {
            this.count--;
            tm.asset.AssetManager.get("countdown").clone().play();
        }

        //手札が五枚揃った
        if (this.deck.numHand == 5 || this.count < 0) {
            this.pick = false;
            this.deck.sortHand();
            this.deck.numHand = 0;
            var sc = this.deck.checkHand();
            this.dispHand(sc);
            if (sc == NOHAND) this.life--;
            if (sc == MISS) this.life -= 2;
            if (sc == ROYALSTRAIGHTFLASH) this.life++;

            //早上がりボーナス
            if (this.count > 5 && sc > 0) {
                sc = ~~(sc*1.5);
                var lb = tm.display.OutlineLabel("EXCELLENT!", 100).addChildTo(this);
                lb.setPosition(SC_W*0.5, SC_H*0.5);
                lb.fontFamily = "'KS-Kohichi-FeltPen'";
                lb.align     = "center";
                lb.baseline  = "middle";
                lb.outlineWidth = 3;
                lb.tweener.clear().wait(1000).call(function(){lb.remove();});
            }
            this.score += sc;
            if (this.score < 0) this.score = 0;

            //ゲームオーバー判定
            if (this.life < 0) {
                this.gameover();
            }
            this.count = 9;
            this.time = 0;
        }
        this.time++;
        this.absTime++;
    },

    //リスタート
    restart: function() {
        this.start = false;
        this.pick = true;
        this.score = 0;
        this.life = 6;
        this.time = 0;
        this.absTime = 0;
    },

    //ゲームオーバー
    gameover: function() {
        this.start = false;
        this.pick = false;
        var lb = this.title2 = tm.display.OutlineLabel("GAME OVER", 200).addChildTo(this.upperLayer);
        lb.setPosition(SC_W*0.6, SC_H*0.5-SC_H);
        lb.fontFamily = "'azuki'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 3;
        lb.tweener.wait(500).move(SC_W*0.6, SC_H*0.35, 1500,"easeOutBounce");
    },

    //役名表示
    dispHand: function(hand) {
        var name;
        switch (hand) {
            case MISS:          name = "MISS!";break;
            case NOHAND:        name = "NO HAND";break;
            case ONEPAIR:       name = "ONE PAIR";break;
            case FLASH:         name = "FLASH";break;
            case TWOPAIR:       name = "TWO PAIR";break;
            case THREECARD:     name = "THREE CARD";break;
            case FULLHOUSE:     name = "FULL HOUSE";break;
            case STRAIGHT:      name = "STRAIGHT";break;
            case FOURCARD:      name = "FOURCARD";break;
            case STRAIGHTFLASH: name = "STRAIGHT FLASH";break;
            case ROYALSTRAIGHTFLASH: name = "ROYAL STRAIGHT FLASH!";break;
        }
        //役名表示
        var that = this;
        var lb = tm.display.OutlineLabel(name, 60).addChildTo(this);
        lb.setPosition(SC_W*0.5, SC_H*0.8);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 3;
        lb.tweener.clear().wait(1000).call(function(){lb.remove(); that.deck.clearHand();that.pick = true;});

        //効果音
        if (hand > 0) {
            tm.asset.AssetManager.get("hand").clone().play();
        } else {
            tm.asset.AssetManager.get("nohand").clone().play();
        }
    },

    //タッチorクリック開始処理
    ontouchesstart: function(e) {
        if (this.touchID > 0)return;
        this.touchID = e.ID;

        var sx = this.startX = e.pointing.x;
        var sy = this.startY = e.pointing.y;
        this.moveX = 0;
        this.moveY = 0;

        this.beforeX = sx;
        this.beforeY = sy;
    },

    //タッチorクリック移動処理
    ontouchesmove: function(e) {
        if (this.touchID != e.ID) return;

        var sx = e.pointing.x;
        var sy = e.pointing.y;
        var moveX = Math.abs(sx - this.beforeX);
        var moveY = Math.abs(sx - this.beforeY);

        if (!this.shuffled) {
            if (moveX > 300) {
                this.deck.shuffle(true);
                this.shuffled = true;
            }
        }

        if (this.time % 10 == 0) {
            this.beforeX = sx;
            this.beforeY = sy;
        }
    },

    //タッチorクリック終了処理
    ontouchesend: function(e) {
        if (this.touchID != e.ID) return;
        this.touchID = -1;

        var sx = e.pointing.x;
        var sy = e.pointing.y;

        if (this.pick && !this.shuffled) {
            var c = this.deck.pickCard(sx, sy);
            if (c) this.deck.addHand(c);
        }
        this.shuffled = false;
    },
});

<commit_msg>level<commit_after>/*
 *  MainScene.js
 *  2014/06/19
 *  @auther minimo
 *  This Program is MIT license.
 *
 */

tm.define("shotgun.MainScene", {
    superClass: tm.app.Scene,

    //マルチタッチ補助クラス
    touches: null,
    touchID: -1,

    //タッチ情報
    startX: 0,
    startY: 0,
    moveX: 0,
    moveY: 0,
    beforeX: 0,
    beforeY: 0,

    //デッキ
    deck: null,

    shuffled: false,
    startTime: 0,

    //ゲーム内情報
    start: false,   //ゲームスタートフラグ
    score: 0,       //スコア
    life: 6,        //ライフ
    pick: true,     //カードピック可能フラグ
    count: 9,       //カード選択カウントダウン用
    level: 0,       //ゲームレベル

    //再生中BGM
    bgm: null,

    //経過時間
    time: 0,
    absTime: 0,

    init: function() {
        this.superInit();
        this.background = "rgba(0, 0, 0, 0.0)";

        //バックグラウンド
        this.bg = tm.display.Sprite("greenback", SC_W*2, SC_H*2).addChildTo(this);

        //マルチタッチ初期化
        this.touches = tm.input.TouchesEx(this);

        //レイヤー準備
        this.lowerLayer = tm.app.Object2D().addChildTo(this);
        this.mainLayer = tm.app.Object2D().addChildTo(this);
        this.upperLayer = tm.app.Object2D().addChildTo(this);

        //スコア表示
        var that = this;
        var lb = this.scoreLabel = tm.display.OutlineLabel("SCORE:", 50).addChildTo(this);
        lb.setPosition(8, 64);
        lb.fontFamily = "'azuki'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 1;
        lb.update = function() {
            this.text = "SCORE:"+that.score;
        }

        //ライフ表示
        var lb = this.lifeLabel = tm.display.OutlineLabel("LIFE:", 50).addChildTo(this);
        lb.setPosition(SC_W*0.7, 64);
        lb.fontFamily = "'azuki'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 1;
        lb.update = function() {
            var life = that.life < 0 ? 0 : that.life;
            this.text = "LIFE:"+life;
        }

        //目隠し
        this.mask = tm.display.Sprite("blackback", SC_W*2, SC_H*2).addChildTo(this);
        this.mask.tweener.clear().fadeOut(100);

        //カードデッキ
        this.deck = shotgun.CardDeck().addChildTo(this.mainLayer);

        //BGM
        this.bgm = tm.asset.AssetManager.get("bgm").clone();
        this.bgm.loop = true;
        this.bgm.play();

        //スタートアップ
        var lb = this.readyLabel = tm.display.OutlineLabel("READY", 100).addChildTo(this.upperLayer);
        lb.setPosition(SC_W/2, SC_H/2);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 2;
        lb.tweener.clear().wait(500).fadeOut(500);
        lb.tweener.call(function(){
            that.deck.startup();
            that.deck.shuffle();
            that.start = true;
        });

        //カウントダウン表示
        var lb = this.countDown = tm.display.OutlineLabel("5", 300).addChildTo(this.upperLayer);
        lb.setPosition(SC_W/2, SC_H/2);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 3;
        lb.beforeCount = 9;
        lb.alpha = 1.0;
        lb.update = function() {
            if (that.count < 6) {
                this.visible = true;
                this.text = ""+that.count;
            } else {
                this.visible = false;
            }
            if (this.beforeCount == that.count) {
                this.alpha -= 0.05;
                if (this.alpha < 0)this.alpha = 0;
            } else {
                this.alpha = 1.0;
            }
            this.beforeCount = that.count;
        }
    },

    update: function() {
        if (!this.start) return;

        var level = Math.sqrt(this.absTime*0.0001)+1;
        var interval = 41-~~(level*10);
        if (interval < 20) interval = 20;
        if (this.time % interval == 0) {
            this.count--;
            tm.asset.AssetManager.get("countdown").clone().play();
        }

        //手札が五枚揃ったor時間切れ
        if (this.deck.numHand == 5 || this.count < 0) {
            this.pick = false;
            this.deck.sortHand();
            this.deck.numHand = 0;
            var sc = this.deck.checkHand();
            this.dispHand(sc);
            if (sc == NOHAND) this.life--;
            if (sc == MISS) this.life -= 2;
            if (sc == ROYALSTRAIGHTFLASH) this.life++;
            if (this.life > 6)this.life = 6;

            //早上がりボーナス
            if (this.count > 5 && sc > 0) {
                sc = ~~(sc*1.5);
                var lb = tm.display.OutlineLabel("EXCELLENT!", 100).addChildTo(this);
                lb.setPosition(SC_W*0.5, SC_H*0.5);
                lb.fontFamily = "'KS-Kohichi-FeltPen'";
                lb.align     = "center";
                lb.baseline  = "middle";
                lb.outlineWidth = 3;
                lb.tweener.clear().wait(1000).call(function(){lb.remove();});
            }
            this.score += sc;
            if (this.score < 0) this.score = 0;

            //ゲームオーバー判定
            if (this.life < 0) {
                this.gameover();
            }
            this.count = 9;
            this.time = 0;
        }
        this.time++;
        this.absTime++;
    },

    //リスタート
    restart: function() {
        this.start = false;
        this.pick = true;
        this.score = 0;
        this.life = 6;
        this.count = 9;
        this.time = 0;
        this.absTime = 0;
        this.level = 0;

        this.readyLabel.tweener.clear().wait(500).fadeOut(500);
        this.readyLabel.tweener.call(function(){
            that.deck.startup();
            that.deck.shuffle();
            that.start = true;
        });
    },

    //ゲームオーバー
    gameover: function() {
        this.start = false;
        this.pick = false;

        var that = this;
        var lb = this.title2 = tm.display.OutlineLabel("GAME OVER", 100).addChildTo(this.upperLayer);
        lb.setPosition(SC_W*0.5, SC_H*0.5-SC_H);
        lb.fontFamily = "'azuki'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.outlineWidth = 4;
        lb.tweener.wait(500).move(SC_W*0.5, SC_H*0.5, 4000,"easeOutBounce");
    },

    //役名表示
    dispHand: function(hand) {
        var name;
        switch (hand) {
            case MISS:          name = "MISS!";break;
            case NOHAND:        name = "NO HAND";break;
            case ONEPAIR:       name = "ONE PAIR";break;
            case FLASH:         name = "FLASH";break;
            case TWOPAIR:       name = "TWO PAIR";break;
            case THREECARD:     name = "THREE CARD";break;
            case FULLHOUSE:     name = "FULL HOUSE";break;
            case STRAIGHT:      name = "STRAIGHT";break;
            case FOURCARD:      name = "FOURCARD";break;
            case STRAIGHTFLASH: name = "STRAIGHT FLASH";break;
            case ROYALSTRAIGHTFLASH: name = "ROYAL STRAIGHT FLASH!";break;
        }
        //役名表示
        var that = this;
        var lb = tm.display.OutlineLabel(name, 60).addChildTo(this);
        lb.setPosition(SC_W*0.5, SC_H*0.8);
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "left";
        lb.baseline  = "middle";
        lb.outlineWidth = 3;
        lb.tweener.clear().wait(1000).call(function(){lb.remove(); that.deck.clearHand();that.pick = true;});

        //効果音
        if (hand > 0) {
            tm.asset.AssetManager.get("hand").clone().play();
        } else {
            tm.asset.AssetManager.get("nohand").clone().play();
        }
    },

    //タッチorクリック開始処理
    ontouchesstart: function(e) {
        if (this.touchID > 0)return;
        this.touchID = e.ID;

        var sx = this.startX = e.pointing.x;
        var sy = this.startY = e.pointing.y;
        this.moveX = 0;
        this.moveY = 0;

        this.beforeX = sx;
        this.beforeY = sy;
    },

    //タッチorクリック移動処理
    ontouchesmove: function(e) {
        if (this.touchID != e.ID) return;

        var sx = e.pointing.x;
        var sy = e.pointing.y;
        var moveX = Math.abs(sx - this.beforeX);
        var moveY = Math.abs(sx - this.beforeY);

        if (!this.shuffled) {
            if (moveX > 300) {
                this.deck.shuffle(true);
                this.shuffled = true;
            }
        }

        if (this.time % 10 == 0) {
            this.beforeX = sx;
            this.beforeY = sy;
        }
    },

    //タッチorクリック終了処理
    ontouchesend: function(e) {
        if (this.touchID != e.ID) return;
        this.touchID = -1;

        var sx = e.pointing.x;
        var sy = e.pointing.y;

        if (this.pick && !this.shuffled) {
            var c = this.deck.pickCard(sx, sy);
            if (c) this.deck.addHand(c);
        }
        this.shuffled = false;
    },
});

<|endoftext|>


<commit_before>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import React, { PureComponent } from 'react';
import { shape, string } from 'prop-types';
import { P, Link } from '../components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default class AvailabilityContainer extends PureComponent {
  static propTypes = {
    availability: shape({
      title: string.isRequired,
      text:  string.isRequired,
    }).isRequired,
  };

  state = {
    availability: this.props.availability.text,
  };

  componentDidMount() {
    const availabilityDate = new Date();
    availabilityDate.setMonth(
      availabilityDate.getMonth() + (availabilityDate.getDate() > 15 ? 2 : 1),
    );
    this.setState({
      availability: availabilityDate.toLocaleDateString('en-GB', {
        month: 'long',
        year:  'numeric',
      }),
    });
  }

  render() {
    return (

        <Link to="mailto:jan@mrozilla.cz" type="primary">
          {this.state.availability}
        </Link>

    );
  }
}
<commit_msg>update availability contact link<commit_after>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import React, { PureComponent } from 'react';
import { shape, string } from 'prop-types';
import { P, Link } from '../components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default class AvailabilityContainer extends PureComponent {
  static propTypes = {
    availability: shape({
      title: string.isRequired,
      text:  string.isRequired,
    }).isRequired,
  };

  state = {
    availability: this.props.availability.text,
  };

  componentDidMount() {
    const availabilityDate = new Date();
    availabilityDate.setMonth(
      availabilityDate.getMonth() + (availabilityDate.getDate() > 15 ? 2 : 1),
    );
    this.setState({
      availability: availabilityDate.toLocaleDateString('en-GB', {
        month: 'long',
        year:  'numeric',
      }),
    });
  }

  render() {
    return (

        <Link to="/contact" type="primary">
          {this.state.availability}
        </Link>

    );
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options.language', function() {
  const List = cr.ui.List;
  const ListItem = cr.ui.ListItem;
  const ArrayDataModel = cr.ui.ArrayDataModel;
  const LanguageOptions = options.LanguageOptions;

  /**
   * Creates a new language list.
   * @param {Object=} opt_propertyBag Optional properties.
   * @constructor
   * @extends {cr.ui.List}
   */
  var LanguageList = cr.ui.define('list');

  /**
   * Gets display name from the given language code.
   * @param {string} languageCode Language code (ex. "fr").
   */
  LanguageList.getDisplayNameFromLanguageCode = function(languageCode) {
    // Build the language code to display name dictionary at first time.
    if (!this.languageCodeToDisplayName_) {
      this.languageCodeToDisplayName_ = {};
      var languageList = templateData.languageList;
      for (var i = 0; i < languageList.length; i++) {
        var language = languageList[i];
        this.languageCodeToDisplayName_[language.code] = language.displayName;
      }
    }

    return this.languageCodeToDisplayName_[languageCode];
  }

  LanguageList.prototype = {
    __proto__: List.prototype,

    pref: 'settings.language.preferred_languages',

    /** @inheritDoc */
    decorate: function() {
      List.prototype.decorate.call(this);

      // HACK(arv): http://crbug.com/40902
      window.addEventListener('resize', cr.bind(this.redraw, this));

      // Listen to pref change.
      Preferences.getInstance().addEventListener(this.pref,
          cr.bind(this.handlePrefChange_, this));
    },

    createItem: function(languageCode) {
      var languageDisplayName =
          LanguageList.getDisplayNameFromLanguageCode(languageCode);
      return new ListItem({label: languageDisplayName});
    },

    /*
     * Adds a language to the language list.
     * @param {string} languageCode language code (ex. "fr").
     */
    addLanguage: function(languageCode) {
      var dataModel = this.dataModel;
      dataModel.push(languageCode);

      this.updateBackend_();
    },

    /*
     * Gets the language codes of the currently listed languages.
     */
    getLanguageCodes: function() {
      return this.dataModel.slice();
    },

    /*
     * Gets the language code of the selected language.
     */
    getSelectedLanguageCode: function() {
      return this.selectedItem;
    },

    /*
     * Removes the currently selected language.
     */
    removeSelectedLanguage: function() {
      if (this.selectionModel.selectedIndex >= 0 &&
          // Don't allow removing the last language.
          this.dataModel.length > 1) {
        // TODO(satorux): Until we switch to the single selection model,
        // it's possible that multiple languages are selected, but we don't
        // handle that case here.
        var originalIndex = this.selectionModel.selectedIndex;
        this.dataModel.splice(this.selectionModel.selectedIndex, 1);
        // Select the item at the original index if possible. Otherwise,
        // select the last item.
        // TODO(satorux): This should be handled by the selection model
        // See crbug.com/49893.
        this.selectionModel.selectedIndex = Math.min(
            originalIndex,
            this.dataModel.length - 1);
        var newLanguageCodes = this.dataModel.slice();
        this.load_(newLanguageCodes);
        this.updateBackend_();
      }
    },

    /**
     * Handles pref change.
     * @param {Event} e The change event object.
     * @private
     */
    handlePrefChange_: function(e) {
      var languageCodesInCsv = e.value;
      var languageCodes = this.filterBadLanguageCodes_(
          languageCodesInCsv.split(','));
      this.load_(languageCodes);
    },

    /**
     * Loads given language list.
     * @param {Array} languageCodes List of language codes.
     * @private
     */
    load_: function(languageCodes) {
      this.dataModel = new ArrayDataModel(languageCodes);
      // Select the first item if it's not empty.
      // TODO(satorux): Switch to a single item selection model that does
      // not allow no selection, one it's ready: crbug.com/49893
      if (this.dataModel.length > 0)
        this.selectionModel.selectedIndex = 0;
    },

    /**
     * Updates backend.
     */
    updateBackend_: function() {
      // Encode the language codes into a CSV string.
      Preferences.setStringPref(this.pref, this.dataModel.slice().join(','));
    },

    /**
     * Filters bad language codes in case bad language codes are
     * stored in the preference.
     * @param {Array} languageCodes List of language codes.
     * @private
     */
    filterBadLanguageCodes_: function(languageCodes) {
      var filteredLanguageCodes = [];
      for (var i = 0; i < languageCodes.length; i++) {
        // Check if the translation for the language code is
        // present. Otherwise, skip it.
        if (LanguageList.getDisplayNameFromLanguageCode(languageCodes[i])) {
          filteredLanguageCodes.push(languageCodes[i]);
        }
      }
      return filteredLanguageCodes;
    },
  };

  return {
    LanguageList: LanguageList
  };
});
<commit_msg>Fix behaviors of Add and Remove buttons.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options.language', function() {
  const List = cr.ui.List;
  const ListItem = cr.ui.ListItem;
  const ArrayDataModel = cr.ui.ArrayDataModel;
  const LanguageOptions = options.LanguageOptions;

  /**
   * Creates a new language list.
   * @param {Object=} opt_propertyBag Optional properties.
   * @constructor
   * @extends {cr.ui.List}
   */
  var LanguageList = cr.ui.define('list');

  /**
   * Gets display name from the given language code.
   * @param {string} languageCode Language code (ex. "fr").
   */
  LanguageList.getDisplayNameFromLanguageCode = function(languageCode) {
    // Build the language code to display name dictionary at first time.
    if (!this.languageCodeToDisplayName_) {
      this.languageCodeToDisplayName_ = {};
      var languageList = templateData.languageList;
      for (var i = 0; i < languageList.length; i++) {
        var language = languageList[i];
        this.languageCodeToDisplayName_[language.code] = language.displayName;
      }
    }

    return this.languageCodeToDisplayName_[languageCode];
  }

  LanguageList.prototype = {
    __proto__: List.prototype,

    pref: 'settings.language.preferred_languages',

    /** @inheritDoc */
    decorate: function() {
      List.prototype.decorate.call(this);

      // HACK(arv): http://crbug.com/40902
      window.addEventListener('resize', cr.bind(this.redraw, this));

      // Listen to pref change.
      Preferences.getInstance().addEventListener(this.pref,
          cr.bind(this.handlePrefChange_, this));
    },

    createItem: function(languageCode) {
      var languageDisplayName =
          LanguageList.getDisplayNameFromLanguageCode(languageCode);
      return new ListItem({label: languageDisplayName});
    },

    /*
     * Adds a language to the language list.
     * @param {string} languageCode language code (ex. "fr").
     */
    addLanguage: function(languageCode) {
      this.dataModel.push(languageCode);
      // Select the last item, which is the language added.
      this.selectionModel.selectedIndex = this.dataModel.length - 1;

      this.updateBackend_();
    },

    /*
     * Gets the language codes of the currently listed languages.
     */
    getLanguageCodes: function() {
      return this.dataModel.slice();
    },

    /*
     * Gets the language code of the selected language.
     */
    getSelectedLanguageCode: function() {
      return this.selectedItem;
    },

    /*
     * Removes the currently selected language.
     */
    removeSelectedLanguage: function() {
      if (this.selectionModel.selectedIndex >= 0 &&
          // Don't allow removing the last language.
          this.dataModel.length > 1) {
        // TODO(satorux): Until we switch to the single selection model,
        // it's possible that multiple languages are selected, but we don't
        // handle that case here.
        var originalIndex = this.selectionModel.selectedIndex;
        this.dataModel.splice(this.selectionModel.selectedIndex, 1);
        // Select the item at the original index if possible. Otherwise,
        // select the last item.
        // TODO(satorux): This should be handled by the selection model
        // See crbug.com/49893.
        this.selectionModel.selectedIndex = Math.min(
            originalIndex,
            this.dataModel.length - 1);
        this.updateBackend_();
      }
    },

    /**
     * Handles pref change.
     * @param {Event} e The change event object.
     * @private
     */
    handlePrefChange_: function(e) {
      var languageCodesInCsv = e.value;
      var languageCodes = this.filterBadLanguageCodes_(
          languageCodesInCsv.split(','));
      this.load_(languageCodes);
    },

    /**
     * Loads given language list.
     * @param {Array} languageCodes List of language codes.
     * @private
     */
    load_: function(languageCodes) {
      // Preserve the original selected index. See comments below.
      var originalSelectedIndex = (this.selectionModel ?
                                   this.selectionModel.selectedIndex : -1);
      this.dataModel = new ArrayDataModel(languageCodes);
      if (originalSelectedIndex >= 0 &&
          originalSelectedIndex < this.dataModel.length) {
        // Restore the original selected index if the selected index is
        // valid after the data model is loaded. This is neeeded to keep
        // the selected language after the languge is added or removed.
        this.selectionModel.selectedIndex = originalSelectedIndex;
      } else if (this.dataModel.length > 0){
        // Otherwise, select the first item if it's not empty.
        // TODO(satorux): Switch to a single item selection model that does
        // not allow no selection, one it's ready: crbug.com/49893
        this.selectionModel.selectedIndex = 0;
      }
    },

    /**
     * Updates backend.
     */
    updateBackend_: function() {
      // Encode the language codes into a CSV string.
      Preferences.setStringPref(this.pref, this.dataModel.slice().join(','));
    },

    /**
     * Filters bad language codes in case bad language codes are
     * stored in the preference.
     * @param {Array} languageCodes List of language codes.
     * @private
     */
    filterBadLanguageCodes_: function(languageCodes) {
      var filteredLanguageCodes = [];
      for (var i = 0; i < languageCodes.length; i++) {
        // Check if the translation for the language code is
        // present. Otherwise, skip it.
        if (LanguageList.getDisplayNameFromLanguageCode(languageCodes[i])) {
          filteredLanguageCodes.push(languageCodes[i]);
        }
      }
      return filteredLanguageCodes;
    },
  };

  return {
    LanguageList: LanguageList
  };
});
<|endoftext|>


<commit_before>import { v4 as uuidv4 } from 'uuid';

// HEAD DATA
export const headData = {
  title: '', // e.g: 'Name | Developer'
  lang: '', // e.g: en, es, fr, jp
  description: '', // e.g: Welcome to my website
};

// HERO DATA
export const heroData = {
  title: '',
  name: '',
  subtitle: '',
  cta: '',
};

// ABOUT DATA
export const aboutData = {
  img: 'profile.jpg',
  paragraphOne: '',
  paragraphTwo: '',
  paragraphThree: '',
  resume: 'https://www.resumemaker.online/es.php', // if no resume, the button will not show up
};

// PROJECTS DATA
export const projectsData = [
  {
    id: uuidv4(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
  {
    id: uuidv4(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
  {
    id: uuidv4(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
];

// CONTACT DATA
export const contactData = {
  cta: '',
  btn: '',
  email: '',
};

// FOOTER DATA
export const footerData = {
  networks: [
    {
      id: uuidv4(),
      name: 'twitter',
      url: '',
    },
    {
      id: uuidv4(),
      name: 'codepen',
      url: '',
    },
    {
      id: uuidv4(),
      name: 'linkedin',
      url: '',
    },
    {
      id: uuidv4(),
      name: 'github',
      url: '',
    },
  ],
};

// Github start/fork buttons
export const githubButtons = {
  isEnabled: true, // set to false to disable the GitHub stars/fork buttons
};
<commit_msg>add nanoid on data.js<commit_after>import { nanoid } from 'nanoid';

// HEAD DATA
export const headData = {
  title: '', // e.g: 'Name | Developer'
  lang: '', // e.g: en, es, fr, jp
  description: '', // e.g: Welcome to my website
};

// HERO DATA
export const heroData = {
  title: '',
  name: '',
  subtitle: '',
  cta: '',
};

// ABOUT DATA
export const aboutData = {
  img: 'profile.jpg',
  paragraphOne: '',
  paragraphTwo: '',
  paragraphThree: '',
  resume: 'https://www.resumemaker.online/es.php', // if no resume, the button will not show up
};

// PROJECTS DATA
export const projectsData = [
  {
    id: nanoid(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
  {
    id: nanoid(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
  {
    id: nanoid(),
    img: 'project.jpg',
    title: '',
    info: '',
    info2: '',
    url: '',
    repo: 'https://github.com/cobidev/react-simplefolio', // if no repo, the button will not show up
  },
];

// CONTACT DATA
export const contactData = {
  cta: '',
  btn: '',
  email: '',
};

// FOOTER DATA
export const footerData = {
  networks: [
    {
      id: nanoid(),
      name: 'twitter',
      url: '',
    },
    {
      id: nanoid(),
      name: 'codepen',
      url: '',
    },
    {
      id: nanoid(),
      name: 'linkedin',
      url: '',
    },
    {
      id: nanoid(),
      name: 'github',
      url: '',
    },
  ],
};

// Github start/fork buttons
export const githubButtons = {
  isEnabled: true, // set to false to disable the GitHub stars/fork buttons
};
<|endoftext|>


<commit_before>/** MIT License (c) copyright B Cavalier & J Hann */

/**
 * curl dojo 1.6 shim
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 */

/**
 * Until AMD becomes well established, there will be issues with the various
 * libs.  This one overcomes some minor issues with dojo 1.6's initial
 * foray into AMD territory. :)
 *
 * usage:
 *  curl(['curl/shim/dojo16', 'curl/domReady'])
 *  	.next(['dojo/parser'])
 *  	.then(function (parser) {
 *  		parser.parse();
 *  	});
 *
 */
define(/*=='curl/shim/dojo16',==*/ ['curl/_privileged', 'curl/domReady'], function (priv, domReady) {
"use strict";

	var _curl = priv['_curl'],
		origExecuteDefFunc = priv['core'].executeDefFunc;

	function duckPunchRequire (req) {
		if (!req['ready']){
			req['ready'] = function (cb) {
				domReady(cb);
			};
		}
		if (!req['nameToUrl']) {
			req['nameToUrl'] = function (name, ext) {
				// map non-standard nameToUrl to toUrl
				return req['toUrl'](name + (ext || ''));
			};
		}
		return req;
	}

	// modify global curl cuz dojo doesn't always use local `require`
	// as a dependency
	duckPunchRequire(_curl);

	// override executeDefFunc to override "require" deps
	priv['core'].executeDefFunc = function (def) {
		duckPunchRequire(def.ctx.require);
		return origExecuteDefFunc(def);
	};

	return true;

});
<commit_msg>collapse of ctx into def and fix to plugin parsing<commit_after>/** MIT License (c) copyright B Cavalier & J Hann */

/**
 * curl dojo 1.6 shim
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 */

/**
 * Until AMD becomes well established, there will be issues with the various
 * libs.  This one overcomes some minor issues with dojo 1.6's initial
 * foray into AMD territory. :)
 *
 * usage:
 *  curl(['curl/shim/dojo16', 'curl/domReady'])
 *  	.next(['dojo/parser'])
 *  	.then(function (parser) {
 *  		parser.parse();
 *  	});
 *
 */
define(/*=='curl/shim/dojo16',==*/ ['curl/_privileged', 'curl/domReady'], function (priv, domReady) {
"use strict";

	var _curl = priv['_curl'],
		origExecuteDefFunc = priv['core'].executeDefFunc;

	function duckPunchRequire (req) {
		if (!req['ready']){
			req['ready'] = function (cb) {
				domReady(cb);
			};
		}
		if (!req['nameToUrl']) {
			req['nameToUrl'] = function (name, ext) {
				// map non-standard nameToUrl to toUrl
				return req['toUrl'](name + (ext || ''));
			};
		}
		return req;
	}

	// modify global curl cuz dojo doesn't always use local `require`
	// as a dependency
	duckPunchRequire(_curl);

	// override executeDefFunc to override "require" deps
	priv['core'].executeDefFunc = function (def) {
		duckPunchRequire(def.require);
		return origExecuteDefFunc(def);
	};

	return true;

});
<|endoftext|>


<commit_before>const fetchSupported = window.fetch !== undefined;

const streamJsonFetch = (url, callback) => {
  let destroyed = false;
  fetch(url).then((response) => {
    const reader = response.body.getReader();
    let partial = '';
    const decoder = new TextDecoder();
    const parseResult = (result) => {
      if (destroyed) return;
      partial += decoder.decode(result.value || new Uint8Array(), {
        stream: !result.done,
      });
      const pieces = partial.split('\n');
      const count = pieces.length;
      if (!result.done) {
        partial = pieces[count - 1];
      } else {
        callback();
      }
      for (let i = 0; i < count - 1; i++) {
        callback(null, pieces[i]);
      }
      if (!result.done) {
        return readFromResponse();
      }
    };
    const readFromResponse = () => (
      reader
        .read()
        .then(parseResult)
        .catch(callback)
    );
    readFromResponse();
  });
  return {
    cancel: () => {
      destroyed = true;
    },
  };
};

const streamJsonXHR = (url, callback) => {
  let destroyed = false;
  const xhr = new XMLHttpRequest();
  let pos = 0;

  const processPiece = (chunk) => {
    callback(null, chunk);
  };

  xhr.onprogress = () => {
    if (destroyed) return;
    const pieces = xhr
      .response
      .slice(pos)
      .split('\n');
    for (let i = 0; i < pieces.length - 1; i++) {
      const piece = pieces[i];
      processPiece(piece);
      pos += piece.length + 1;
    }
  };

  xhr.onload = () => {
    const piece = xhr.response.slice(pos);
    if (piece) processPiece(piece);
    callback();
  };

  xhr.onerror = () => {
    if (!destroyed) {
      callback(Error('Connection failed'));
    }
  };
  xhr.responseType = 'text';
  xhr.open('GET', url);
  xhr.send();
  return {
    cancel: () => {
      xhr.abort();
      destroyed = true;
    },
  };
};

export default (url, callback) => (
  fetchSupported
    ? streamJsonFetch(url, callback)
    : streamJsonXHR(url, callback)
);
<commit_msg>default to xhr version of stream-json for better performance<commit_after>const fetchSupported = window.fetch !== undefined;

const streamJsonFetch = (url, callback) => {
  let destroyed = false;
  fetch(url).then((response) => {
    const reader = response.body.getReader();
    let partial = '';
    const decoder = new TextDecoder();
    const parseResult = (result) => {
      if (destroyed) return;
      partial += decoder.decode(result.value || new Uint8Array(), {
        stream: !result.done,
      });
      const pieces = partial.split('\n');
      const count = pieces.length;
      if (!result.done) {
        partial = pieces[count - 1];
      } else {
        callback();
      }
      for (let i = 0; i < count - 1; i++) {
        callback(null, pieces[i]);
      }
      if (!result.done) {
        return readFromResponse();
      }
    };
    const readFromResponse = () => (
      reader
        .read()
        .then(parseResult)
        .catch(callback)
    );
    readFromResponse();
  });
  return {
    cancel: () => {
      destroyed = true;
    },
  };
};

const streamJsonXHR = (url, callback) => {
  let destroyed = false;
  const xhr = new XMLHttpRequest();
  let pos = 0;

  const processPiece = (chunk) => {
    callback(null, chunk);
  };

  xhr.onprogress = () => {
    if (destroyed) return;
    const pieces = xhr
      .response
      .slice(pos)
      .split('\n');
    for (let i = 0; i < pieces.length - 1; i++) {
      const piece = pieces[i];
      processPiece(piece);
      pos += piece.length + 1;
    }
  };

  xhr.onload = () => {
    const piece = xhr.response.slice(pos);
    if (piece) processPiece(piece);
    callback();
  };

  xhr.onerror = () => {
    if (!destroyed) {
      callback(Error('Connection failed'));
    }
  };
  xhr.responseType = 'text';
  xhr.open('GET', url);
  xhr.send();
  return {
    cancel: () => {
      xhr.abort();
      destroyed = true;
    },
  };
};

// NOTE: Not including Fetch version, because performance:
export default streamJsonXHR;
<|endoftext|>