<commit_before>import DateTitle from './mixins/date-title'
import DateHeader from './mixins/date-header'
import DateTable from './mixins/date-table'
import DateYears from './mixins/date-years'
import CardActions from '../../mixins/card-actions'

export default {
  name: 'date-picker',

  mixins: [CardActions, DateTitle, DateHeader, DateTable, DateYears],

  data () {
    return {
      tableDate: new Date(),
      originalDate: this.value,
      currentDay: null,
      currentMonth: null,
      currentYear: null,
      isSelected: false,
      isReversing: false
    }
  },

  props: {
    actions: Boolean,
    dark: Boolean,
    dateFormat: {
      type: Function,
      default: val => {
        return new Date(val).toISOString().substr(0, 10)
      }
    },
    days: {
      type: Array,
      default: () => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
    },
    landscape: Boolean,
    months: {
      type: Array,
      default: () => [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
      ]
    },
    value: {
      required: true
    }
  },

  computed: {
    inputDate: {
      get () {
        if (!this.value) return new Date()
        if (this.value instanceof Date) return this.value
        if (!isNaN(this.value) && this.value.indexOf(':') !== -1) return new Date(this.value)

        return new Date(`${this.value}T12:00:00`)
      },
      set (val) {
        this.$emit('input', val ? this.dateFormat(val) : this.originalDate)
      }
    },
    day () {
      return this.inputDate.getDate()
    },
    month () {
      return this.inputDate.getMonth()
    },
    year () {
      return this.inputDate.getFullYear()
    },
    tableMonth () {
      return this.tableDate.getMonth()
    },
    tableYear () {
      return this.tableDate.getFullYear()
    },
    dayName () {
      return this.inputDate ? this.days[this.inputDate.getDay()] : ''
    },
    monthName () {
      return this.inputDate ? this.months[this.month] : ''
    },
    computedTransition () {
      return this.isReversing ? 'v-tab-reverse-transition' : 'v-tab-transition'
    }
  },

  watch: {
    isSelected (val) {
      val && this.$nextTick(() => {
        this.$refs.years.scrollTop = this.$refs.years.scrollHeight / 2 - 165
      })
    },
    tableDate (val, prev) {
      this.isReversing = val < prev
    },
    value (val) {
      if (val) this.tableDate = this.inputDate
      if (!this.$scopedSlots.default && !this.actions) this.originalDate = val
    }
  },

  methods: {
    actionCancel () {
      this.inputDate = this.originalDate
      if (this.$parent && this.$parent.isActive) this.$parent.isActive = false
    },
    actionOk () {
      this.originalDate = this.value
      if (this.$parent && this.$parent.isActive) this.$parent.isActive = false
    }
  },

  mounted () {
    this.currentDay = this.tableDate.getDate()
    this.currentMonth = this.tableDate.getMonth()
    this.currentYear = this.tableDate.getFullYear()
    this.tableDate = this.inputDate
  },

  render (h) {
    const children = [this.genTitle()]

    if (!this.isSelected) {
      const bodyChildren = []

      bodyChildren.push(this.genHeader())
      bodyChildren.push(this.genTable())
      bodyChildren.push(this.genFooter(this.$scopedSlots.default))

      children.push(h('div', {
        'class': 'date-picker__body'
      }, bodyChildren))
    } else {
      children.push(this.genYears())
    }

    return h('v-card', {
      'class': {
        'date-picker': true,
        'date-picker--dark': this.dark,
        'date-picker--landscape': this.landscape
      },
      props: {
        height: this.landscape ? '310px' : '535px'
      }
    }, children)
  }
}
<commit_msg>fixed height<commit_after>import DateTitle from './mixins/date-title'
import DateHeader from './mixins/date-header'
import DateTable from './mixins/date-table'
import DateYears from './mixins/date-years'
import CardActions from '../../mixins/card-actions'

export default {
  name: 'date-picker',

  mixins: [CardActions, DateTitle, DateHeader, DateTable, DateYears],

  data () {
    return {
      tableDate: new Date(),
      originalDate: this.value,
      currentDay: null,
      currentMonth: null,
      currentYear: null,
      isSelected: false,
      isReversing: false
    }
  },

  props: {
    actions: Boolean,
    dark: Boolean,
    dateFormat: {
      type: Function,
      default: val => {
        return new Date(val).toISOString().substr(0, 10)
      }
    },
    days: {
      type: Array,
      default: () => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
    },
    landscape: Boolean,
    months: {
      type: Array,
      default: () => [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
      ]
    },
    value: {
      required: true
    }
  },

  computed: {
    inputDate: {
      get () {
        if (!this.value) return new Date()
        if (this.value instanceof Date) return this.value
        if (!isNaN(this.value) && this.value.indexOf(':') !== -1) return new Date(this.value)

        return new Date(`${this.value}T12:00:00`)
      },
      set (val) {
        this.$emit('input', val ? this.dateFormat(val) : this.originalDate)
      }
    },
    day () {
      return this.inputDate.getDate()
    },
    month () {
      return this.inputDate.getMonth()
    },
    year () {
      return this.inputDate.getFullYear()
    },
    tableMonth () {
      return this.tableDate.getMonth()
    },
    tableYear () {
      return this.tableDate.getFullYear()
    },
    dayName () {
      return this.inputDate ? this.days[this.inputDate.getDay()] : ''
    },
    monthName () {
      return this.inputDate ? this.months[this.month] : ''
    },
    computedTransition () {
      return this.isReversing ? 'v-tab-reverse-transition' : 'v-tab-transition'
    }
  },

  watch: {
    isSelected (val) {
      val && this.$nextTick(() => {
        this.$refs.years.scrollTop = this.$refs.years.scrollHeight / 2 - 165
      })
    },
    tableDate (val, prev) {
      this.isReversing = val < prev
    },
    value (val) {
      if (val) this.tableDate = this.inputDate
      if (!this.$scopedSlots.default && !this.actions) this.originalDate = val
    }
  },

  methods: {
    actionCancel () {
      this.inputDate = this.originalDate
      if (this.$parent && this.$parent.isActive) this.$parent.isActive = false
    },
    actionOk () {
      this.originalDate = this.value
      if (this.$parent && this.$parent.isActive) this.$parent.isActive = false
    }
  },

  mounted () {
    this.currentDay = this.tableDate.getDate()
    this.currentMonth = this.tableDate.getMonth()
    this.currentYear = this.tableDate.getFullYear()
    this.tableDate = this.inputDate
  },

  render (h) {
    const children = [this.genTitle()]

    if (!this.isSelected) {
      const bodyChildren = []

      bodyChildren.push(this.genHeader())
      bodyChildren.push(this.genTable())
      bodyChildren.push(this.genFooter(this.$scopedSlots.default))

      children.push(h('div', {
        'class': 'date-picker__body'
      }, bodyChildren))
    } else {
      children.push(this.genYears())
    }

    return h('v-card', {
      'class': {
        'date-picker': true,
        'date-picker--dark': this.dark,
        'date-picker--landscape': this.landscape
      },
      props: {
        height: this.landscape ? '310px' : 'auto'
      }
    }, children)
  }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow

import * as React from 'react';
import bisection from 'bisection';
import clamp from 'clamp';
import arrayMove from 'array-move';
import { getContentRect, getMarginRect } from '../../utils/css-geometry-tools';

import type { DOMRectLiteral } from '../../utils/dom-rect';

type Props = {|
  orient: 'horizontal' | 'vertical',
  tagName: string,
  className: string,
  order: number[],
  onChangeOrder: (number[]) => mixed,
  // Reorderable elements should set a class name to match against. This allows
  // nested reorderable elements to set different matching class names.
  grippyClassName: string,
  // This forces the children to be an array of React Elements.
  // See https://flow.org/en/docs/react/children/ for more information.
  // Be careful: children need to handle a `style` property.
  children: React.ChildrenArray<React.Element<any>>,
|};

type State = {|
  phase: 'RESTING' | 'FINISHING' | 'MANIPULATING',
  manipulatingIndex: number,
  destinationIndex: number,
  manipulationDelta: number,
  adjustPrecedingBy: number,
  adjustSucceedingBy: number,
  finalOffset: number,
|};

type XY = {|
  pageXY: 'pageX' | 'pageY',
  translateXY: 'translateX' | 'translateY',
  lefttop: 'left' | 'top',
  rightbottom: 'right' | 'bottom',
|};

type EventWithPageProperties = { pageX: number, pageY: number };

class Reorderable extends React.PureComponent<Props, State> {
  _xy: {| horizontal: XY, vertical: XY |};

  constructor(props: Props) {
    super(props);
    (this: any)._onMouseDown = this._onMouseDown.bind(this);
    this.state = {
      phase: 'RESTING',
      manipulatingIndex: -1,
      destinationIndex: -1,
      manipulationDelta: 0,
      adjustPrecedingBy: 0,
      adjustSucceedingBy: 0,
      finalOffset: 0,
    };

    this._xy = {
      horizontal: {
        pageXY: 'pageX',
        translateXY: 'translateX',
        lefttop: 'left',
        rightbottom: 'right',
      },
      vertical: {
        pageXY: 'pageY',
        translateXY: 'translateY',
        lefttop: 'top',
        rightbottom: 'bottom',
      },
    };
  }

  _onMouseDown(
    event: { target: EventTarget } & SyntheticMouseEvent<HTMLElement>
  ) {
    const container = event.currentTarget;

    if (
      event.target === container ||
      !(event.target instanceof HTMLElement) ||
      // Only run for left clicks.
      event.button !== 0
    ) {
      return;
    }

    // Flow: Coerce the event target into an HTMLElement in combination with the above
    // `instanceof` statement.
    let element = (event.target: HTMLElement);
    const { grippyClassName } = this.props;
    if (!element.matches(`.${grippyClassName}, .${grippyClassName} *`)) {
      // Don't handle this event. Only clicking inside a matching grippy class
      // name should start the dragging process.
      return;
    }

    while (element instanceof HTMLElement && element.parentNode !== container) {
      element = element.parentNode;
    }

    if (!(element instanceof HTMLElement)) {
      return;
    }

    this._startDraggingElement(container, element, event);
  }

  _getXY(): XY {
    return this._xy[this.props.orient];
  }

  _startDraggingElement(
    container: HTMLElement,
    element: HTMLElement,
    event: EventWithPageProperties
  ) {
    const xy = this._getXY();
    // Coerce the SyntheticMouseEvent and DOMRect instances into an object literals
    // to dynamically access certain properties.
    const mouseDownPos = event[xy.pageXY];
    const elementRect: DOMRectLiteral = getMarginRect(element);
    const containerRect: DOMRectLiteral = getContentRect(container);
    const spaceBefore = elementRect[xy.lefttop] - containerRect[xy.lefttop];
    const spaceAfter =
      containerRect[xy.rightbottom] - elementRect[xy.rightbottom];

    const children = Array.from(container.children);
    if (children.length < 2) {
      return;
    }

    let isBefore = true;
    let elementIndex = -1;
    const offsets = children.map((child, childIndex) => {
      if (child === element) {
        elementIndex = childIndex;
        isBefore = false;
        return 0;
      }
      const childRect: DOMRectLiteral = getMarginRect(child);
      return isBefore
        ? childRect[xy.lefttop] - elementRect[xy.lefttop]
        : childRect[xy.rightbottom] - elementRect[xy.rightbottom];
    });

    if (elementIndex === -1) {
      return;
    }

    const midPoints = offsets.map((offset, childIndex) => {
      if (childIndex === offsets.length - 1) {
        // This will be popped off at the end.
        return 0;
      }
      return (offsets[childIndex + 1] + offset) / 2;
    });
    midPoints.pop();

    const nextEdgeAfterElement =
      elementIndex === children.length - 1
        ? containerRect[xy.rightbottom]
        : (getMarginRect(children[elementIndex + 1]): DOMRectLiteral)[
            xy.lefttop
          ];

    const nextEdgeBeforeElement =
      elementIndex === 0
        ? containerRect[xy.lefttop]
        : (getMarginRect(children[elementIndex - 1]): DOMRectLiteral)[
            xy.rightbottom
          ];

    this.setState({
      phase: 'MANIPULATING',
      manipulatingIndex: elementIndex,
      manipulationDelta: 0,
      destinationIndex: elementIndex,
      adjustPrecedingBy: nextEdgeAfterElement - elementRect[xy.lefttop],
      adjustSucceedingBy: nextEdgeBeforeElement - elementRect[xy.rightbottom],
    });

    const mouseMoveListener = (event: EventWithPageProperties) => {
      const delta = clamp(
        event[xy.pageXY] - mouseDownPos,
        -spaceBefore,
        spaceAfter
      );
      this.setState({
        manipulationDelta: delta,
        destinationIndex: bisection.right(midPoints, delta),
      });
    };
    const mouseUpListener = (event: EventWithPageProperties) => {
      mouseMoveListener(event);
      const destinationIndex = this.state.destinationIndex;
      this.setState({
        phase: 'FINISHING',
        finalOffset: offsets[destinationIndex],
      });
      window.removeEventListener('mousemove', mouseMoveListener, true);
      window.removeEventListener('mouseup', mouseUpListener, true);
      setTimeout(() => {
        this.setState({
          phase: 'RESTING',
        });
        if (elementIndex !== destinationIndex) {
          const newOrder = arrayMove(
            this.props.order,
            elementIndex,
            destinationIndex
          );
          this.props.onChangeOrder(newOrder);
        }
      }, 200);
    };
    window.addEventListener('mousemove', mouseMoveListener, true);
    window.addEventListener('mouseup', mouseUpListener, true);
  }

  render() {
    const { className, order } = this.props;
    const children = React.Children.toArray(this.props.children);
    const orderedChildren = order.map(childIndex => children[childIndex]);
    const TagName = this.props.tagName;
    const xy = this._getXY();

    if (this.state.phase === 'RESTING') {
      return (
        <TagName className={className} onMouseDown={this._onMouseDown}>
          {orderedChildren}
        </TagName>
      );
    }

    const {
      phase,
      manipulatingIndex,
      destinationIndex,
      adjustPrecedingBy,
      adjustSucceedingBy,
    } = this.state;
    const adjustedClassName =
      phase === 'MANIPULATING' ? className + ' beingReordered' : className;
    return (
      <TagName className={adjustedClassName}>
        {orderedChildren.map((child, childIndex) => {
          const style = {
            transition: '200ms ease-in-out transform',
            willChange: 'transform',
            position: 'relative',
            zIndex: '1',
            transform: '',
          };
          if (childIndex === manipulatingIndex) {
            style.zIndex = '2';
            if (phase === 'MANIPULATING') {
              delete style.transition;
              style.transform = `${xy.translateXY}(${
                this.state.manipulationDelta
              }px)`;
            } else {
              style.transform = `${xy.translateXY}(${
                this.state.finalOffset
              }px)`;
            }
          } else if (
            childIndex < manipulatingIndex &&
            childIndex >= destinationIndex
          ) {
            style.transform = `${xy.translateXY}(${adjustPrecedingBy}px)`;
          } else if (
            childIndex > manipulatingIndex &&
            childIndex <= destinationIndex
          ) {
            style.transform = `${xy.translateXY}(${adjustSucceedingBy}px)`;
          }

          // Note: the child element needs to handle this `style` property.
          return React.cloneElement(child, { style });
        })}
      </TagName>
    );
  }
}

export default Reorderable;
<commit_msg>Tweak the Reorderable component to only activate on mousemove (Merge PR #1191)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow

import * as React from 'react';
import bisection from 'bisection';
import clamp from 'clamp';
import arrayMove from 'array-move';
import { getContentRect, getMarginRect } from '../../utils/css-geometry-tools';

import type { DOMRectLiteral } from '../../utils/dom-rect';

type Props = {|
  orient: 'horizontal' | 'vertical',
  tagName: string,
  className: string,
  order: number[],
  onChangeOrder: (number[]) => mixed,
  // Reorderable elements should set a class name to match against. This allows
  // nested reorderable elements to set different matching class names.
  grippyClassName: string,
  // This forces the children to be an array of React Elements.
  // See https://flow.org/en/docs/react/children/ for more information.
  // Be careful: children need to handle a `style` property.
  children: React.ChildrenArray<React.Element<any>>,
|};

type State = {|
  phase: 'RESTING' | 'FINISHING' | 'MANIPULATING',
  manipulatingIndex: number,
  destinationIndex: number,
  manipulationDelta: number,
  adjustPrecedingBy: number,
  adjustSucceedingBy: number,
  finalOffset: number,
|};

type XY = {|
  pageXY: 'pageX' | 'pageY',
  translateXY: 'translateX' | 'translateY',
  lefttop: 'left' | 'top',
  rightbottom: 'right' | 'bottom',
|};

type EventWithPageProperties = { pageX: number, pageY: number };

class Reorderable extends React.PureComponent<Props, State> {
  _xy: {| horizontal: XY, vertical: XY |};

  constructor(props: Props) {
    super(props);
    (this: any)._onMouseDown = this._onMouseDown.bind(this);
    this.state = {
      phase: 'RESTING',
      manipulatingIndex: -1,
      destinationIndex: -1,
      manipulationDelta: 0,
      adjustPrecedingBy: 0,
      adjustSucceedingBy: 0,
      finalOffset: 0,
    };

    this._xy = {
      horizontal: {
        pageXY: 'pageX',
        translateXY: 'translateX',
        lefttop: 'left',
        rightbottom: 'right',
      },
      vertical: {
        pageXY: 'pageY',
        translateXY: 'translateY',
        lefttop: 'top',
        rightbottom: 'bottom',
      },
    };
  }

  _onMouseDown(
    event: { target: EventTarget } & SyntheticMouseEvent<HTMLElement>
  ) {
    const container = event.currentTarget;

    if (
      event.target === container ||
      !(event.target instanceof HTMLElement) ||
      // Only run for left clicks.
      event.button !== 0
    ) {
      return;
    }

    // Flow: Coerce the event target into an HTMLElement in combination with the above
    // `instanceof` statement.
    let element = (event.target: HTMLElement);
    const { grippyClassName } = this.props;
    if (!element.matches(`.${grippyClassName}, .${grippyClassName} *`)) {
      // Don't handle this event. Only clicking inside a matching grippy class
      // name should start the dragging process.
      return;
    }

    while (element instanceof HTMLElement && element.parentNode !== container) {
      element = element.parentNode;
    }

    if (!(element instanceof HTMLElement)) {
      return;
    }

    this._startDraggingElement(container, element, event);
  }

  _getXY(): XY {
    return this._xy[this.props.orient];
  }

  _startDraggingElement(
    container: HTMLElement,
    element: HTMLElement,
    event: EventWithPageProperties
  ) {
    const xy = this._getXY();
    // Coerce the SyntheticMouseEvent and DOMRect instances into an object literals
    // to dynamically access certain properties.
    const mouseDownPos = event[xy.pageXY];
    const elementRect: DOMRectLiteral = getMarginRect(element);
    const containerRect: DOMRectLiteral = getContentRect(container);
    const spaceBefore = elementRect[xy.lefttop] - containerRect[xy.lefttop];
    const spaceAfter =
      containerRect[xy.rightbottom] - elementRect[xy.rightbottom];

    const children = Array.from(container.children);
    if (children.length < 2) {
      return;
    }

    let isBefore = true;
    let elementIndex = -1;
    const offsets = children.map((child, childIndex) => {
      if (child === element) {
        elementIndex = childIndex;
        isBefore = false;
        return 0;
      }
      const childRect: DOMRectLiteral = getMarginRect(child);
      return isBefore
        ? childRect[xy.lefttop] - elementRect[xy.lefttop]
        : childRect[xy.rightbottom] - elementRect[xy.rightbottom];
    });

    if (elementIndex === -1) {
      return;
    }

    const midPoints = offsets.map((offset, childIndex) => {
      if (childIndex === offsets.length - 1) {
        // This will be popped off at the end.
        return 0;
      }
      return (offsets[childIndex + 1] + offset) / 2;
    });
    midPoints.pop();

    const nextEdgeAfterElement =
      elementIndex === children.length - 1
        ? containerRect[xy.rightbottom]
        : (getMarginRect(children[elementIndex + 1]): DOMRectLiteral)[
            xy.lefttop
          ];

    const nextEdgeBeforeElement =
      elementIndex === 0
        ? containerRect[xy.lefttop]
        : (getMarginRect(children[elementIndex - 1]): DOMRectLiteral)[
            xy.rightbottom
          ];

    this.setState({
      manipulatingIndex: elementIndex,
      manipulationDelta: 0,
      destinationIndex: elementIndex,
      adjustPrecedingBy: nextEdgeAfterElement - elementRect[xy.lefttop],
      adjustSucceedingBy: nextEdgeBeforeElement - elementRect[xy.rightbottom],
    });

    const mouseMoveListener = (event: EventWithPageProperties) => {
      if (this.state.phase === 'RESTING') {
        // Only start manipulating on the mouse move.
        this.setState({ phase: 'MANIPULATING' });
      }
      const delta = clamp(
        event[xy.pageXY] - mouseDownPos,
        -spaceBefore,
        spaceAfter
      );
      this.setState({
        manipulationDelta: delta,
        destinationIndex: bisection.right(midPoints, delta),
      });
    };
    const mouseUpListener = (event: EventWithPageProperties) => {
      window.removeEventListener('mousemove', mouseMoveListener, true);
      window.removeEventListener('mouseup', mouseUpListener, true);
      if (this.state.phase === 'RESTING') {
        // A mousemove never transitioned to the MANIPULATING state, so
        // exit out now.
        return;
      }
      mouseMoveListener(event);
      const destinationIndex = this.state.destinationIndex;
      this.setState({
        phase: 'FINISHING',
        finalOffset: offsets[destinationIndex],
      });
      setTimeout(() => {
        this.setState({
          phase: 'RESTING',
        });
        if (elementIndex !== destinationIndex) {
          const newOrder = arrayMove(
            this.props.order,
            elementIndex,
            destinationIndex
          );
          this.props.onChangeOrder(newOrder);
        }
      }, 200);
    };
    window.addEventListener('mousemove', mouseMoveListener, true);
    window.addEventListener('mouseup', mouseUpListener, true);
  }

  render() {
    const { className, order } = this.props;
    const children = React.Children.toArray(this.props.children);
    const orderedChildren = order.map(childIndex => children[childIndex]);
    const TagName = this.props.tagName;
    const xy = this._getXY();

    if (this.state.phase === 'RESTING') {
      return (
        <TagName className={className} onMouseDown={this._onMouseDown}>
          {orderedChildren}
        </TagName>
      );
    }

    const {
      phase,
      manipulatingIndex,
      destinationIndex,
      adjustPrecedingBy,
      adjustSucceedingBy,
    } = this.state;
    const adjustedClassName =
      phase === 'MANIPULATING' ? className + ' beingReordered' : className;
    return (
      <TagName className={adjustedClassName}>
        {orderedChildren.map((child, childIndex) => {
          const style = {
            transition: '200ms ease-in-out transform',
            willChange: 'transform',
            position: 'relative',
            zIndex: '1',
            transform: '',
          };
          if (childIndex === manipulatingIndex) {
            style.zIndex = '2';
            if (phase === 'MANIPULATING') {
              delete style.transition;
              style.transform = `${xy.translateXY}(${
                this.state.manipulationDelta
              }px)`;
            } else {
              style.transform = `${xy.translateXY}(${
                this.state.finalOffset
              }px)`;
            }
          } else if (
            childIndex < manipulatingIndex &&
            childIndex >= destinationIndex
          ) {
            style.transform = `${xy.translateXY}(${adjustPrecedingBy}px)`;
          } else if (
            childIndex > manipulatingIndex &&
            childIndex <= destinationIndex
          ) {
            style.transform = `${xy.translateXY}(${adjustSucceedingBy}px)`;
          }

          // Note: the child element needs to handle this `style` property.
          return React.cloneElement(child, { style });
        })}
      </TagName>
    );
  }
}

export default Reorderable;
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import IconButton from 'material-ui/IconButton';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import messages from '../../resources/messages';

const localMessages = {
  allTopics: { id: 'menu.items.allTopics', defaultMessage: 'All Topics' },
  createNewTopic: { id: 'menu.items.newTopic', defaultMessage: 'Create a New Topic' },
};

class TopicsDrawer extends React.Component {

  constructor(props) {
    super(props);
    this.state = { open: false };
  }

  handleToggle = () => this.setState({ open: !this.state.open });

  close = () => this.setState({ open: false });

  render() {
    const { user, handleMenuItemClick } = this.props;
    const { formatMessage } = this.props.intl;
    // gotta show login or logout correctly based on the user state
    let loginLogoutMenuItem = null;
    if (user.isLoggedIn) {
      loginLogoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/logout'); }}>
          <FormattedMessage {...messages.userLogout} />
        </MenuItem>
      );
    } else {
      loginLogoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/login'); }}>
          <FormattedMessage {...messages.userLogin} />
        </MenuItem>
      );
    }
    // only show app actions if they are logged in
    let appMenuItems = null;
    if (user.isLoggedIn) {
      appMenuItems = (

          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/home'); }}>
            <FormattedMessage {...localMessages.allTopics} />
          </MenuItem>
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/topics/create'); }}>
            <FormattedMessage {...localMessages.createNewTopic} />
          </MenuItem>

      );
    }
    // bring it all together in a controlled drawer
    return (

        <IconButton
          iconClassName="material-icons"
          tooltip={formatMessage(messages.menuOpenTooltip)}
          onTouchTap={this.handleToggle}
        >
          menu
        </IconButton>
        <Drawer
          width={300}
          openSecondary
          docked={false}
          open={this.state.open}
          onRequestChange={(open) => this.setState({ open })}
        >
          <AppBar title={formatMessage(messages.menuTitle)} showMenuIconButton={false} />
          {appMenuItems}
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/about'); }}>
            <FormattedMessage {...messages.menuAbout} />
          </MenuItem>
          <Divider />
          {loginLogoutMenuItem}
        </Drawer>

    );
  }
}

TopicsDrawer.propTypes = {
  // state
  user: React.PropTypes.object.isRequired,
  // from dispatch
  handleMenuItemClick: React.PropTypes.func.isRequired,
  // from context
  intl: React.PropTypes.object.isRequired,
};

const mapStateToProps = (state) => ({
  user: state.user,
});

const mapDispatchToProps = (dispatch) => ({
  handleMenuItemClick: (path) => {
    dispatch(push(path));
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      TopicsDrawer
    )
  );
<commit_msg>change home menu link text (#51)<commit_after>import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import IconButton from 'material-ui/IconButton';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import messages from '../../resources/messages';

const localMessages = {
  home: { id: 'menu.items.home', defaultMessage: 'Explore Topics' },
  createNewTopic: { id: 'menu.items.newTopic', defaultMessage: 'Create a New Topic' },
};

class TopicsDrawer extends React.Component {

  constructor(props) {
    super(props);
    this.state = { open: false };
  }

  handleToggle = () => this.setState({ open: !this.state.open });

  close = () => this.setState({ open: false });

  render() {
    const { user, handleMenuItemClick } = this.props;
    const { formatMessage } = this.props.intl;
    // gotta show login or logout correctly based on the user state
    let loginLogoutMenuItem = null;
    if (user.isLoggedIn) {
      loginLogoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/logout'); }}>
          <FormattedMessage {...messages.userLogout} />
        </MenuItem>
      );
    } else {
      loginLogoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/login'); }}>
          <FormattedMessage {...messages.userLogin} />
        </MenuItem>
      );
    }
    // only show app actions if they are logged in
    let appMenuItems = null;
    if (user.isLoggedIn) {
      appMenuItems = (

          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/home'); }}>
            <FormattedMessage {...localMessages.home} />
          </MenuItem>
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/topics/create'); }}>
            <FormattedMessage {...localMessages.createNewTopic} />
          </MenuItem>

      );
    }
    // bring it all together in a controlled drawer
    return (

        <IconButton
          iconClassName="material-icons"
          tooltip={formatMessage(messages.menuOpenTooltip)}
          onTouchTap={this.handleToggle}
        >
          menu
        </IconButton>
        <Drawer
          width={300}
          openSecondary
          docked={false}
          open={this.state.open}
          onRequestChange={(open) => this.setState({ open })}
        >
          <AppBar title={formatMessage(messages.menuTitle)} showMenuIconButton={false} />
          {appMenuItems}
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/about'); }}>
            <FormattedMessage {...messages.menuAbout} />
          </MenuItem>
          <Divider />
          {loginLogoutMenuItem}
        </Drawer>

    );
  }
}

TopicsDrawer.propTypes = {
  // state
  user: React.PropTypes.object.isRequired,
  // from dispatch
  handleMenuItemClick: React.PropTypes.func.isRequired,
  // from context
  intl: React.PropTypes.object.isRequired,
};

const mapStateToProps = (state) => ({
  user: state.user,
});

const mapDispatchToProps = (dispatch) => ({
  handleMenuItemClick: (path) => {
    dispatch(push(path));
  },
});

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


<commit_before>'use strict';
let Promise = require('promise');
const cassandra = require('cassandra-driver');
const cassandraTableStorageManager = require('../storageClients/CassandraTableStorageManager');
const distance = cassandra.types.distance;

/** Cassandra Client Options
 * https://docs.datastax.com/en/developer/nodejs-driver/3.2/api/type.ClientOptions/
 *
 * pooling:
 * Used to preallocate connections to cassandra rather than wait for an open connection to cassandra.
 *
 * CORE_CONNECTIONS_PER_HOST:
 * TODO: 10 is a placeholder for now. Need to find a number that will not drain Cassandra.
 */
const CORE_CONNECTIONS_PER_HOST = 10;
const options = {
  contactPoints: [process.env.CASSANDRA_CONTACT_POINTS],
  keyspace: process.env.CASSANDRA_KEYSPACE,
  pooling: {
    coreConnectionsPerHost: {
      [distance.local] : CORE_CONNECTIONS_PER_HOST,
      [distance.remote] : CORE_CONNECTIONS_PER_HOST
    }
  }
};

/** Code should share the same Client instance across the application i.e.
 * the client should be a singleton.
 * http://docs.datastax.com/en/developer/nodejs-driver/3.2/coding-rules/
 *
 * cassandra.Client will fail if options value is not set.
 *
 * TODO: Look into client.shutdown().
 * Does this close all the connections in the connection pool?
 * If one of the connections has an exception, I don't
 * want shutdown to close all the connections in the pool.
 */
const client = new cassandra.Client(options);

module.exports = {

  /** Execute a batch of mutations
   * @param Array<{query, params}> mutations - cassandra prepared statements
   * @return {Promise}
   */
  executeBatchMutations: (mutations) => {
    return new Promise((resolve, reject) => {
      if(!client) reject('Cassandra client is null');
      cassandraTableStorageManager.batchMutations(client, mutations, function(err) {
        if(err) {
          const errMsg = `[${err}] occured while performing a batch of mutations`;
          console.error(errMsg);
          reject(errMsg);
        } else {
          resolve();
        }
      });
    });
  }

};<commit_msg>Edit comment about cassandra client and when it would fail<commit_after>'use strict';
let Promise = require('promise');
const cassandra = require('cassandra-driver');
const cassandraTableStorageManager = require('../storageClients/CassandraTableStorageManager');
const distance = cassandra.types.distance;

/** Cassandra Client Options
 * https://docs.datastax.com/en/developer/nodejs-driver/3.2/api/type.ClientOptions/
 *
 * pooling:
 * Used to preallocate connections to cassandra rather than wait for an open connection to cassandra.
 *
 * CORE_CONNECTIONS_PER_HOST:
 * TODO: 10 is a placeholder for now. Need to find a number that will not drain Cassandra.
 */
const CORE_CONNECTIONS_PER_HOST = 10;
const options = {
  contactPoints: [process.env.CASSANDRA_CONTACT_POINTS],
  keyspace: process.env.CASSANDRA_KEYSPACE,
  pooling: {
    coreConnectionsPerHost: {
      [distance.local] : CORE_CONNECTIONS_PER_HOST,
      [distance.remote] : CORE_CONNECTIONS_PER_HOST
    }
  }
};

/** Code should share the same Client instance across the application i.e.
 * the client should be a singleton.
 * http://docs.datastax.com/en/developer/nodejs-driver/3.2/coding-rules/
 *
 * cassandra.Client will fail if its client options property, contactPoints, is not set.
 *
 * TODO: Look into client.shutdown().
 * Does this close all the connections in the connection pool?
 * If one of the connections has an exception, I don't
 * want shutdown to close all the connections in the pool.
 */
const client = new cassandra.Client(options);

module.exports = {

  /** Execute a batch of mutations
   * @param Array<{query, params}> mutations - cassandra prepared statements
   * @return {Promise}
   */
  executeBatchMutations: (mutations) => {
    return new Promise((resolve, reject) => {
      if(!client) reject('Cassandra client is null');
      cassandraTableStorageManager.batchMutations(client, mutations, function(err) {
        if(err) {
          const errMsg = `[${err}] occured while performing a batch of mutations`;
          console.error(errMsg);
          reject(errMsg);
        } else {
          resolve();
        }
      });
    });
  }

};<|endoftext|>


<commit_before>/**
 * @ngdoc controller
 * @name Umbraco.Editors.Content.EditController
 * @function
 *
 * @description
 * The controller for the content editor
 */
function ContentEditController($scope, $rootScope, $routeParams, contentResource) {

    var infiniteMode = $scope.model && $scope.model.infiniteMode;

    function scaffoldEmpty() {
        return contentResource.getScaffold($routeParams.id, $routeParams.doctype);
    }
    function scaffoldBlueprint() {
        return contentResource.getBlueprintScaffold($routeParams.id, $routeParams.blueprintId);
    }

    $scope.contentId = infiniteMode ? $scope.model.id : $routeParams.id;
    $scope.saveMethod = contentResource.save;
    $scope.getMethod = contentResource.getById;
    $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty;
    $scope.page = $routeParams.page;
    $scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create;
    //load the default culture selected in the main tree if any
    $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;

    //Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered.
    //This is so we can listen to changes on the cculture parameter since that will not cause a route change
    // and then we can pass in the updated culture to the editor
    $scope.$on('$routeUpdate', function (event, next) {
        $scope.culture = next.params.cculture;
    });
}

angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);
<commit_msg>Fallback to main language<commit_after>/**
 * @ngdoc controller
 * @name Umbraco.Editors.Content.EditController
 * @function
 *
 * @description
 * The controller for the content editor
 */
function ContentEditController($scope, $rootScope, $routeParams, contentResource) {

    var infiniteMode = $scope.model && $scope.model.infiniteMode;

    function scaffoldEmpty() {
        return contentResource.getScaffold($routeParams.id, $routeParams.doctype);
    }
    function scaffoldBlueprint() {
        return contentResource.getBlueprintScaffold($routeParams.id, $routeParams.blueprintId);
    }

    $scope.contentId = infiniteMode ? $scope.model.id : $routeParams.id;
    $scope.saveMethod = contentResource.save;
    $scope.getMethod = contentResource.getById;
    $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty;
    $scope.page = $routeParams.page;
    $scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create;
    //load the default culture selected in the main tree if any
    $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;

    //Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered.
    //This is so we can listen to changes on the cculture parameter since that will not cause a route change
    // and then we can pass in the updated culture to the editor
    $scope.$on('$routeUpdate', function (event, next) {
        $scope.culture = next.params.cculture ? next.params.cculture : $routeParams.mculture;
    });
}

angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);
<|endoftext|>


<commit_before>/**
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.

This method applied internally in $().editable(). You should subscribe on it's events (save / cancel) to get profit of it.

Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.

Applied as jQuery method.

@class editableContainer
@uses editableform
**/
(function ($) {

    var Popup = function (element, options) {
        this.init(element, options);
    };

    var Inline = function (element, options) {
        this.init(element, options);
    };

    //methods
    Popup.prototype = {
        containerName: null, //tbd in child class
        innerCss: null, //tbd in child class
        init: function(element, options) {
            this.$element = $(element);
            //since 1.4.1 container do not use data-* directly as they already merged into options.
            this.options = $.extend({}, $.fn.editableContainer.defaults, options);
            this.splitOptions();

            //set scope of form callbacks to element
            this.formOptions.scope = this.$element[0];

            this.initContainer();

            //bind 'destroyed' listener to destroy container when element is removed from dom
            this.$element.on('destroyed', $.proxy(function(){
                this.destroy();
            }, this));

            //attach document handler to close containers on click / escape
            if(!$(document).data('editable-handlers-attached')) {
                //close all on escape
                $(document).on('keyup.editable', function (e) {
                    if (e.which === 27) {
                        $('.editable-open').editableContainer('hide');
                        //todo: return focus on element
                    }
                });

                //close containers when click outside
                $(document).on('click.editable', function(e) {
                    var $target = $(e.target), i,
                        exclude_classes = ['.editable-container',
                                           '.ui-datepicker-header',
                                           '.modal-backdrop',
                                           '.bootstrap-wysihtml5-insert-image-modal',
                                           '.bootstrap-wysihtml5-insert-link-modal'];

                    //if click inside one of exclude classes --> no nothing
                    for(i=0; i<exclude_classes.length; i++) {
                         if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
                             return;
                         }
                    }

                    //close all open containers (except one - target)
                    Popup.prototype.closeOthers(e.target);
                });

                $(document).data('editable-handlers-attached', true);
            }
        },

        //split options on containerOptions and formOptions
        splitOptions: function() {
            this.containerOptions = {};
            this.formOptions = {};
            var cDef = $.fn[this.containerName].defaults;
            //keys defined in container defaults go to container, others go to form
            for(var k in this.options) {
              if(k in cDef) {
                 this.containerOptions[k] = this.options[k];
              } else {
                 this.formOptions[k] = this.options[k];
              }
            }
        },

        /*
        Returns jquery object of container
        @method tip()
        */
        tip: function() {
            return this.container() ? this.container().$tip : null;
        },

        /* returns container object */
        container: function() {
            return this.$element.data(this.containerName);
        },

        call: function() {
            this.$element[this.containerName].apply(this.$element, arguments);
        },

        initContainer: function(){
            this.call(this.containerOptions);
        },

        renderForm: function() {
            this.$form
            .editableform(this.formOptions)
            .on({
                save: $.proxy(this.save, this), //click on submit button (value changed)
                nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
                cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
                show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
                rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
                resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
                rendered: $.proxy(function(){
                    /**
                    Fired when container is shown and form is rendered (for select will wait for loading dropdown options)

                    @event shown
                    @param {Object} event event object
                    @example
                    $('#username').on('shown', function() {
                        var editable = $(this).data('editable');
                        editable.input.$input.val('overwriting value of input..');
                    });
                    **/
                    this.$element.triggerHandler('shown');
                }, this)
            })
            .editableform('render');
        },

        /**
        Shows container with form
        @method show()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        /* Note: poshytip owerwrites this method totally! */
        show: function (closeAll) {
            this.$element.addClass('editable-open');
            if(closeAll !== false) {
                //close all open containers (except this)
                this.closeOthers(this.$element[0]);
            }

            //show container itself
            this.innerShow();
            this.tip().addClass('editable-container');

            /*
            Currently, form is re-rendered on every show.
            The main reason is that we dont know, what container will do with content when closed:
            remove(), detach() or just hide().

            Detaching form itself before hide and re-insert before show is good solution,
            but visually it looks ugly, as container changes size before hide.
            */

            //if form already exist - delete previous data
            if(this.$form) {
                //todo: destroy prev data!
                //this.$form.destroy();
            }

            this.$form = $('
');

            //insert form into container body
            if(this.tip().is(this.innerCss)) {
                //for inline container
                this.tip().append(this.$form);
            } else {
                this.tip().find(this.innerCss).append(this.$form);
            }

            //render form
            this.renderForm();
        },

        /**
        Hides container with form
        @method hide()
        @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
        **/
        hide: function(reason) {
            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
                return;
            }

            this.$element.removeClass('editable-open');
            this.innerHide();

            /**
            Fired when container was hidden. It occurs on both save or cancel.

            @event hidden
            @param {object} event event object
            @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
            @example
            $('#username').on('hidden', function(e, reason) {
                if(reason === 'save' || reason === 'cancel') {
                    //auto-open next editable
                    $(this).closest('tr').next().find('.editable').editable('show');
                }
            });
            **/
            this.$element.triggerHandler('hidden', reason);
        },

        /* internal show method. To be overwritten in child classes */
        innerShow: function () {

        },

        /* internal hide method. To be overwritten in child classes */
        innerHide: function () {

        },

        /**
        Toggles container visibility (show / hide)
        @method toggle()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        toggle: function(closeAll) {
            if(this.container() && this.tip() && this.tip().is(':visible')) {
                this.hide();
            } else {
                this.show(closeAll);
            }
        },

        /*
        Updates the position of container when content changed.
        @method setPosition()
        */
        setPosition: function() {
            //tbd in child class
        },

        save: function(e, params) {
            /**
            Fired when new value was submitted. You can use $(this).data('editableContainer') inside handler to access to editableContainer instance

            @event save
            @param {Object} event event object
            @param {Object} params additional params
            @param {mixed} params.newValue submitted value
            @param {Object} params.response ajax response
            @example
            $('#username').on('save', function(e, params) {
                //assuming server response: '{success: true}'
                var pk = $(this).data('editableContainer').options.pk;
                if(params.response && params.response.success) {
                    alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
                } else {
                    alert('error!');
                }
            });
            **/
            this.$element.triggerHandler('save', params);

            //hide must be after trigger, as saving value may require methods od plugin, applied to input
            this.hide('save');
        },

        /**
        Sets new option

        @method option(key, value)
        @param {string} key
        @param {mixed} value
        **/
        option: function(key, value) {
            this.options[key] = value;
            if(key in this.containerOptions) {
                this.containerOptions[key] = value;
                this.setContainerOption(key, value);
            } else {
                this.formOptions[key] = value;
                if(this.$form) {
                    this.$form.editableform('option', key, value);
                }
            }
        },

        setContainerOption: function(key, value) {
            this.call('option', key, value);
        },

        /**
        Destroys the container instance
        @method destroy()
        **/
        destroy: function() {
            this.hide();
            this.innerDestroy();
            this.$element.off('destroyed');
            this.$element.removeData('editableContainer');
        },

        /* to be overwritten in child classes */
        innerDestroy: function() {

        },

        /*
        Closes other containers except one related to passed element.
        Other containers can be cancelled or submitted (depends on onblur option)
        */
        closeOthers: function(element) {
            $('.editable-open').each(function(i, el){
                //do nothing with passed element and it's children
                if(el === element || $(el).find(element).length) {
                    return;
                }

                //otherwise cancel or submit all open containers
                var $el = $(el),
                ec = $el.data('editableContainer');

                if(!ec) {
                    return;
                }

                if(ec.options.onblur === 'cancel') {
                    $el.data('editableContainer').hide('onblur');
                } else if(ec.options.onblur === 'submit') {
                    $el.data('editableContainer').tip().find('form').submit();
                }
            });

        },

        /**
        Activates input of visible container (e.g. set focus)
        @method activate()
        **/
        activate: function() {
            if(this.tip && this.tip().is(':visible') && this.$form) {
               this.$form.data('editableform').input.activate();
            }
        }

    };

    /**
    jQuery method to initialize editableContainer.

    @method $().editableContainer(options)
    @params {Object} options
    @example
    $('#edit').editableContainer({
        type: 'text',
        url: '/post',
        pk: 1,
        value: 'hello'
    });
    **/
    $.fn.editableContainer = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            dataKey = 'editableContainer',
            data = $this.data(dataKey),
            options = typeof option === 'object' && option,
            Constructor = (options.mode === 'inline') ? Inline : Popup;

            if (!data) {
                $this.data(dataKey, (data = new Constructor(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //store constructors
    $.fn.editableContainer.Popup = Popup;
    $.fn.editableContainer.Inline = Inline;

    //defaults
    $.fn.editableContainer.defaults = {
        /**
        Initial value of form input

        @property value
        @type mixed
        @default null
        @private
        **/
        value: null,
        /**
        Placement of container relative to element. Can be top|right|bottom|left. Not used for inline container.

        @property placement
        @type string
        @default 'top'
        **/
        placement: 'top',
        /**
        Whether to hide container on save/cancel.

        @property autohide
        @type boolean
        @default true
        @private
        **/
        autohide: true,
        /**
        Action when user clicks outside the container. Can be cancel|submit|ignore.
        Setting ignore allows to have several containers open.

        @property onblur
        @type string
        @default 'cancel'
        @since 1.1.1
        **/
        onblur: 'cancel',

        /**
        Animation speed (inline mode)
        @property anim
        @type string
        @default false
        **/
        anim: false,

        /**
        Mode of editable, can be `popup` or `inline`

        @property mode
        @type string
        @default 'popup'
        @since 1.4.0
        **/
        mode: 'popup'
    };

    /*
    * workaround to have 'destroyed' event to destroy popover when element is destroyed
    * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
    */
    jQuery.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };

}(window.jQuery));
<commit_msg>comments<commit_after>/**
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.

This method applied internally in $().editable(). You should subscribe on it's events (save / cancel) to get profit of it.

Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.

Applied as jQuery method.

@class editableContainer
@uses editableform
**/
(function ($) {

    var Popup = function (element, options) {
        this.init(element, options);
    };

    var Inline = function (element, options) {
        this.init(element, options);
    };

    //methods
    Popup.prototype = {
        containerName: null, //tbd in child class
        innerCss: null, //tbd in child class
        init: function(element, options) {
            this.$element = $(element);
            //since 1.4.1 container do not use data-* directly as they already merged into options.
            this.options = $.extend({}, $.fn.editableContainer.defaults, options);
            this.splitOptions();

            //set scope of form callbacks to element
            this.formOptions.scope = this.$element[0];

            this.initContainer();

            //bind 'destroyed' listener to destroy container when element is removed from dom
            this.$element.on('destroyed', $.proxy(function(){
                this.destroy();
            }, this));

            //attach document handler to close containers on click / escape
            if(!$(document).data('editable-handlers-attached')) {
                //close all on escape
                $(document).on('keyup.editable', function (e) {
                    if (e.which === 27) {
                        $('.editable-open').editableContainer('hide');
                        //todo: return focus on element
                    }
                });

                //close containers when click outside
                //(mousedown could be better than click, it closes everything also on drag drop)
                $(document).on('click.editable', function(e) {
                    var $target = $(e.target), i,
                        exclude_classes = ['.editable-container',
                                           '.ui-datepicker-header',
                                           '.modal-backdrop',
                                           '.bootstrap-wysihtml5-insert-image-modal',
                                           '.bootstrap-wysihtml5-insert-link-modal'];

                    //if click inside one of exclude classes --> no nothing
                    for(i=0; i<exclude_classes.length; i++) {
                         if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
                             return;
                         }
                    }

                    //close all open containers (except one - target)
                    Popup.prototype.closeOthers(e.target);
                });

                $(document).data('editable-handlers-attached', true);
            }
        },

        //split options on containerOptions and formOptions
        splitOptions: function() {
            this.containerOptions = {};
            this.formOptions = {};
            var cDef = $.fn[this.containerName].defaults;
            //keys defined in container defaults go to container, others go to form
            for(var k in this.options) {
              if(k in cDef) {
                 this.containerOptions[k] = this.options[k];
              } else {
                 this.formOptions[k] = this.options[k];
              }
            }
        },

        /*
        Returns jquery object of container
        @method tip()
        */
        tip: function() {
            return this.container() ? this.container().$tip : null;
        },

        /* returns container object */
        container: function() {
            return this.$element.data(this.containerName);
        },

        call: function() {
            this.$element[this.containerName].apply(this.$element, arguments);
        },

        initContainer: function(){
            this.call(this.containerOptions);
        },

        renderForm: function() {
            this.$form
            .editableform(this.formOptions)
            .on({
                save: $.proxy(this.save, this), //click on submit button (value changed)
                nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
                cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
                show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
                rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
                resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
                rendered: $.proxy(function(){
                    /**
                    Fired when container is shown and form is rendered (for select will wait for loading dropdown options)

                    @event shown
                    @param {Object} event event object
                    @example
                    $('#username').on('shown', function() {
                        var editable = $(this).data('editable');
                        editable.input.$input.val('overwriting value of input..');
                    });
                    **/
                    this.$element.triggerHandler('shown');
                }, this)
            })
            .editableform('render');
        },

        /**
        Shows container with form
        @method show()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        /* Note: poshytip owerwrites this method totally! */
        show: function (closeAll) {
            this.$element.addClass('editable-open');
            if(closeAll !== false) {
                //close all open containers (except this)
                this.closeOthers(this.$element[0]);
            }

            //show container itself
            this.innerShow();
            this.tip().addClass('editable-container');

            /*
            Currently, form is re-rendered on every show.
            The main reason is that we dont know, what container will do with content when closed:
            remove(), detach() or just hide().

            Detaching form itself before hide and re-insert before show is good solution,
            but visually it looks ugly, as container changes size before hide.
            */

            //if form already exist - delete previous data
            if(this.$form) {
                //todo: destroy prev data!
                //this.$form.destroy();
            }

            this.$form = $('
');

            //insert form into container body
            if(this.tip().is(this.innerCss)) {
                //for inline container
                this.tip().append(this.$form);
            } else {
                this.tip().find(this.innerCss).append(this.$form);
            }

            //render form
            this.renderForm();
        },

        /**
        Hides container with form
        @method hide()
        @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
        **/
        hide: function(reason) {
            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
                return;
            }

            this.$element.removeClass('editable-open');
            this.innerHide();

            /**
            Fired when container was hidden. It occurs on both save or cancel.

            @event hidden
            @param {object} event event object
            @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
            @example
            $('#username').on('hidden', function(e, reason) {
                if(reason === 'save' || reason === 'cancel') {
                    //auto-open next editable
                    $(this).closest('tr').next().find('.editable').editable('show');
                }
            });
            **/
            this.$element.triggerHandler('hidden', reason);
        },

        /* internal show method. To be overwritten in child classes */
        innerShow: function () {

        },

        /* internal hide method. To be overwritten in child classes */
        innerHide: function () {

        },

        /**
        Toggles container visibility (show / hide)
        @method toggle()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        toggle: function(closeAll) {
            if(this.container() && this.tip() && this.tip().is(':visible')) {
                this.hide();
            } else {
                this.show(closeAll);
            }
        },

        /*
        Updates the position of container when content changed.
        @method setPosition()
        */
        setPosition: function() {
            //tbd in child class
        },

        save: function(e, params) {
            /**
            Fired when new value was submitted. You can use $(this).data('editableContainer') inside handler to access to editableContainer instance

            @event save
            @param {Object} event event object
            @param {Object} params additional params
            @param {mixed} params.newValue submitted value
            @param {Object} params.response ajax response
            @example
            $('#username').on('save', function(e, params) {
                //assuming server response: '{success: true}'
                var pk = $(this).data('editableContainer').options.pk;
                if(params.response && params.response.success) {
                    alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
                } else {
                    alert('error!');
                }
            });
            **/
            this.$element.triggerHandler('save', params);

            //hide must be after trigger, as saving value may require methods od plugin, applied to input
            this.hide('save');
        },

        /**
        Sets new option

        @method option(key, value)
        @param {string} key
        @param {mixed} value
        **/
        option: function(key, value) {
            this.options[key] = value;
            if(key in this.containerOptions) {
                this.containerOptions[key] = value;
                this.setContainerOption(key, value);
            } else {
                this.formOptions[key] = value;
                if(this.$form) {
                    this.$form.editableform('option', key, value);
                }
            }
        },

        setContainerOption: function(key, value) {
            this.call('option', key, value);
        },

        /**
        Destroys the container instance
        @method destroy()
        **/
        destroy: function() {
            this.hide();
            this.innerDestroy();
            this.$element.off('destroyed');
            this.$element.removeData('editableContainer');
        },

        /* to be overwritten in child classes */
        innerDestroy: function() {

        },

        /*
        Closes other containers except one related to passed element.
        Other containers can be cancelled or submitted (depends on onblur option)
        */
        closeOthers: function(element) {
            $('.editable-open').each(function(i, el){
                //do nothing with passed element and it's children
                if(el === element || $(el).find(element).length) {
                    return;
                }

                //otherwise cancel or submit all open containers
                var $el = $(el),
                ec = $el.data('editableContainer');

                if(!ec) {
                    return;
                }

                if(ec.options.onblur === 'cancel') {
                    $el.data('editableContainer').hide('onblur');
                } else if(ec.options.onblur === 'submit') {
                    $el.data('editableContainer').tip().find('form').submit();
                }
            });

        },

        /**
        Activates input of visible container (e.g. set focus)
        @method activate()
        **/
        activate: function() {
            if(this.tip && this.tip().is(':visible') && this.$form) {
               this.$form.data('editableform').input.activate();
            }
        }

    };

    /**
    jQuery method to initialize editableContainer.

    @method $().editableContainer(options)
    @params {Object} options
    @example
    $('#edit').editableContainer({
        type: 'text',
        url: '/post',
        pk: 1,
        value: 'hello'
    });
    **/
    $.fn.editableContainer = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            dataKey = 'editableContainer',
            data = $this.data(dataKey),
            options = typeof option === 'object' && option,
            Constructor = (options.mode === 'inline') ? Inline : Popup;

            if (!data) {
                $this.data(dataKey, (data = new Constructor(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //store constructors
    $.fn.editableContainer.Popup = Popup;
    $.fn.editableContainer.Inline = Inline;

    //defaults
    $.fn.editableContainer.defaults = {
        /**
        Initial value of form input

        @property value
        @type mixed
        @default null
        @private
        **/
        value: null,
        /**
        Placement of container relative to element. Can be top|right|bottom|left. Not used for inline container.

        @property placement
        @type string
        @default 'top'
        **/
        placement: 'top',
        /**
        Whether to hide container on save/cancel.

        @property autohide
        @type boolean
        @default true
        @private
        **/
        autohide: true,
        /**
        Action when user clicks outside the container. Can be cancel|submit|ignore.
        Setting ignore allows to have several containers open.

        @property onblur
        @type string
        @default 'cancel'
        @since 1.1.1
        **/
        onblur: 'cancel',

        /**
        Animation speed (inline mode)
        @property anim
        @type string
        @default false
        **/
        anim: false,

        /**
        Mode of editable, can be `popup` or `inline`

        @property mode
        @type string
        @default 'popup'
        @since 1.4.0
        **/
        mode: 'popup'
    };

    /*
    * workaround to have 'destroyed' event to destroy popover when element is destroyed
    * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
    */
    jQuery.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };

}(window.jQuery));
<|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'
const { object } = React.PropTypes

@connect((state, { params }) => ({person: state.people[params.id]}))
export default class AboutPerson extends React.Component {
  static propTypes = {
    person: object
  }

  render () {
    let { person } = this.props
    return

What I am doing

{person.bio}

What I would like to do

{person.intention}

Skills

          {person.skills.map(skill =>

{skill}
)}

Affiliations

          {person.organizations.map(org =>

{org}
)}

  }
}
<commit_msg>make AboutPerson a stateless component<commit_after>import React from 'react'
import { connect } from 'react-redux'
import { markdown } from '../../util/text'

const AboutPerson = connect(
  (state, { params }) => ({person: state.people[params.id]})
)(({ person }) => {
  return

About me

{person.bio}

What I am doing

{person.work}

What I would like to do

{person.intention}

Skills

        {person.skills.map(skill =>

{skill}
)}

Affiliations

        {person.organizations.map(org =>

{org}
)}

    {person.extra_info &&

Other information

}

})

export default AboutPerson
<|endoftext|>


<commit_before>import Utils from "../Utils.js";

/**
 *
 */
class SetOps {
    /**
     *
     */
    constructor() {
        this._sampleDelimiter = "\\n\\n";
        this._operation = ["Union", "Intersection", "Set Difference", "Symmetric Difference", "Cartesian Product", "Power Set"];
        this._itemDelimiter = ",";
    }

    /**
     *
     */
    get OPERATION() {
        return this._operation;
    }

    /**
     *
     */
    get SAMPLE_DELIMITER() {
        return this._sampleDelimiter;
    }

    /**
     *
     */
    get ITEM_DELIMITER() {
        return this._itemDelimiter;
    }

    /**
     *
     * @param {*} input
     * @param {*} args
     */
    runSetOperation(input, args) {
        const [sampleDelim, itemDelimiter, operation] = args;
        const sets = input.split(sampleDelim);

        if (!sets || (sets.length !== 2 && operation !== "Power Set") || (sets.length !== 1 && operation === "Power Set")) {
            return "Incorrect number of sets, perhaps you need to modify the sample delimiter or add more samples?";
        }

        if (this._operation.indexOf(operation) === -1) {
            return "Invalid 'Operation' option.";
        }

        let result = {
            "Union": this.runUnion,
            "Intersection": this.runIntersect,
            "Set Difference": this.runSetDifference,
            "Symmetric Difference": this.runSymmetricDifference,
            "Cartesian Product": this.runCartesianProduct,
            "Power Set": this.runPowerSet(itemDelimiter),
        }[operation]
            .apply(null, sets.map(s => s.split(itemDelimiter)));

            // Formatting issues due to the nested characteristics of power set.
        if (operation === "Power Set") {
            result = result.map(i => `${i}\n`).join("");
        } else {
            result = result.join(itemDelimiter);
        }

        return Utils.escapeHtml(result);
    }

    /**
     *
     * @param {*} a
     * @param {*} a
     */
    runUnion(a, b) {

        const result = {};

        /**
         *
         * @param {*} r
         */
        const addUnique = (hash) => (item) => {
            if (!hash[item]) {
                hash[item] = true;
            }
        };

        a.map(addUnique(result));
        b.map(addUnique(result));

        return Object.keys(result);
    }

    /**
     *
     * @param {*} a
     * @param {*} b
     */
    runIntersect(a, b) {
        return a.filter((item) => {
            return b.indexOf(item) > -1;
        });
    }

    /**
     *
     * @param {*} a
     * @param {*} b
     */
    runSetDifference(a, b) {
        return a.filter((item) => {
            return b.indexOf(item) === -1;
        });
    }

    /**
     *
     * @param {*} a
     * @param {*} b
     */
    runSymmetricDifference(a, b) {
        /**
         *
         * @param {*} refArray
         */
        const getDifference = (refArray) => (item) => {
            return refArray.indexOf(item) === -1;
        };

        return a
            .filter(getDifference(b))
            .concat(b.filter(getDifference(a)));
    }

    /**
     *
     * @param {*} a
     * @param {*} b
     */
    runCartesianProduct(a, b) {
        return Array(Math.max(a.length, b.length))
            .fill(null)
            .map((item, index) => `(${a[index] || undefined},${b[index] || undefined})`);
    }

    /**
     *
     * @param {*} a
     */
    runPowerSet(delimiter) {
        return function(a) {

            // empty array items getting picked up
            a = a.filter(i => i.length);
            if (!a.length) {
                return [];
            }

            /**
             *
             * @param {*} dec
             */
            const toBinary = (dec) => (dec >>> 0).toString(2);
            const result = new Set();
            const maxBinaryValue = parseInt(Number(a.map(i => "1").reduce((p, c) => p + c)), 2);
            const binaries = [...Array(maxBinaryValue + 1).keys()]
                .map(toBinary)
                .map(i => i.padStart(toBinary(maxBinaryValue).length, "0"));

            binaries.forEach((binary) => {
                const split = binary.split("");
                result.add(a.filter((item, index) => split[index] === "1"));
            });

            // map for formatting & put in length order.
            return [...result].map(r => r.join(delimiter)).sort((a, b) => a.length - b.length);
        };
    }
}

export default new SetOps();<commit_msg>add comments<commit_after>import Utils from "../Utils.js";

/**
 * Set operations.
 *
 * @author d98762625 [d98762625@gmail.com]
 * @copyright Crown Copyright 2018
 * @license APache-2.0
 *
 * @namespace
 */
class SetOps {

    /**
     * Set default options for operation
     */
    constructor() {
        this._sampleDelimiter = "\\n\\n";
        this._operation = ["Union", "Intersection", "Set Difference", "Symmetric Difference", "Cartesian Product", "Power Set"];
        this._itemDelimiter = ",";
    }

    /**
     * Get operations array
     * @returns {String[]}
     */
    get OPERATION() {
        return this._operation;
    }

    /**
     * Get sample delimiter
     * @returns {String}
     */
    get SAMPLE_DELIMITER() {
        return this._sampleDelimiter;
    }

    /**
     * Get item delimiter
     * @returns {String}
     */
    get ITEM_DELIMITER() {
        return this._itemDelimiter;
    }

    /**
     * Run the configured set operation.
     *
     * @param {String} input
     * @param {String[]} args
     * @returns {html}
     */
    runSetOperation(input, args) {
        const [sampleDelim, itemDelimiter, operation] = args;
        const sets = input.split(sampleDelim);

        if (!sets || (sets.length !== 2 && operation !== "Power Set") || (sets.length !== 1 && operation === "Power Set")) {
            return "Incorrect number of sets, perhaps you need to modify the sample delimiter or add more samples?";
        }

        if (this._operation.indexOf(operation) === -1) {
            return "Invalid 'Operation' option.";
        }

        let result = {
            "Union": this.runUnion,
            "Intersection": this.runIntersect,
            "Set Difference": this.runSetDifference,
            "Symmetric Difference": this.runSymmetricDifference,
            "Cartesian Product": this.runCartesianProduct,
            "Power Set": this.runPowerSet(itemDelimiter),
        }[operation]
            .apply(null, sets.map(s => s.split(itemDelimiter)));

            // Formatting issues due to the nested characteristics of power set.
        if (operation === "Power Set") {
            result = result.map(i => `${i}\n`).join("");
        } else {
            result = result.join(itemDelimiter);
        }

        return Utils.escapeHtml(result);
    }

    /**
     * Get the union of the two sets.
     *
     * @param {Object[]} a
     * @param {Object[]} b
     * @returns {Object[]}
     */
    runUnion(a, b) {

        const result = {};

        /**
         * Only add non-existing items
         * @param {Object} hash
         */
        const addUnique = (hash) => (item) => {
            if (!hash[item]) {
                hash[item] = true;
            }
        };

        a.map(addUnique(result));
        b.map(addUnique(result));

        return Object.keys(result);
    }

    /**
     * Get the intersection of the two sets.
     * @param {Object[]} a
     * @param {Object[]} b
     * @returns {Object[]}
     */
    runIntersect(a, b) {
        return a.filter((item) => {
            return b.indexOf(item) > -1;
        });
    }

    /**
     * Get elements in set a that are not in set b
     * @param {Object[]} a
     * @param {Object[]} b
     * @returns {Object[]}
     */
    runSetDifference(a, b) {
        return a.filter((item) => {
            return b.indexOf(item) === -1;
        });
    }

    /**
     * Get elements of each set that aren't in the other set.
     * @param {Object[]} a
     * @param {Object[]} b
     * @return {Object}
     */
    runSymmetricDifference(a, b) {

        /**
         * One-way difference function, formatted for mapping.
         * @param {Object[]} refArray
         */
        const getDifference = (refArray) => (item) => {
            return refArray.indexOf(item) === -1;
        };

        return a
            .filter(getDifference(b))
            .concat(b.filter(getDifference(a)));
    }

    /**
     *
     * @param {*} a
     * @param {*} b
     */
    runCartesianProduct(a, b) {
        return Array(Math.max(a.length, b.length))
            .fill(null)
            .map((item, index) => `(${a[index] || undefined},${b[index] || undefined})`);
    }

    /**
     *
     * @param {*} a
     */
    runPowerSet(delimiter) {
        return function(a) {

            // empty array items getting picked up
            a = a.filter(i => i.length);
            if (!a.length) {
                return [];
            }

            /**
             *
             * @param {*} dec
             */
            const toBinary = (dec) => (dec >>> 0).toString(2);
            const result = new Set();
            const maxBinaryValue = parseInt(Number(a.map(i => "1").reduce((p, c) => p + c)), 2);
            const binaries = [...Array(maxBinaryValue + 1).keys()]
                .map(toBinary)
                .map(i => i.padStart(toBinary(maxBinaryValue).length, "0"));

            binaries.forEach((binary) => {
                const split = binary.split("");
                result.add(a.filter((item, index) => split[index] === "1"));
            });

            // map for formatting & put in length order.
            return [...result].map(r => r.join(delimiter)).sort((a, b) => a.length - b.length);
        };
    }
}

export default new SetOps();<|endoftext|>


<commit_before><commit_msg>Updated ID so DIP dialog will come up.<commit_after><|endoftext|>