<commit_before>import { h, ref, watch, computed, getCurrentInstance } from 'vue'

import QBtn from '../btn/QBtn.js'
import QInput from '../input/QInput.js'

import useDark, { useDarkProps } from '../../composables/private/use-dark.js'
import { btnDesignOptions, btnPadding, getBtnDesign } from '../btn/use-btn.js'

import { createComponent } from '../../utils/private/create.js'
import { between } from '../../utils/format.js'
import { isKeyCode } from '../../utils/private/key-composition.js'

function getBool (val, otherwise) {
  return [ true, false ].includes(val)
    ? val
    : otherwise
}

export default createComponent({
  name: 'QPagination',

  props: {
    ...useDarkProps,

    modelValue: {
      type: Number,
      required: true
    },
    min: {
      type: [ Number, String ],
      default: 1
    },
    max: {
      type: [ Number, String ],
      required: true
    },
    maxPages: {
      type: [ Number, String ],
      default: 0,
      validator: v => (
        (typeof v === 'string' ? parseInt(v, 10) : v) >= 0
      )
    },

    inputStyle: [ Array, String, Object ],
    inputClass: [ Array, String, Object ],

    size: String,

    disable: Boolean,

    input: Boolean,

    iconPrev: String,
    iconNext: String,
    iconFirst: String,
    iconLast: String,

    toFn: Function,

    boundaryLinks: {
      type: Boolean,
      default: null
    },
    boundaryNumbers: {
      type: Boolean,
      default: null
    },
    directionLinks: {
      type: Boolean,
      default: null
    },
    ellipses: {
      type: Boolean,
      default: null
    },

    ripple: {
      type: [ Boolean, Object ],
      default: null
    },

    round: Boolean,
    rounded: Boolean,

    flat: Boolean,
    outline: Boolean,
    unelevated: Boolean,
    push: Boolean,
    glossy: Boolean,

    color: {
      type: String,
      default: 'primary'
    },
    textColor: String,

    activeDesign: {
      type: String,
      default: '',
      values: v => v === '' || btnDesignOptions.includes(v)
    },
    activeColor: String,
    activeTextColor: String,

    gutter: String,
    padding: {
      type: String,
      default: '3px 2px'
    }
  },

  emits: [ 'update:modelValue' ],

  setup (props, { emit }) {
    const { proxy } = getCurrentInstance()
    const { $q } = proxy

    const isDark = useDark(props, $q)

    const minProp = computed(() => parseInt(props.min, 10))
    const maxProp = computed(() => parseInt(props.max, 10))
    const maxPagesProp = computed(() => parseInt(props.maxPages, 10))

    const inputPlaceholder = computed(() => model.value + ' / ' + maxProp.value)
    const boundaryLinksProp = computed(() => getBool(props.boundaryLinks, props.input))
    const boundaryNumbersProp = computed(() => getBool(props.boundaryNumbers, !props.input))
    const directionLinksProp = computed(() => getBool(props.directionLinks, props.input))
    const ellipsesProp = computed(() => getBool(props.ellipses, !props.input))

    const newPage = ref(null)
    const model = computed({
      get: () => props.modelValue,
      set: val => {
        val = parseInt(val, 10)
        if (props.disable || isNaN(val)) {
          return
        }
        const value = between(val, minProp.value, maxProp.value)
        if (props.modelValue !== value) {
          emit('update:modelValue', value)
        }
      }
    })

    watch(() => `${ minProp.value }|${ maxProp.value }`, () => {
      model.value = props.modelValue
    })

    const classes = computed(() =>
      'q-pagination row no-wrap items-center'
      + (props.disable === true ? ' disabled' : '')
    )

    const gutterProp = computed(() => (
      props.gutter in btnPadding
        ? `${ btnPadding[ props.gutter ] }px`
        : props.gutter || null
    ))
    const gutterStyle = computed(() => (
      gutterProp.value !== null
        ? `--q-pagination-gutter-parent:-${ gutterProp.value };--q-pagination-gutter-child:${ gutterProp.value }`
        : null
    ))

    const icons = computed(() => {
      const ico = [
        props.iconFirst || $q.iconSet.pagination.first,
        props.iconPrev || $q.iconSet.pagination.prev,
        props.iconNext || $q.iconSet.pagination.next,
        props.iconLast || $q.iconSet.pagination.last
      ]
      return $q.lang.rtl === true ? ico.reverse() : ico
    })

    const attrs = computed(() => ({
      'aria-disabled': props.disable === true ? 'true' : 'false',
      role: 'navigation'
    }))

    const btnDesignProp = computed(() => getBtnDesign(props, 'flat'))
    const btnProps = computed(() => ({
      [ btnDesignProp.value ]: true,

      round: props.round,
      rounded: props.rounded,

      padding: props.padding,

      color: props.color,
      textColor: props.textColor,

      size: props.size,
      ripple: props.ripple !== null
        ? props.ripple
        : true
    }))

    const btnActiveDesignProp = computed(() => {
      // we also reset non-active design
      const acc = { [ btnDesignProp.value ]: false }
      if (props.activeDesign !== '') {
        acc[ props.activeDesign ] = true
      }
      return acc
    })
    const activeBtnProps = computed(() => ({
      ...btnActiveDesignProp.value,
      color: props.activeColor || props.color,
      textColor: props.activeTextColor || props.textColor
    }))

    const btnConfig = computed(() => {
      let maxPages = Math.max(
        maxPagesProp.value,
        1 + (ellipsesProp.value ? 2 : 0) + (boundaryNumbersProp.value ? 2 : 0)
      )

      const acc = {
        pgFrom: minProp.value,
        pgTo: maxProp.value,
        ellipsesStart: false,
        ellipsesEnd: false,
        boundaryStart: false,
        boundaryEnd: false,
        marginalStyle: {
          minWidth: `${ Math.max(2, String(maxProp.value).length) }em`
        }
      }

      if (maxPagesProp.value && maxPages < (maxProp.value - minProp.value + 1)) {
        maxPages = 1 + Math.floor(maxPages / 2) * 2
        acc.pgFrom = Math.max(minProp.value, Math.min(maxProp.value - maxPages + 1, props.modelValue - Math.floor(maxPages / 2)))
        acc.pgTo = Math.min(maxProp.value, acc.pgFrom + maxPages - 1)

        if (boundaryNumbersProp.value) {
          acc.boundaryStart = true
          acc.pgFrom++
        }

        if (ellipsesProp.value && acc.pgFrom > (minProp.value + (boundaryNumbersProp.value ? 1 : 0))) {
          acc.ellipsesStart = true
          acc.pgFrom++
        }

        if (boundaryNumbersProp.value) {
          acc.boundaryEnd = true
          acc.pgTo--
        }

        if (ellipsesProp.value && acc.pgTo < (maxProp.value - (boundaryNumbersProp.value ? 1 : 0))) {
          acc.ellipsesEnd = true
          acc.pgTo--
        }
      }

      return acc
    })

    function set (value) {
      model.value = value
    }

    function setByOffset (offset) {
      model.value = model.value + offset
    }

    const inputEvents = computed(() => {
      function updateModel () {
        model.value = newPage.value
        newPage.value = null
      }

      return {
        'onUpdate:modelValue': val => { newPage.value = val },
        onKeyup: e => { isKeyCode(e, 13) === true && updateModel() },
        onBlur: updateModel
      }
    })

    function getBtn (cfg, page, active) {
      const data = {
        'aria-label': page,
        'aria-current': 'false',
        ...btnProps.value,
        ...cfg
      }

      if (active === true) {
        Object.assign(data, {
          'aria-current': 'true',
          ...activeBtnProps.value
        })
      }

      if (page !== void 0) {
        if (props.toFn !== void 0) {
          data.to = props.toFn(page)
        }
        else {
          data.onClick = () => { set(page) }
        }
      }

      return h(QBtn, data)
    }

    // expose public methods
    Object.assign(proxy, { set, setByOffset })

    return () => {
      const contentStart = []
      const contentEnd = []
      let contentMiddle

      if (boundaryLinksProp.value === true) {
        contentStart.push(
          getBtn({
            key: 'bls',
            disable: props.disable || props.modelValue <= minProp.value,
            icon: icons.value[ 0 ]
          }, minProp.value)
        )

        contentEnd.unshift(
          getBtn({
            key: 'ble',
            disable: props.disable || props.modelValue >= maxProp.value,
            icon: icons.value[ 3 ]
          }, maxProp.value)
        )
      }

      if (directionLinksProp.value === true) {
        contentStart.push(
          getBtn({
            key: 'bdp',
            disable: props.disable || props.modelValue <= minProp.value,
            icon: icons.value[ 1 ]
          }, props.modelValue - 1)
        )

        contentEnd.unshift(
          getBtn({
            key: 'bdn',
            disable: props.disable || props.modelValue >= maxProp.value,
            icon: icons.value[ 2 ]
          }, props.modelValue + 1)
        )
      }

      if (props.input !== true) { // has buttons instead of inputbox
        contentMiddle = []
        const { pgFrom, pgTo, marginalStyle: style } = btnConfig.value

        if (btnConfig.value.boundaryStart === true) {
          const active = minProp.value === props.modelValue
          contentStart.push(
            getBtn({
              key: 'bns',
              style,
              disable: props.disable,
              label: minProp.value
            }, minProp.value, active)
          )
        }

        if (btnConfig.value.boundaryEnd === true) {
          const active = maxProp.value === props.modelValue
          contentEnd.unshift(
            getBtn({
              key: 'bne',
              style,
              disable: props.disable,
              label: maxProp.value
            }, maxProp.value, active)
          )
        }

        if (btnConfig.value.ellipsesStart === true) {
          contentStart.push(
            getBtn({
              key: 'bes',
              style,
              disable: props.disable,
              label: '…',
              ripple: false
            }, btnConfig.value.pgFrom - 1)
          )
        }

        if (btnConfig.value.ellipsesEnd === true) {
          contentEnd.unshift(
            getBtn({
              key: 'bee',
              style,
              disable: props.disable,
              label: '…',
              ripple: false
            }, pgTo + 1)
          )
        }

        for (let i = pgFrom; i <= pgTo; i++) {
          contentMiddle.push(
            getBtn({
              key: `bpg${ i }`,
              style,
              disable: props.disable,
              label: i
            }, i, i === props.modelValue)
          )
        }
      }

      return h('div', {
        class: classes.value,
        ...attrs.value
      }, [
        h('div', {
          class: 'q-pagination__content row no-wrap items-center',
          style: gutterStyle.value
        }, [
          ...contentStart,

          props.input === true
            ? h(QInput, {
              class: 'inline',
              style: { width: `${ inputPlaceholder.value.length / 1.5 }em` },
              type: 'number',
              dense: true,
              value: newPage.value,
              disable: props.disable,
              dark: isDark.value,
              borderless: true,
              inputClass: props.inputClass,
              inputStyle: props.inputStyle,
              placeholder: inputPlaceholder.value,
              min: minProp.value,
              max: maxProp.value,
              ...inputEvents.value
            })
            : h('div', {
              class: 'q-pagination__middle row justify-center'
            }, contentMiddle),

          ...contentEnd
        ])
      ])
    }
  }
})
<commit_msg>chore(QPagination): really small tweak<commit_after>import { h, ref, watch, computed, getCurrentInstance } from 'vue'

import QBtn from '../btn/QBtn.js'
import QInput from '../input/QInput.js'

import useDark, { useDarkProps } from '../../composables/private/use-dark.js'
import { btnDesignOptions, btnPadding, getBtnDesign } from '../btn/use-btn.js'

import { createComponent } from '../../utils/private/create.js'
import { between } from '../../utils/format.js'
import { isKeyCode } from '../../utils/private/key-composition.js'

function getBool (val, otherwise) {
  return [ true, false ].includes(val)
    ? val
    : otherwise
}

export default createComponent({
  name: 'QPagination',

  props: {
    ...useDarkProps,

    modelValue: {
      type: Number,
      required: true
    },
    min: {
      type: [ Number, String ],
      default: 1
    },
    max: {
      type: [ Number, String ],
      required: true
    },
    maxPages: {
      type: [ Number, String ],
      default: 0,
      validator: v => (
        (typeof v === 'string' ? parseInt(v, 10) : v) >= 0
      )
    },

    inputStyle: [ Array, String, Object ],
    inputClass: [ Array, String, Object ],

    size: String,

    disable: Boolean,

    input: Boolean,

    iconPrev: String,
    iconNext: String,
    iconFirst: String,
    iconLast: String,

    toFn: Function,

    boundaryLinks: {
      type: Boolean,
      default: null
    },
    boundaryNumbers: {
      type: Boolean,
      default: null
    },
    directionLinks: {
      type: Boolean,
      default: null
    },
    ellipses: {
      type: Boolean,
      default: null
    },

    ripple: {
      type: [ Boolean, Object ],
      default: null
    },

    round: Boolean,
    rounded: Boolean,

    flat: Boolean,
    outline: Boolean,
    unelevated: Boolean,
    push: Boolean,
    glossy: Boolean,

    color: {
      type: String,
      default: 'primary'
    },
    textColor: String,

    activeDesign: {
      type: String,
      default: '',
      values: v => v === '' || btnDesignOptions.includes(v)
    },
    activeColor: String,
    activeTextColor: String,

    gutter: String,
    padding: {
      type: String,
      default: '3px 2px'
    }
  },

  emits: [ 'update:modelValue' ],

  setup (props, { emit }) {
    const { proxy } = getCurrentInstance()
    const { $q } = proxy

    const isDark = useDark(props, $q)

    const minProp = computed(() => parseInt(props.min, 10))
    const maxProp = computed(() => parseInt(props.max, 10))
    const maxPagesProp = computed(() => parseInt(props.maxPages, 10))

    const inputPlaceholder = computed(() => model.value + ' / ' + maxProp.value)
    const boundaryLinksProp = computed(() => getBool(props.boundaryLinks, props.input))
    const boundaryNumbersProp = computed(() => getBool(props.boundaryNumbers, !props.input))
    const directionLinksProp = computed(() => getBool(props.directionLinks, props.input))
    const ellipsesProp = computed(() => getBool(props.ellipses, !props.input))

    const newPage = ref(null)
    const model = computed({
      get: () => props.modelValue,
      set: val => {
        val = parseInt(val, 10)
        if (props.disable || isNaN(val)) {
          return
        }
        const value = between(val, minProp.value, maxProp.value)
        if (props.modelValue !== value) {
          emit('update:modelValue', value)
        }
      }
    })

    watch(() => `${ minProp.value }|${ maxProp.value }`, () => {
      model.value = props.modelValue
    })

    const classes = computed(() =>
      'q-pagination row no-wrap items-center'
      + (props.disable === true ? ' disabled' : '')
    )

    const gutterProp = computed(() => (
      props.gutter in btnPadding
        ? `${ btnPadding[ props.gutter ] }px`
        : props.gutter || null
    ))
    const gutterStyle = computed(() => (
      gutterProp.value !== null
        ? `--q-pagination-gutter-parent:-${ gutterProp.value };--q-pagination-gutter-child:${ gutterProp.value }`
        : null
    ))

    const icons = computed(() => {
      const ico = [
        props.iconFirst || $q.iconSet.pagination.first,
        props.iconPrev || $q.iconSet.pagination.prev,
        props.iconNext || $q.iconSet.pagination.next,
        props.iconLast || $q.iconSet.pagination.last
      ]
      return $q.lang.rtl === true ? ico.reverse() : ico
    })

    const attrs = computed(() => ({
      'aria-disabled': props.disable === true ? 'true' : 'false',
      role: 'navigation'
    }))

    const btnDesignProp = computed(() => getBtnDesign(props, 'flat'))
    const btnProps = computed(() => ({
      [ btnDesignProp.value ]: true,

      round: props.round,
      rounded: props.rounded,

      padding: props.padding,

      color: props.color,
      textColor: props.textColor,

      size: props.size,
      ripple: props.ripple !== null
        ? props.ripple
        : true
    }))

    const btnActiveDesignProp = computed(() => {
      // we also reset non-active design
      const acc = { [ btnDesignProp.value ]: false }
      if (props.activeDesign !== '') {
        acc[ props.activeDesign ] = true
      }
      return acc
    })
    const activeBtnProps = computed(() => ({
      ...btnActiveDesignProp.value,
      color: props.activeColor || props.color,
      textColor: props.activeTextColor || props.textColor
    }))

    const btnConfig = computed(() => {
      let maxPages = Math.max(
        maxPagesProp.value,
        1 + (ellipsesProp.value ? 2 : 0) + (boundaryNumbersProp.value ? 2 : 0)
      )

      const acc = {
        pgFrom: minProp.value,
        pgTo: maxProp.value,
        ellipsesStart: false,
        ellipsesEnd: false,
        boundaryStart: false,
        boundaryEnd: false,
        marginalStyle: {
          minWidth: `${ Math.max(2, String(maxProp.value).length) }em`
        }
      }

      if (maxPagesProp.value && maxPages < (maxProp.value - minProp.value + 1)) {
        maxPages = 1 + Math.floor(maxPages / 2) * 2
        acc.pgFrom = Math.max(minProp.value, Math.min(maxProp.value - maxPages + 1, props.modelValue - Math.floor(maxPages / 2)))
        acc.pgTo = Math.min(maxProp.value, acc.pgFrom + maxPages - 1)

        if (boundaryNumbersProp.value) {
          acc.boundaryStart = true
          acc.pgFrom++
        }

        if (ellipsesProp.value && acc.pgFrom > (minProp.value + (boundaryNumbersProp.value ? 1 : 0))) {
          acc.ellipsesStart = true
          acc.pgFrom++
        }

        if (boundaryNumbersProp.value) {
          acc.boundaryEnd = true
          acc.pgTo--
        }

        if (ellipsesProp.value && acc.pgTo < (maxProp.value - (boundaryNumbersProp.value ? 1 : 0))) {
          acc.ellipsesEnd = true
          acc.pgTo--
        }
      }

      return acc
    })

    function set (value) {
      model.value = value
    }

    function setByOffset (offset) {
      model.value = model.value + offset
    }

    const inputEvents = computed(() => {
      function updateModel () {
        model.value = newPage.value
        newPage.value = null
      }

      return {
        'onUpdate:modelValue': val => { newPage.value = val },
        onKeyup: e => { isKeyCode(e, 13) === true && updateModel() },
        onBlur: updateModel
      }
    })

    function getBtn (cfg, page, active) {
      const data = {
        'aria-label': page,
        'aria-current': 'false',
        ...btnProps.value,
        ...cfg
      }

      if (active === true) {
        Object.assign(data, {
          'aria-current': 'true',
          ...activeBtnProps.value
        })
      }

      if (page !== void 0) {
        if (props.toFn !== void 0) {
          data.to = props.toFn(page)
        }
        else {
          data.onClick = () => { set(page) }
        }
      }

      return h(QBtn, data)
    }

    // expose public methods
    Object.assign(proxy, { set, setByOffset })

    return () => {
      const contentStart = []
      const contentEnd = []
      let contentMiddle

      if (boundaryLinksProp.value === true) {
        contentStart.push(
          getBtn({
            key: 'bls',
            disable: props.disable || props.modelValue <= minProp.value,
            icon: icons.value[ 0 ]
          }, minProp.value)
        )

        contentEnd.unshift(
          getBtn({
            key: 'ble',
            disable: props.disable || props.modelValue >= maxProp.value,
            icon: icons.value[ 3 ]
          }, maxProp.value)
        )
      }

      if (directionLinksProp.value === true) {
        contentStart.push(
          getBtn({
            key: 'bdp',
            disable: props.disable || props.modelValue <= minProp.value,
            icon: icons.value[ 1 ]
          }, props.modelValue - 1)
        )

        contentEnd.unshift(
          getBtn({
            key: 'bdn',
            disable: props.disable || props.modelValue >= maxProp.value,
            icon: icons.value[ 2 ]
          }, props.modelValue + 1)
        )
      }

      if (props.input !== true) { // has buttons instead of inputbox
        contentMiddle = []
        const { pgFrom, pgTo, marginalStyle: style } = btnConfig.value

        if (btnConfig.value.boundaryStart === true) {
          const active = minProp.value === props.modelValue
          contentStart.push(
            getBtn({
              key: 'bns',
              style,
              disable: props.disable,
              label: minProp.value
            }, minProp.value, active)
          )
        }

        if (btnConfig.value.boundaryEnd === true) {
          const active = maxProp.value === props.modelValue
          contentEnd.unshift(
            getBtn({
              key: 'bne',
              style,
              disable: props.disable,
              label: maxProp.value
            }, maxProp.value, active)
          )
        }

        if (btnConfig.value.ellipsesStart === true) {
          contentStart.push(
            getBtn({
              key: 'bes',
              style,
              disable: props.disable,
              label: '…',
              ripple: false
            }, pgFrom - 1)
          )
        }

        if (btnConfig.value.ellipsesEnd === true) {
          contentEnd.unshift(
            getBtn({
              key: 'bee',
              style,
              disable: props.disable,
              label: '…',
              ripple: false
            }, pgTo + 1)
          )
        }

        for (let i = pgFrom; i <= pgTo; i++) {
          contentMiddle.push(
            getBtn({
              key: `bpg${ i }`,
              style,
              disable: props.disable,
              label: i
            }, i, i === props.modelValue)
          )
        }
      }

      return h('div', {
        class: classes.value,
        ...attrs.value
      }, [
        h('div', {
          class: 'q-pagination__content row no-wrap items-center',
          style: gutterStyle.value
        }, [
          ...contentStart,

          props.input === true
            ? h(QInput, {
              class: 'inline',
              style: { width: `${ inputPlaceholder.value.length / 1.5 }em` },
              type: 'number',
              dense: true,
              value: newPage.value,
              disable: props.disable,
              dark: isDark.value,
              borderless: true,
              inputClass: props.inputClass,
              inputStyle: props.inputStyle,
              placeholder: inputPlaceholder.value,
              min: minProp.value,
              max: maxProp.value,
              ...inputEvents.value
            })
            : h('div', {
              class: 'q-pagination__middle row justify-center'
            }, contentMiddle),

          ...contentEnd
        ])
      ])
    }
  }
})
<|endoftext|>


<commit_before>const initialState = {
  time: 0,
  songIndex: 0,
  isPlaying: false
}

export default function player(state = initialState, action) {
  switch (action.type) {
  case 'UPDATE_CURRENT_PLAYER_TIME':
    if (state.isPlaying) {
      return Object.assign({}, state, {
        time: action.time,
        isPlaying: true
      })
    }
    else {
      return Object.assign({}, state, {
        time: action.time,
        isPlaying: false
      })
   }
  case 'UPDATE_CURRENT_PLAYLIST_SONG':
    return Object.assign({}, state, {
      songIndex: action.songIndex
    })
  case 'SET_IS_PLAYING':
    return Object.assign({}, state, {
      isPlaying: true
    })
  case 'SET_NOT_PLAYING':
    return Object.assign({}, state, {
      isPlaying: false
    })
  default:
    return state
  }
}

<commit_msg>Fix join on room with currently playing song bug<commit_after>const initialState = {
  time: 0,
  songIndex: 0,
  isPlaying: false,
  isPaused: false
}

export default function player(state = initialState, action) {
  switch (action.type) {
  case 'UPDATE_CURRENT_PLAYER_TIME':
    if (!state.isPaused) {
      return Object.assign({}, state, {
        time: action.time,
        isPlaying: true
      })
    }
    else {
      return Object.assign({}, state, {
        time: action.time,
        isPlaying: false
      })
    }
  case 'UPDATE_CURRENT_PLAYLIST_SONG':
    return Object.assign({}, state, {
      songIndex: action.songIndex
    })
  case 'SET_IS_PLAYING':
    return Object.assign({}, state, {
      isPlaying: true,
      isPaused: false
    })
  case 'SET_NOT_PLAYING':
    return Object.assign({}, state, {
      isPlaying: false,
      isPaused: true
    })
  default:
    return state
  }
}

<|endoftext|>


<commit_before>const Utility = require( '../../../js/utility.js' )
const $ = Utility.create

module.exports = ( patternObject, marker, className, cm, track, patternNode, Marker ) => {
  let val ='/* ' + patternObject.values.join('')  + ' */',
      pos = marker.find(),
      end = Object.assign( {}, pos.to ),
      annotationStartCh = pos.from.ch + 3,
      annotationEndCh   = annotationStartCh + 1,
      memberAnnotationStart   = Object.assign( {}, pos.from ),
      memberAnnotationEnd     = Object.assign( {}, pos.to ),
      initialized = false,
      markStart = null,
      commentMarker,
      currentMarker, chEnd

  patternObject.__delayAnnotations = false
  end.ch = pos.from.ch + val.length

  pos.to.ch -= 1
  cm.replaceRange( val, pos.from, pos.to )

  patternObject.commentMarker = cm.markText( pos.from, end, { className, atomic:false })

  if( track.markup === undefined ) Marker.prepareObject( track )
  track.markup.textMarkers[ className ] = {}

  let mark = () => {
    // first time through, use the position given to us by the parser
    let range,start, end

    // get new position in case the pattern has moved via inserted line breaks
    const pos = patternObject.commentMarker.find()
    if( pos === undefined ) return
    let memberAnnotationStart   = Object.assign( {}, pos.from ),
          memberAnnotationEnd     = Object.assign( {}, pos.to )

    if( initialized === false ) {
      memberAnnotationStart.ch = annotationStartCh
      memberAnnotationEnd.ch   = annotationEndCh
      initialized = true
    }else{
      // after the first time through, every update to the pattern store the current
      // position of the first element (in markStart) before replacing. Use this to generate position
      // info. REPLACING TEXT REMOVES TEXT MARKERS.
      range = markStart
      start = range.from
      memberAnnotationStart.ch = start.ch
      memberAnnotationEnd.ch = start.ch + 1
    }

    for( let i = 0; i < patternObject.values.length; i++ ) {
      track.markup.textMarkers[ className ][ i ] = cm.markText(
        memberAnnotationStart,  memberAnnotationEnd,
        { 'className': `${className}_${i} euclid` }
      )

      memberAnnotationStart.ch += 1
      memberAnnotationEnd.ch   += 1
    }

    if( start !== undefined ) {
      start.ch -= 3
      end = Object.assign({}, start )
      end.ch = memberAnnotationEnd.ch + 3
      patternObject.commentMarker = cm.markText( start, end, { className, atomic:true })
    }
  }

  mark()

  // XXX: there's a bug when you sequence pattern transformations, and then insert newlines ABOVE the annotation
  let count = 0, span, update, activeSpans = []

  update = () => {
    // XXX what happened??? this should be incremented by 1, and there
    // should be no need for Math.floor
    // works with 1 increment in HexSteps but not in Hex... :(
    count += 1
    let currentIdx = Math.floor( count ) % patternObject.values.length

    if( span !== undefined ) {
      span.remove( 'euclid0' )
    }

    let spanName = `.${className}_${currentIdx}`,
        currentValue = patternObject.values[ currentIdx ]

    span = $( spanName )

    // deliberate ==
    if( currentValue == 1 ) {
      span.add( 'euclid0' )
      span.add( 'euclid1' )
      activeSpans.push( span )
      setTimeout( ()=> {
        activeSpans.forEach( _span => _span.remove( 'euclid1' ) )
        activeSpans.length = 0
      }, 50 )
    }else{
      span.add( 'euclid0' )
    }
  }

  patternObject._onchange = () => {
    //let delay = Utility.beatsToMs( 1,  Gibber.Scheduler.bpm )

    // markStart is a closure variable that will be used in the call to mark()
    markStart = track.markup.textMarkers[ className ][ 0 ].find()
    markEnd   = track.markup.textMarkers[ className ][ patternObject.values.length - 1  ].find()

    if( markStart !== undefined && markEnd !== undefined ) {
      marker.doc.replaceRange( '' + patternObject.values.join(''), markStart.from, markEnd.to )
    }

    //for( let i = 0; i < patternObject.values.length; i++ ) {

    //  let markerCh = track.markup.textMarkers[ className ][ i ],
    //      pos = markerCh.find()

    //  // break for loop and remark pattern if a character
    //  // isn't found
    //  if( pos === undefined ) {
    //    break
    //  }

    //  marker.doc.replaceRange( '' + patternObject.values[ i ], pos.from, pos.to )
    //}

    mark()
  }

  patternObject.clear = () => {
    const commentPos = patternObject.commentMarker.find()

    // if this gets called twice...
    if( commentPos === undefined ) return

    // check to see if the last character is a parenthesis... if so we could
    // not add 1 to commentPos.to.ch so that we don't accidentally delete it.
    const end = { line:commentPos.to.line, ch:commentPos.to.ch+1 }
    const text = cm.getRange( commentPos.from, end )
    const replacement = text[ text.length - 1 ] === ')' ? ')' : ''

    cm.replaceRange( replacement, commentPos.from, end )
    patternObject.commentMarker.clear()
  }

  Gibber.subscribe( 'clear', patternObject.clear )

  return update
}

<commit_msg>freezing euclidean annotations<commit_after>const Utility = require( '../../../js/utility.js' )
const $ = Utility.create

module.exports = ( patternObject, marker, className, cm, track, patternNode, Marker ) => {
  let val ='/* ' + patternObject.values.join('')  + ' */',
      pos = marker.find(),
      end = Object.assign( {}, pos.to ),
      annotationStartCh = pos.from.ch + 3,
      annotationEndCh   = annotationStartCh + 1,
      memberAnnotationStart   = Object.assign( {}, pos.from ),
      memberAnnotationEnd     = Object.assign( {}, pos.to ),
      initialized = false,
      markStart = null,
      commentMarker,
      currentMarker, chEnd

  patternObject.__delayAnnotations = false
  end.ch = pos.from.ch + val.length

  pos.to.ch -= 1
  cm.replaceRange( val, pos.from, pos.to )

  patternObject.commentMarker = cm.markText( pos.from, end, { className, atomic:false })
  patternObject.__onclick = e => {
    if( e.altKey == true ) {
      console.log( 'click', e.shiftKey )
      if( e.shiftKey === true ) {
        patternObject.reset()
      }else{
        patternObject.__frozen = !patternObject.__frozen
      }
    }
  }

  setTimeout( ()=> {
    document.querySelector( '.' + className ).onclick = patternObject.__onclick
  }, 500 )

  if( track.markup === undefined ) Marker.prepareObject( track )
  track.markup.textMarkers[ className ] = {}

  let mark = () => {
    // first time through, use the position given to us by the parser
    let range,start, end

    // get new position in case the pattern has moved via inserted line breaks
    const pos = patternObject.commentMarker.find()
    if( pos === undefined ) return
    let memberAnnotationStart   = Object.assign( {}, pos.from ),
        memberAnnotationEnd     = Object.assign( {}, pos.to )

    if( initialized === false ) {
      memberAnnotationStart.ch = annotationStartCh
      memberAnnotationEnd.ch   = annotationEndCh
      initialized = true
    }else{
      // after the first time through, every update to the pattern store the current
      // position of the first element (in markStart) before replacing. Use this to generate position
      // info. REPLACING TEXT REMOVES TEXT MARKERS.
      range = markStart
      start = range.from
      memberAnnotationStart.ch = start.ch
      memberAnnotationEnd.ch = start.ch + 1
    }

    for( let i = 0; i < patternObject.values.length; i++ ) {
      window.__ignore = patternObject
      track.markup.textMarkers[ className ][ i ] = cm.markText(
        memberAnnotationStart,  memberAnnotationEnd,
        {
          'className': `${className}_${i} euclid`
        }
      )

      memberAnnotationStart.ch += 1
      memberAnnotationEnd.ch   += 1

      setTimeout( ()=> {
        document.querySelector( `.${className}_${i}` ).onclick = patternObject.__onclick
      }, 50 )
    }

    if( start !== undefined ) {
      start.ch -= 3
      end = Object.assign({}, start )
      end.ch = memberAnnotationEnd.ch + 3
      patternObject.commentMarker = cm.markText( start, end, { className, atomic:true })
    }
  }

  mark()

  // XXX: there's a bug when you sequence pattern transformations, and then insert newlines ABOVE the annotation
  let count = 0, span, update, activeSpans = []

  update = () => {
    // XXX what happened??? this should be incremented by 1, and there
    // should be no need for Math.floor
    // works with 1 increment in HexSteps but not in Hex... :(
    count += 1
    let currentIdx = Math.floor( count ) % patternObject.values.length

    if( span !== undefined ) {
      span.remove( 'euclid0' )
    }

    let spanName = `.${className}_${currentIdx}`,
        currentValue = patternObject.values[ currentIdx ]

    span = $( spanName )

    // deliberate ==
    if( currentValue == 1 ) {
      span.add( 'euclid0' )
      span.add( 'euclid1' )
      activeSpans.push( span )
      setTimeout( ()=> {
        activeSpans.forEach( _span => _span.remove( 'euclid1' ) )
        activeSpans.length = 0
      }, 50 )
    }else{
      span.add( 'euclid0' )
    }
  }

  patternObject._onchange = () => {
    //let delay = Utility.beatsToMs( 1,  Gibber.Scheduler.bpm )

    // markStart is a closure variable that will be used in the call to mark()
    markStart = track.markup.textMarkers[ className ][ 0 ].find()
    markEnd   = track.markup.textMarkers[ className ][ patternObject.values.length - 1  ].find()

    if( markStart !== undefined && markEnd !== undefined ) {
      marker.doc.replaceRange( '' + patternObject.values.join(''), markStart.from, markEnd.to )
    }

    //for( let i = 0; i < patternObject.values.length; i++ ) {

    //  let markerCh = track.markup.textMarkers[ className ][ i ],
    //      pos = markerCh.find()

    //  // break for loop and remark pattern if a character
    //  // isn't found
    //  if( pos === undefined ) {
    //    break
    //  }

    //  marker.doc.replaceRange( '' + patternObject.values[ i ], pos.from, pos.to )
    //}

    mark()
  }

  patternObject.clear = () => {
    const commentPos = patternObject.commentMarker.find()

    // if this gets called twice...
    if( commentPos === undefined ) return

    // check to see if the last character is a parenthesis... if so we could
    // not add 1 to commentPos.to.ch so that we don't accidentally delete it.
    const end = { line:commentPos.to.line, ch:commentPos.to.ch+1 }
    const text = cm.getRange( commentPos.from, end )
    const replacement = text[ text.length - 1 ] === ')' ? ')' : ''

    cm.replaceRange( replacement, commentPos.from, end )
    patternObject.commentMarker.clear()
  }

  Gibber.subscribe( 'clear', patternObject.clear )

  return update
}

<|endoftext|>


<commit_before>/* Author:

*/
var last_search = null;
var current_search_obj = null;
var search_url = null;

function init_livesearch(searchUrl) {
    var typeDelay = 200;
    search_url = searchUrl;

    $('#searchbox').keyup(function () {
        if (this.value != this.lastValue) {
            var query = this.value;

            if (this.requestTimer) {
                clearTimeout(this.requestTimer);
            }

            this.requestTimer = setTimeout(function() {
			    if (current_search_obj != null && current_search_obj.abort) {
				    current_search_obj.abort();
				}
				do_search(query);

            }, typeDelay);

            this.lastValue = query;
        }
    });
};

function do_search(url) {
    last_search = search_url + url;

    current_search_obj = jQuery.get(last_search, function(data) {
        if (data.length) {
            $('#changelist-form').html(data);
            prepare_changelist();
        } else {
            last_search = null;
			$('#changelist-form').html('');
        }
    });
}

function repeat_search() {
    do_search(last_search);
}

function prepare_changelist() {
    // apply the "success, failure and warning" class tocells containing a span
    // with the equivalent classes. Ugly hack to allow admin generated values to
    // change the display of the parent container

    $("td span.success").parent().addClass("success");
    $("td span.warning").parent().addClass("warning");
    $("td span.error").parent().addClass("error");

	if ($(".iframe").fancybox) {
		$(".iframe").fancybox({
					'width'				: '100%',
					'height'				: '100%',
					'scrolling'				: 'no',
					'autoScale'				: true,
					'transitionIn'			: 'elastic',
					'transitionOut'			: 'elastic',
					'type'				: 'iframe'
				});
	}

}

$(document).ready(prepare_changelist);<commit_msg>Fixed problem with check-in window for low resolutions<commit_after>/* Author:

*/
var last_search = null;
var current_search_obj = null;
var search_url = null;

function init_livesearch(searchUrl) {
    var typeDelay = 200;
    search_url = searchUrl;

    $('#searchbox').keyup(function () {
        if (this.value != this.lastValue) {
            var query = this.value;

            if (this.requestTimer) {
                clearTimeout(this.requestTimer);
            }

            this.requestTimer = setTimeout(function() {
			    if (current_search_obj != null && current_search_obj.abort) {
				    current_search_obj.abort();
				}
				do_search(query);

            }, typeDelay);

            this.lastValue = query;
        }
    });
};

function do_search(url) {
    last_search = search_url + url;

    current_search_obj = jQuery.get(last_search, function(data) {
        if (data.length) {
            $('#changelist-form').html(data);
            prepare_changelist();
        } else {
            last_search = null;
			$('#changelist-form').html('');
        }
    });
}

function repeat_search() {
    do_search(last_search);
}

function prepare_changelist() {
    // apply the "success, failure and warning" class tocells containing a span
    // with the equivalent classes. Ugly hack to allow admin generated values to
    // change the display of the parent container

    $("td span.success").parent().addClass("success");
    $("td span.warning").parent().addClass("warning");
    $("td span.error").parent().addClass("error");

	if ($(".iframe").fancybox) {
		$(".iframe").fancybox({
					'width'				: '100%',
					'height'				: '100%',
					'scrolling'				: 'auto',
					'autoScale'				: true,
					'transitionIn'			: 'elastic',
					'transitionOut'			: 'elastic',
					'type'				: 'iframe'
				});
	}

}

$(document).ready(prepare_changelist);<|endoftext|>


<commit_before>/*
  ApiVis - Simple JavaScript objects API visualization

  Copyright (c) 2017 Radoslav Peev <rpeev@ymail.com> (MIT License)
*/

(function (exports) {

exports.typeStr = function (obj) {
  let t = Object.prototype.toString.call(obj).
    match(/^\[object ([^\]]*)\]$/)[1];

  if (t == 'Object' &&
    obj.constructor && obj.constructor.name
  ) {
    t = obj.constructor.name;
  }

  if (obj instanceof Error &&
    obj.message
  ) {
    t = `${t}("${obj.message}")`;
  }

  if (obj instanceof Function) {
    t = `${t}(${obj.length})`;
  }

  return t;
};

exports.privateMembers = function (obj) {
  let pms = {};

  for (let k in obj) {
    if (k.startsWith('_')) {
      pms[k] = obj[k];
    }
  }

  return pms;
};

exports.props = function (obj) {
  return Object.getOwnPropertyNames(obj).sort();
};

exports.propsStr = function (obj, inst = obj, indent = '  ', level = 0) {
  return exports.props(obj).
    map(k => {
      let v = null;

      try {
        v = inst[k];
      } catch (err) {
        v = err;
      }

      return `${indent.repeat(level)}${k}: ${exports.typeStr(v)}`;
    }).
    join('\n');
};

exports.protos = function (obj) {
  let chain = [];

  for (let proto = Object.getPrototypeOf(obj);
    proto;
    proto = Object.getPrototypeOf(proto)
  ) {
    chain.push(proto);
  }

  return chain;
};

exports.protosStr = function (obj, indent = '  ') {
  return exports.protos(obj).
    reverse().
    map((o, i) => `${indent.repeat(i)}${(i != 0) ? '\u25b2' : ''}${exports.typeStr(o)}`).
    join('\n');
};

exports.apiStr = function (inst, filters = [], indent = '  ') {
  let chain = exports.protos(inst);

  chain.unshift(inst);

  if (filters.length > 0) {
    if (typeof filters == 'string') {
      filters = [filters];
    }

    chain = chain.filter(o => filters.some(f => exports.typeStr(o).includes(f)));
  }

  return chain.
    reverse().
    map((o, i) => `${indent.repeat(i)}${(i != 0) ? '\u25b2' : ''}${exports.typeStr(o)}\n${exports.propsStr(o, inst, indent, i + 1)}`).
    join('\n');
};

})((typeof window != 'undefined') ? window.apivis = {} : exports);
<commit_msg>Tweak property lookup in propsStr<commit_after>/*
  ApiVis - Simple JavaScript objects API visualization

  Copyright (c) 2017 Radoslav Peev <rpeev@ymail.com> (MIT License)
*/

(function (exports) {

exports.typeStr = function (obj) {
  let t = Object.prototype.toString.call(obj).
    match(/^\[object ([^\]]*)\]$/)[1];

  if (t == 'Object' &&
    obj.constructor && obj.constructor.name
  ) {
    t = obj.constructor.name;
  }

  if (obj instanceof Error &&
    obj.message
  ) {
    t = `${t}("${obj.message}")`;
  }

  if (obj instanceof Function) {
    t = `${t}(${obj.length})`;
  }

  return t;
};

exports.privateMembers = function (obj) {
  let pms = {};

  for (let k in obj) {
    if (k.startsWith('_')) {
      pms[k] = obj[k];
    }
  }

  return pms;
};

exports.props = function (obj) {
  return Object.getOwnPropertyNames(obj).sort();
};

exports.propsStr = function (obj, inst = obj, indent = '  ', level = 0) {
  return exports.props(obj).
    map(k => {
      let v = null;

      try {
        let o = (['constructor', 'prototype'].includes(k)) ?
          obj :
          inst;

        v = o[k];
      } catch (err) {
        v = err;
      }

      return `${indent.repeat(level)}${k}: ${exports.typeStr(v)}`;
    }).
    join('\n');
};

exports.protos = function (obj) {
  let chain = [];

  for (let proto = Object.getPrototypeOf(obj);
    proto;
    proto = Object.getPrototypeOf(proto)
  ) {
    chain.push(proto);
  }

  return chain;
};

exports.protosStr = function (obj, indent = '  ') {
  return exports.protos(obj).
    reverse().
    map((o, i) => `${indent.repeat(i)}${(i != 0) ? '\u25b2' : ''}${exports.typeStr(o)}`).
    join('\n');
};

exports.apiStr = function (inst, filters = [], indent = '  ') {
  let chain = exports.protos(inst);

  chain.unshift(inst);

  if (filters.length > 0) {
    if (typeof filters == 'string') {
      filters = [filters];
    }

    chain = chain.filter(o => filters.some(f => exports.typeStr(o).includes(f)));
  }

  return chain.
    reverse().
    map((o, i) => `${indent.repeat(i)}${(i != 0) ? '\u25b2' : ''}${exports.typeStr(o)}\n${exports.propsStr(o, inst, indent, i + 1)}`).
    join('\n');
};

})((typeof window != 'undefined') ? window.apivis = {} : exports);
<|endoftext|>


<commit_before>
var tumblr = require('../lib/tumblr');
var utils = require('../lib/utils');
var settings = require('../settings');
var views = require('./index');

exports.index = function(req, res) {
  var oauth = req.session.oauth || {};
  var username = req.params.username;
  var format = req.params.format || 'html';
  var page = (req.query.page || 1) * 1;

  var options = {
    consumer_key: settings.TUMBLR.CONSUMER_KEY,
    secret_key: settings.TUMBLR.SECRET_KEY,
    type: 'photo',
    limit: 20,
    username: username,
    page: page
  };

  if (username === '_dashboard') {
    if (oauth.access_token && oauth.access_token_secret) {
      options.access_token = oauth.access_token;
      options.access_token_secret = oauth.access_token_secret;
    } else {
      res.redirect('/_oauth/signin');
      return;
    }
  }

  tumblr.request(options, function(tum) {
    tum.on('data', function(data) {
      console.log(tum);
      var posts = data.response.posts || [];
      var sections = posts.map(utils.section_simplify);
      if (!sections.length) {
        tum.emit('error');
        return;
      }
      res.render('user', {
        username: username,
        page: page,
        sections: sections
      });
    }).on('error', function(e) {
      views.http404(req, res);
    });
  }).end();
};
<commit_msg>デバッグ用のconsole.logを消しわすれていたのを消去。<commit_after>
var tumblr = require('../lib/tumblr');
var utils = require('../lib/utils');
var settings = require('../settings');
var views = require('./index');

exports.index = function(req, res) {
  var oauth = req.session.oauth || {};
  var username = req.params.username;
  var format = req.params.format || 'html';
  var page = (req.query.page || 1) * 1;

  var options = {
    consumer_key: settings.TUMBLR.CONSUMER_KEY,
    secret_key: settings.TUMBLR.SECRET_KEY,
    type: 'photo',
    limit: 20,
    username: username,
    page: page
  };

  if (username === '_dashboard') {
    if (oauth.access_token && oauth.access_token_secret) {
      options.access_token = oauth.access_token;
      options.access_token_secret = oauth.access_token_secret;
    } else {
      res.redirect('/_oauth/signin');
      return;
    }
  }

  tumblr.request(options, function(tum) {
    tum.on('data', function(data) {
      var posts = data.response.posts || [];
      var sections = posts.map(utils.section_simplify);
      if (!sections.length) {
        tum.emit('error');
        return;
      }
      res.render('user', {
        username: username,
        page: page,
        sections: sections
      });
    }).on('error', function(e) {
      views.http404(req, res);
    });
  }).end();
};
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2015 Instituto Nacional de Servicios Sociales para Jubilados y Pensionados

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

(function(){
  "use strict";

var module = angular.module("elastic.search",[]);

module.provider('$elasticsearch',function(){

  self = this;

  var config = {
    url:"",
    size:10,
    cssClass :""
  };

  this.setConfig = function(config){
    self.config = config;
  };

  this.$get = ["$http",function($http){
    return new ElasticSearch($http,self.config);
  }];

});

function ElasticSearch($http,config){

    self = this;
    this.url = config.url;

    this.buildQuery = function(index){

      return this.url+"/"+index+"/_search";

    };

    this.search = function(index,fields,query,transformation){
      var q = this.buildQuery(index);

      var params = {
        size: config.size
      };

      var fieldsToHigh = this.fieldsToHighlight(fields);
      var body = self.createBody(fields,query,fieldsToHigh,config.cssClass);

      return $http.post(q,body,{params:params}).then(function(response){
        var data = response.data;
        return data.hits.hits.map(transformation);
      });

    };

    this.createBody = function(fields,query,fieldsToHigh,cssClass){
      return {
        query: {
          fuzzy_like_this : {
            fields : fields,
            like_text : query
          }
          },
          highlight : {
              pre_tags : [''],
              post_tags : [""],
              fields : fieldsToHigh
          }

      };
    };

      this.fieldsToHighlight = function(fields){
            var response = {};
            fields.forEach(function(field){
              response[field] = {force_source : true};
            });
        return response;
      };

    this.fuzzyWithHighlights = function(index,fields,query){

      return this.search(index,fields,query,function(elem){

        var item = elem._source;
        var highlights = elem.highlight;

        fields.forEach(function(field){

          var fieldName = self.generateHighlightedFieldName(field);
          var highlightedField = highlights[field];
          if(highlightedField !== undefined){
            // Si esta existe el campo en highlight entonces
            // reemplazo el valor original por el highlight;
            item[fieldName] = highlightedField[0];
          }else{
            // En caso contrario escribo el campo pero uso el valor original
            item[fieldName] = item[field];
          }
        });
        return item;
      });
    };

    this.generateHighlightedFieldName = function(field){
      return field+"_highlighted";
    };

    this.fuzzyWithoutHighlights = function(index,fields,query){
      return this.search(index,fields,query,function(elem){
        return elem._source;
      });
    };

    this.fuzzy  = function(index,fields,query,highlight){
      if(highlight === true){
        return this.fuzzyWithHighlights(index,fields,query);
      }else{
        return this.fuzzyWithoutHighlights(index,fields,query);
      }
    };

}

})();
<commit_msg>indent<commit_after>/*
The MIT License (MIT)

Copyright (c) 2015 Instituto Nacional de Servicios Sociales para Jubilados y Pensionados

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

(function(){
  "use strict";

var module = angular.module("elastic.search",[]);

module.provider('$elasticsearch',function(){

  self = this;

  var config = {
    url:"http://localhost:9200",
    size:10,
    cssClass :""
  };

  this.setConfig = function(config){
    self.config = config;
  };

  this.$get = ["$http",function($http){
    return new ElasticSearch($http,self.config);
  }];

});

function ElasticSearch($http,config){

    self = this;
    this.url = config.url;

    this.buildQuery = function(index){

      return this.url+"/"+index+"/_search";

    };

    this.search = function(index,fields,query,transformation){
      var q = this.buildQuery(index);

      var params = {
        size: config.size
      };

      var fieldsToHigh = this.fieldsToHighlight(fields);
      var body = self.createBody(fields,query,fieldsToHigh,config.cssClass);

      return $http.post(q,body,{params:params}).then(function(response){
        var data = response.data;
        return data.hits.hits.map(transformation);
      });

    };

    this.createBody = function(fields,query,fieldsToHigh,cssClass){
      return {
        query: {
          fuzzy_like_this : {
            fields : fields,
            like_text : query
          }
          },
          highlight : {
              pre_tags : [''],
              post_tags : [""],
              fields : fieldsToHigh
          }

      };
    };

      this.fieldsToHighlight = function(fields){
            var response = {};
            fields.forEach(function(field){
              response[field] = {force_source : true};
            });
        return response;
      };

    this.fuzzyWithHighlights = function(index,fields,query){

      return this.search(index,fields,query,function(elem){

        var item = elem._source;
        var highlights = elem.highlight;

        fields.forEach(function(field){

          var fieldName = self.generateHighlightedFieldName(field);
          var highlightedField = highlights[field];

          if(highlightedField !== undefined){
            // Si esta existe el campo en highlight entonces
            // reemplazo el valor original por el highlight;
            item[fieldName] = highlightedField[0];
          }else{
            // En caso contrario escribo el campo pero uso el valor original
            item[fieldName] = item[field];
          }

        });
        return item;
      });
    };

    this.generateHighlightedFieldName = function(field)
       return field+"_highlighted";
    };

    this.fuzzyWithoutHighlights = function(index,fields,query){
      return this.search(index,fields,query,function(elem){
        return elem._source;
      });
    };

    this.fuzzy  = function(index,fields,query,highlight){
      if(highlight === true){
        return this.fuzzyWithHighlights(index,fields,query);
      }else{
        return this.fuzzyWithoutHighlights(index,fields,query);
      }
    };

}

})();
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Link } from 'react-router';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import { FormattedMessage } from 'react-intl';
import IconButton from 'material-ui/IconButton';
import styled from 'styled-components';
import Projects from './drawer/Projects';
import { stringHelper } from '../customHelpers';
import UserMenuItems from './UserMenuItems';
import UserAvatarRelay from '../relay/UserAvatarRelay';
import CheckContext from '../CheckContext';
import {
  Row,
  Offset,
  HeaderTitle,
  white,
  black05,
  black54,
  units,
  caption,
  avatarSize,
  avatarStyle,
  body2,
} from '../styles/js/shared';

class DrawerNavigation extends Component {
  getHistory() {
    const history = new CheckContext(this).getContextStore().history;
    return history;
  }

  render() {
    const { inTeamContext, loggedIn, drawerToggle } = this.props;

    // This component now renders based on teamPublicFragment
    // and decides whether to include <Project> which has its own team route/relay
    //
    // See DrawerNavigation
    //
    // — @chris with @alex 2017-10-3

    const currentUserIsMember = this.props.currentUserIsMember;

    const drawerHeaderHeight = units(14);

    const styles = {
      drawerFooter: {
        display: 'flex',
        alignItems: 'flex-start',
        justifyContent: 'space-between',
        backgroundColor: white,
        padding: `${units(2)}`,
        flexWrap: 'wrap',
      },
      drawerFooterLink: {
        font: caption,
      },
      drawerProjects: {
        overflow: 'auto',
        marginBottom: 'auto',
      },
      drawerProjectsAndFooter: {
        display: 'flex',
        flexDirection: 'column',
        height: `calc(100vh - ${drawerHeaderHeight})`,
      },
    };

    const DrawerHeader = styled.div`
      height: ${drawerHeaderHeight};
      background-color: ${black05};
      padding: ${units(2)};
    `;

    // Team Avatar
    const TeamAvatar = styled.div`
      ${avatarStyle}
      width: ${props => (props.size ? props.size : avatarSize)};
      height: ${props => (props.size ? props.size : avatarSize)};
    `;

    const Headline = styled(HeaderTitle)`
      font: ${body2};
      font-weight: 700;
      padding-top: ${units(1)};
      color: ${black54};
    `;

    const TosMenuItem = (

        <FormattedMessage id="headerActions.tos" defaultMessage="Terms" />

    );

    const privacyMenuItem = (

        <FormattedMessage id="headerActions.privacyPolicy" defaultMessage="Privacy" />

    );

    const aboutMenuItem = (

        <FormattedMessage id="headerActions.about" defaultMessage="About" />

    );

    const contactMenuItem = (

        <FormattedMessage id="headerActions.contactHuman" defaultMessage="Contact" />

    );

    const productGuidesMenuItem = (

        <MenuItem
          primaryText={
            <FormattedMessage id="headerActions.productGuides" defaultMessage="Product Guides" />
          }
        />

    );

    const checkLogo = ;

    return (
      <Drawer {...this.props}>

          {inTeamContext
            ? <DrawerHeader>
              <Row style={{ alignItems: 'flex-start', justifyContent: 'space-between' }}>
                <Link style={{ cursor: 'pointer' }} to={`/${this.props.team.slug}/`}>
                  <TeamAvatar
                    style={{ backgroundImage: `url(${this.props.team.avatar})` }}
                    size={units(7)}
                  />
                </Link>
              </Row>

              <Link className="team-header__drawer-team-link" to={`/${this.props.team.slug}/`}>
                <Headline>{this.props.team.name}</Headline>
              </Link>
            </DrawerHeader>
            : <DrawerHeader>
              <Row style={{ alignItems: 'flex-start', justifyContent: 'space-between' }}>
                <Offset>
                  {loggedIn && checkLogo}
                </Offset>
              </Row>
            </DrawerHeader>}

              {inTeamContext && (currentUserIsMember || !this.props.team.private)
                ? <Projects team={this.props.team.slug} />
                : null}

            {loggedIn ?
<UserMenuItems hideContactMenuItem {...this.props} />
 : null}

            {productGuidesMenuItem}

              {TosMenuItem}
              {privacyMenuItem}
              {aboutMenuItem}
              {contactMenuItem}

      </Drawer>
    );
  }
}

DrawerNavigation.contextTypes = {
  store: React.PropTypes.object,
};

export default DrawerNavigation;
<commit_msg>#6455 Use same style for team name on header and drawer<commit_after>import React, { Component } from 'react';
import { Link } from 'react-router';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import { FormattedMessage } from 'react-intl';
import IconButton from 'material-ui/IconButton';
import styled from 'styled-components';
import Projects from './drawer/Projects';
import { stringHelper } from '../customHelpers';
import UserMenuItems from './UserMenuItems';
import UserAvatarRelay from '../relay/UserAvatarRelay';
import CheckContext from '../CheckContext';
import {
  Row,
  Offset,
  OffsetBothSides,
  HeaderTitle,
  white,
  black05,
  black54,
  units,
  caption,
  avatarSize,
  avatarStyle,
  body2,
} from '../styles/js/shared';

class DrawerNavigation extends Component {
  getHistory() {
    const history = new CheckContext(this).getContextStore().history;
    return history;
  }

  render() {
    const { inTeamContext, loggedIn, drawerToggle } = this.props;

    // This component now renders based on teamPublicFragment
    // and decides whether to include <Project> which has its own team route/relay
    //
    // See DrawerNavigation
    //
    // — @chris with @alex 2017-10-3

    const currentUserIsMember = this.props.currentUserIsMember;

    const drawerHeaderHeight = units(14);

    const styles = {
      drawerFooter: {
        display: 'flex',
        alignItems: 'flex-start',
        justifyContent: 'space-between',
        backgroundColor: white,
        padding: `${units(2)}`,
        flexWrap: 'wrap',
      },
      drawerFooterLink: {
        font: caption,
      },
      drawerProjects: {
        overflow: 'auto',
        marginBottom: 'auto',
      },
      drawerProjectsAndFooter: {
        display: 'flex',
        flexDirection: 'column',
        height: `calc(100vh - ${drawerHeaderHeight})`,
      },
    };

    const DrawerHeader = styled.div`
      height: ${drawerHeaderHeight};
      background-color: ${black05};
      padding: ${units(2)};
    `;

    // Team Avatar
    const TeamAvatar = styled.div`
      ${avatarStyle}
      width: ${props => (props.size ? props.size : avatarSize)};
      height: ${props => (props.size ? props.size : avatarSize)};
    `;

    const Headline = styled(HeaderTitle)`
      font: ${body2};
      font-weight: 700;
      padding-top: ${units(1)};
      color: ${black54};
    `;

    const TosMenuItem = (

        <FormattedMessage id="headerActions.tos" defaultMessage="Terms" />

    );

    const privacyMenuItem = (

        <FormattedMessage id="headerActions.privacyPolicy" defaultMessage="Privacy" />

    );

    const aboutMenuItem = (

        <FormattedMessage id="headerActions.about" defaultMessage="About" />

    );

    const contactMenuItem = (

        <FormattedMessage id="headerActions.contactHuman" defaultMessage="Contact" />

    );

    const productGuidesMenuItem = (

        <MenuItem
          primaryText={
            <FormattedMessage id="headerActions.productGuides" defaultMessage="Product Guides" />
          }
        />

    );

    const checkLogo = ;

    return (
      <Drawer {...this.props}>

          {inTeamContext ?
            <DrawerHeader>
              <Link
                className="team-header__drawer-team-link"
                style={{ cursor: 'pointer' }}
                to={`/${this.props.team.slug}/`}
                >
                <Row>
                  <TeamAvatar
                    style={{ backgroundImage: `url(${this.props.team.avatar})` }}
                    size={units(7)}
                    />
                  <OffsetBothSides>
                    <HeaderTitle>{this.props.team.name}</HeaderTitle>
                  </OffsetBothSides>
                </Row>
              </Link>
            </DrawerHeader>
            : <DrawerHeader>
              <Row style={{ alignItems: 'flex-start', justifyContent: 'space-between' }}>
                <Offset>
                  {loggedIn && checkLogo}
                </Offset>
              </Row>
            </DrawerHeader>}

              {inTeamContext && (currentUserIsMember || !this.props.team.private)
                ? <Projects team={this.props.team.slug} />
                : null}

            {loggedIn ?
<UserMenuItems hideContactMenuItem {...this.props} />
 : null}

            {productGuidesMenuItem}

              {TosMenuItem}
              {privacyMenuItem}
              {aboutMenuItem}
              {contactMenuItem}

      </Drawer>
    );
  }
}

DrawerNavigation.contextTypes = {
  store: React.PropTypes.object,
};

export default DrawerNavigation;
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Expedia, Inc.
 *
 *         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.
 */

const Q = require('q');
const config = require('../../../config/config');
const _ = require('lodash');
const fetcher = require('../../fetchers/restFetcher');

const trendsFetcher = fetcher('trends');

const connector = {};
const metricTankUrl = config.connectors.trends.metricTankUrl;

function createOperationTarget(service, operationName, timeWindow, metricStats, metricNames) {
    return `haystack.serviceName.${service}.operationName.${operationName}.interval.${timeWindow}.stat.{${metricStats}}.{${metricNames}}`;
}

function getServiceTargetStat(service, timeWindow, metricStats, metricNames) {
    return `haystack.serviceName.${service}.interval.${timeWindow}.stat.{${metricStats}}.{${metricNames}}`;
}

function convertGranularityToTimeWindow(timespan) {
    switch (timespan) {
        case '60000':
            return 'OneMinute';
        case '300000':
            return 'FiveMinute';
        case '900000':
            return 'FifteenMinute';
        case '3600000':
            return 'OneHour';
        default:
            return 'OneMinute';
    }
}

function convertEpochTimeInSecondsToMillis(timestamp) {
    return timestamp * 1000;
}

function toMetricTankOperationName(operationName) {
    return operationName.replace(/\./gi, '___');
}

function fromMetricTankOperationName(operationName) {
    return operationName.replace(/___/gi, '.');
}

function toMilliseconds(micro) {
    return Math.ceil(micro / 1000);
}

function groupResponseByServiceOperation(data) {
    return data.map((op) => {
        const targetSplit = op.target.split('.');

        const serviceNameTagIndex = targetSplit.indexOf('serviceName');
        const operationNameTagIndex = targetSplit.indexOf('operationName');
        const serviceName = (serviceNameTagIndex !== -1) ? targetSplit[serviceNameTagIndex + 1] : null;
        const operationName = (operationNameTagIndex !== -1) ? fromMetricTankOperationName(targetSplit[operationNameTagIndex + 1]) : null;
        const trendStatTagIndex = targetSplit.indexOf('stat');
        const trendStat = `${targetSplit[trendStatTagIndex + 1]}.${targetSplit[trendStatTagIndex + 2]}`;

        return {
            serviceName,
            operationName,
            [trendStat]: op.datapoints.map(datapoint => ({value: datapoint[0], timestamp: convertEpochTimeInSecondsToMillis(datapoint[1])}))
        };
    });
}

function fetchTrendValues(target, from, until) {
    return trendsFetcher
        .fetch(`${metricTankUrl}/render?target=${target}&from=${from}&to=${until}`)
        .then(data => groupResponseByServiceOperation(data));
}

function extractTrendPointsForSingleServiceOperation(operationTrends, trendStat) {
    const dataPoints = operationTrends.find(trend => trendStat in trend);
    const trendStatDataPoints = dataPoints ? dataPoints[trendStat] : [];

    if (trendStatDataPoints.length && trendStatDataPoints[trendStatDataPoints.length - 1].value === null) {
        trendStatDataPoints.pop();
    }

    return trendStatDataPoints;
}

function dataPointsSum(dataPoints) {
    return dataPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
}

function toSuccessPercent(successPoints, failurePoints) {
    const successCount = successPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
    const failureCount = failurePoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);

    return 100 - ((failureCount / (successCount + failureCount)) * 100);
}

function toSuccessPercentPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        if (successVal + failureVal) {
            return {
                value: (100 - ((failureVal / (successVal + failureVal)) * 100)),
                timestamp
            };
        }
        return null;
    }));
}

function extractServicePerfStats({countValues, successValues, failureValues, tp99Values}) {
    const trendResults = [];

    const groupedByServiceName = _.groupBy(countValues.concat(successValues, failureValues, tp99Values), val => val.serviceName);
    Object.keys(groupedByServiceName).forEach((service) => {
        const serviceTrends = groupedByServiceName[service];
        const count = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.received-span'));
        const successCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span'));
        const failureCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span'));
        const successPercent = ((successCount / (successCount + failureCount)) * 100);

        const opKV = {
            serviceName: service,
            successPercent,
            failureCount,
            successCount,
            totalCount: count
        };

        trendResults.push(opKV);
    });
    return trendResults;
}

function extractServiceSummary(serviceTrends) {
    const countPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.received-span');
    const successCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span');
    const failureCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span');
    const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, '*_99.duration');
    const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

    return [{
        type: 'Incoming Requests',
        totalCount: dataPointsSum(countPoints),
        countPoints,
        avgSuccessPercent: toSuccessPercent(successCount, failureCount),
        successPercentPoints: toSuccessPercentPoints(successCount, failureCount),
        latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
        tp99DurationPoints
    }];
}

function extractOperationSummary(values) {
    const groupedByOperationName = _.groupBy(values, val => val.operationName);

    return Object.keys(groupedByOperationName).map((operationName) => {
        const operationTrends = groupedByOperationName[operationName];

        const countPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.received-span');
        const successPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.success-span');
        const failurePoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.failure-span');
        const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(operationTrends, '*_99.duration');
        const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

        return {
            operationName,
            totalCount: dataPointsSum(countPoints),
            countPoints,
            avgSuccessPercent: toSuccessPercent(successPoints, failurePoints),
            successPercentPoints: toSuccessPercentPoints(successPoints, failurePoints),
            latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
            tp99DurationPoints,
            failurePoints
        };
    });
}

function getServicePerfStatsResults(timeWindow, from, until) {
    const CountTarget = getServiceTargetStat('*', timeWindow, 'count.received-span');
    const SuccessTarget = getServiceTargetStat('*', timeWindow, 'count.success-span');
    const FailureTarget = getServiceTargetStat('*', timeWindow, 'count.failure-span');
    const tp99Target = getServiceTargetStat('*', timeWindow, '*_99.duration');

    return Q.all([
        fetchTrendValues(CountTarget, from, until),
        fetchTrendValues(SuccessTarget, from, until),
        fetchTrendValues(FailureTarget, from, until),
        fetchTrendValues(tp99Target, from, until)
    ])
        .then(values => extractServicePerfStats({
                countValues: values[0],
                successValues: values[1],
                failureValues: values[2],
                tp99Values: values[3]
            })
        );
}

function getServiceSummaryResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, 'count,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
    .then(values => extractServiceSummary(values));
}

function getServiceTrendResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, 'count,mean,*_95,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
    .then(trends => ({
            count: extractTrendPointsForSingleServiceOperation(trends, 'count.received-span'),
            successCount: extractTrendPointsForSingleServiceOperation(trends, 'count.success-span'),
            failureCount: extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span'),
            meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
            tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
            tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
        }));
}

function getOperationSummaryResults(service, timeWindow, from, until) {
    const target = createOperationTarget(service, '*', timeWindow, 'count,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
    .then(values => extractOperationSummary(values));
}

function getOperationTrendResults(serviceName, operationName, timeWindow, from, until) {
    const target = createOperationTarget(serviceName, operationName, timeWindow, 'count,mean,*_95,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
        .then(trends => ({
                count: extractTrendPointsForSingleServiceOperation(trends, 'count.received-span'),
                successCount: extractTrendPointsForSingleServiceOperation(trends, 'count.success-span'),
                failureCount: extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span'),
                meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
                tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
                tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
            }));
}

// api
connector.getServicePerfStats = (granularity, from, until) =>
    getServicePerfStatsResults(convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceStats = (serviceName, granularity, from, until) =>
    getServiceSummaryResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceTrends = (serviceName, granularity, from, until) =>
    getServiceTrendResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationStats = (serviceName, granularity, from, until) =>
    getOperationSummaryResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationTrends = (serviceName, operationName, granularity, from, until) =>
    getOperationTrendResults(serviceName, toMetricTankOperationName(operationName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

module.exports = connector;
<commit_msg>Update calls so that the homepage shows the report<commit_after>/*
 * Copyright 2018 Expedia, Inc.
 *
 *         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.
 */

const Q = require('q');
const config = require('../../../config/config');
const _ = require('lodash');
const fetcher = require('../../fetchers/restFetcher');

const trendsFetcher = fetcher('trends');

const connector = {};
const metricTankUrl = config.connectors.trends.metricTankUrl;

function createOperationTarget(service, operationName, timeWindow, metricStats, metricNames) {
    return `haystack.serviceName.${service}.operationName.${operationName}.interval.${timeWindow}.stat.{${metricStats}}.{${metricNames}}`;
}

function getServiceTargetStat(service, timeWindow, metricStats, metricNames) {
    return `haystack.serviceName.${service}.interval.${timeWindow}.stat.{${metricStats}}.{${metricNames}}`;
}

function convertGranularityToTimeWindow(timespan) {
    switch (timespan) {
        case '60000':
            return 'OneMinute';
        case '300000':
            return 'FiveMinute';
        case '900000':
            return 'FifteenMinute';
        case '3600000':
            return 'OneHour';
        default:
            return 'OneMinute';
    }
}

function convertEpochTimeInSecondsToMillis(timestamp) {
    return timestamp * 1000;
}

function toMetricTankOperationName(operationName) {
    return operationName.replace(/\./gi, '___');
}

function fromMetricTankOperationName(operationName) {
    return operationName.replace(/___/gi, '.');
}

function toMilliseconds(micro) {
    return Math.ceil(micro / 1000);
}

function groupResponseByServiceOperation(data) {
    return data.map((op) => {
        const targetSplit = op.target.split('.');

        const serviceNameTagIndex = targetSplit.indexOf('serviceName');
        const operationNameTagIndex = targetSplit.indexOf('operationName');
        const serviceName = (serviceNameTagIndex !== -1) ? targetSplit[serviceNameTagIndex + 1] : null;
        const operationName = (operationNameTagIndex !== -1) ? fromMetricTankOperationName(targetSplit[operationNameTagIndex + 1]) : null;
        const trendStatTagIndex = targetSplit.indexOf('stat');
        const trendStat = `${targetSplit[trendStatTagIndex + 1]}.${targetSplit[trendStatTagIndex + 2]}`;

        return {
            serviceName,
            operationName,
            [trendStat]: op.datapoints.map(datapoint => ({value: datapoint[0], timestamp: convertEpochTimeInSecondsToMillis(datapoint[1])}))
        };
    });
}

function fetchTrendValues(target, from, until) {
    return trendsFetcher
        .fetch(`${metricTankUrl}/render?target=${target}&from=${from}&to=${until}`)
        .then(data => groupResponseByServiceOperation(data));
}

function extractTrendPointsForSingleServiceOperation(operationTrends, trendStat) {
    const dataPoints = operationTrends.find(trend => trendStat in trend);
    const trendStatDataPoints = dataPoints ? dataPoints[trendStat] : [];

    if (trendStatDataPoints.length && trendStatDataPoints[trendStatDataPoints.length - 1].value === null) {
        trendStatDataPoints.pop();
    }

    return trendStatDataPoints;
}

function dataPointsSum(dataPoints) {
    return dataPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
}

function toSuccessPercent(successPoints, failurePoints) {
    const successCount = successPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
    const failureCount = failurePoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);

    return 100 - ((failureCount / (successCount + failureCount)) * 100);
}

function toSuccessPercentPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        if (successVal + failureVal) {
            return {
                value: (100 - ((failureVal / (successVal + failureVal)) * 100)),
                timestamp
            };
        }
        return null;
    }));
}

function extractServicePerfStats({countValues, successValues, failureValues, tp99Values}) {
    const trendResults = [];

    const groupedByServiceName = _.groupBy(countValues.concat(successValues, failureValues, tp99Values), val => val.serviceName);
    Object.keys(groupedByServiceName).forEach((service) => {
        const serviceTrends = groupedByServiceName[service];
        const count = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.received-span'));
        const successCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span'));
        const failureCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span'));
        const successPercent = ((successCount / (successCount + failureCount)) * 100);

        const opKV = {
            serviceName: service,
            successPercent,
            failureCount,
            successCount,
            totalCount: count
        };

        trendResults.push(opKV);
    });
    return trendResults;
}

function extractServiceSummary(serviceTrends) {
    const countPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.received-span');
    const successCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span');
    const failureCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span');
    const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, '*_99.duration');
    const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

    return [{
        type: 'Incoming Requests',
        totalCount: dataPointsSum(countPoints),
        countPoints,
        avgSuccessPercent: toSuccessPercent(successCount, failureCount),
        successPercentPoints: toSuccessPercentPoints(successCount, failureCount),
        latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
        tp99DurationPoints
    }];
}

function extractOperationSummary(values) {
    const groupedByOperationName = _.groupBy(values, val => val.operationName);

    return Object.keys(groupedByOperationName).map((operationName) => {
        const operationTrends = groupedByOperationName[operationName];

        const countPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.received-span');
        const successPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.success-span');
        const failurePoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.failure-span');
        const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(operationTrends, '*_99.duration');
        const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

        return {
            operationName,
            totalCount: dataPointsSum(countPoints),
            countPoints,
            avgSuccessPercent: toSuccessPercent(successPoints, failurePoints),
            successPercentPoints: toSuccessPercentPoints(successPoints, failurePoints),
            latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
            tp99DurationPoints,
            failurePoints
        };
    });
}

function getServicePerfStatsResults(timeWindow, from, until) {
    const CountTarget = getServiceTargetStat('*', timeWindow, 'count', 'received-span');
    const SuccessTarget = getServiceTargetStat('*', timeWindow, 'count', 'success-span');
    const FailureTarget = getServiceTargetStat('*', timeWindow, 'count', 'failure-span');
    const tp99Target = getServiceTargetStat('*', timeWindow, '*_99', 'duration');

    return Q.all([
        fetchTrendValues(CountTarget, from, until),
        fetchTrendValues(SuccessTarget, from, until),
        fetchTrendValues(FailureTarget, from, until),
        fetchTrendValues(tp99Target, from, until)
    ])
        .then(values => extractServicePerfStats({
                countValues: values[0],
                successValues: values[1],
                failureValues: values[2],
                tp99Values: values[3]
            })
        );
}

function getServiceSummaryResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, 'count,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
        .then(values => extractServiceSummary(values));
}

function getServiceTrendResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, 'count,mean,*_95,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
        .then(trends => ({
            count: extractTrendPointsForSingleServiceOperation(trends, 'count.received-span'),
            successCount: extractTrendPointsForSingleServiceOperation(trends, 'count.success-span'),
            failureCount: extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span'),
            meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
            tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
            tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
        }));
}

function getOperationSummaryResults(service, timeWindow, from, until) {
    const target = createOperationTarget(service, '*', timeWindow, 'count,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
        .then(values => extractOperationSummary(values));
}

function getOperationTrendResults(serviceName, operationName, timeWindow, from, until) {
    const target = createOperationTarget(serviceName, operationName, timeWindow, 'count,mean,*_95,*_99', 'received-span,success-span,failure-span,duration');

    return fetchTrendValues(target, from, until)
        .then(trends => ({
            count: extractTrendPointsForSingleServiceOperation(trends, 'count.received-span'),
            successCount: extractTrendPointsForSingleServiceOperation(trends, 'count.success-span'),
            failureCount: extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span'),
            meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
            tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
            tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
        }));
}

// api
connector.getServicePerfStats = (granularity, from, until) =>
    getServicePerfStatsResults(convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceStats = (serviceName, granularity, from, until) =>
    getServiceSummaryResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceTrends = (serviceName, granularity, from, until) =>
    getServiceTrendResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationStats = (serviceName, granularity, from, until) =>
    getOperationSummaryResults(serviceName, convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationTrends = (serviceName, operationName, granularity, from, until) =>
    getOperationTrendResults(serviceName, toMetricTankOperationName(operationName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

module.exports = connector;
<|endoftext|>