<commit_before>// @flow
import * as React from 'react'
import {partition} from 'lodash-es'
import * as Kb from '../../../../common-adapters'
import * as Styles from '../../../../styles'
import * as RPCChatTypes from '../../../../constants/types/rpc-chat-gen'

type Props = {|
  result: RPCChatTypes.UICoinFlipResult,
|}
const CoinFlipResult = (props: Props) => {
  switch (props.result.typ) {
    case RPCChatTypes.chatUiUICoinFlipResultTyp.shuffle:
      return <CoinFlipResultShuffle shuffle={props.result.shuffle} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.deck:
      return <CoinFlipResultDeck deck={props.result.deck} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.hands:
      return <CoinFlipResultHands hands={props.result.hands} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.coin:
      return <CoinFlipResultCoin coin={props.result.coin} />
    default:
      return <CoinFlipResultNumber number={props.result.number} />
  }
}

// The order here is important.
const cards = [
  {suit: 'spades', value: '2'},
  {suit: 'spades', value: '3'},
  {suit: 'spades', value: '4'},
  {suit: 'spades', value: '5'},
  {suit: 'spades', value: '6'},
  {suit: 'spades', value: '7'},
  {suit: 'spades', value: '8'},
  {suit: 'spades', value: '9'},
  {suit: 'spades', value: '10'},
  {suit: 'spades', value: 'J'},
  {suit: 'spades', value: 'Q'},
  {suit: 'spades', value: 'K'},
  {suit: 'spades', value: 'A'},
  {suit: 'clubs', value: '2'},
  {suit: 'clubs', value: '3'},
  {suit: 'clubs', value: '4'},
  {suit: 'clubs', value: '5'},
  {suit: 'clubs', value: '6'},
  {suit: 'clubs', value: '7'},
  {suit: 'clubs', value: '8'},
  {suit: 'clubs', value: '9'},
  {suit: 'clubs', value: '10'},
  {suit: 'clubs', value: 'J'},
  {suit: 'clubs', value: 'Q'},
  {suit: 'clubs', value: 'K'},
  {suit: 'clubs', value: 'A'},
  {suit: 'diamonds', value: '2'},
  {suit: 'diamonds', value: '3'},
  {suit: 'diamonds', value: '4'},
  {suit: 'diamonds', value: '5'},
  {suit: 'diamonds', value: '6'},
  {suit: 'diamonds', value: '7'},
  {suit: 'diamonds', value: '8'},
  {suit: 'diamonds', value: '9'},
  {suit: 'diamonds', value: '10'},
  {suit: 'diamonds', value: 'J'},
  {suit: 'diamonds', value: 'Q'},
  {suit: 'diamonds', value: 'K'},
  {suit: 'diamonds', value: 'A'},
  {suit: 'hearts', value: '2'},
  {suit: 'hearts', value: '3'},
  {suit: 'hearts', value: '4'},
  {suit: 'hearts', value: '5'},
  {suit: 'hearts', value: '6'},
  {suit: 'hearts', value: '7'},
  {suit: 'hearts', value: '8'},
  {suit: 'hearts', value: '9'},
  {suit: 'hearts', value: '10'},
  {suit: 'hearts', value: 'J'},
  {suit: 'hearts', value: 'Q'},
  {suit: 'hearts', value: 'K'},
  {suit: 'hearts', value: 'A'},
]

const suits = {
  clubs: {
    color: Styles.globalColors.black,
    icon: 'iconfont-club',
  },
  diamonds: {
    color: Styles.globalColors.red,
    icon: 'iconfont-diamond',
  },
  hearts: {
    color: Styles.globalColors.red,
    icon: 'iconfont-heart',
  },
  spades: {
    color: Styles.globalColors.black,
    icon: 'iconfont-spade',
  },
}

type CardType = {|
  card: number,
  hand?: boolean,
|}
const Card = (props: CardType) => (
  <Kb.Box2 direction="vertical" centerChildren={true} style={styles.card}>
    <Kb.Box2 direction="horizontal">
      <Kb.Text
        selectable={true}
        type={Styles.isMobile ? 'BodySmall' : 'Body'}
        style={{color: suits[cards[props.card].suit].color}}
      >
        {cards[props.card].value}
      </Kb.Text>
    </Kb.Box2>
    <Kb.Box2 direction="horizontal">
      <Kb.Icon
        fontSize={Styles.isMobile ? 10 : 12}
        type={suits[cards[props.card].suit].icon}
        color={suits[cards[props.card].suit].color}
        style={styles.cardSuit}
      />
    </Kb.Box2>
  </Kb.Box2>
)

type DeckType = {|
  deck: ?Array<number>,
  hand?: boolean,
|}
const CoinFlipResultDeck = (props: DeckType) => (
  <Kb.Box2
    direction="horizontal"
    fullWidth={true}
    style={Styles.collapseStyles([styles.cards, !props.hand && styles.noMarginTop])}
  >
    {props.deck && props.deck.map(card => <Card key={card} card={card} hand={props.hand} />)}
  </Kb.Box2>
)

type CoinType = {|
  coin: ?boolean,
|}
const CoinFlipResultCoin = (props: CoinType) => (
  <Kb.Box2 direction="horizontal" fullWidth={true} gap="tiny" style={styles.commonContainer}>
    <Kb.Box2 direction="vertical" style={styles.coin} centerChildren={true}>
      <Kb.Icon type={props.coin ? 'icon-coin-heads-48-48' : 'icon-coin-tails-48-48'} />
    </Kb.Box2>
    <Kb.Box2 direction="vertical" centerChildren={true}>
      <Kb.Text selectable={true} type="Header">
        {props.coin ? 'Heads!' : 'Tails!'}
      </Kb.Text>
    </Kb.Box2>
  </Kb.Box2>
)

type HandType = {|
  hands: ?Array<RPCChatTypes.UICoinFlipHand>,
|}
const CoinFlipResultHands = (props: HandType) => {
  if (!props.hands) return null
  const [handsWithCards, handsWithoutCards] = partition(props.hands, hand => hand.hand)
  return (
    <Kb.Box2 direction="vertical" fullWidth={true}>
      <Kb.Box2 direction="horizontal" fullWidth={true}>
        <Kb.Box2 direction="vertical" fullHeight={true} style={styles.handTarget}>
          {handsWithCards.map(hand => (
            <Kb.Box2 key={hand.target} alignSelf="flex-start" alignItems="stretch" direction="vertical">
              <Kb.Text selectable={true} type="BodyBig">
                {hand.target}
              </Kb.Text>
            </Kb.Box2>
          ))}
        </Kb.Box2>
        <Kb.Box2 direction="vertical" style={styles.handContainer}>
          {handsWithCards.map(hand => (
            <Kb.Box2
              key={hand.target}
              direction="vertical"
              alignSelf="flex-start"
              style={styles.commonContainer}
            >
              <CoinFlipResultDeck deck={hand.hand} hand={true} />
            </Kb.Box2>
          ))}
        </Kb.Box2>
      </Kb.Box2>
      {handsWithoutCards.length > 0 && (
        <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.commonContainer}>
          <Kb.Text type="BodySmallSemibold">
            Not enough cards for:{' '}
            <Kb.Text type="BodySmall">{handsWithoutCards.map(hand => hand.target).join(', ')}</Kb.Text>
          </Kb.Text>
        </Kb.Box2>
      )}
    </Kb.Box2>
  )
}

type NumberType = {|
  number: ?string,
|}
const CoinFlipResultNumber = (props: NumberType) => (
  <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.commonContainer}>
    <Kb.Text selectable={true} type="Header" style={styles.break}>
      {props.number}
    </Kb.Text>
  </Kb.Box2>
)

type ShuffleType = {|
  shuffle: ?Array<string>,
|}
const CoinFlipResultShuffle = (props: ShuffleType) => (
  <Kb.Box2 direction="vertical" alignSelf="flex-start" gap="xtiny">
    {props.shuffle &&
      props.shuffle.slice(0, 5).map((item, i) => <CoinFlipResultShuffleItem key={i} item={item} index={i} />)}
    {props.shuffle && props.shuffle.length > 5 && (
      <Kb.Box2 direction="horizontal" style={styles.listFullContainer}>
        <Kb.Text selectable={true} type="BodySmallSemibold" style={styles.listFull}>
          Full shuffle:{' '}
          <Kb.Text selectable={true} type="BodySmall" style={styles.listFull}>
            {props.shuffle && props.shuffle.join(', ')}
          </Kb.Text>
        </Kb.Text>
      </Kb.Box2>
    )}
  </Kb.Box2>
)

const CoinFlipResultShuffleItem = props => (
  <Kb.Box2 direction="horizontal" alignSelf="flex-start" centerChildren={true} style={styles.commonContainer}>
    <Kb.Box2 direction="vertical" centerChildren={true} alignItems="center" style={styles.listOrderContainer}>
      <Kb.Text
        selectable={true}
        center={true}
        type={props.index === 0 ? 'BodyBig' : 'BodyTiny'}
        style={Styles.collapseStyles([styles.listOrder, props.index === 0 && styles.listOrderFirst])}
      >
        {props.index + 1}
      </Kb.Text>
    </Kb.Box2>
    <Kb.Markdown allowFontScaling={true} styleOverride={props.index === 0 ? paragraphOverrides : undefined}>
      {props.item}
    </Kb.Markdown>
  </Kb.Box2>
)

const paragraphOverrides = {
  paragraph: {
    // These are Header's styles.
    fontSize: Styles.isMobile ? 20 : 18,
    fontWeight: '700',
  },
}

const styles = Styles.styleSheetCreate({
  break: Styles.platformStyles({
    isElectron: {
      wordBreak: 'break-all',
    },
  }),
  card: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.white,
      borderColor: Styles.globalColors.black_10,
      borderRadius: Styles.borderRadius,
      borderStyle: 'solid',
      borderWidth: 1,
      flexShrink: 0,
      height: 44,
      marginRight: -4,
      marginTop: Styles.globalMargins.tiny,
      width: 28,
    },
    isMobile: {
      height: 36,
      marginRight: -2,
      width: 20,
    },
  }),
  cardSuit: Styles.platformStyles({
    isMobile: {
      position: 'relative',
      top: -1,
    },
  }),
  // compensate for the bottom margin on cards
  cards: Styles.platformStyles({
    common: {
      flexWrap: 'wrap',
    },
    isElectron: {
      marginTop: -Styles.globalMargins.tiny,
    },
    isMobile: {
      marginTop: -Styles.globalMargins.xtiny,
    },
  }),
  coin: {
    height: 48,
    width: 48,
  },
  commonContainer: {
    marginTop: Styles.globalMargins.tiny,
  },
  handContainer: {
    flexShrink: 1,
    paddingRight: Styles.globalMargins.tiny,
  },
  handTarget: {
    height: 'auto',
    justifyContent: 'space-around',
    paddingRight: Styles.globalMargins.tiny,
  },
  listFull: Styles.platformStyles({
    common: {
      color: Styles.globalColors.black,
    },
    isElectron: {
      wordBreak: 'break-word',
    },
  }),
  listFullContainer: {
    marginTop: Styles.globalMargins.tiny,
  },
  listOrder: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.grey,
      borderRadius: 2,
      color: Styles.globalColors.black,
      height: 14,
      width: 14,
    },
    isMobile: {
      height: 16,
      position: 'relative',
      top: -1,
    },
  }),
  listOrderContainer: {
    marginLeft: Styles.globalMargins.xtiny,
    marginRight: Styles.globalMargins.xtiny,
    width: 20,
  },
  listOrderFirst: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.black,
      color: Styles.globalColors.white,
      height: 18,
      width: 18,
    },
    isMobile: {
      height: 20,
      top: -2,
    },
  }),
  noMarginTop: {
    marginTop: 0,
  },
})

export default CoinFlipResult
<commit_msg>Fix shuffle spacing. (#16490)<commit_after>// @flow
import * as React from 'react'
import {partition} from 'lodash-es'
import * as Kb from '../../../../common-adapters'
import * as Styles from '../../../../styles'
import * as RPCChatTypes from '../../../../constants/types/rpc-chat-gen'

type Props = {|
  result: RPCChatTypes.UICoinFlipResult,
|}
const CoinFlipResult = (props: Props) => {
  switch (props.result.typ) {
    case RPCChatTypes.chatUiUICoinFlipResultTyp.shuffle:
      return <CoinFlipResultShuffle shuffle={props.result.shuffle} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.deck:
      return <CoinFlipResultDeck deck={props.result.deck} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.hands:
      return <CoinFlipResultHands hands={props.result.hands} />
    case RPCChatTypes.chatUiUICoinFlipResultTyp.coin:
      return <CoinFlipResultCoin coin={props.result.coin} />
    default:
      return <CoinFlipResultNumber number={props.result.number} />
  }
}

// The order here is important.
const cards = [
  {suit: 'spades', value: '2'},
  {suit: 'spades', value: '3'},
  {suit: 'spades', value: '4'},
  {suit: 'spades', value: '5'},
  {suit: 'spades', value: '6'},
  {suit: 'spades', value: '7'},
  {suit: 'spades', value: '8'},
  {suit: 'spades', value: '9'},
  {suit: 'spades', value: '10'},
  {suit: 'spades', value: 'J'},
  {suit: 'spades', value: 'Q'},
  {suit: 'spades', value: 'K'},
  {suit: 'spades', value: 'A'},
  {suit: 'clubs', value: '2'},
  {suit: 'clubs', value: '3'},
  {suit: 'clubs', value: '4'},
  {suit: 'clubs', value: '5'},
  {suit: 'clubs', value: '6'},
  {suit: 'clubs', value: '7'},
  {suit: 'clubs', value: '8'},
  {suit: 'clubs', value: '9'},
  {suit: 'clubs', value: '10'},
  {suit: 'clubs', value: 'J'},
  {suit: 'clubs', value: 'Q'},
  {suit: 'clubs', value: 'K'},
  {suit: 'clubs', value: 'A'},
  {suit: 'diamonds', value: '2'},
  {suit: 'diamonds', value: '3'},
  {suit: 'diamonds', value: '4'},
  {suit: 'diamonds', value: '5'},
  {suit: 'diamonds', value: '6'},
  {suit: 'diamonds', value: '7'},
  {suit: 'diamonds', value: '8'},
  {suit: 'diamonds', value: '9'},
  {suit: 'diamonds', value: '10'},
  {suit: 'diamonds', value: 'J'},
  {suit: 'diamonds', value: 'Q'},
  {suit: 'diamonds', value: 'K'},
  {suit: 'diamonds', value: 'A'},
  {suit: 'hearts', value: '2'},
  {suit: 'hearts', value: '3'},
  {suit: 'hearts', value: '4'},
  {suit: 'hearts', value: '5'},
  {suit: 'hearts', value: '6'},
  {suit: 'hearts', value: '7'},
  {suit: 'hearts', value: '8'},
  {suit: 'hearts', value: '9'},
  {suit: 'hearts', value: '10'},
  {suit: 'hearts', value: 'J'},
  {suit: 'hearts', value: 'Q'},
  {suit: 'hearts', value: 'K'},
  {suit: 'hearts', value: 'A'},
]

const suits = {
  clubs: {
    color: Styles.globalColors.black,
    icon: 'iconfont-club',
  },
  diamonds: {
    color: Styles.globalColors.red,
    icon: 'iconfont-diamond',
  },
  hearts: {
    color: Styles.globalColors.red,
    icon: 'iconfont-heart',
  },
  spades: {
    color: Styles.globalColors.black,
    icon: 'iconfont-spade',
  },
}

type CardType = {|
  card: number,
  hand?: boolean,
|}
const Card = (props: CardType) => (
  <Kb.Box2 direction="vertical" centerChildren={true} style={styles.card}>
    <Kb.Box2 direction="horizontal">
      <Kb.Text
        selectable={true}
        type={Styles.isMobile ? 'BodySmall' : 'Body'}
        style={{color: suits[cards[props.card].suit].color}}
      >
        {cards[props.card].value}
      </Kb.Text>
    </Kb.Box2>
    <Kb.Box2 direction="horizontal">
      <Kb.Icon
        fontSize={Styles.isMobile ? 10 : 12}
        type={suits[cards[props.card].suit].icon}
        color={suits[cards[props.card].suit].color}
        style={styles.cardSuit}
      />
    </Kb.Box2>
  </Kb.Box2>
)

type DeckType = {|
  deck: ?Array<number>,
  hand?: boolean,
|}
const CoinFlipResultDeck = (props: DeckType) => (
  <Kb.Box2
    direction="horizontal"
    fullWidth={true}
    style={Styles.collapseStyles([styles.cards, !props.hand && styles.noMarginTop])}
  >
    {props.deck && props.deck.map(card => <Card key={card} card={card} hand={props.hand} />)}
  </Kb.Box2>
)

type CoinType = {|
  coin: ?boolean,
|}
const CoinFlipResultCoin = (props: CoinType) => (
  <Kb.Box2 direction="horizontal" fullWidth={true} gap="tiny" style={styles.commonContainer}>
    <Kb.Box2 direction="vertical" style={styles.coin} centerChildren={true}>
      <Kb.Icon type={props.coin ? 'icon-coin-heads-48-48' : 'icon-coin-tails-48-48'} />
    </Kb.Box2>
    <Kb.Box2 direction="vertical" centerChildren={true}>
      <Kb.Text selectable={true} type="Header">
        {props.coin ? 'Heads!' : 'Tails!'}
      </Kb.Text>
    </Kb.Box2>
  </Kb.Box2>
)

type HandType = {|
  hands: ?Array<RPCChatTypes.UICoinFlipHand>,
|}
const CoinFlipResultHands = (props: HandType) => {
  if (!props.hands) return null
  const [handsWithCards, handsWithoutCards] = partition(props.hands, hand => hand.hand)
  return (
    <Kb.Box2 direction="vertical" fullWidth={true}>
      <Kb.Box2 direction="horizontal" fullWidth={true}>
        <Kb.Box2 direction="vertical" fullHeight={true} style={styles.handTarget}>
          {handsWithCards.map(hand => (
            <Kb.Box2 key={hand.target} alignSelf="flex-start" alignItems="stretch" direction="vertical">
              <Kb.Text selectable={true} type="BodyBig">
                {hand.target}
              </Kb.Text>
            </Kb.Box2>
          ))}
        </Kb.Box2>
        <Kb.Box2 direction="vertical" style={styles.handContainer}>
          {handsWithCards.map(hand => (
            <Kb.Box2
              key={hand.target}
              direction="vertical"
              alignSelf="flex-start"
              style={styles.commonContainer}
            >
              <CoinFlipResultDeck deck={hand.hand} hand={true} />
            </Kb.Box2>
          ))}
        </Kb.Box2>
      </Kb.Box2>
      {handsWithoutCards.length > 0 && (
        <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.commonContainer}>
          <Kb.Text type="BodySmallSemibold">
            Not enough cards for:{' '}
            <Kb.Text type="BodySmall">{handsWithoutCards.map(hand => hand.target).join(', ')}</Kb.Text>
          </Kb.Text>
        </Kb.Box2>
      )}
    </Kb.Box2>
  )
}

type NumberType = {|
  number: ?string,
|}
const CoinFlipResultNumber = (props: NumberType) => (
  <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.commonContainer}>
    <Kb.Text selectable={true} type="Header" style={styles.break}>
      {props.number}
    </Kb.Text>
  </Kb.Box2>
)

type ShuffleType = {|
  shuffle: ?Array<string>,
|}
const CoinFlipResultShuffle = (props: ShuffleType) => (
  <Kb.Box2 direction="vertical" alignSelf="flex-start" gap="xtiny" style={styles.listContainer}>
    {props.shuffle &&
      props.shuffle.slice(0, 5).map((item, i) => <CoinFlipResultShuffleItem key={i} item={item} index={i} />)}
    {props.shuffle && props.shuffle.length > 5 && (
      <Kb.Box2 direction="horizontal" style={styles.listFullContainer}>
        <Kb.Text selectable={true} type="BodySmallSemibold" style={styles.listFull}>
          Full shuffle:{' '}
          <Kb.Text selectable={true} type="BodySmall" style={styles.listFull}>
            {props.shuffle && props.shuffle.join(', ')}
          </Kb.Text>
        </Kb.Text>
      </Kb.Box2>
    )}
  </Kb.Box2>
)

const CoinFlipResultShuffleItem = props => (
  <Kb.Box2 direction="horizontal" alignSelf="flex-start" centerChildren={true}>
    <Kb.Box2 direction="vertical" centerChildren={true} alignItems="center" style={styles.listOrderContainer}>
      <Kb.Text
        selectable={true}
        center={true}
        type={props.index === 0 ? 'BodyBig' : 'BodyTiny'}
        style={Styles.collapseStyles([styles.listOrder, props.index === 0 && styles.listOrderFirst])}
      >
        {props.index + 1}
      </Kb.Text>
    </Kb.Box2>
    <Kb.Markdown allowFontScaling={true} styleOverride={props.index === 0 ? paragraphOverrides : undefined}>
      {props.item}
    </Kb.Markdown>
  </Kb.Box2>
)

const paragraphOverrides = {
  paragraph: {
    // These are Header's styles.
    fontSize: Styles.isMobile ? 20 : 18,
    fontWeight: '700',
  },
}

const styles = Styles.styleSheetCreate({
  break: Styles.platformStyles({
    isElectron: {
      wordBreak: 'break-all',
    },
  }),
  card: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.white,
      borderColor: Styles.globalColors.black_10,
      borderRadius: Styles.borderRadius,
      borderStyle: 'solid',
      borderWidth: 1,
      flexShrink: 0,
      height: 44,
      marginRight: -4,
      marginTop: Styles.globalMargins.tiny,
      width: 28,
    },
    isMobile: {
      height: 36,
      marginRight: -2,
      width: 20,
    },
  }),
  cardSuit: Styles.platformStyles({
    isMobile: {
      position: 'relative',
      top: -1,
    },
  }),
  // compensate for the bottom margin on cards
  cards: Styles.platformStyles({
    common: {
      flexWrap: 'wrap',
    },
    isElectron: {
      marginTop: -Styles.globalMargins.tiny,
    },
    isMobile: {
      marginTop: -Styles.globalMargins.xtiny,
    },
  }),
  coin: {
    height: 48,
    width: 48,
  },
  commonContainer: {
    marginTop: Styles.globalMargins.tiny,
  },
  handContainer: {
    flexShrink: 1,
    paddingRight: Styles.globalMargins.tiny,
  },
  handTarget: {
    height: 'auto',
    justifyContent: 'space-around',
    paddingRight: Styles.globalMargins.tiny,
  },
  listContainer: {
    marginTop: Styles.globalMargins.xsmall,
  },
  listFull: Styles.platformStyles({
    common: {
      color: Styles.globalColors.black,
    },
    isElectron: {
      wordBreak: 'break-word',
    },
  }),
  listFullContainer: {
    marginTop: Styles.globalMargins.tiny,
  },
  listOrder: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.grey,
      borderRadius: 2,
      color: Styles.globalColors.black,
      height: 14,
      width: 14,
    },
    isMobile: {
      height: 16,
      position: 'relative',
      top: -1,
    },
  }),
  listOrderContainer: {
    marginLeft: Styles.globalMargins.xtiny,
    marginRight: Styles.globalMargins.xtiny,
    width: 20,
  },
  listOrderFirst: Styles.platformStyles({
    common: {
      backgroundColor: Styles.globalColors.black,
      color: Styles.globalColors.white,
      height: 18,
      width: 18,
    },
    isMobile: {
      height: 20,
      top: -2,
    },
  }),
  noMarginTop: {
    marginTop: 0,
  },
})

export default CoinFlipResult
<|endoftext|>


<commit_before>import debounce                          from 'lodash.debounce';
import {
    action,
    observable,
    reaction }                           from 'mobx';
import { processPurchase }               from './Actions/purchase';
import * as Symbol                       from './Actions/symbol';
import {
    allowed_query_string_variables,
    non_proposal_query_string_variable } from './Constants/query_string';
import validation_rules                  from './Constants/validation_rules';
import { setChartBarrier }               from './Helpers/chart';
import ContractType                      from './Helpers/contract_type';
import { convertDurationLimit }          from './Helpers/duration';
import { processTradeParams }            from './Helpers/process';
import {
    createProposalRequests,
    getProposalInfo,
    getProposalParametersName }          from './Helpers/proposal';
import { pickDefaultSymbol }             from './Helpers/symbol';
import BaseStore                         from '../../base_store';
import { WS }                            from '../../../Services';
import URLHelper                         from '../../../Utils/URL/url_helper';
import Client                            from '../../../../_common/base/client_base';
import { cloneObject, isEmptyObject }    from '../../../../_common/utility';

export default class TradeStore extends BaseStore {
    // Control values
    @observable is_trade_component_mounted = false;
    @observable is_purchase_enabled        = false;
    @observable is_trade_enabled           = false;

    // Underlying
    @observable symbol;

    // Contract Type
    @observable contract_expiry_type = '';
    @observable contract_start_type  = '';
    @observable contract_type        = '';
    @observable contract_types_list  = {};
    @observable form_components      = [];
    @observable trade_types          = {};

    // Amount
    @observable amount          = 10;
    @observable basis           = '';
    @observable basis_list      = [];
    @observable currencies_list = {};
    @observable currency        = Client.get('currency');

    // Duration
    @observable duration            = 5;
    @observable duration_unit       = '';
    @observable duration_units_list = [];
    @observable duration_min_max    = {};
    @observable expiry_date         = '';
    @observable expiry_time         = '09:40';
    @observable expiry_type         = 'duration';

    // Barrier
    @observable barrier_1     = '';
    @observable barrier_2     = '';
    @observable barrier_count = 0;

    // Start Time
    @observable start_date       = Number(0); // Number(0) refers to 'now'
    @observable start_dates_list = [];
    @observable start_time       = '12:30';
    @observable sessions         = [];

    // Last Digit
    @observable last_digit = 5;

    // Purchase
    @observable proposal_info = {};
    @observable purchase_info = {};

    // Chart
    chart_id = 1;

    debouncedProposal = debounce(this.requestProposal, 500);

    constructor({ root_store }) {
        const session_storage_properties = allowed_query_string_variables;
        const options = {
            root_store,
            session_storage_properties,
            validation_rules,
        };

        URLHelper.pruneQueryString(allowed_query_string_variables);

        super(options);

        Object.defineProperty(
            this,
            'is_query_string_applied',
            {
                enumerable: false,
                value     : false,
                writable  : true,
            }
        );

        if (Client.isLoggedIn) {
            this.processNewValuesAsync({ currency: Client.get('currency') });
        }

        // Adds intercept to change min_max value of duration validation
        reaction(
            ()=> [this.duration_min_max, this.contract_expiry_type, this.duration_unit],
            () => {
                this.changeDurationValidationRules();
            }
        );
    }

    @action.bound
    async init() {
        const query_string_values = this.updateQueryString();
        this.smart_chart = this.root_store.modules.smart_chart;

        if (!this.symbol) {
            const active_symbols = await WS.activeSymbols();
            await this.processNewValuesAsync({
                symbol: pickDefaultSymbol(active_symbols.active_symbols),
                ...query_string_values,
            });
        }

        if (this.symbol) {
            ContractType.buildContractTypesConfig(this.symbol).then(action(() => {
                this.processNewValuesAsync({
                    ...ContractType.getContractValues(this),
                    ...ContractType.getContractCategories(),
                    ...query_string_values,
                });
            }));
        }
    }

    @action.bound
    onChange(e) {
        const { name, value } = e.target;
        if (!(name in this)) {
            throw new Error(`Invalid Argument: ${name}`);
        }

        this.processNewValuesAsync({ [name]: value }, true);
    }

    @action.bound
    onHoverPurchase(is_over, contract_type) {
        this.smart_chart.updateBarrierShade(is_over, contract_type);
    }

    @action.bound
    onPurchase(proposal_id, price) {
        if (proposal_id) {
            processPurchase(proposal_id, price).then(action((response) => {
                WS.forgetAll('proposal');
                this.purchase_info = response;
            }));
        }
    }

    @action.bound
    onClickNewTrade(e) {
        this.requestProposal();
        e.preventDefault();
    }

    /**
     * Updates the store with new values
     * @param  {Object} new_state - new values to update the store with
     * @return {Object} returns the object having only those values that are updated
     */
    @action.bound
    updateStore(new_state) {
        Object.keys(cloneObject(new_state)).forEach((key) => {
            if (key === 'root_store' || ['validation_rules', 'validation_errors'].indexOf(key) > -1) return;
            if (JSON.stringify(this[key]) === JSON.stringify(new_state[key])) {
                delete new_state[key];
            } else {
                if (key === 'symbol') {
                    this.is_purchase_enabled = false;
                    this.is_trade_enabled    = false;
                }

                // Add changes to queryString of the url
                if (
                    allowed_query_string_variables.indexOf(key) !== -1 &&
                    this.is_trade_component_mounted
                ) {
                    URLHelper.setQueryParam({ [key]: new_state[key] });
                }

                this[key] = new_state[key];
            }
        });

        return new_state;
    }

    async processNewValuesAsync(obj_new_values = {}, is_changed_by_user) {
        const new_state = this.updateStore(cloneObject(obj_new_values));

        if (is_changed_by_user || /\b(symbol|contract_types_list)\b/.test(Object.keys(new_state))) {
            if ('symbol' in new_state) {
                await Symbol.onChangeSymbolAsync(new_state.symbol);
            }

            this.updateStore({ // disable purchase button(s), clear contract info
                is_purchase_enabled: false,
                proposal_info      : {},
            });

            if (!this.smart_chart.is_contract_mode) {
                const is_barrier_changed = 'barrier_1' in new_state || 'barrier_2' in new_state;
                if (is_barrier_changed) {
                    this.smart_chart.updateBarriers(this.barrier_1, this.barrier_2);
                } else {
                    this.smart_chart.removeBarriers();
                }
            }

            const snapshot = await processTradeParams(this, new_state);
            const query_string_values = this.updateQueryString();
            snapshot.is_trade_enabled = true;

            this.updateStore({
                ...snapshot,
                ...(this.is_query_string_applied ? {} : query_string_values),
            });

            this.is_query_string_applied = true;

            if (/\bcontract_type\b/.test(Object.keys(new_state))) {
                this.validateAllProperties();
            }

            this.debouncedProposal();
        }
    }

    proposal_requests = {};

    @action.bound
    requestProposal() {
        const requests = createProposalRequests(this);

        if (Object.values(this.validation_errors).some(e => e.length)) {
            this.proposal_info     = {};
            this.purchase_info     = {};
            WS.forgetAll('proposal');
            return;
        }

        if (!isEmptyObject(requests)) {
            const proper_proposal_params_for_query_string = getProposalParametersName(requests);

            URLHelper.pruneQueryString(
                [
                    ...proper_proposal_params_for_query_string,
                    ...non_proposal_query_string_variable,
                ]
            );

            this.proposal_requests = requests;
            this.proposal_info     = {};
            this.purchase_info     = {};

            WS.forgetAll('proposal').then(() => {
                Object.keys(this.proposal_requests).forEach((type) => {
                    WS.subscribeProposal(this.proposal_requests[type], this.onProposalResponse);
                });
            });
        }
    }

    @action.bound
    onProposalResponse(response) {
        const contract_type = response.echo_req.contract_type;
        this.proposal_info = {
            ...this.proposal_info,
            [contract_type]: getProposalInfo(this, response),
        };

        if (!this.smart_chart.is_contract_mode) {
            setChartBarrier(this.smart_chart, response, this.onChartBarrierChange);
        }

        this.is_purchase_enabled = true;
    }

    @action.bound
    onChartBarrierChange(barrier_1, barrier_2) {
        this.processNewValuesAsync({ barrier_1, barrier_2 }, true);
    }

    @action.bound
    updateQueryString() {

        // Update the url's query string by default values of the store
        const query_params = URLHelper.updateQueryString(
            this,
            allowed_query_string_variables,
            this.is_trade_component_mounted
        );

        // update state values from query string
        const config = {};
        [...query_params].forEach(param => config[param[0]] = param[1]);
        return config;
    }

    @action.bound
    changeDurationValidationRules() {
        const index = this.validation_rules.duration.findIndex(item => item[0] === 'number');
        const limits = this.duration_min_max[this.contract_expiry_type] || false;
        const duration_options = {
            min: convertDurationLimit(+limits.min, this.duration_unit),
            max: convertDurationLimit(+limits.max, this.duration_unit),
        };

        if (limits) {
            if (index > -1) {
                this.validation_rules.duration[index][1] = duration_options;
            } else {
                this.validation_rules.duration.push(['number', duration_options]);
            }
            this.validateProperty('duration', this.duration);
        }
    }

    @action.bound
    onMount() {
        this.is_trade_component_mounted = true;
        this.updateQueryString();
    }

    @action.bound
    onUnmount() {
        this.is_trade_component_mounted = false;
    }
}
<commit_msg>add purchase and contract param change events<commit_after>import debounce                          from 'lodash.debounce';
import {
    action,
    observable,
    reaction }                           from 'mobx';
import { processPurchase }               from './Actions/purchase';
import * as Symbol                       from './Actions/symbol';
import {
    allowed_query_string_variables,
    non_proposal_query_string_variable } from './Constants/query_string';
import validation_rules                  from './Constants/validation_rules';
import { setChartBarrier }               from './Helpers/chart';
import ContractType                      from './Helpers/contract_type';
import { convertDurationLimit }          from './Helpers/duration';
import { processTradeParams }            from './Helpers/process';
import {
    createProposalRequests,
    getProposalInfo,
    getProposalParametersName }          from './Helpers/proposal';
import { pickDefaultSymbol }             from './Helpers/symbol';
import BaseStore                         from '../../base_store';
import { WS }                            from '../../../Services';
import URLHelper                         from '../../../Utils/URL/url_helper';
import Client                            from '../../../../_common/base/client_base';
import { cloneObject, isEmptyObject }    from '../../../../_common/utility';

export default class TradeStore extends BaseStore {
    // Control values
    @observable is_trade_component_mounted = false;
    @observable is_purchase_enabled        = false;
    @observable is_trade_enabled           = false;

    // Underlying
    @observable symbol;

    // Contract Type
    @observable contract_expiry_type = '';
    @observable contract_start_type  = '';
    @observable contract_type        = '';
    @observable contract_types_list  = {};
    @observable form_components      = [];
    @observable trade_types          = {};

    // Amount
    @observable amount          = 10;
    @observable basis           = '';
    @observable basis_list      = [];
    @observable currencies_list = {};
    @observable currency        = Client.get('currency');

    // Duration
    @observable duration            = 5;
    @observable duration_unit       = '';
    @observable duration_units_list = [];
    @observable duration_min_max    = {};
    @observable expiry_date         = '';
    @observable expiry_time         = '09:40';
    @observable expiry_type         = 'duration';

    // Barrier
    @observable barrier_1     = '';
    @observable barrier_2     = '';
    @observable barrier_count = 0;

    // Start Time
    @observable start_date       = Number(0); // Number(0) refers to 'now'
    @observable start_dates_list = [];
    @observable start_time       = '12:30';
    @observable sessions         = [];

    // Last Digit
    @observable last_digit = 5;

    // Purchase
    @observable proposal_info = {};
    @observable purchase_info = {};

    // Chart
    chart_id = 1;

    debouncedProposal = debounce(this.requestProposal, 500);

    constructor({ root_store }) {
        const session_storage_properties = allowed_query_string_variables;
        const options = {
            root_store,
            session_storage_properties,
            validation_rules,
        };

        URLHelper.pruneQueryString(allowed_query_string_variables);

        super(options);

        Object.defineProperty(
            this,
            'is_query_string_applied',
            {
                enumerable: false,
                value     : false,
                writable  : true,
            }
        );

        if (Client.isLoggedIn) {
            this.processNewValuesAsync({ currency: Client.get('currency') });
        }

        // Adds intercept to change min_max value of duration validation
        reaction(
            ()=> [this.duration_min_max, this.contract_expiry_type, this.duration_unit],
            () => {
                this.changeDurationValidationRules();
            }
        );
    }

    @action.bound
    async init() {
        const query_string_values = this.updateQueryString();
        this.smart_chart = this.root_store.modules.smart_chart;

        if (!this.symbol) {
            const active_symbols = await WS.activeSymbols();
            await this.processNewValuesAsync({
                symbol: pickDefaultSymbol(active_symbols.active_symbols),
                ...query_string_values,
            });
        }

        if (this.symbol) {
            ContractType.buildContractTypesConfig(this.symbol).then(action(() => {
                this.processNewValuesAsync({
                    ...ContractType.getContractValues(this),
                    ...ContractType.getContractCategories(),
                    ...query_string_values,
                });
            }));
        }
    }

    @action.bound
    onChange(e) {
        const { name, value } = e.target;
        if (!(name in this)) {
            throw new Error(`Invalid Argument: ${name}`);
        }

        console.log(name, value);
        window.dataLayer.push({ event: `contractParamChange-${name}`, contract: { [name]: value } });
        this.processNewValuesAsync({ [name]: value }, true);
    }

    @action.bound
    onHoverPurchase(is_over, contract_type) {
        this.smart_chart.updateBarrierShade(is_over, contract_type);
    }

    @action.bound
    onPurchase(proposal_id, price) {
        if (proposal_id) {
            processPurchase(proposal_id, price).then(action((response) => {
                console.log(response);
                window.dataLayer.push({ event: 'contractPurchase' });
                WS.forgetAll('proposal');
                this.purchase_info = response;
            }));
        }
    }

    @action.bound
    onClickNewTrade(e) {
        this.requestProposal();
        e.preventDefault();
    }

    /**
     * Updates the store with new values
     * @param  {Object} new_state - new values to update the store with
     * @return {Object} returns the object having only those values that are updated
     */
    @action.bound
    updateStore(new_state) {
        Object.keys(cloneObject(new_state)).forEach((key) => {
            if (key === 'root_store' || ['validation_rules', 'validation_errors'].indexOf(key) > -1) return;
            if (JSON.stringify(this[key]) === JSON.stringify(new_state[key])) {
                delete new_state[key];
            } else {
                if (key === 'symbol') {
                    this.is_purchase_enabled = false;
                    this.is_trade_enabled    = false;
                }

                // Add changes to queryString of the url
                if (
                    allowed_query_string_variables.indexOf(key) !== -1 &&
                    this.is_trade_component_mounted
                ) {
                    URLHelper.setQueryParam({ [key]: new_state[key] });
                }

                this[key] = new_state[key];
            }
        });

        return new_state;
    }

    async processNewValuesAsync(obj_new_values = {}, is_changed_by_user = false) {
        console.log(obj_new_values, is_changed_by_user);
        const new_state = this.updateStore(cloneObject(obj_new_values));

        if (is_changed_by_user || /\b(symbol|contract_types_list)\b/.test(Object.keys(new_state))) {
            if ('symbol' in new_state) {
                await Symbol.onChangeSymbolAsync(new_state.symbol);
            }

            this.updateStore({ // disable purchase button(s), clear contract info
                is_purchase_enabled: false,
                proposal_info      : {},
            });

            if (!this.smart_chart.is_contract_mode) {
                const is_barrier_changed = 'barrier_1' in new_state || 'barrier_2' in new_state;
                if (is_barrier_changed) {
                    this.smart_chart.updateBarriers(this.barrier_1, this.barrier_2);
                } else {
                    this.smart_chart.removeBarriers();
                }
            }

            const snapshot = await processTradeParams(this, new_state);
            const query_string_values = this.updateQueryString();
            snapshot.is_trade_enabled = true;

            this.updateStore({
                ...snapshot,
                ...(this.is_query_string_applied ? {} : query_string_values),
            });

            this.is_query_string_applied = true;

            if (/\bcontract_type\b/.test(Object.keys(new_state))) {
                this.validateAllProperties();
            }

            this.debouncedProposal();
        }
    }

    proposal_requests = {};

    @action.bound
    requestProposal() {
        const requests = createProposalRequests(this);

        if (Object.values(this.validation_errors).some(e => e.length)) {
            this.proposal_info     = {};
            this.purchase_info     = {};
            WS.forgetAll('proposal');
            return;
        }

        if (!isEmptyObject(requests)) {
            const proper_proposal_params_for_query_string = getProposalParametersName(requests);

            URLHelper.pruneQueryString(
                [
                    ...proper_proposal_params_for_query_string,
                    ...non_proposal_query_string_variable,
                ]
            );

            this.proposal_requests = requests;
            this.proposal_info     = {};
            this.purchase_info     = {};

            WS.forgetAll('proposal').then(() => {
                Object.keys(this.proposal_requests).forEach((type) => {
                    WS.subscribeProposal(this.proposal_requests[type], this.onProposalResponse);
                });
            });
        }
    }

    @action.bound
    onProposalResponse(response) {
        const contract_type = response.echo_req.contract_type;
        this.proposal_info = {
            ...this.proposal_info,
            [contract_type]: getProposalInfo(this, response),
        };

        if (!this.smart_chart.is_contract_mode) {
            setChartBarrier(this.smart_chart, response, this.onChartBarrierChange);
        }

        this.is_purchase_enabled = true;
    }

    @action.bound
    onChartBarrierChange(barrier_1, barrier_2) {
        this.processNewValuesAsync({ barrier_1, barrier_2 }, true);
    }

    @action.bound
    updateQueryString() {

        // Update the url's query string by default values of the store
        const query_params = URLHelper.updateQueryString(
            this,
            allowed_query_string_variables,
            this.is_trade_component_mounted
        );

        // update state values from query string
        const config = {};
        [...query_params].forEach(param => config[param[0]] = param[1]);
        return config;
    }

    @action.bound
    changeDurationValidationRules() {
        const index = this.validation_rules.duration.findIndex(item => item[0] === 'number');
        const limits = this.duration_min_max[this.contract_expiry_type] || false;
        const duration_options = {
            min: convertDurationLimit(+limits.min, this.duration_unit),
            max: convertDurationLimit(+limits.max, this.duration_unit),
        };

        if (limits) {
            if (index > -1) {
                this.validation_rules.duration[index][1] = duration_options;
            } else {
                this.validation_rules.duration.push(['number', duration_options]);
            }
            this.validateProperty('duration', this.duration);
        }
    }

    @action.bound
    onMount() {
        this.is_trade_component_mounted = true;
        this.updateQueryString();
    }

    @action.bound
    onUnmount() {
        this.is_trade_component_mounted = false;
    }
}
<|endoftext|>


<commit_before>define([
  'lodash',
  'app/plugins/sdk',
  'app/core/utils/kbn',
  'app/core/time_series',
  './rendering',
  './legend',
],
function (_, sdk, kbn, TimeSeries, rendering) {
  'use strict';

    // Set and populate defaults
  var panelDefaults = {
    pieType: 'pie',
    legend: {
      show: true, // disable/enable legend
      legendType: 'rightSide',
      values: false, // disable/enable legend values
      min: false,
      max: false,
      current: false,
      total: false,
      avg: false
    },
    links: [],
    datasource: null,
    maxDataPoints: 3,
    interval: null,
    targets: [{}],
    cacheTimeout: null,
    nullText: null,
    nullPointMode: 'connected'
  };

  var PieChartCtrl = (function(_super) {

    function PieChartCtrl($scope, $injector, $rootScope) {
      _super.call(this, $scope, $injector);
      this.$rootScope = $rootScope;

      _.defaults(this.panel, panelDefaults);
      _.defaults(this.panel.legend, panelDefaults.legend);
    }

    PieChartCtrl.prototype = Object.create(_super.prototype);
    PieChartCtrl.prototype.constructor = PieChartCtrl;

    PieChartCtrl.templateUrl = 'public/plugins/piechart/module.html';

    PieChartCtrl.prototype.initEditMode = function() {
      _super.prototype.initEditMode.call(this);
      this.icon =  "fa fa-dashboard";
      this.addEditorTab('Options', 'public/plugins/piechart/editor.html', 2);
      this.unitFormats = kbn.getUnitFormats();
    };

    PieChartCtrl.prototype.setUnitFormat = function(subItem) {
      this.panel.format = subItem.value;
      this.render();
    };

    PieChartCtrl.prototype.refreshData = function(datasource) {
      return this.issueQueries(datasource)
      .then(this.dataHandler.bind(this))
      .catch(function(err) {
        this.series = [];
        this.render();
        throw err;
      }.bind(this));
    };

    PieChartCtrl.prototype.dataHandler = function(results) {
      this.series = _.map(results.data, this.seriesHandler.bind(this));
      this.render();
    };

    PieChartCtrl.prototype.seriesHandler = function(seriesData) {
      var series = new TimeSeries({
        datapoints: seriesData.datapoints,
        alias: seriesData.target
      });

      series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
      return series;
    };

    PieChartCtrl.prototype.getDecimalsForValue = function(value) {
      if (_.isNumber(this.panel.decimals)) {
        return { decimals: this.panel.decimals, scaledDecimals: null };
      }

      var delta = value / 2;
      var dec = -Math.floor(Math.log(delta) / Math.LN10);

      var magn = Math.pow(10, -dec);
      var norm = delta / magn; // norm is between 1.0 and 10.0
      var size;

      if (norm < 1.5) {
        size = 1;
      } else if (norm < 3) {
        size = 2;
        // special case for 2.5, requires an extra decimal
        if (norm > 2.25) {
          size = 2.5;
          ++dec;
        }
      } else if (norm < 7.5) {
        size = 5;
      } else {
        size = 10;
      }

      size *= magn;

      // reduce starting decimals if not needed
      if (Math.floor(value) === value) { dec = 0; }

      var result = {};
      result.decimals = Math.max(0, dec);
      result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;

      return result;
    };

    PieChartCtrl.prototype.render = function() {
      var data = [];

      if (this.series && this.series.length > 0) {
        for (var i=0; i < this.series.length; i++) {
          data.push({label: this.series[i].alias, data: this.series[i].stats.current, color: this.$rootScope.colors[i]});
        }
      }

      this.data = data;
      this.broadcastRender(data);
    };

    PieChartCtrl.prototype.setValues = function(data) {
      data.flotpairs = [];

      if (this.series && this.series.length > 0) {
        var lastPoint = _.last(this.series[0].datapoints);
        var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;

        if (_.isString(lastValue)) {
          data.value = 0;
          data.valueFormated = lastValue;
          data.valueRounded = 0;
        } else {
          data.value = this.series[0].stats[this.panel.valueName];
          data.flotpairs = this.series[0].flotpairs;

          var decimalInfo = this.getDecimalsForValue(data.value);
          var formatFunc = kbn.valueFormats[this.panel.format];
          data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
          data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
        }
      }

      if (data.value === null || data.value === void 0) {
        data.valueFormated = "no value";
      }
    };

    PieChartCtrl.prototype.removeValueMap = function(map) {
      var index = _.indexOf(this.panel.valueMaps, map);
      this.panel.valueMaps.splice(index, 1);
      this.render();
    };

    PieChartCtrl.prototype.addValueMap = function() {
      this.panel.valueMaps.push({value: '', op: '=', text: '' });
    };

    PieChartCtrl.prototype.legendValuesOptionChanged = function() {
      var legend = this.panel.legend;
      legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
      this.render();
    };

    PieChartCtrl.prototype.link = function(scope, elem, attrs, ctrl) {
      rendering.link(scope, elem, attrs, ctrl);
    };

    return PieChartCtrl;

  })(sdk.MetricsPanelCtrl);

  return {
    PanelCtrl: PieChartCtrl
  };
});
<commit_msg>Fixed failing grunt test in master<commit_after>define([
  'lodash',
  'app/plugins/sdk',
  'app/core/utils/kbn',
  'app/core/time_series',
  './rendering',
  './legend',
],
function (_, sdk, kbn, TimeSeries, rendering) {
  'use strict';

    // Set and populate defaults
  var panelDefaults = {
    pieType: 'pie',
    legend: {
      show: true, // disable/enable legend
      legendType: 'rightSide',
      values: false, // disable/enable legend values
      min: false,
      max: false,
      current: false,
      total: false,
      avg: false
    },
    links: [],
    datasource: null,
    maxDataPoints: 3,
    interval: null,
    targets: [{}],
    cacheTimeout: null,
    nullText: null,
    nullPointMode: 'connected'
  };

  var PieChartCtrl = (function(_super) {

    function PieChartCtrl($scope, $injector, $rootScope) {
      _super.call(this, $scope, $injector);
      this.$rootScope = $rootScope;

      _.defaults(this.panel, panelDefaults);
      _.defaults(this.panel.legend, panelDefaults.legend);
    }

    PieChartCtrl.prototype = Object.create(_super.prototype);
    PieChartCtrl.prototype.constructor = PieChartCtrl;

    PieChartCtrl.templateUrl = 'public/plugins/piechart/module.html';

    PieChartCtrl.prototype.initEditMode = function() {
      _super.prototype.initEditMode.call(this);
      this.icon =  "fa fa-dashboard";
      this.addEditorTab('Options', 'public/plugins/piechart/editor.html', 2);
      this.unitFormats = kbn.getUnitFormats();
    };

    PieChartCtrl.prototype.setUnitFormat = function(subItem) {
      this.panel.format = subItem.value;
      this.render();
    };

    PieChartCtrl.prototype.refreshData = function(datasource) {
      return this.issueQueries(datasource)
      .then(this.dataHandler.bind(this))
      .catch(function(err) {
        this.series = [];
        this.render();
        throw err;
      }.bind(this));
    };

    PieChartCtrl.prototype.dataHandler = function(results) {
      this.series = _.map(results.data, this.seriesHandler.bind(this));
      this.render();
    };

    PieChartCtrl.prototype.seriesHandler = function(seriesData) {
      var series = new TimeSeries({
        datapoints: seriesData.datapoints,
        alias: seriesData.target
      });

      series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
      return series;
    };

    PieChartCtrl.prototype.getDecimalsForValue = function(value) {
      if (_.isNumber(this.panel.decimals)) {
        return { decimals: this.panel.decimals, scaledDecimals: null };
      }

      var delta = value / 2;
      var dec = -Math.floor(Math.log(delta) / Math.LN10);

      var magn = Math.pow(10, -dec);
      var norm = delta / magn; // norm is between 1.0 and 10.0
      var size;

      if (norm < 1.5) {
        size = 1;
      } else if (norm < 3) {
        size = 2;
        // special case for 2.5, requires an extra decimal
        if (norm > 2.25) {
          size = 2.5;
          ++dec;
        }
      } else if (norm < 7.5) {
        size = 5;
      } else {
        size = 10;
      }

      size *= magn;

      // reduce starting decimals if not needed
      if (Math.floor(value) === value) { dec = 0; }

      var result = {};
      result.decimals = Math.max(0, dec);
      result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;

      return result;
    };

    PieChartCtrl.prototype.render = function() {
      var data = [];

      if (this.series && this.series.length > 0) {
        for (var i=0; i < this.series.length; i++) {
          data.push({label: this.series[i].alias, data: this.series[i].stats.current, color: this.$rootScope.colors[i]});
        }
      }

      this.data = data;
      this.broadcastRender(data);
    };

    PieChartCtrl.prototype.setValues = function(data) {
      data.flotpairs = [];

      if (this.series && this.series.length > 0) {
        var lastPoint = _.last(this.series[0].datapoints);
        var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;

        if (_.isString(lastValue)) {
          data.value = 0;
          data.valueFormated = lastValue;
          data.valueRounded = 0;
        } else {
          data.value = this.series[0].stats[this.panel.valueName];
          data.flotpairs = this.series[0].flotpairs;

          var decimalInfo = this.getDecimalsForValue(data.value);
          var formatFunc = kbn.valueFormats[this.panel.format];
          data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
          data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
        }
      }

      if (data.value === null || data.value === void 0) {
        data.valueFormated = "no value";
      }
    };

    PieChartCtrl.prototype.removeValueMap = function(map) {
      var index = _.indexOf(this.panel.valueMaps, map);
      this.panel.valueMaps.splice(index, 1);
      this.render();
    };

    PieChartCtrl.prototype.addValueMap = function() {
      this.panel.valueMaps.push({value: '', op: '=', text: '' });
    };

    PieChartCtrl.prototype.legendValuesOptionChanged = function() {
      var legend = this.panel.legend;
      legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
      this.render();
    };

    PieChartCtrl.prototype.link = function(scope, elem, attrs, ctrl) {
      rendering.link(scope, elem, attrs, ctrl);
    };

    return PieChartCtrl;

  })(sdk.MetricsPanelCtrl);

  return {
    PanelCtrl: PieChartCtrl
  };
});
<|endoftext|>


<commit_before>/*
 * Copyright 2006 The eFaps Team
 *
 * 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.
 *
 * Author:          tmo
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

importClass(Packages.java.io.File);
importClass(Packages.java.io.FileWriter);

importClass(Packages.org.efaps.db.Delete);
importClass(Packages.org.efaps.db.Insert);
importClass(Packages.org.efaps.db.Instance);
importClass(Packages.org.efaps.db.SearchQuery);

/**
 * Implements the Type represenation in JavaScript.
 */

var TYPE = null;

function Type(_name)  {
  this.object = new EFapsInstance("Admin_DataModel_Type", _name);
  this.instance = new Instance(Shell.getContext(), this.object.oid);

  // add helper methods for getter and setter
  this._setAttrValue      = Abstract.prototype._setAttrValue;
  this._getAttrValue      = Abstract.prototype._getAttrValue;

  // add getter and setter methods
  this.getInstance        = Abstract.prototype.getInstance;
  this.getId              = Abstract.prototype.getId;
  this.getOid             = Abstract.prototype.getOid;
  this.getType            = Abstract.prototype.getType;
  this.getName            = Abstract.prototype.getName;
  this.getRevision        = Abstract.prototype.getRevision;
  this.setRevision        = Abstract.prototype.setRevision;

  // add property methods
  this._writeProperties   = Abstract.prototype._writeProperties;
  this.addProperty        = Abstract.prototype.addProperty;
  this.cleanupProperties  = Abstract.prototype.cleanupProperties;
  this.deleteProperty     = Abstract.prototype.addProperty;
  this.printProperties    = Abstract.prototype.printProperties;

  // add common methods
  this._writeHeader       = Abstract.prototype._writeHeader;
}

///////////////////////////////////////////////////////////////////////////////
// instance variables

/**
 * Prefix of script file names.
 */
Type.prototype.FILE_PREFIX = new String("TYPE_");

/**
 * Name of the variable used in update scripts.
 */
Type.prototype.VARNAME     = new String("TYPE");

///////////////////////////////////////////////////////////////////////////////
// trigger methods

/**
 * A new trigger is created and connected to this type.
 *
 * @param _type     trigger type to add
 * @param _indexPo  index position
 * @param _subject  name of the trigger to create and connect
 * @return
 */
Type.prototype.addTrigger = function(_type, _indexPos, _subject)  {
  var insert = new Insert(Shell.getContext(), _type);
  insert.add(Shell.getContext(), "Name", _subject);
  insert.add(Shell.getContext(), "IndexPosition", _indexPos);
  insert.add(Shell.getContext(), "Abstract", this.getId());
  insert.execute(Shell.getContext());

  return new Trigger(insert.getInstance());
}

/**
 * All triggers connected to this type are removed.
 */
Type.prototype.cleanupTriggers = function()  {
  var query = new SearchQuery();
  query.setExpand(Shell.getContext(), this.getOid(), "Admin_Event_Trigger\\Abstract");
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  while (query.next())  {
    var attrOid  = query.get(Shell.getContext(), "OID");
    (new Trigger(new Instance(Shell.getContext(), attrOid))).remove();
  }
  query.close();
}

///////////////////////////////////////////////////////////////////////////////
// attribute methods

/**
 * First, it is checked if an attribute with this name already exists on the
 * type. If yes, this attribute is returned. Otherwise a new attribute is
 * created and connected to this type. The new attribute gets a few default
 * values:
 *

 *
the attribute type is always String.
 *
the SQL Table is Admin_DataModel_AbstractTable
 *
the SQL column is the name of the attribute plus the prefix -
 *

 *
 * @param _name name of the attribute to create and connect
 * @return new created attribute javascript representation
 */
Type.prototype.addAttribute = function(_name)  {
  var attr;

  var query = new SearchQuery();
  query.setQueryTypes(Shell.getContext(), "Admin_DataModel_Attribute");
  query.addWhereExprEqValue(Shell.getContext(), "Name", _name);
  query.addWhereExprEqValue(Shell.getContext(), "ParentType", this.getId());
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  if (query.next())  {
    var attrOid = query.get(Shell.getContext(), "OID");
    attr = new Attribute(new Instance(Shell.getContext(), attrOid));
  } else  {
    var insert = new Insert(Shell.getContext(), "Admin_DataModel_Attribute");
    insert.add(Shell.getContext(), "ParentType", this.getId());
    insert.add(Shell.getContext(), "Name", _name);
//  attr.AttributeType  = (new EFapsInstance("Admin_DataModel_AttributeType", "String")).ID;
    insert.add(Shell.getContext(), "AttributeType", 99);
    insert.add(Shell.getContext(), "Table", (new SQLTable("Admin_AbstractTable")).getId());
    insert.add(Shell.getContext(), "SQLColumn", "-"+_name);
    insert.execute(Shell.getContext());

    attr = new Attribute(insert.getInstance());
  }
  return attr;
}

Type.prototype.printAttributes = function() {
/*
  var attributes = this.object.Admin_DataModel_Attribute\ParentType;
  if (attributes!=null)  {
    var i = 0;
    while (i < attributes.length) {
      print(attributes[i].Name);
      i=i+1;
    }
  }
*/
}

/**
 * All defined attributes of this types are cleaned up in the database (not
 * deleted, only all properties are removed!). A remove of the attribute is not
 * always allowed, because it could be that the attribute is already referenced
 * (e.g. in a history entry).
 */
Type.prototype.cleanupAttributes = function()  {
  var query = new SearchQuery();
  query.setExpand(Shell.getContext(), this.getOid(), "Admin_DataModel_Attribute\\ParentType");
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  while (query.next())  {
    var attrOid  = query.get(Shell.getContext(), "OID");
    (new Attribute(new Instance(Shell.getContext(), attrOid))).cleanup();
  }
  query.close();
}

/**
 * Writes the JS update scripts for the attributes.
 *
 * @param _file   (Writer)  open file to write through
 * @param _space  (String)  space to write in front of the attributes
 */
Type.prototype._writeAttributes = function(_file, _space)  {
/*
  var attrs = this.object.Admin_DataModel_Attribute\ParentType;
  if (attrs!=null)  {
    var i = 0;
    while (i < attrs.length) {
      var name  = attrs[i].Name;
      _file.println(_space + "  with (addAttribute(\""+name+"\"))  {");
      (new Attribute(attrs[i]))._writeUpdateScript(_file, _space + "  ");
//  this._writeProperties(file);
      _file.println(_space + "  }");
      i++;
    }
  }
*/
}

///////////////////////////////////////////////////////////////////////////////
// common methods

Type.prototype.cleanup = function()  {
  this.cleanupAttributes();
  this.cleanupProperties();
  this.cleanupTriggers();
  this.setParentType(null);
}

Type.prototype.update = function(_fileName, _objName)  {
  if (this.getOid()==null || this.getOid()=="" || this.getOid()=="0")  {
    print("  - create");
    this.object.Name = _objName;
    this.object.create();
  } else  {
    print("  - cleanup");
    this.cleanup();
  }
  TYPE = this;
  print("  - import");
  load(_fileName);
  TYPE = null;
}

/**
 * Creates the update script for current type.
 *
 * @param _path   (String)  file is created in this path
 * @param _author (String)  author used in the header
 */
Type.prototype.writeUpdateScript = function(_path, _author)  {
  var file = new PrintWriter(new FileWriter(_path+"/"+this.FILE_PREFIX+this.getName()+".js"));

  this._writeHeader(file, _author);

  file.println("with ("+this.VARNAME+")  {");

  var query = new SearchQuery();
  query.setObject(Shell.getContext(), this.getOid());
  query.addSelect(Shell.getContext(), "OID");
  query.addSelect(Shell.getContext(), "ParentType");
  query.addSelect(Shell.getContext(), "ParentType.Name");
  query.execute(Shell.getContext());
  if (query.next())  {
    var parType   = query.get(Shell.getContext(), "ParentType.Name").value;

    if (parType.length()>0)  {
      file.println("  setParentType(new Type(\""+parType+"\"));");
    }
  }
  query.close();

  this._writeAttributes(file, "");
  this._writeProperties(file);
  file.println("}");

  file.close();
}

///////////////////////////////////////////////////////////////////////////////
// getter and setter methods

/**
 * The parent type of this type is set.
 *
 * @param _parentType   parent type to set (name string or instance of class
 *                      Type)
 */
Type.prototype.setParentType = function(_parentType)  {
  if (_parentType!=null)  {
    var parentType;
    if (typeof(_parentType)=="string")  {
      parentType = new Type(_parentType);
    } else  {
      parentType = _parentType;
    }
    this._setAttrValue("ParentType", parentType.getId());
  } else  {
    this._setAttrValue("ParentType", null);
  }
}

///////////////////////////////////////////////////////////////////////////////
// static functions

/**
 *
 */
function importType(_fileName)  {
  var fileName = new File(_fileName);
  if (fileName.getName().startsWith(Type.prototype.FILE_PREFIX) && fileName.getName().endsWith(".js"))  {
    var objName = new String(fileName.getName().substring(5, fileName.getName().length()-3));
    print("Import Type '"+objName+"'");
    var impType = new Type(objName);
    impType.update(_fileName, objName);
  }
}

/**
 * A list of Type Definition files is imported. The algorithmen checks first
 * for existance of a type and creates the not existant types.
 *
 * @param _fileList array with list of files
 */
function importTypes(_fileList)  {
  print("");
  print("Import Types");
  print("~~~~~~~~~~~~");
  for (indx in _fileList)  {
    var fileName = new File(_fileList[indx]);
    if (fileName.getName().startsWith(Type.prototype.FILE_PREFIX) && fileName.getName().endsWith(".js"))  {
      var objName = new String(fileName.getName().substring(5, fileName.getName().length()-3));
      var obj = new EFapsInstance("Admin_DataModel_Type", objName);
      if (obj.oid==null || obj.oid=="" || obj.oid=="0")  {
        print("Create Type '"+objName+"'");
        obj.Name = objName;
        obj.create();
      }
    }
  }
  for (indx in _fileList)  {
    importType(_fileList[indx]);
  }
  print("");
}

/**
 * @param _path   (String)  files are created in this path
 * @param _match  (String)  match for menu names
 * @param _author (String)  author used in the header
 */
function createScriptTypes(_path, _match, _author)  {
  var query = new SearchQuery();
  query.setQueryTypes(Shell.getContext(), "Admin_DataModel_Type");
  query.addWhereExprEqValue(Shell.getContext(), "Name", _match);
  query.addSelect(Shell.getContext(), "Name");

  query.execute(Shell.getContext());

  print("");
  print("Create Type Scripts:");
  print("~~~~~~~~~~~~~~~~~~~~");
  while (query.next())  {
    var value = query.get(Shell.getContext(), "Name")
    var name = value.value;
    print("  - '"+  name  +"'");
    var type = new Type(name);
    type.writeUpdateScript(_path, _author);
  }

  query.close();
}
<commit_msg>- expand with type 'Admin_Event_Definition' (instead of type    'Admin_Event_Trigger')<commit_after>/*
 * Copyright 2006 The eFaps Team
 *
 * 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.
 *
 * Author:          tmo
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

importClass(Packages.java.io.File);
importClass(Packages.java.io.FileWriter);

importClass(Packages.org.efaps.db.Delete);
importClass(Packages.org.efaps.db.Insert);
importClass(Packages.org.efaps.db.Instance);
importClass(Packages.org.efaps.db.SearchQuery);

/**
 * Implements the Type represenation in JavaScript.
 */

var TYPE = null;

function Type(_name)  {
  this.object = new EFapsInstance("Admin_DataModel_Type", _name);
  this.instance = new Instance(Shell.getContext(), this.object.oid);

  // add helper methods for getter and setter
  this._setAttrValue      = Abstract.prototype._setAttrValue;
  this._getAttrValue      = Abstract.prototype._getAttrValue;

  // add getter and setter methods
  this.getInstance        = Abstract.prototype.getInstance;
  this.getId              = Abstract.prototype.getId;
  this.getOid             = Abstract.prototype.getOid;
  this.getType            = Abstract.prototype.getType;
  this.getName            = Abstract.prototype.getName;
  this.getRevision        = Abstract.prototype.getRevision;
  this.setRevision        = Abstract.prototype.setRevision;

  // add property methods
  this._writeProperties   = Abstract.prototype._writeProperties;
  this.addProperty        = Abstract.prototype.addProperty;
  this.cleanupProperties  = Abstract.prototype.cleanupProperties;
  this.deleteProperty     = Abstract.prototype.addProperty;
  this.printProperties    = Abstract.prototype.printProperties;

  // add common methods
  this._writeHeader       = Abstract.prototype._writeHeader;
}

///////////////////////////////////////////////////////////////////////////////
// instance variables

/**
 * Prefix of script file names.
 */
Type.prototype.FILE_PREFIX = new String("TYPE_");

/**
 * Name of the variable used in update scripts.
 */
Type.prototype.VARNAME     = new String("TYPE");

///////////////////////////////////////////////////////////////////////////////
// trigger methods

/**
 * A new trigger is created and connected to this type.
 *
 * @param _type     trigger type to add
 * @param _indexPo  index position
 * @param _subject  name of the trigger to create and connect
 * @return
 */
Type.prototype.addTrigger = function(_type, _indexPos, _subject)  {
  var insert = new Insert(Shell.getContext(), _type);
  insert.add(Shell.getContext(), "Name", _subject);
  insert.add(Shell.getContext(), "IndexPosition", _indexPos);
  insert.add(Shell.getContext(), "Abstract", this.getId());
  insert.execute(Shell.getContext());

  return new Trigger(insert.getInstance());
}

/**
 * All triggers connected to this type are removed.
 */
Type.prototype.cleanupTriggers = function()  {
  var query = new SearchQuery();
  query.setExpand(Shell.getContext(), this.getOid(), "Admin_Event_Definition\\Abstract");
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  while (query.next())  {
    var attrOid  = query.get(Shell.getContext(), "OID");
    (new Trigger(new Instance(Shell.getContext(), attrOid))).remove();
  }
  query.close();
}

///////////////////////////////////////////////////////////////////////////////
// attribute methods

/**
 * First, it is checked if an attribute with this name already exists on the
 * type. If yes, this attribute is returned. Otherwise a new attribute is
 * created and connected to this type. The new attribute gets a few default
 * values:
 *

 *
the attribute type is always String.
 *
the SQL Table is Admin_DataModel_AbstractTable
 *
the SQL column is the name of the attribute plus the prefix -
 *

 *
 * @param _name name of the attribute to create and connect
 * @return new created attribute javascript representation
 */
Type.prototype.addAttribute = function(_name)  {
  var attr;

  var query = new SearchQuery();
  query.setQueryTypes(Shell.getContext(), "Admin_DataModel_Attribute");
  query.addWhereExprEqValue(Shell.getContext(), "Name", _name);
  query.addWhereExprEqValue(Shell.getContext(), "ParentType", this.getId());
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  if (query.next())  {
    var attrOid = query.get(Shell.getContext(), "OID");
    attr = new Attribute(new Instance(Shell.getContext(), attrOid));
  } else  {
    var insert = new Insert(Shell.getContext(), "Admin_DataModel_Attribute");
    insert.add(Shell.getContext(), "ParentType", this.getId());
    insert.add(Shell.getContext(), "Name", _name);
//  attr.AttributeType  = (new EFapsInstance("Admin_DataModel_AttributeType", "String")).ID;
    insert.add(Shell.getContext(), "AttributeType", 99);
    insert.add(Shell.getContext(), "Table", (new SQLTable("Admin_AbstractTable")).getId());
    insert.add(Shell.getContext(), "SQLColumn", "-"+_name);
    insert.execute(Shell.getContext());

    attr = new Attribute(insert.getInstance());
  }
  return attr;
}

Type.prototype.printAttributes = function() {
/*
  var attributes = this.object.Admin_DataModel_Attribute\ParentType;
  if (attributes!=null)  {
    var i = 0;
    while (i < attributes.length) {
      print(attributes[i].Name);
      i=i+1;
    }
  }
*/
}

/**
 * All defined attributes of this types are cleaned up in the database (not
 * deleted, only all properties are removed!). A remove of the attribute is not
 * always allowed, because it could be that the attribute is already referenced
 * (e.g. in a history entry).
 */
Type.prototype.cleanupAttributes = function()  {
  var query = new SearchQuery();
  query.setExpand(Shell.getContext(), this.getOid(), "Admin_DataModel_Attribute\\ParentType");
  query.addSelect(Shell.getContext(), "OID");
  query.execute(Shell.getContext());
  while (query.next())  {
    var attrOid  = query.get(Shell.getContext(), "OID");
    (new Attribute(new Instance(Shell.getContext(), attrOid))).cleanup();
  }
  query.close();
}

/**
 * Writes the JS update scripts for the attributes.
 *
 * @param _file   (Writer)  open file to write through
 * @param _space  (String)  space to write in front of the attributes
 */
Type.prototype._writeAttributes = function(_file, _space)  {
/*
  var attrs = this.object.Admin_DataModel_Attribute\ParentType;
  if (attrs!=null)  {
    var i = 0;
    while (i < attrs.length) {
      var name  = attrs[i].Name;
      _file.println(_space + "  with (addAttribute(\""+name+"\"))  {");
      (new Attribute(attrs[i]))._writeUpdateScript(_file, _space + "  ");
//  this._writeProperties(file);
      _file.println(_space + "  }");
      i++;
    }
  }
*/
}

///////////////////////////////////////////////////////////////////////////////
// common methods

Type.prototype.cleanup = function()  {
  this.cleanupAttributes();
  this.cleanupProperties();
  this.cleanupTriggers();
  this.setParentType(null);
}

Type.prototype.update = function(_fileName, _objName)  {
  if (this.getOid()==null || this.getOid()=="" || this.getOid()=="0")  {
    print("  - create");
    this.object.Name = _objName;
    this.object.create();
  } else  {
    print("  - cleanup");
    this.cleanup();
  }
  TYPE = this;
  print("  - import");
  load(_fileName);
  TYPE = null;
}

/**
 * Creates the update script for current type.
 *
 * @param _path   (String)  file is created in this path
 * @param _author (String)  author used in the header
 */
Type.prototype.writeUpdateScript = function(_path, _author)  {
  var file = new PrintWriter(new FileWriter(_path+"/"+this.FILE_PREFIX+this.getName()+".js"));

  this._writeHeader(file, _author);

  file.println("with ("+this.VARNAME+")  {");

  var query = new SearchQuery();
  query.setObject(Shell.getContext(), this.getOid());
  query.addSelect(Shell.getContext(), "OID");
  query.addSelect(Shell.getContext(), "ParentType");
  query.addSelect(Shell.getContext(), "ParentType.Name");
  query.execute(Shell.getContext());
  if (query.next())  {
    var parType   = query.get(Shell.getContext(), "ParentType.Name").value;

    if (parType.length()>0)  {
      file.println("  setParentType(new Type(\""+parType+"\"));");
    }
  }
  query.close();

  this._writeAttributes(file, "");
  this._writeProperties(file);
  file.println("}");

  file.close();
}

///////////////////////////////////////////////////////////////////////////////
// getter and setter methods

/**
 * The parent type of this type is set.
 *
 * @param _parentType   parent type to set (name string or instance of class
 *                      Type)
 */
Type.prototype.setParentType = function(_parentType)  {
  if (_parentType!=null)  {
    var parentType;
    if (typeof(_parentType)=="string")  {
      parentType = new Type(_parentType);
    } else  {
      parentType = _parentType;
    }
    this._setAttrValue("ParentType", parentType.getId());
  } else  {
    this._setAttrValue("ParentType", null);
  }
}

///////////////////////////////////////////////////////////////////////////////
// static functions

/**
 *
 */
function importType(_fileName)  {
  var fileName = new File(_fileName);
  if (fileName.getName().startsWith(Type.prototype.FILE_PREFIX) && fileName.getName().endsWith(".js"))  {
    var objName = new String(fileName.getName().substring(5, fileName.getName().length()-3));
    print("Import Type '"+objName+"'");
    var impType = new Type(objName);
    impType.update(_fileName, objName);
  }
}

/**
 * A list of Type Definition files is imported. The algorithmen checks first
 * for existance of a type and creates the not existant types.
 *
 * @param _fileList array with list of files
 */
function importTypes(_fileList)  {
  print("");
  print("Import Types");
  print("~~~~~~~~~~~~");
  for (indx in _fileList)  {
    var fileName = new File(_fileList[indx]);
    if (fileName.getName().startsWith(Type.prototype.FILE_PREFIX) && fileName.getName().endsWith(".js"))  {
      var objName = new String(fileName.getName().substring(5, fileName.getName().length()-3));
      var obj = new EFapsInstance("Admin_DataModel_Type", objName);
      if (obj.oid==null || obj.oid=="" || obj.oid=="0")  {
        print("Create Type '"+objName+"'");
        obj.Name = objName;
        obj.create();
      }
    }
  }
  for (indx in _fileList)  {
    importType(_fileList[indx]);
  }
  print("");
}

/**
 * @param _path   (String)  files are created in this path
 * @param _match  (String)  match for menu names
 * @param _author (String)  author used in the header
 */
function createScriptTypes(_path, _match, _author)  {
  var query = new SearchQuery();
  query.setQueryTypes(Shell.getContext(), "Admin_DataModel_Type");
  query.addWhereExprEqValue(Shell.getContext(), "Name", _match);
  query.addSelect(Shell.getContext(), "Name");

  query.execute(Shell.getContext());

  print("");
  print("Create Type Scripts:");
  print("~~~~~~~~~~~~~~~~~~~~");
  while (query.next())  {
    var value = query.get(Shell.getContext(), "Name")
    var name = value.value;
    print("  - '"+  name  +"'");
    var type = new Type(name);
    type.writeUpdateScript(_path, _author);
  }

  query.close();
}
<|endoftext|>


<commit_before>'use strict';
var request = require('request'),
    async = require('async');
var error = {
        url: '',
        avatar: '/avatar/ServerCooties',
        author: 'ServerCooties',
        body: '
+++Error At Address: 14, Treacle Mine Road, Ankh-Morpork+++' +
            '
+++Divide By Cucumber Error. Please Reinstall Universe And' +
            ' Reboot +++
',
        name: '',
        authorTitle: ''
    },
    avatars = {},
    definitions = [error],
    users = {},
    host = 'http://what.thedailywtf.com';

function getAvatarPath(username, callback) {
    request(host + '/users/' + username + '.json', function (err, _, body) {
        var letter = '/letter_avatar/' + username +
            '/45/3_af0dc75e546be11ab6c09c8b9d61c787.png';
        if (err) {
            return callback(null, letter);
        }
        try {
            body = JSON.parse(body);
            if (!body.user.avatar_template) {
                return callback(null, letter);
            }

            callback(null, body.user.avatar_template.replace('{size}', '45'));
        } catch (__) {
            callback(null, letter);
        }
    });
}

function getAvatar(username, callback) {
    getAvatarPath(username, function (_, path) {
        var req = request(host + path),
            parts = [],
            res;
        req.on('error', callback);
        req.on('data', function (d) {
            parts.push(d);
        });
        req.on('response', function (resp) {
            res = resp;
        });
        req.on('end', function () {
            callback(null, {
                retrievedAt: Date.now(),
                contentType: res.headers['content-type'],
                lastModified: res.headers['last-modified'],
                data: Buffer.concat(parts)
            });
        });
    });
}

function serve(avatar, response) {
    response.writeHead(200, {
        'Content-Type': avatar.contentType,
        'Last-Modified': avatar.lastModified
    });
    response.write(avatar.data, 'binary');
    response.end();
}

exports.serveAvatar = function serveAvatar(uri, _, response) {
    var username = /[/]([^/]+)$/.exec(uri)[1];
    if (avatars[username]) {
        return serve(avatars[username], response);
    }
    getAvatar(username, function (err, avatar) {
        if (err) {
            response.writeHead(500);
            response.write(err, 'binary');
            return response.end();
        }
        avatars[username] = avatar;
        serve(avatar, response);
    });
};

function getPosts(id, complete) {
    var base = '/t/' + id + '/posts.json?include_raw=1';
    request(host + base + '&post_ids=0', function (err, _, topic) {
        var posts, results = [];
        try {
            posts = JSON.parse(topic).post_stream.stream;
        } catch (e) {
            err = e;
        }
        if (err) {
            return complete([]);
        }
        async.whilst(function () {
            return posts.length > 0;
        }, function (next) {
            var part = [];
            while (part.length < 100 && posts.length > 0) {
                part.push(posts.shift());
            }
            part = part.join('&post_ids[]=');
            request(host + base + '&post_ids[]=' + part,
                function (err2, resp, defs) {
                    if (err2 || resp.statusCode !== 200) {
                        return next();
                    }
                    try {
                        defs = JSON.parse(defs).post_stream.posts;
                    } catch (e) {
                        return next(e);
                    }
                    results = results.concat(defs.filter(function (f) {
                        return f.wiki && f.post_number > 1;
                    }).map(function (post) {
                        return {
                            url: host + '/p/' + post.id,
                            avatar: '/avatar/' + post.username,
                            author: post.username,
                            body: post.cooked,
                            name: post.name,
                            authorTitle: post.user_title
                        };
                    }));
                    next();
                });
        }, function () {
            complete(results);
        });
    });
}

function loadDefinitions(callback) {
    getPosts(3866, function (defs) {
        definitions = defs || [error];
        var usr = definitions.map(function (d) {
            return d.author;
        });
        usr.push('ServerCooties');
        users = {};
        usr.forEach(function (u) {
            users[u] = users[u] ? users[u] + 1 : 1;
        });
        callback();
    });
}

exports.getQuote = function getQuote() {
    var def = definitions[Math.floor(Math.random() * definitions.length)];
    def.body = def.body.replace(/(src|href)="([^"]+)"/g,
        function (_, prefix, value) {
            if (value.substr(0, 2) === '//') {
                value = 'http:' + value;
            }
            if (value[0] === '/') {
                value = host + value;
            }
            return prefix + '="' + value + '"';
        });
    return def;
};

if (process.env.SOCKDEV && !process.env.SOCKQUOTES) {
    async.forever(function (next) {
        var refresh = 5 * 60 * 60 * 1000;
        loadDefinitions(function () {
            console.log('quotes loaded'); //eslint-disable-line no-console
            var now = Date.now() - refresh;
            async.each(Object.keys(users), function (user, innerNext) {
                if (!avatars[user] || avatars[user].retrievedAt < now) {
                    return getAvatar(user, function (err, avatar) {
                        if (!err) {
                            avatars[user] = avatar;
                        }
                        innerNext();
                    });
                }
                innerNext();
            });
            setTimeout(next, refresh);
        });
    });
}

async.forever(function (next) {
    var cutoff = Date.now() - 10 * 60 * 1000;
    Object.keys(avatars).forEach(function (key) {
        if (!users[key] && avatars[key].retrievedAt < cutoff) {
            delete users[key];
        }
    });
    setTimeout(next, 60 * 1000);
});
<commit_msg> slow quote loading to prevent triggering mini cootiestorm on server start/restart<commit_after>'use strict';
var request = require('request'),
    async = require('async');
var error = {
        url: '',
        avatar: '/avatar/ServerCooties',
        author: 'ServerCooties',
        body: '
+++Error At Address: 14, Treacle Mine Road, Ankh-Morpork+++' +
            '
+++Divide By Cucumber Error. Please Reinstall Universe And' +
            ' Reboot +++
',
        name: '',
        authorTitle: ''
    },
    avatars = {},
    definitions = [error],
    users = {},
    host = 'http://what.thedailywtf.com';

function getAvatarPath(username, callback) {
    request(host + '/users/' + username + '.json', function (err, _, body) {
        var letter = '/letter_avatar/' + username +
            '/45/3_af0dc75e546be11ab6c09c8b9d61c787.png';
        if (err) {
            return callback(null, letter);
        }
        try {
            body = JSON.parse(body);
            if (!body.user.avatar_template) {
                return callback(null, letter);
            }

            callback(null, body.user.avatar_template.replace('{size}', '45'));
        } catch (__) {
            callback(null, letter);
        }
    });
}

function getAvatar(username, callback) {
    getAvatarPath(username, function (_, path) {
        var req = request(host + path),
            parts = [],
            res;
        req.on('error', callback);
        req.on('data', function (d) {
            parts.push(d);
        });
        req.on('response', function (resp) {
            res = resp;
        });
        req.on('end', function () {
            callback(null, {
                retrievedAt: Date.now(),
                contentType: res.headers['content-type'],
                lastModified: res.headers['last-modified'],
                data: Buffer.concat(parts)
            });
        });
    });
}

function serve(avatar, response) {
    response.writeHead(200, {
        'Content-Type': avatar.contentType,
        'Last-Modified': avatar.lastModified
    });
    response.write(avatar.data, 'binary');
    response.end();
}

exports.serveAvatar = function serveAvatar(uri, _, response) {
    var username = /[/]([^/]+)$/.exec(uri)[1];
    if (avatars[username]) {
        return serve(avatars[username], response);
    }
    getAvatar(username, function (err, avatar) {
        if (err) {
            response.writeHead(500);
            response.write(err, 'binary');
            return response.end();
        }
        avatars[username] = avatar;
        serve(avatar, response);
    });
};

function getPosts(id, complete) {
    var base = '/t/' + id + '/posts.json?include_raw=1';
    request(host + base + '&post_ids=0', function (err, _, topic) {
        var posts, results = [];
        try {
            posts = JSON.parse(topic).post_stream.stream;
        } catch (e) {
            err = e;
        }
        if (err) {
            return complete([]);
        }
        async.whilst(function () {
            return posts.length > 0;
        }, function (next) {
            var part = [];
            while (part.length < 100 && posts.length > 0) {
                part.push(posts.shift());
            }
            part = part.join('&post_ids[]=');
            request(host + base + '&post_ids[]=' + part,
                function (err2, resp, defs) {
                    if (err2 || resp.statusCode !== 200) {
                        return next();
                    }
                    try {
                        defs = JSON.parse(defs).post_stream.posts;
                    } catch (e) {
                        return next(e);
                    }
                    results = results.concat(defs.filter(function (f) {
                        return f.wiki && f.post_number > 1;
                    }).map(function (post) {
                        return {
                            url: host + '/p/' + post.id,
                            avatar: '/avatar/' + post.username,
                            author: post.username,
                            body: post.cooked,
                            name: post.name,
                            authorTitle: post.user_title
                        };
                    }));
                    setTimeout(next, 1000);
                });
        }, function () {
            complete(results);
        });
    });
}

function loadDefinitions(callback) {
    getPosts(3866, function (defs) {
        definitions = defs || [error];
        var usr = definitions.map(function (d) {
            return d.author;
        });
        usr.push('ServerCooties');
        users = {};
        usr.forEach(function (u) {
            users[u] = users[u] ? users[u] + 1 : 1;
        });
        callback();
    });
}

exports.getQuote = function getQuote() {
    var def = definitions[Math.floor(Math.random() * definitions.length)];
    def.body = def.body.replace(/(src|href)="([^"]+)"/g,
        function (_, prefix, value) {
            if (value.substr(0, 2) === '//') {
                value = 'http:' + value;
            }
            if (value[0] === '/') {
                value = host + value;
            }
            return prefix + '="' + value + '"';
        });
    return def;
};

if (process.env.SOCKDEV && !process.env.SOCKQUOTES) {
    async.forever(function (next) {
        var refresh = 5 * 60 * 60 * 1000;
        loadDefinitions(function () {
            console.log('quotes loaded'); //eslint-disable-line no-console
            var now = Date.now() - refresh;
            async.each(Object.keys(users), function (user, innerNext) {
                if (!avatars[user] || avatars[user].retrievedAt < now) {
                    return getAvatar(user, function (err, avatar) {
                        if (!err) {
                            avatars[user] = avatar;
                        }
                        setTimeout(innerNext, 3*1000);
                    });
                }
                innerNext();
            });
            setTimeout(next, refresh);
        });
    });
}

async.forever(function (next) {
    var cutoff = Date.now() - 10 * 60 * 1000;
    Object.keys(avatars).forEach(function (key) {
        if (!users[key] && avatars[key].retrievedAt < cutoff) {
            delete users[key];
        }
    });
    setTimeout(next, 60 * 1000);
});
<|endoftext|>