<commit_before>// @flow
import { StyleSheet } from './sheet'
import { forEach, map, reduce } from './utils'
import { hashString as hash, hashArray, hashObject } from './hash'
import { createMarkupForStyles } from './glamor/CSSPropertyOperations'
import clean from './glamor/clean.js'

const IS_DEV = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV

export const sheet = new StyleSheet()
// 🚀
sheet.inject()

export let inserted: { [string | number]: boolean | void } = {}

type inputVar = string | number

type vars = Array<inputVar>

export function flush () {
  sheet.flush()
  inserted = {}
  sheet.inject()
}

// a simple cache to store generated obj styles
let registered = (sheet.registered = {})

function register (spec) {
  if (!registered[spec.id]) {
    registered[spec.id] = spec
  }
}

function _getRegistered (rule) {
  if (isLikeRule(rule)) {
    let ret = registered[idFor(rule)]
    if (ret == null) {
      throw new Error(
        '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79'
      )
    }
    return ret
  }
  return rule
}

// The idea on how to merge object class names come from glamorous
// 💄
// https://github.com/paypal/glamorous/blob/master/src/get-glamor-classname.js
function getEmotionStylesFromClassName (className) {
  const id = className.trim().slice('css-'.length)
  if (sheet.registered[id]) {
    return sheet.registered[id].style
  } else {
    return []
  }
}

function buildStyles (objs) {
  let computedClassName = ''
  let objectStyles = []

  // This needs to be moved into the core
  forEach(objs, (cls): void => {
    if (typeof cls === 'string') {
      if (cls.trim().indexOf('css-') === 0) {
        objectStyles.push(getEmotionStylesFromClassName(cls))
      } else {
        computedClassName && (computedClassName += ' ')
        computedClassName += cls
      }
    } else {
      objectStyles.push(cls)
    }
  })

  return { computedClassName, objectStyles }
}

export function css (objs: any, vars: Array<any>, content: () => Array<any>) {
  if (!Array.isArray(objs)) {
    objs = [objs]
  }

  let { computedClassName = '', objectStyles = [] } = buildStyles(
    content ? objs.concat(content.apply(null, vars)) : objs
  )
  if (objectStyles.length) {
    computedClassName += ' ' + objStyle.apply(null, objectStyles).toString()
  }

  return computedClassName.trim()
}

function insertRawRule (css: string) {
  let spec = {
    id: hash(css, css.length),
    css,
    type: 'raw'
  }

  register(spec)

  if (!inserted[spec.id]) {
    sheet.insert(spec.css)
    inserted[spec.id] = true
  }
}

export function injectGlobal (
  objs: Array<any>,
  vars: Array<any>,
  content: () => Array<any>
) {
  const combined = content ? objs.concat(content.apply(null, vars)) : objs

  // injectGlobal is flattened by postcss
  // we don't support nested selectors on objects
  forEach(combined, obj => {
    forEach(Object.keys(obj), selector => {
      insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`)
    })
  })
}

export function fontFace (
  objs: Array<any>,
  vars: Array<any>,
  content: () => Array<any>
) {
  const combined = reduce(
    content ? objs.concat(content.apply(null, vars)) : objs,
    (accum, item, i) => Object.assign(accum, item),
    {}
  )

  insertRawRule(`@font-face{${createMarkupForStyles(combined)}}`)
}

function insertKeyframe (spec) {
  if (!inserted[spec.id]) {
    const inner = map(
      Object.keys(spec.keyframes),
      kf => `${kf} {${createMarkupForStyles(spec.keyframes[kf])}}`
    ).join('')

    forEach(['-webkit-', ''], prefix =>
      sheet.insert(`@${prefix}keyframes ${spec.name + '_' + spec.id}{${inner}}`)
    )

    inserted[spec.id] = true
  }
}

export function keyframes (
  objs: any,
  vars: Array<any>,
  content: () => Array<any>
) {
  const [kfs] = content.apply(null, vars)
  const name = 'animation'

  let spec = {
    id: hashObject(kfs),
    type: 'keyframes',
    name,
    keyframes: kfs
  }

  register(spec)
  insertKeyframe(spec)
  return `${name}_${spec.id}`
}

export function hydrate (ids: string[]) {
  forEach(ids, id => (inserted[id] = true))
}

type EmotionRule = { [string]: any }

type CSSRuleList = Array<EmotionRule>

type EmotionClassName = {
  [string]: any
}

let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !==
  'undefined'
  ? multiIndexCache(_css)
  : _css

// 🍩
// https://github.com/threepointone/glamor
export function objStyle (...rules: CSSRuleList): EmotionClassName {
  rules = clean(rules)
  if (!rules) {
    return nullrule
  }

  return cachedCss(rules)
}

function _css (rules) {
  let style = {}
  build(style, { src: rules }) // mutative! but worth it.

  let spec = {
    id: hashObject(style),
    style,
    type: 'css'
  }
  return toRule(spec)
}

// takes a string, converts to lowercase, strips out nonalphanumeric.
function simple (str) {
  return str.toLowerCase().replace(/[^a-z0-9]/g, '')
}

// of shape { 'data-css-<id>': '' }
export function isLikeRule (rule: EmotionRule) {
  let keys = Object.keys(rule).filter(x => x !== 'toString')
  if (keys.length !== 1) {
    return false
  }
  return !!/css\-([a-zA-Z0-9]+)/.exec(keys[0])
}

// extracts id from a { 'css-<id>': ''} like object
export function idFor (rule: EmotionRule) {
  let keys = Object.keys(rule).filter(x => x !== 'toString')
  if (keys.length !== 1) throw new Error('not a rule')
  let regex = /css\-([a-zA-Z0-9]+)/
  let match = regex.exec(keys[0])
  if (!match) throw new Error('not a rule')
  return match[1]
}

function selector (id: string, path: string = '') {
  if (!id) {
    return path.replace(/\&/g, '')
  }
  if (!path) return `.css-${id}`

  let x = path
    .split(',')
    .map(
      x =>
        x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.css-${id}`) : `.css-${id}${x}`
    )
    .join(',')

  return x
}

function deconstruct (style) {
  // we can be sure it's not infinitely nested here
  let plain, selects, medias, supports
  Object.keys(style).forEach(key => {
    if (key.indexOf('&') >= 0) {
      selects = selects || {}
      selects[key] = style[key]
    } else if (key.indexOf('@media') === 0) {
      medias = medias || {}
      medias[key] = deconstruct(style[key])
    } else if (key.indexOf('@supports') === 0) {
      supports = supports || {}
      supports[key] = deconstruct(style[key])
    } else {
      plain = plain || {}
      plain[key] = style[key]
    }
  })
  return { plain, selects, medias, supports }
}

function deconstructedStyleToCSS (id, style) {
  let { plain, selects, medias, supports } = style
  let css = []

  if (plain) {
    css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`)
  }
  if (selects) {
    Object.keys(selects).forEach((key: string) =>
      css.push(`${selector(id, key)}{${createMarkupForStyles(selects[key])}}`)
    )
  }
  if (medias) {
    Object.keys(medias).forEach(key =>
      css.push(`${key}{${deconstructedStyleToCSS(id, medias[key]).join('')}}`)
    )
  }
  if (supports) {
    Object.keys(supports).forEach(key =>
      css.push(`${key}{${deconstructedStyleToCSS(id, supports[key]).join('')}}`)
    )
  }
  return css
}

// and helpers to insert rules into said sheet
function insert (spec) {
  if (!inserted[spec.id]) {
    inserted[spec.id] = true
    let deconstructed = deconstruct(spec.style)
    deconstructedStyleToCSS(spec.id, deconstructed).map(cssRule =>
      sheet.insert(cssRule)
    )
  }
}

// todo - perf
let ruleCache = {}

function toRule (spec) {
  register(spec)
  insert(spec)
  if (ruleCache[spec.id]) {
    return ruleCache[spec.id]
  }

  let ret = { [`css-${spec.id}`]: '' }
  Object.defineProperty(ret, 'toString', {
    enumerable: false,
    value () {
      return 'css-' + spec.id
    }
  })
  ruleCache[spec.id] = ret
  return ret
}

function isSelector (key) {
  let possibles = [':', '.', '[', '>', ' '],
    found = false,
    ch = key.charAt(0)
  for (let i = 0; i < possibles.length; i++) {
    if (ch === possibles[i]) {
      found = true
      break
    }
  }
  return found || key.indexOf('&') >= 0
}

function joinSelectors (a, b) {
  let as = a.split(',').map(a => (!(a.indexOf('&') >= 0) ? '&' + a : a))
  let bs = b.split(',').map(b => (!(b.indexOf('&') >= 0) ? '&' + b : b))

  return bs
    .reduce((arr, b) => arr.concat(as.map(a => b.replace(/\&/g, a))), [])
    .join(',')
}

function joinMediaQueries (a, b) {
  return a ? `@media ${a.substring(6)} and ${b.substring(6)}` : b
}

function isMediaQuery (key) {
  return key.indexOf('@media') === 0
}

function isSupports (key) {
  return key.indexOf('@supports') === 0
}

function joinSupports (a, b) {
  return a ? `@supports ${a.substring(9)} and ${b.substring(9)}` : b
}

// flatten a nested array
function flatten (inArr) {
  let arr = []
  for (let i = 0; i < inArr.length; i++) {
    if (Array.isArray(inArr[i])) arr = arr.concat(flatten(inArr[i]))
    else arr = arr.concat(inArr[i])
  }
  return arr
}

// mutable! modifies dest.
function build (dest, { selector = '', mq = '', supp = '', src = {} }) {
  if (!Array.isArray(src)) {
    src = [src]
  }
  src = flatten(src)

  src.forEach(_src => {
    if (isLikeRule(_src)) {
      let reg = _getRegistered(_src)
      if (reg.type !== 'css') {
        throw new Error('cannot merge this rule')
      }
      _src = reg.style
    }
    _src = clean(_src)
    if (_src && _src.composes) {
      build(dest, { selector, mq, supp, src: _src.composes })
    }
    Object.keys(_src || {}).forEach(key => {
      if (isSelector(key)) {
        if (key === '::placeholder') {
          build(dest, {
            selector: joinSelectors(selector, '::-webkit-input-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
          build(dest, {
            selector: joinSelectors(selector, '::-moz-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
          build(dest, {
            selector: joinSelectors(selector, '::-ms-input-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
        }

        build(dest, {
          selector: joinSelectors(selector, key),
          mq,
          supp,
          src: _src[key]
        })
      } else if (isMediaQuery(key)) {
        build(dest, {
          selector,
          mq: joinMediaQueries(mq, key),
          supp,
          src: _src[key]
        })
      } else if (isSupports(key)) {
        build(dest, {
          selector,
          mq,
          supp: joinSupports(supp, key),
          src: _src[key]
        })
      } else if (key === 'composes') {
        // ignore, we already dealth with it
      } else {
        let _dest = dest
        if (supp) {
          _dest[supp] = _dest[supp] || {}
          _dest = _dest[supp]
        }
        if (mq) {
          _dest[mq] = _dest[mq] || {}
          _dest = _dest[mq]
        }
        if (selector) {
          _dest[selector] = _dest[selector] || {}
          _dest = _dest[selector]
        }

        _dest[key] = _src[key]
      }
    })
  })
}

let nullrule: EmotionClassName = {
  // 'data-css-nil': ''
}

Object.defineProperty(nullrule, 'toString', {
  enumerable: false,
  value () {
    return 'css-nil'
  }
})

let inputCaches = typeof WeakMap !== 'undefined'
  ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()]
  : [nullrule]

let warnedWeakMapError = false

function multiIndexCache (fn) {
  return function (args) {
    if (inputCaches[args.length]) {
      let coi = inputCaches[args.length]
      let ctr = 0
      while (ctr < args.length - 1) {
        if (!coi.has(args[ctr])) {
          coi.set(args[ctr], new WeakMap())
        }
        coi = coi.get(args[ctr])
        ctr++
      }
      if (coi.has(args[args.length - 1])) {
        let ret = coi.get(args[ctr])

        if (registered[ret.toString().substring(4)]) {
          // make sure it hasn't been flushed
          return ret
        }
      }
    }
    let value = fn(args)
    if (inputCaches[args.length]) {
      let ctr = 0,
        coi = inputCaches[args.length]
      while (ctr < args.length - 1) {
        coi = coi.get(args[ctr])
        ctr++
      }
      try {
        coi.set(args[ctr], value)
      } catch (err) {
        if (IS_DEV && !warnedWeakMapError) {
          warnedWeakMapError = true
          console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console
          console.warn(
            'this should NOT happen, please file a bug on the github repo.'
          ) // eslint-disable-line no-console
        }
      }
    }
    return value
  }
}
<commit_msg>Formatting, remove unused stuff, remove useless regex escapes and fix typo<commit_after>// @flow
import { StyleSheet } from './sheet'
import { forEach, map, reduce } from './utils'
import { hashString as hash, hashObject } from './hash'
import { createMarkupForStyles } from './glamor/CSSPropertyOperations'
import clean from './glamor/clean.js'

const IS_DEV = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV

export const sheet = new StyleSheet()
// 🚀
sheet.inject()

export let inserted: { [string | number]: boolean | void } = {}

type inputVar = string | number

type vars = Array<inputVar>

export function flush () {
  sheet.flush()
  inserted = {}
  sheet.inject()
}

// a simple cache to store generated obj styles
let registered = (sheet.registered = {})

function register (spec) {
  if (!registered[spec.id]) {
    registered[spec.id] = spec
  }
}

function _getRegistered (rule) {
  if (isLikeRule(rule)) {
    let ret = registered[idFor(rule)]
    if (ret == null) {
      throw new Error(
        '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79'
      )
    }
    return ret
  }
  return rule
}

// The idea on how to merge object class names come from glamorous
// 💄
// https://github.com/paypal/glamorous/blob/master/src/get-glamor-classname.js
function getEmotionStylesFromClassName (className) {
  const id = className.trim().slice('css-'.length)
  if (sheet.registered[id]) {
    return sheet.registered[id].style
  } else {
    return []
  }
}

function buildStyles (objs) {
  let computedClassName = ''
  let objectStyles = []

  // This needs to be moved into the core
  forEach(objs, (cls): void => {
    if (typeof cls === 'string') {
      if (cls.trim().indexOf('css-') === 0) {
        objectStyles.push(getEmotionStylesFromClassName(cls))
      } else {
        computedClassName && (computedClassName += ' ')
        computedClassName += cls
      }
    } else {
      objectStyles.push(cls)
    }
  })

  return { computedClassName, objectStyles }
}

export function css (objs: any, vars: Array<any>, content: () => Array<any>) {
  if (!Array.isArray(objs)) {
    objs = [objs]
  }

  let { computedClassName = '', objectStyles = [] } = buildStyles(
    content ? objs.concat(content.apply(null, vars)) : objs
  )
  if (objectStyles.length) {
    computedClassName += ' ' + objStyle.apply(null, objectStyles).toString()
  }

  return computedClassName.trim()
}

function insertRawRule (css: string) {
  let spec = {
    id: hash(css, css.length),
    css,
    type: 'raw'
  }

  register(spec)

  if (!inserted[spec.id]) {
    sheet.insert(spec.css)
    inserted[spec.id] = true
  }
}

export function injectGlobal (
  objs: Array<any>,
  vars: Array<any>,
  content: () => Array<any>
) {
  const combined = content ? objs.concat(content.apply(null, vars)) : objs

  // injectGlobal is flattened by postcss
  // we don't support nested selectors on objects
  forEach(combined, obj => {
    forEach(Object.keys(obj), selector => {
      insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`)
    })
  })
}

export function fontFace (
  objs: Array<any>,
  vars: Array<any>,
  content: () => Array<any>
) {
  const combined = reduce(
    content ? objs.concat(content.apply(null, vars)) : objs,
    (accum, item, i) => Object.assign(accum, item),
    {}
  )

  insertRawRule(`@font-face{${createMarkupForStyles(combined)}}`)
}

function insertKeyframe (spec) {
  if (!inserted[spec.id]) {
    const inner = map(
      Object.keys(spec.keyframes),
      kf => `${kf} {${createMarkupForStyles(spec.keyframes[kf])}}`
    ).join('')

    forEach(['-webkit-', ''], prefix =>
      sheet.insert(`@${prefix}keyframes ${spec.name + '_' + spec.id}{${inner}}`)
    )

    inserted[spec.id] = true
  }
}

export function keyframes (
  objs: any,
  vars: Array<any>,
  content: () => Array<any>
) {
  const [kfs] = content.apply(null, vars)
  const name = 'animation'

  let spec = {
    id: hashObject(kfs),
    type: 'keyframes',
    name,
    keyframes: kfs
  }

  register(spec)
  insertKeyframe(spec)
  return `${name}_${spec.id}`
}

export function hydrate (ids: string[]) {
  forEach(ids, id => (inserted[id] = true))
}

type EmotionRule = { [string]: any }

type CSSRuleList = Array<EmotionRule>

type EmotionClassName = {
  [string]: any
}

let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !==
  'undefined'
  ? multiIndexCache(_css)
  : _css

// 🍩
// https://github.com/threepointone/glamor
export function objStyle (...rules: CSSRuleList): EmotionClassName {
  rules = clean(rules)
  if (!rules) {
    return nullrule
  }

  return cachedCss(rules)
}

function _css (rules) {
  let style = {}
  build(style, { src: rules }) // mutative! but worth it.

  let spec = {
    id: hashObject(style),
    style,
    type: 'css'
  }
  return toRule(spec)
}

// of shape { 'data-css-<id>': '' }
export function isLikeRule (rule: EmotionRule) {
  let keys = Object.keys(rule).filter(x => x !== 'toString')
  if (keys.length !== 1) {
    return false
  }
  return !!/css-([a-zA-Z0-9]+)/.exec(keys[0])
}

// extracts id from a { 'css-<id>': ''} like object
export function idFor (rule: EmotionRule) {
  let keys = Object.keys(rule).filter(x => x !== 'toString')
  if (keys.length !== 1) throw new Error('not a rule')
  let regex = /css-([a-zA-Z0-9]+)/
  let match = regex.exec(keys[0])
  if (!match) throw new Error('not a rule')
  return match[1]
}

function selector (id: string, path: string = '') {
  if (!id) {
    return path.replace(/&/g, '')
  }
  if (!path) return `.css-${id}`

  let x = path
    .split(',')
    .map(
      x =>
        x.indexOf('&') >= 0 ? x.replace(/&/gm, `.css-${id}`) : `.css-${id}${x}`
    )
    .join(',')

  return x
}

function deconstruct (style) {
  // we can be sure it's not infinitely nested here
  let plain, selects, medias, supports
  Object.keys(style).forEach(key => {
    if (key.indexOf('&') >= 0) {
      selects = selects || {}
      selects[key] = style[key]
    } else if (key.indexOf('@media') === 0) {
      medias = medias || {}
      medias[key] = deconstruct(style[key])
    } else if (key.indexOf('@supports') === 0) {
      supports = supports || {}
      supports[key] = deconstruct(style[key])
    } else {
      plain = plain || {}
      plain[key] = style[key]
    }
  })
  return { plain, selects, medias, supports }
}

function deconstructedStyleToCSS (id, style) {
  let { plain, selects, medias, supports } = style
  let css = []

  if (plain) {
    css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`)
  }
  if (selects) {
    Object.keys(selects).forEach((key: string) =>
      css.push(`${selector(id, key)}{${createMarkupForStyles(selects[key])}}`)
    )
  }
  if (medias) {
    Object.keys(medias).forEach(key =>
      css.push(`${key}{${deconstructedStyleToCSS(id, medias[key]).join('')}}`)
    )
  }
  if (supports) {
    Object.keys(supports).forEach(key =>
      css.push(`${key}{${deconstructedStyleToCSS(id, supports[key]).join('')}}`)
    )
  }
  return css
}

// and helpers to insert rules into said sheet
function insert (spec) {
  if (!inserted[spec.id]) {
    inserted[spec.id] = true
    let deconstructed = deconstruct(spec.style)
    deconstructedStyleToCSS(spec.id, deconstructed).map(cssRule =>
      sheet.insert(cssRule)
    )
  }
}

// todo - perf
let ruleCache = {}

function toRule (spec) {
  register(spec)
  insert(spec)
  if (ruleCache[spec.id]) {
    return ruleCache[spec.id]
  }

  let ret = { [`css-${spec.id}`]: '' }
  Object.defineProperty(ret, 'toString', {
    enumerable: false,
    value () {
      return 'css-' + spec.id
    }
  })
  ruleCache[spec.id] = ret
  return ret
}

function isSelector (key) {
  const possibles = [':', '.', '[', '>', ' ']
  let found = false
  const ch = key.charAt(0)
  for (let i = 0; i < possibles.length; i++) {
    if (ch === possibles[i]) {
      found = true
      break
    }
  }
  return found || key.indexOf('&') >= 0
}

function joinSelectors (a, b) {
  let as = a.split(',').map(a => (!(a.indexOf('&') >= 0) ? '&' + a : a))
  let bs = b.split(',').map(b => (!(b.indexOf('&') >= 0) ? '&' + b : b))

  return bs
    .reduce((arr, b) => arr.concat(as.map(a => b.replace(/&/g, a))), [])
    .join(',')
}

function joinMediaQueries (a, b) {
  return a ? `@media ${a.substring(6)} and ${b.substring(6)}` : b
}

function isMediaQuery (key) {
  return key.indexOf('@media') === 0
}

function isSupports (key) {
  return key.indexOf('@supports') === 0
}

function joinSupports (a, b) {
  return a ? `@supports ${a.substring(9)} and ${b.substring(9)}` : b
}

// flatten a nested array
function flatten (inArr) {
  let arr = []
  for (let i = 0; i < inArr.length; i++) {
    if (Array.isArray(inArr[i])) arr = arr.concat(flatten(inArr[i]))
    else arr = arr.concat(inArr[i])
  }
  return arr
}

// mutable! modifies dest.
function build (dest, { selector = '', mq = '', supp = '', src = {} }) {
  if (!Array.isArray(src)) {
    src = [src]
  }
  src = flatten(src)

  src.forEach(_src => {
    if (isLikeRule(_src)) {
      let reg = _getRegistered(_src)
      if (reg.type !== 'css') {
        throw new Error('cannot merge this rule')
      }
      _src = reg.style
    }
    _src = clean(_src)
    if (_src && _src.composes) {
      build(dest, { selector, mq, supp, src: _src.composes })
    }
    Object.keys(_src || {}).forEach(key => {
      if (isSelector(key)) {
        if (key === '::placeholder') {
          build(dest, {
            selector: joinSelectors(selector, '::-webkit-input-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
          build(dest, {
            selector: joinSelectors(selector, '::-moz-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
          build(dest, {
            selector: joinSelectors(selector, '::-ms-input-placeholder'),
            mq,
            supp,
            src: _src[key]
          })
        }

        build(dest, {
          selector: joinSelectors(selector, key),
          mq,
          supp,
          src: _src[key]
        })
      } else if (isMediaQuery(key)) {
        build(dest, {
          selector,
          mq: joinMediaQueries(mq, key),
          supp,
          src: _src[key]
        })
      } else if (isSupports(key)) {
        build(dest, {
          selector,
          mq,
          supp: joinSupports(supp, key),
          src: _src[key]
        })
      } else if (key === 'composes') {
        // ignore, we already dealt with it
      } else {
        let _dest = dest
        if (supp) {
          _dest[supp] = _dest[supp] || {}
          _dest = _dest[supp]
        }
        if (mq) {
          _dest[mq] = _dest[mq] || {}
          _dest = _dest[mq]
        }
        if (selector) {
          _dest[selector] = _dest[selector] || {}
          _dest = _dest[selector]
        }

        _dest[key] = _src[key]
      }
    })
  })
}

let nullrule: EmotionClassName = {
  // 'data-css-nil': ''
}

Object.defineProperty(nullrule, 'toString', {
  enumerable: false,
  value () {
    return 'css-nil'
  }
})

let inputCaches = typeof WeakMap !== 'undefined'
  ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()]
  : [nullrule]

let warnedWeakMapError = false

function multiIndexCache (fn) {
  return function (args) {
    if (inputCaches[args.length]) {
      let coi = inputCaches[args.length]
      let ctr = 0
      while (ctr < args.length - 1) {
        if (!coi.has(args[ctr])) {
          coi.set(args[ctr], new WeakMap())
        }
        coi = coi.get(args[ctr])
        ctr++
      }
      if (coi.has(args[args.length - 1])) {
        let ret = coi.get(args[ctr])

        if (registered[ret.toString().substring(4)]) {
          // make sure it hasn't been flushed
          return ret
        }
      }
    }
    let value = fn(args)
    if (inputCaches[args.length]) {
      let ctr = 0,
        coi = inputCaches[args.length]
      while (ctr < args.length - 1) {
        coi = coi.get(args[ctr])
        ctr++
      }
      try {
        coi.set(args[ctr], value)
      } catch (err) {
        if (IS_DEV && !warnedWeakMapError) {
          warnedWeakMapError = true
          console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console
          console.warn(
            'this should NOT happen, please file a bug on the github repo.'
          ) // eslint-disable-line no-console
        }
      }
    }
    return value
  }
}
<|endoftext|>


<commit_before>'use strict';

var Alexa = require('alexa-sdk');
var APP_ID = undefined; //OPTIONAL: replace with your app ID;
var recipes = require('./recipes');

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

var handlers = {
    'NewSession': function () {
        this.attributes['speechOutput'] = this.t("WELCOME_MESSAGE", this.t("SKILL_NAME"));
        // If the user either does not reply to the welcome message or says something that is not
        // understood, they will be prompted again with this text.
        this.attributes['repromptSpeech'] = this.t("WELCOME_REPROMT");
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'RecipeIntent': function () {
        var itemSlot = this.event.request.intent.slots.Item;
        var itemName;
        if (itemSlot && itemSlot.value) {
            itemName = itemSlot.value.toLowerCase();
        }

        var cardTitle = this.t("DISPLAY_CARD_TITLE", this.t("SKILL_NAME"), itemName);
        var recipes = this.t("RECIPES");
        var recipe = recipes[itemName];
        console.log(recipe);

        if (recipe) {
            this.attributes['speechOutput'] = recipe;
            this.attributes['repromptSpeech'] = this.t("RECIPE_REPEAT_MESSAGE");
            this.emit(':askWithCard', recipe, this.attributes['repromptSpeech'], cardTitle, recipe);
        } else {
            var speechOutput = this.t("RECIPE_NOT_FOUND_MESSAGE");
            var repromptSpeech = this.t("RECIPE_NOT_FOUND_REPROMPT");
            if (itemName) {
                speechOutput += this.t("RECIPE_NOT_FOUND_WITH_ITEM_NAME", itemName);
            } else {
                speechOutput += this.t("RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME");
            }
            speechOutput += repromptSpeech;

            this.attributes['speechOutput'] = speechOutput;
            this.attributes['repromptSpeech'] = repromptSpeech;

            this.emit(':ask', speechOutput, repromptSpeech);
        }
    },
    'AMAZON.HelpIntent': function () {
        this.attributes['speechOutput'] = this.t("HELP_MESSAGE");
        this.attributes['repromptSpeech'] = this.t("HELP_REPROMT");
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'AMAZON.RepeatIntent': function () {
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'AMAZON.StopIntent': function () {
        this.emit('SessionEndedRequest');
    },
    'AMAZON.CancelIntent': function () {
        this.emit('SessionEndedRequest');
    },
    'SessionEndedRequest':function () {
        this.emit(':tell', this.t("STOP_MESSAGE"));
    }
};

var languageStrings = {
    "en-GB": {
        "translation": {
            "RECIPES": recipes.RECIPE_EN_GB,
            "SKILL_NAME": "British Minecraft Helper",
            "WELCOME_MESSAGE": "Welcome to %s. You can ask a question like, what\'s the recipe for a chest? ... Now, what can I help you with.",
            "WELCOME_REPROMT": "For instructions on what you can say, please say help me.",
            "DISPLAY_CARD_TITLE": "%s  - Recipe for %s.",
            "HELP_MESSAGE": "You can ask questions such as, what\'s the recipe, or, you can say exit...Now, what can I help you with?",
            "HELP_REPROMT": "You can say things like, what\'s the recipe, or you can say exit...Now, what can I help you with?",
            "STOP_MESSAGE": "Goodbye!",
            "RECIPE_REPEAT_MESSAGE": "Try saying repeat.",
            "RECIPE_NOT_FOUND_MESSAGE": "I\'m sorry, I currently do not know ",
            "RECIPE_NOT_FOUND_WITH_ITEM_NAME": "the recipe for %s.",
            "RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME": "that recipe. ",
            "RECIPE_NOT_FOUND_REPROMPT": "What else can I help with?"
        }
    },
    "en-US": {
        "translation": {
            "RECIPES" : recipes.RECIPE_EN_US,
            "SKILL_NAME" : "American Minecraft Helper",
            "WELCOME_MESSAGE": "Welcome to %s. You can ask a question like, what\'s the recipe for a chest? ... Now, what can I help you with.",
            "WELCOME_REPROMT": "For instructions on what you can say, please say help me.",
            "DISPLAY_CARD_TITLE": "s%  - Recipe for %s.",
            "HELP_MESSAGE": "You can ask questions such as, what\'s the recipe, or, you can say exit...Now, what can I help you with?",
            "HELP_REPROMT": "You can say things like, what\'s the recipe, or you can say exit...Now, what can I help you with?",
            "STOP_MESSAGE": "Goodbye!",
            "RECIPE_REPEAT_MESSAGE": "Try saying repeat.",
            "RECIPE_NOT_FOUND_MESSAGE": "I\'m sorry, I currently do not know ",
            "RECIPE_NOT_FOUND_WITH_ITEM_NAME": "the recipe for %s.",
            "RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME": "that recipe. ",
            "RECIPE_NOT_FOUND_REPROMPT": "What else can I help with?"
        }
    }
};<commit_msg>add todo comment for appId<commit_after>'use strict';

var Alexa = require('alexa-sdk');
var APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
var recipes = require('./recipes');

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

var handlers = {
    'NewSession': function () {
        this.attributes['speechOutput'] = this.t("WELCOME_MESSAGE", this.t("SKILL_NAME"));
        // If the user either does not reply to the welcome message or says something that is not
        // understood, they will be prompted again with this text.
        this.attributes['repromptSpeech'] = this.t("WELCOME_REPROMT");
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'RecipeIntent': function () {
        var itemSlot = this.event.request.intent.slots.Item;
        var itemName;
        if (itemSlot && itemSlot.value) {
            itemName = itemSlot.value.toLowerCase();
        }

        var cardTitle = this.t("DISPLAY_CARD_TITLE", this.t("SKILL_NAME"), itemName);
        var recipes = this.t("RECIPES");
        var recipe = recipes[itemName];
        console.log(recipe);

        if (recipe) {
            this.attributes['speechOutput'] = recipe;
            this.attributes['repromptSpeech'] = this.t("RECIPE_REPEAT_MESSAGE");
            this.emit(':askWithCard', recipe, this.attributes['repromptSpeech'], cardTitle, recipe);
        } else {
            var speechOutput = this.t("RECIPE_NOT_FOUND_MESSAGE");
            var repromptSpeech = this.t("RECIPE_NOT_FOUND_REPROMPT");
            if (itemName) {
                speechOutput += this.t("RECIPE_NOT_FOUND_WITH_ITEM_NAME", itemName);
            } else {
                speechOutput += this.t("RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME");
            }
            speechOutput += repromptSpeech;

            this.attributes['speechOutput'] = speechOutput;
            this.attributes['repromptSpeech'] = repromptSpeech;

            this.emit(':ask', speechOutput, repromptSpeech);
        }
    },
    'AMAZON.HelpIntent': function () {
        this.attributes['speechOutput'] = this.t("HELP_MESSAGE");
        this.attributes['repromptSpeech'] = this.t("HELP_REPROMT");
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'AMAZON.RepeatIntent': function () {
        this.emit(':ask', this.attributes['speechOutput'], this.attributes['repromptSpeech'])
    },
    'AMAZON.StopIntent': function () {
        this.emit('SessionEndedRequest');
    },
    'AMAZON.CancelIntent': function () {
        this.emit('SessionEndedRequest');
    },
    'SessionEndedRequest':function () {
        this.emit(':tell', this.t("STOP_MESSAGE"));
    }
};

var languageStrings = {
    "en-GB": {
        "translation": {
            "RECIPES": recipes.RECIPE_EN_GB,
            "SKILL_NAME": "British Minecraft Helper",
            "WELCOME_MESSAGE": "Welcome to %s. You can ask a question like, what\'s the recipe for a chest? ... Now, what can I help you with.",
            "WELCOME_REPROMT": "For instructions on what you can say, please say help me.",
            "DISPLAY_CARD_TITLE": "%s  - Recipe for %s.",
            "HELP_MESSAGE": "You can ask questions such as, what\'s the recipe, or, you can say exit...Now, what can I help you with?",
            "HELP_REPROMT": "You can say things like, what\'s the recipe, or you can say exit...Now, what can I help you with?",
            "STOP_MESSAGE": "Goodbye!",
            "RECIPE_REPEAT_MESSAGE": "Try saying repeat.",
            "RECIPE_NOT_FOUND_MESSAGE": "I\'m sorry, I currently do not know ",
            "RECIPE_NOT_FOUND_WITH_ITEM_NAME": "the recipe for %s.",
            "RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME": "that recipe. ",
            "RECIPE_NOT_FOUND_REPROMPT": "What else can I help with?"
        }
    },
    "en-US": {
        "translation": {
            "RECIPES" : recipes.RECIPE_EN_US,
            "SKILL_NAME" : "American Minecraft Helper",
            "WELCOME_MESSAGE": "Welcome to %s. You can ask a question like, what\'s the recipe for a chest? ... Now, what can I help you with.",
            "WELCOME_REPROMT": "For instructions on what you can say, please say help me.",
            "DISPLAY_CARD_TITLE": "s%  - Recipe for %s.",
            "HELP_MESSAGE": "You can ask questions such as, what\'s the recipe, or, you can say exit...Now, what can I help you with?",
            "HELP_REPROMT": "You can say things like, what\'s the recipe, or you can say exit...Now, what can I help you with?",
            "STOP_MESSAGE": "Goodbye!",
            "RECIPE_REPEAT_MESSAGE": "Try saying repeat.",
            "RECIPE_NOT_FOUND_MESSAGE": "I\'m sorry, I currently do not know ",
            "RECIPE_NOT_FOUND_WITH_ITEM_NAME": "the recipe for %s.",
            "RECIPE_NOT_FOUND_WITHOUT_ITEM_NAME": "that recipe. ",
            "RECIPE_NOT_FOUND_REPROMPT": "What else can I help with?"
        }
    }
};<|endoftext|>


<commit_before>require('./config/quark');
require('./styles/base.styl');

const React = require('react')
    , Router = require('react-router')
    , { Route, DefaultRoute, RouteHandler, Link } = Router
    , TransitionGroup = require('react/lib/ReactCSSTransitionGroup')

    // components
    , { SearchBar, StoryList } = require('./components/index')
  ;

var App = React.createClass({

  mixins: [ Router.State ],

  render() {
    var routeName = this.getRoutes().reverse()[0].name;

    return (

        <TransitionGroup component="div" transitionName="stpane">
          <RouteHandler key={routeName} />
        </TransitionGroup>

    );
  }
});

const Routes = (
  <Route handler={App} path="/">
    <DefaultRoute handler={StoryList} />
  </Route>
);

Router.run(Routes, function (Handler) {
  React.render(<Handler/>, document.getElementById('render-spot'));
});

// add listener to refocus cursor in search input on CMD + L combo
document.addEventListener('keydown', function(e) {
  if(e.metaKey && e.keyCode == 76)
  var input = document.getElementById('searchInput');
  if(input) input.focus();
});<commit_msg>update global key listener<commit_after>require('./config/quark');
require('./styles/base.styl');

const React = require('react')
    , Router = require('react-router')
    , { Route, DefaultRoute, RouteHandler, Link } = Router
    , TransitionGroup = require('react/lib/ReactCSSTransitionGroup')

    // components
    , { SearchBar, StoryList } = require('./components/index')
  ;

var App = React.createClass({

  mixins: [ Router.State ],

  render() {
    var routeName = this.getRoutes().reverse()[0].name;

    return (

        <TransitionGroup component="div" transitionName="stpane">
          <RouteHandler key={routeName} />
        </TransitionGroup>

    );
  }
});

const Routes = (
  <Route handler={App} path="/">
    <DefaultRoute handler={StoryList} />
  </Route>
);

Router.run(Routes, function (Handler) {
  React.render(<Handler/>, document.getElementById('render-spot'));
});

document.addEventListener('keydown', function(e) {
  // add listener to refocus cursor in search input on CMD + L combo
  if(e.metaKey && e.keyCode == 76) {
    var input = document.getElementById('searchInput');
    if(input) input.focus();
  }
  // add listener to close panel on ESC regardless of whether input is focused
  else if(e.keyCode == 27) {
    quark.closePopup();
  }
});<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './ui/App';
import AppReducer from './model/reducers/AppReducer';
import DefaultStatePromise from './model/DefaultState';
import './index.css';

DefaultStatePromise
    .then((state) => (createStore(AppReducer, state)))
    .then((store) => {
        ReactDOM.render(
            <Provider store={store}>
                <App />
            </Provider>,
            document.getElementById('root')
        )}
    );
<commit_msg>Adding middleware to createStore call.<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import App from './ui/App';
import AppReducer from './model/reducers/AppReducer';
import DefaultStatePromise from './model/DefaultState';
import './index.css';

const loggerMiddleware = createLogger();

DefaultStatePromise
    .then((state) => (
        createStore(AppReducer, state, applyMiddleware(thunkMiddleware, loggerMiddleware))))
    .then((store) => {
      ReactDOM.render(
          <Provider store={store}>
            <App />
          </Provider>,
          document.getElementById('root')
      )
    }
);
<|endoftext|>


<commit_before>import { parse } from 'acorn';
import Program from './program/Program.js';

export function transform ( source, options ) {
	var ast = parse( source, {
		ecmaVersion: 6,
		preserveParens: true
	});

	var program = new Program( source, ast );

	return program.export();
}
<commit_msg>use sourceType: module<commit_after>import { parse } from 'acorn';
import Program from './program/Program.js';

export function transform ( source, options ) {
	var ast = parse( source, {
		ecmaVersion: 6,
		preserveParens: true,
		sourceType: 'module'
	});

	var program = new Program( source, ast );

	return program.export();
}
<|endoftext|>


<commit_before>/**
 * @module index.js
 * @author Joe Groseclose <@benderTheCrime>
 * @date 8/16/2015
 */

// We need to require this before anything else
import './Config';

// System Modules
import { argv } from                'yargs';
import { exec } from                'child_process';
import { gray, bold } from          'chalk';
import $LogProvider from            'angie-log';

// Angie Modules
import $$createProject from         './util/scaffold/project';
import {
    $$watch,
    $$cluster,
    $$server
} from                              './Server';

const t = s => s.toString().replace(/\s{2,}/g, '');
let args = [];

// Remove trivial arguments
process.argv.forEach(function(v) {
    if (!v.match(/((babel-)?node|iojs|index|angie|--?)/)) {
        args.push(v);
    }
});

if (argv.help || argv.h) {
    help();
} else {
    let main = (args[0] || argv._).toLowerCase();

    // Route the CLI request to a specific command
    switch (main) {
        case 'help':
            help();
            break;
        case 'server':
            $$server(args);
            break;
        case 'watch':
            $$watch(args);
            break;
        case 's':
            $$server(args);
            break;
        case 'cluster':
            $$cluster();
            break;
        case 'createproject':
            $$createProject({
                name: args[ 1 ],
                dir: args[ 2 ]
            });
            break;
        case 'syncdb':
            require('angie-orm');
            break;
        case 'migrate':
            require('angie-orm');
            break;
        case 'test':
            runTests();
            break;
        case 'shell':
            watch();
            break;
        default:
            help();
    }
}

function runTests() {

    // TODO is there any way to carry the stream output from gulp instead
    // of capturing stdout?
    exec(`cd ${__dirname} && gulp`, function(e, std, err) {
        $LogProvider.info(std);
        if (err) {
            $LogProvider.error(err);
        }
        if (e) {
            throw new Error(e);
        }
    });
}

function help() {
    const GRAY = (...args) => console.log(gray.apply(null, args)),
        BOLD = (...args) => console.log(bold.apply(null, args));

    BOLD('Angie');
    console.log('A Module-Based NodeJS Web Application Framework in ES6');
    console.log('\r');
    BOLD('Version:');
    console.log(global.ANGIE_VERSION);
    console.log('\r');
    BOLD('Commands:');

    console.log('angie server [-p=<port>] [--port=<port>] [--usessl]');
    GRAY(t`
        Start the Angie Webserver (shortcut with s). Default port
        is 3000. "usessl" forces the port to 443.
    `);

    console.log(
        'angie watch [-p=<port>] [--port=<port>] [-d] [--devmode] [--usessl]'
    );
    GRAY(t`
        Starts the Angie Webserver as a watched process and watches the
        project directory. If started in "devmode," watch will target
        the Angie module "src" directory
    `);

    console.log('cluster [-p=<port>] [--port=<port>] [--usessl] [--norefork]');
    GRAY(t`
        Start the Angie Webserver as a cluster of forked webserver processes.
        Unless \`--norefork\` option is passed, forks will respawn on exit
    `);

    console.log('angie project [-n=<name>][--name=<name>] [--dir=<directory>]');
    console.log(
        'angie createproject [-n=<name>][--name=<name>] [--dir=<directory>]'
    );
    GRAY(t`
        Create a new Angie project with the specified name in the
        current directory.
    `);

    console.log('angie test');
    GRAY('Runs the Angie test suite and prints the results in the console');
}<commit_msg>Added fix for the shell command<commit_after>/**
 * @module index.js
 * @author Joe Groseclose <@benderTheCrime>
 * @date 8/16/2015
 */

// We need to require this before anything else
import './Config';

// System Modules
import { argv } from                'yargs';
import { exec } from                'child_process';
import { gray, bold } from          'chalk';
import $LogProvider from            'angie-log';

// Angie Modules
import $$createProject from         './util/scaffold/project';
import {
    $$watch,
    $$cluster,
    $$server
} from                              './Server';

const t = s => s.toString().replace(/\s{2,}/g, '');
let args = [];

// Remove trivial arguments
process.argv.forEach(function(v) {
    if (!v.match(/((babel-)?node|iojs|index|angie|--?)/)) {
        args.push(v);
    }
});

if (argv.help || argv.h) {
    help();
} else {
    let main = (args[0] || argv._).toLowerCase();

    // Route the CLI request to a specific command
    switch (main) {
        case 'help':
            help();
            break;
        case 'server':
            $$server(args);
            break;
        case 'watch':
            $$watch(args);
            break;
        case 's':
            $$server(args);
            break;
        case 'cluster':
            $$cluster();
            break;
        case 'createproject':
            $$createProject({
                name: args[ 1 ],
                dir: args[ 2 ]
            });
            break;
        case 'syncdb':
            require('angie-orm');
            break;
        case 'migrate':
            require('angie-orm');
            break;
        case 'test':
            runTests();
            break;
        case 'shell':
            $$watch(args);
            break;
        default:
            help();
    }
}

function runTests() {

    // TODO is there any way to carry the stream output from gulp instead
    // of capturing stdout?
    exec(`cd ${__dirname} && gulp`, function(e, std, err) {
        $LogProvider.info(std);
        if (err) {
            $LogProvider.error(err);
        }
        if (e) {
            throw new Error(e);
        }
    });
}

function help() {
    const GRAY = (...args) => console.log(gray.apply(null, args)),
        BOLD = (...args) => console.log(bold.apply(null, args));

    BOLD('Angie');
    console.log('A Module-Based NodeJS Web Application Framework in ES6');
    console.log('\r');
    BOLD('Version:');
    console.log(global.ANGIE_VERSION);
    console.log('\r');
    BOLD('Commands:');

    console.log('angie server [-p=<port>] [--port=<port>] [--usessl]');
    GRAY(t`
        Start the Angie Webserver (shortcut with s). Default port
        is 3000. "usessl" forces the port to 443.
    `);

    console.log(
        'angie watch [-p=<port>] [--port=<port>] [-d] [--devmode] [--usessl]'
    );
    GRAY(t`
        Starts the Angie Webserver as a watched process and watches the
        project directory. If started in "devmode," watch will target
        the Angie module "src" directory
    `);

    console.log('cluster [-p=<port>] [--port=<port>] [--usessl] [--norefork]');
    GRAY(t`
        Start the Angie Webserver as a cluster of forked webserver processes.
        Unless \`--norefork\` option is passed, forks will respawn on exit
    `);

    console.log('angie project [-n=<name>][--name=<name>] [--dir=<directory>]');
    console.log(
        'angie createproject [-n=<name>][--name=<name>] [--dir=<directory>]'
    );
    GRAY(t`
        Create a new Angie project with the specified name in the
        current directory.
    `);

    console.log('angie test');
    GRAY('Runs the Angie test suite and prints the results in the console');
}<|endoftext|>


<commit_before>import {
  app,
  BrowserWindow,
  shell,
  ipcMain,
} from 'electron';
import isDevMode from 'electron-is-dev';
import fs from 'fs-extra';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import { enforceMacOSAppLocation } from 'electron-util';

// Set app directory before loading user modules
if (process.env.FRANZ_APPDATA_DIR != null) {
  app.setPath('appData', process.env.FRANZ_APPDATA_DIR);
  app.setPath('userData', path.join(app.getPath('appData')));
} else if (process.platform === 'win32') {
  app.setPath('appData', process.env.APPDATA);
  app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
}

if (isDevMode) {
  app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
}

/* eslint-disable import/first */
import {
  isMac,
  isWindows,
  isLinux,
} from './environment';
import { mainIpcHandler as basicAuthHandler } from './features/basicAuth';
import ipcApi from './electron/ipc-api';
import Tray from './lib/Tray';
import Settings from './electron/Settings';
import handleDeepLink from './electron/deepLinking';
import { isPositionValid } from './electron/windowUtils';
import askFormacOSPermissions from './electron/macOSPermissions';
import { appId } from './package.json'; // eslint-disable-line import/no-unresolved
import './electron/exception';

import {
  DEFAULT_APP_SETTINGS,
  DEFAULT_WINDOW_OPTIONS,
} from './config';
import { asarPath } from './helpers/asar-helpers';
import { isValidExternalURL } from './helpers/url-helpers';
import userAgent from './helpers/userAgent-helpers';

/* eslint-enable import/first */
const debug = require('debug')('Franz:App');

// Globally set useragent to fix user agent override in service workers
debug('Set userAgent to ', userAgent());
app.userAgentFallback = userAgent();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let willQuitApp = false;

// Register methods to be called once the window has been loaded.
let onDidLoadFns = [];

function onDidLoad(fn) {
  if (onDidLoadFns) {
    onDidLoadFns.push(fn);
  } else if (mainWindow) {
    fn(mainWindow);
  }
}

// Ensure that the recipe directory exists
fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp'));
fs.ensureFileSync(path.join(app.getPath('userData'), 'window-state.json'));

// Set App ID for Windows
if (isWindows) {
  app.setAppUserModelId(appId);
}

// Initialize Settings
const settings = new Settings('app', DEFAULT_APP_SETTINGS);
const proxySettings = new Settings('proxy');

// add `liftSingleInstanceLock` to settings.json to override the single instance lock
const liftSingleInstanceLock = settings.get('liftSingleInstanceLock') || false;

// Force single window
const gotTheLock = liftSingleInstanceLock ? true : app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Someone tried to run a second instance, we should focus our window.
    if (mainWindow) {
      mainWindow.show();
      if (mainWindow.isMinimized()) {
        mainWindow.restore();
      }
      mainWindow.focus();

      if (isWindows) {
        onDidLoad((window) => {
          // Keep only command line / deep linked arguments
          const url = argv.slice(1);
          if (url) {
            handleDeepLink(window, url.toString());
          }

          if (argv.includes('--reset-window')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Resetting windows via Task');
              window.setPosition(DEFAULT_WINDOW_OPTIONS.x + 100, DEFAULT_WINDOW_OPTIONS.y + 100);
              window.setSize(DEFAULT_WINDOW_OPTIONS.width, DEFAULT_WINDOW_OPTIONS.height);
            }, 1);
          } else if (argv.includes('--quit')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Quitting Franz via Task');
              app.quit();
            }, 1);
          }
        });
      }
    }
  });
}

// Fix Unity indicator issue
// https://github.com/electron/electron/issues/9046
if (isLinux && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) {
  process.env.XDG_CURRENT_DESKTOP = 'Unity';
}

// Disable GPU acceleration
if (!settings.get('enableGPUAcceleration')) {
  debug('Disable GPU Acceleration');
  app.disableHardwareAcceleration();
}

const createWindow = () => {
  // Remember window size
  const mainWindowState = windowStateKeeper({
    defaultWidth: DEFAULT_WINDOW_OPTIONS.width,
    defaultHeight: DEFAULT_WINDOW_OPTIONS.height,
  });

  let posX = mainWindowState.x || DEFAULT_WINDOW_OPTIONS.x;
  let posY = mainWindowState.y || DEFAULT_WINDOW_OPTIONS.y;

  if (!isPositionValid({ x: posX, y: posY })) {
    debug('Window is out of screen bounds, resetting window');
    posX = DEFAULT_WINDOW_OPTIONS.x;
    posY = DEFAULT_WINDOW_OPTIONS.y;
  }

  // Create the browser window.
  mainWindow = new BrowserWindow({
    x: posX,
    y: posY,
    width: mainWindowState.width,
    height: mainWindowState.height,
    minWidth: 600,
    minHeight: 500,
    titleBarStyle: isMac ? 'hidden' : '',
    frame: isLinux,
    backgroundColor: !settings.get('darkMode') ? '#3498db' : '#1E1E1E',
    webPreferences: {
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  mainWindow.webContents.on('did-finish-load', () => {
    const fns = onDidLoadFns;
    onDidLoadFns = [];

    if (!fns) return;

    for (const fn of fns) {
      fn(mainWindow);
    }
  });

  // Initialize System Tray
  const trayIcon = new Tray();

  // Initialize ipcApi
  ipcApi({
    mainWindow,
    settings: {
      app: settings,
      proxy: proxySettings,
    },
    trayIcon,
  });

  // Manage Window State
  mainWindowState.manage(mainWindow);

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`);

  // Open the DevTools.
  if (isDevMode || process.argv.includes('--devtools')) {
    mainWindow.webContents.openDevTools();
  }

  // Windows deep linking handling on app launch
  if (isWindows) {
    onDidLoad((window) => {
      const url = process.argv.slice(1);
      if (url) {
        handleDeepLink(window, url.toString());
      }
    });
  }

  // Emitted when the window is closed.
  mainWindow.on('close', (e) => {
    debug('Window: close window');
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    if (!willQuitApp && (settings.get('runInBackground') === undefined || settings.get('runInBackground'))) {
      e.preventDefault();
      if (isWindows) {
        debug('Window: minimize');
        mainWindow.minimize();

        if (settings.get('minimizeToSystemTray')) {
          debug('Skip taskbar: true');
          mainWindow.setSkipTaskbar(true);
        }
      } else {
        debug('Window: hide');
        mainWindow.hide();
      }
    } else {
      app.quit();
    }
  });

  // For Windows we need to store a flag to properly restore the window
  // if the window was maximized before minimizing it so system tray
  mainWindow.on('minimize', () => {
    app.wasMaximized = app.isMaximized;

    if (settings.get('minimizeToSystemTray')) {
      debug('Skip taskbar: true');
      mainWindow.setSkipTaskbar(true);
      trayIcon.show();
    }
  });

  mainWindow.on('maximize', () => {
    debug('Window: maximize');
    app.isMaximized = true;
  });

  mainWindow.on('unmaximize', () => {
    debug('Window: unmaximize');
    app.isMaximized = false;
  });

  mainWindow.on('restore', () => {
    debug('Window: restore');
    mainWindow.setSkipTaskbar(false);

    if (app.wasMaximized) {
      debug('Window: was maximized before, maximize window');
      mainWindow.maximize();
    }

    if (!settings.get('enableSystemTray')) {
      debug('Tray: hiding tray icon');
      trayIcon.hide();
    }
  });

  if (isMac) {
    askFormacOSPermissions();
  }

  mainWindow.on('show', () => {
    debug('Skip taskbar: false');
    mainWindow.setSkipTaskbar(false);
  });

  app.mainWindow = mainWindow;
  app.isMaximized = mainWindow.isMaximized();

  mainWindow.webContents.on('new-window', (e, url) => {
    debug('Open url', url);
    e.preventDefault();

    if (isValidExternalURL(url)) {
      shell.openExternal(url);
    }
  });
};

// Allow passing command line parameters/switches to electron
// https://electronjs.org/docs/api/chrome-command-line-switches
// used for Kerberos support
// Usage e.g. MACOS
// $ Franz.app/Contents/MacOS/Franz --auth-server-whitelist *.mydomain.com --auth-negotiate-delegate-whitelist *.mydomain.com
const argv = require('minimist')(process.argv.slice(1));

if (argv['auth-server-whitelist']) {
  app.commandLine.appendSwitch('auth-server-whitelist', argv['auth-server-whitelist']);
}
if (argv['auth-negotiate-delegate-whitelist']) {
  app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', argv['auth-negotiate-delegate-whitelist']);
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
  // force app to live in /Applications
  enforceMacOSAppLocation();

  // Register App URL
  app.setAsDefaultProtocolClient('franz');

  if (isDevMode) {
    app.setAsDefaultProtocolClient('franz-dev');
  }

  if (process.platform === 'win32') {
    app.setUserTasks([{
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--reset-window`,
      iconPath: asarPath(path.join(isDevMode ? `${__dirname}../src/` : __dirname, 'assets/images/taskbar/win32/display.ico')),
      iconIndex: 0,
      title: 'Move Franz to Current Display',
      description: 'Restore the position and size of Franz',
    }, {
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--quit`,
      iconIndex: 0,
      title: 'Quit Franz',
    }]);
  }

  createWindow();
});

// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕
// TODO: rewrite to handle multiple login calls
const noop = () => null;
let authCallback = noop;

app.on('login', (event, webContents, request, authInfo, callback) => {
  authCallback = callback;
  debug('browser login event', authInfo);
  event.preventDefault();

  if (!authInfo.isProxy && authInfo.scheme === 'basic') {
    debug('basic auth handler', authInfo);
    basicAuthHandler(mainWindow, authInfo);
  }
});

// TODO: evaluate if we need to store the authCallback for every service
ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => {
  debug('Received basic auth credentials', user, '********');

  authCallback(user, password);
  authCallback = noop;
});

ipcMain.on('feature-basic-auth-cancel', () => {
  debug('Cancel basic auth');

  authCallback(null);
  authCallback = noop;
});

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (settings.get('runInBackground') === undefined
    || settings.get('runInBackground')) {
    debug('Window: all windows closed, quit app');
    app.quit();
  } else {
    debug('Window: don\'t quit app');
  }
});

app.on('before-quit', () => {
  willQuitApp = true;
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  } else {
    mainWindow.show();
  }
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, url) => {
    event.preventDefault();

    onDidLoad((window) => {
      debug('open-url event', url);
      handleDeepLink(window, url);
    });
  });
});
<commit_msg>fix-fix deep linking<commit_after>import {
  app,
  BrowserWindow,
  shell,
  ipcMain,
} from 'electron';
import isDevMode from 'electron-is-dev';
import fs from 'fs-extra';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import { enforceMacOSAppLocation } from 'electron-util';

// Set app directory before loading user modules
if (process.env.FRANZ_APPDATA_DIR != null) {
  app.setPath('appData', process.env.FRANZ_APPDATA_DIR);
  app.setPath('userData', path.join(app.getPath('appData')));
} else if (process.platform === 'win32') {
  app.setPath('appData', process.env.APPDATA);
  app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
}

if (isDevMode) {
  app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
}

/* eslint-disable import/first */
import {
  isMac,
  isWindows,
  isLinux,
} from './environment';
import { mainIpcHandler as basicAuthHandler } from './features/basicAuth';
import ipcApi from './electron/ipc-api';
import Tray from './lib/Tray';
import Settings from './electron/Settings';
import handleDeepLink from './electron/deepLinking';
import { isPositionValid } from './electron/windowUtils';
import askFormacOSPermissions from './electron/macOSPermissions';
import { appId } from './package.json'; // eslint-disable-line import/no-unresolved
import './electron/exception';

import {
  DEFAULT_APP_SETTINGS,
  DEFAULT_WINDOW_OPTIONS,
} from './config';
import { asarPath } from './helpers/asar-helpers';
import { isValidExternalURL } from './helpers/url-helpers';
import userAgent from './helpers/userAgent-helpers';

/* eslint-enable import/first */
const debug = require('debug')('Franz:App');

// Globally set useragent to fix user agent override in service workers
debug('Set userAgent to ', userAgent());
app.userAgentFallback = userAgent();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let willQuitApp = false;

// Register methods to be called once the window has been loaded.
let onDidLoadFns = [];

function onDidLoad(fn) {
  if (onDidLoadFns) {
    onDidLoadFns.push(fn);
  } else if (mainWindow) {
    fn(mainWindow);
  }
}

// Ensure that the recipe directory exists
fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp'));
fs.ensureFileSync(path.join(app.getPath('userData'), 'window-state.json'));

// Set App ID for Windows
if (isWindows) {
  app.setAppUserModelId(appId);
}

// Initialize Settings
const settings = new Settings('app', DEFAULT_APP_SETTINGS);
const proxySettings = new Settings('proxy');

// add `liftSingleInstanceLock` to settings.json to override the single instance lock
const liftSingleInstanceLock = settings.get('liftSingleInstanceLock') || false;

// Force single window
const gotTheLock = liftSingleInstanceLock ? true : app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Someone tried to run a second instance, we should focus our window.
    if (mainWindow) {
      mainWindow.show();
      if (mainWindow.isMinimized()) {
        mainWindow.restore();
      }
      mainWindow.focus();

      if (isWindows) {
        onDidLoad((window) => {
          // Keep only command line / deep linked arguments
          const url = argv.slice(1);
          if (url) {
            handleDeepLink(window, url.toString());
          }

          if (argv.includes('--reset-window')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Resetting windows via Task');
              window.setPosition(DEFAULT_WINDOW_OPTIONS.x + 100, DEFAULT_WINDOW_OPTIONS.y + 100);
              window.setSize(DEFAULT_WINDOW_OPTIONS.width, DEFAULT_WINDOW_OPTIONS.height);
            }, 1);
          } else if (argv.includes('--quit')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Quitting Franz via Task');
              app.quit();
            }, 1);
          }
        });
      }
    }
  });
}

// Fix Unity indicator issue
// https://github.com/electron/electron/issues/9046
if (isLinux && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) {
  process.env.XDG_CURRENT_DESKTOP = 'Unity';
}

// Disable GPU acceleration
if (!settings.get('enableGPUAcceleration')) {
  debug('Disable GPU Acceleration');
  app.disableHardwareAcceleration();
}

const createWindow = () => {
  // Remember window size
  const mainWindowState = windowStateKeeper({
    defaultWidth: DEFAULT_WINDOW_OPTIONS.width,
    defaultHeight: DEFAULT_WINDOW_OPTIONS.height,
  });

  let posX = mainWindowState.x || DEFAULT_WINDOW_OPTIONS.x;
  let posY = mainWindowState.y || DEFAULT_WINDOW_OPTIONS.y;

  if (!isPositionValid({ x: posX, y: posY })) {
    debug('Window is out of screen bounds, resetting window');
    posX = DEFAULT_WINDOW_OPTIONS.x;
    posY = DEFAULT_WINDOW_OPTIONS.y;
  }

  // Create the browser window.
  mainWindow = new BrowserWindow({
    x: posX,
    y: posY,
    width: mainWindowState.width,
    height: mainWindowState.height,
    minWidth: 600,
    minHeight: 500,
    titleBarStyle: isMac ? 'hidden' : '',
    frame: isLinux,
    backgroundColor: !settings.get('darkMode') ? '#3498db' : '#1E1E1E',
    webPreferences: {
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  mainWindow.webContents.on('did-finish-load', () => {
    const fns = onDidLoadFns;
    onDidLoadFns = null;

    if (!fns) return;

    for (const fn of fns) {
      fn(mainWindow);
    }
  });

  // Initialize System Tray
  const trayIcon = new Tray();

  // Initialize ipcApi
  ipcApi({
    mainWindow,
    settings: {
      app: settings,
      proxy: proxySettings,
    },
    trayIcon,
  });

  // Manage Window State
  mainWindowState.manage(mainWindow);

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`);

  // Open the DevTools.
  if (isDevMode || process.argv.includes('--devtools')) {
    mainWindow.webContents.openDevTools();
  }

  // Windows deep linking handling on app launch
  if (isWindows) {
    onDidLoad((window) => {
      const url = process.argv.slice(1);
      if (url) {
        handleDeepLink(window, url.toString());
      }
    });
  }

  // Emitted when the window is closed.
  mainWindow.on('close', (e) => {
    debug('Window: close window');
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    if (!willQuitApp && (settings.get('runInBackground') === undefined || settings.get('runInBackground'))) {
      e.preventDefault();
      if (isWindows) {
        debug('Window: minimize');
        mainWindow.minimize();

        if (settings.get('minimizeToSystemTray')) {
          debug('Skip taskbar: true');
          mainWindow.setSkipTaskbar(true);
        }
      } else {
        debug('Window: hide');
        mainWindow.hide();
      }
    } else {
      app.quit();
    }
  });

  // For Windows we need to store a flag to properly restore the window
  // if the window was maximized before minimizing it so system tray
  mainWindow.on('minimize', () => {
    app.wasMaximized = app.isMaximized;

    if (settings.get('minimizeToSystemTray')) {
      debug('Skip taskbar: true');
      mainWindow.setSkipTaskbar(true);
      trayIcon.show();
    }
  });

  mainWindow.on('maximize', () => {
    debug('Window: maximize');
    app.isMaximized = true;
  });

  mainWindow.on('unmaximize', () => {
    debug('Window: unmaximize');
    app.isMaximized = false;
  });

  mainWindow.on('restore', () => {
    debug('Window: restore');
    mainWindow.setSkipTaskbar(false);

    if (app.wasMaximized) {
      debug('Window: was maximized before, maximize window');
      mainWindow.maximize();
    }

    if (!settings.get('enableSystemTray')) {
      debug('Tray: hiding tray icon');
      trayIcon.hide();
    }
  });

  if (isMac) {
    askFormacOSPermissions();
  }

  mainWindow.on('show', () => {
    debug('Skip taskbar: false');
    mainWindow.setSkipTaskbar(false);
  });

  app.mainWindow = mainWindow;
  app.isMaximized = mainWindow.isMaximized();

  mainWindow.webContents.on('new-window', (e, url) => {
    debug('Open url', url);
    e.preventDefault();

    if (isValidExternalURL(url)) {
      shell.openExternal(url);
    }
  });
};

// Allow passing command line parameters/switches to electron
// https://electronjs.org/docs/api/chrome-command-line-switches
// used for Kerberos support
// Usage e.g. MACOS
// $ Franz.app/Contents/MacOS/Franz --auth-server-whitelist *.mydomain.com --auth-negotiate-delegate-whitelist *.mydomain.com
const argv = require('minimist')(process.argv.slice(1));

if (argv['auth-server-whitelist']) {
  app.commandLine.appendSwitch('auth-server-whitelist', argv['auth-server-whitelist']);
}
if (argv['auth-negotiate-delegate-whitelist']) {
  app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', argv['auth-negotiate-delegate-whitelist']);
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
  // force app to live in /Applications
  enforceMacOSAppLocation();

  // Register App URL
  app.setAsDefaultProtocolClient('franz');

  if (isDevMode) {
    app.setAsDefaultProtocolClient('franz-dev');
  }

  if (process.platform === 'win32') {
    app.setUserTasks([{
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--reset-window`,
      iconPath: asarPath(path.join(isDevMode ? `${__dirname}../src/` : __dirname, 'assets/images/taskbar/win32/display.ico')),
      iconIndex: 0,
      title: 'Move Franz to Current Display',
      description: 'Restore the position and size of Franz',
    }, {
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--quit`,
      iconIndex: 0,
      title: 'Quit Franz',
    }]);
  }

  createWindow();
});

// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕
// TODO: rewrite to handle multiple login calls
const noop = () => null;
let authCallback = noop;

app.on('login', (event, webContents, request, authInfo, callback) => {
  authCallback = callback;
  debug('browser login event', authInfo);
  event.preventDefault();

  if (!authInfo.isProxy && authInfo.scheme === 'basic') {
    debug('basic auth handler', authInfo);
    basicAuthHandler(mainWindow, authInfo);
  }
});

// TODO: evaluate if we need to store the authCallback for every service
ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => {
  debug('Received basic auth credentials', user, '********');

  authCallback(user, password);
  authCallback = noop;
});

ipcMain.on('feature-basic-auth-cancel', () => {
  debug('Cancel basic auth');

  authCallback(null);
  authCallback = noop;
});

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (settings.get('runInBackground') === undefined
    || settings.get('runInBackground')) {
    debug('Window: all windows closed, quit app');
    app.quit();
  } else {
    debug('Window: don\'t quit app');
  }
});

app.on('before-quit', () => {
  willQuitApp = true;
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  } else {
    mainWindow.show();
  }
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, url) => {
    event.preventDefault();

    onDidLoad((window) => {
      debug('open-url event', url);
      handleDeepLink(window, url);
    });
  });
});
<|endoftext|>


<commit_before>/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import createHistory from 'history/createBrowserHistory';
import { render, hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import store from './store';
import { getMetadata, getStrings, getAbilities, getHeroAbilities, getNeutralAbilities, getAbilityIds } from './actions';
import App from './components/App';
// import { unregister } from './common/serviceWorker';

// Fetch metadata (used on all pages)
store.dispatch(getMetadata());
// Fetch strings
store.dispatch(getStrings());
store.dispatch(getAbilities());
store.dispatch(getHeroAbilities());
store.dispatch(getNeutralAbilities());
store.dispatch(getAbilityIds());

const history = createHistory();

if (process.env.NODE_ENV === 'production') {
  const ReactGA = require('react-ga'); // eslint-disable-line global-require
  ReactGA.initialize('UA-55757642-1');
  ReactGA.pageview(window.location.pathname + window.location.search);

  history.listen((location) => {
    ReactGA.pageview(location.pathname);
  });
}

const rootElement = document.getElementById('root');

const app = (
  <Provider store={store}>
    <BrowserRouter history={history}>
      <App />
    </BrowserRouter>
  </Provider>
);

if (rootElement.hasChildNodes()) {
  hydrate(app, rootElement);
} else {
  render(app, rootElement);
}

// unregister();
<commit_msg>Changed deprecated createBrowserHistory import to recommended<commit_after>/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import { createBrowserHistory } from 'history';
import { render, hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import store from './store';
import { getMetadata, getStrings, getAbilities, getHeroAbilities, getNeutralAbilities, getAbilityIds } from './actions';
import App from './components/App';
// import { unregister } from './common/serviceWorker';

// Fetch metadata (used on all pages)
store.dispatch(getMetadata());
// Fetch strings
store.dispatch(getStrings());
store.dispatch(getAbilities());
store.dispatch(getHeroAbilities());
store.dispatch(getNeutralAbilities());
store.dispatch(getAbilityIds());

const history = createBrowserHistory();

if (process.env.NODE_ENV === 'production') {
  const ReactGA = require('react-ga'); // eslint-disable-line global-require
  ReactGA.initialize('UA-55757642-1');
  ReactGA.pageview(window.location.pathname + window.location.search);

  history.listen((location) => {
    ReactGA.pageview(location.pathname);
  });
}

const rootElement = document.getElementById('root');

const app = (
  <Provider store={store}>
    <BrowserRouter history={history}>
      <App />
    </BrowserRouter>
  </Provider>
);

if (rootElement.hasChildNodes()) {
  hydrate(app, rootElement);
} else {
  render(app, rootElement);
}

// unregister();
<|endoftext|>