<commit_before>var globalInvokeLaterStack = []

export function app(props) {
  var appState
  var appActions = {}
  var appEvents = {}
  var appMixins = []
  var appView = props.view
  var appRoot = props.root || document.body
  var element = appRoot.children[0]
  var oldNode
  var willRender

  for (var i = -1; i < appMixins.length; i++) {
    props = appMixins[i] ? appMixins[i](emit) : props

    Object.keys(props.events || []).map(function(key) {
      appEvents[key] = (appEvents[key] || []).concat(props.events[key])
    })

    adaptActions(appActions, props.actions)

    appMixins = appMixins.concat(props.mixins || [])
    appState = merge(appState, props.state)
  }

  requestRender(
    (oldNode = emit("load", element)) === element && (oldNode = element = null)
  )

  return emit

  function render(cb) {
    element = patch(
      appRoot,
      element,
      oldNode,
      (oldNode = emit("render", appView)(appState, appActions)),
      (willRender = !willRender)
    )
    while ((cb = globalInvokeLaterStack.pop())) cb()
  }

  function requestRender() {
    if (appView && !willRender) {
      requestAnimationFrame(render, (willRender = !willRender))
    }
  }

  function update(withState) {
    if (withState && (withState = emit("update", merge(appState, withState)))) {
      requestRender((appState = withState))
    }
    return appState
  }

  function adaptActions(namespace, children, lastName) {
    Object.keys(children || []).map(function(key) {
      var action = children[key]
      var name = lastName ? lastName + "." + key : key

      if (typeof action === "function") {
        namespace[key] = function(data) {
          emit("action", { name: name, data: data })

          var result = emit("resolve", action(appState, appActions, data))

          return typeof result === "function" ? result(update) : update(result)
        }
      } else {
        adaptActions(namespace[key] || (namespace[key] = {}), action, name)
      }
    })
  }

  function emit(name, data) {
    return (appEvents[name] || []).map(function(cb) {
      var result = cb(appState, appActions, data)
      if (result != null) {
        data = result
      }
    }), data
  }

  function merge(a, b) {
    var obj = {}

    for (var i in a) {
      obj[i] = a[i]
    }

    for (var i in b) {
      obj[i] = b[i]
    }

    return obj
  }

  function getKey(node) {
    if (node && (node = node.data)) {
      return node.key
    }
  }

  function createElement(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)
    } else {
      var element = (isSVG = isSVG || node.tag === "svg")
        ? document.createElementNS("http://www.w3.org/2000/svg", node.tag)
        : document.createElement(node.tag)

      if (node.data && node.data.oncreate) {
        globalInvokeLaterStack.push(function() {
          node.data.oncreate(element)
        })
      }

      for (var i in node.data) {
        setData(element, i, node.data[i])
      }

      for (var i = 0; i < node.children.length; ) {
        element.appendChild(createElement(node.children[i++], isSVG))
      }
    }

    return element
  }

  function setData(element, name, value, oldValue) {
    if (name === "key") {
    } else if (name === "style") {
      for (var i in merge(oldValue, (value = value || {}))) {
        element.style[i] = value[i] || ""
      }
    } else {
      try {
        element[name] = value
      } catch (_) {}

      if (typeof value !== "function") {
        if (value) {
          element.setAttribute(name, value)
        } else {
          element.removeAttribute(name)
        }
      }
    }
  }

  function updateElement(element, oldData, data) {
    for (var i in merge(oldData, data)) {
      var value = data[i]
      var oldValue = i === "value" || i === "checked" ? element[i] : oldData[i]

      if (value !== oldValue) {
        setData(element, i, value, oldValue)
      }
    }

    if (data && data.onupdate) {
      globalInvokeLaterStack.push(function() {
        data.onupdate(element, oldData)
      })
    }
  }

  function removeElement(parent, element, data) {
    if (data && data.onremove) {
      data.onremove(element)
    } else {
      parent.removeChild(element)
    }
  }

  function patch(parent, element, oldNode, node, isSVG, nextSibling) {
    if (oldNode == null) {
      element = parent.insertBefore(createElement(node, isSVG), element)
    } else if (node.tag != null && node.tag === oldNode.tag) {
      updateElement(element, oldNode.data, node.data)

      isSVG = isSVG || node.tag === "svg"

      var len = node.children.length
      var oldLen = oldNode.children.length
      var oldKeyed = {}
      var oldElements = []
      var keyed = {}

      for (var i = 0; i < oldLen; i++) {
        var oldElement = (oldElements[i] = element.childNodes[i])
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)

        if (null != oldKey) {
          oldKeyed[oldKey] = [oldElement, oldChild]
        }
      }

      var i = 0
      var j = 0

      while (j < len) {
        var oldElement = oldElements[i]
        var oldChild = oldNode.children[i]
        var newChild = node.children[j]

        var oldKey = getKey(oldChild)
        if (keyed[oldKey]) {
          i++
          continue
        }

        var newKey = getKey(newChild)

        var keyedNode = oldKeyed[newKey] || []

        if (null == newKey) {
          if (null == oldKey) {
            patch(element, oldElement, oldChild, newChild, isSVG)
            j++
          }
          i++
        } else {
          if (oldKey === newKey) {
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
            i++
          } else if (keyedNode[0]) {
            element.insertBefore(keyedNode[0], oldElement)
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
          } else {
            patch(element, oldElement, null, newChild, isSVG)
          }

          j++
          keyed[newKey] = newChild
        }
      }

      while (i < oldLen) {
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)
        if (null == oldKey) {
          removeElement(element, oldElements[i], oldChild.data)
        }
        i++
      }

      for (var i in oldKeyed) {
        var keyedNode = oldKeyed[i]
        var reusableNode = keyedNode[1]
        if (!keyed[reusableNode.data.key]) {
          removeElement(element, keyedNode[0], reusableNode.data)
        }
      }
    } else if (element && node !== element.nodeValue) {
      element = parent.insertBefore(
        createElement(node, isSVG),
        (nextSibling = element)
      )
      removeElement(parent, nextSibling, oldNode.data)
    }

    return element
  }
}
<commit_msg>Refactor function names.<commit_after>var globalInvokeLaterStack = []

export function app(props) {
  var appState
  var appActions = {}
  var appEvents = {}
  var appMixins = []
  var appView = props.view
  var appRoot = props.root || document.body
  var element = appRoot.children[0]
  var oldNode
  var willRender

  for (var i = -1; i < appMixins.length; i++) {
    props = appMixins[i] ? appMixins[i](emit) : props

    Object.keys(props.events || []).map(function(key) {
      appEvents[key] = (appEvents[key] || []).concat(props.events[key])
    })

    initialize(appActions, props.actions)

    appMixins = appMixins.concat(props.mixins || [])
    appState = merge(appState, props.state)
  }

  requestRender(
    (oldNode = emit("load", element)) === element && (oldNode = element = null)
  )

  return emit

  function initialize(actions, withActions, lastName) {
    Object.keys(withActions || []).map(function(key) {
      var action = withActions[key]
      var name = lastName ? lastName + "." + key : key

      if (typeof action === "function") {
        actions[key] = function(data) {
          emit("action", { name: name, data: data })

          var result = emit("resolve", action(appState, appActions, data))

          return typeof result === "function" ? result(update) : update(result)
        }
      } else {
        initialize(actions[key] || (actions[key] = {}), action, name)
      }
    })
  }

  function render(cb) {
    element = patch(
      appRoot,
      element,
      oldNode,
      (oldNode = emit("render", appView)(appState, appActions)),
      (willRender = !willRender)
    )
    while ((cb = globalInvokeLaterStack.pop())) cb()
  }

  function requestRender() {
    if (appView && !willRender) {
      requestAnimationFrame(render, (willRender = !willRender))
    }
  }

  function update(withState) {
    if (withState && (withState = emit("update", merge(appState, withState)))) {
      requestRender((appState = withState))
    }
    return appState
  }

  function emit(name, data) {
    return (appEvents[name] || []).map(function(cb) {
      var result = cb(appState, appActions, data)
      if (result != null) {
        data = result
      }
    }), data
  }

  function merge(a, b) {
    var obj = {}

    for (var i in a) {
      obj[i] = a[i]
    }

    for (var i in b) {
      obj[i] = b[i]
    }

    return obj
  }

  function getKey(node) {
    if (node && (node = node.data)) {
      return node.key
    }
  }

  function createElement(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)
    } else {
      var element = (isSVG = isSVG || node.tag === "svg")
        ? document.createElementNS("http://www.w3.org/2000/svg", node.tag)
        : document.createElement(node.tag)

      if (node.data && node.data.oncreate) {
        globalInvokeLaterStack.push(function() {
          node.data.oncreate(element)
        })
      }

      for (var i in node.data) {
        setData(element, i, node.data[i])
      }

      for (var i = 0; i < node.children.length; ) {
        element.appendChild(createElement(node.children[i++], isSVG))
      }
    }

    return element
  }

  function setData(element, name, value, oldValue) {
    if (name === "key") {
    } else if (name === "style") {
      for (var i in merge(oldValue, (value = value || {}))) {
        element.style[i] = value[i] || ""
      }
    } else {
      try {
        element[name] = value
      } catch (_) {}

      if (typeof value !== "function") {
        if (value) {
          element.setAttribute(name, value)
        } else {
          element.removeAttribute(name)
        }
      }
    }
  }

  function updateElement(element, oldData, data) {
    for (var i in merge(oldData, data)) {
      var value = data[i]
      var oldValue = i === "value" || i === "checked" ? element[i] : oldData[i]

      if (value !== oldValue) {
        setData(element, i, value, oldValue)
      }
    }

    if (data && data.onupdate) {
      globalInvokeLaterStack.push(function() {
        data.onupdate(element, oldData)
      })
    }
  }

  function removeElement(parent, element, data) {
    if (data && data.onremove) {
      data.onremove(element)
    } else {
      parent.removeChild(element)
    }
  }

  function patch(parent, element, oldNode, node, isSVG, nextSibling) {
    if (oldNode == null) {
      element = parent.insertBefore(createElement(node, isSVG), element)
    } else if (node.tag != null && node.tag === oldNode.tag) {
      updateElement(element, oldNode.data, node.data)

      isSVG = isSVG || node.tag === "svg"

      var len = node.children.length
      var oldLen = oldNode.children.length
      var oldKeyed = {}
      var oldElements = []
      var keyed = {}

      for (var i = 0; i < oldLen; i++) {
        var oldElement = (oldElements[i] = element.childNodes[i])
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)

        if (null != oldKey) {
          oldKeyed[oldKey] = [oldElement, oldChild]
        }
      }

      var i = 0
      var j = 0

      while (j < len) {
        var oldElement = oldElements[i]
        var oldChild = oldNode.children[i]
        var newChild = node.children[j]

        var oldKey = getKey(oldChild)
        if (keyed[oldKey]) {
          i++
          continue
        }

        var newKey = getKey(newChild)

        var keyedNode = oldKeyed[newKey] || []

        if (null == newKey) {
          if (null == oldKey) {
            patch(element, oldElement, oldChild, newChild, isSVG)
            j++
          }
          i++
        } else {
          if (oldKey === newKey) {
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
            i++
          } else if (keyedNode[0]) {
            element.insertBefore(keyedNode[0], oldElement)
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
          } else {
            patch(element, oldElement, null, newChild, isSVG)
          }

          j++
          keyed[newKey] = newChild
        }
      }

      while (i < oldLen) {
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)
        if (null == oldKey) {
          removeElement(element, oldElements[i], oldChild.data)
        }
        i++
      }

      for (var i in oldKeyed) {
        var keyedNode = oldKeyed[i]
        var reusableNode = keyedNode[1]
        if (!keyed[reusableNode.data.key]) {
          removeElement(element, keyedNode[0], reusableNode.data)
        }
      }
    } else if (element && node !== element.nodeValue) {
      element = parent.insertBefore(
        createElement(node, isSVG),
        (nextSibling = element)
      )
      removeElement(parent, nextSibling, oldNode.data)
    }

    return element
  }
}
<|endoftext|>


<commit_before>var SVG_NS = "http://www.w3.org/2000/svg"

export default function (app) {
  var view = app.view || function () {
    return ""
  }

  var model
  var actions = {}
  var subscriptions = []
  var hooks = {
    onError: [],
    onAction: [],
    onUpdate: [],
    onRender: []
  }

  var node
  var element
  var root
  var batch = []
  var plugins = app.plugins || []

  for (var i = -1; i < plugins.length; i++) {
    var plugin = i < 0 ? app : plugins[i](app)
    var obj = plugin.model

    if (obj != null) {
      model = merge(model, obj)
    }

    if (obj = plugin.actions) {
      init(actions, obj)
    }

    if (obj = plugin.subscriptions) {
      subscriptions = subscriptions.concat(obj)
    }

    if (obj = plugin.hooks) {
      Object.keys(obj).forEach(function (key) {
        hooks[key].push(obj[key])
      })
    }
  }

  load(function () {
    root = app.root || document.body

    render(model, view)

    for (var i = 0; i < subscriptions.length; i++) {
      subscriptions[i](model, actions, onError)
    }
  })

  function onError(error) {
    for (var i = 0; i < hooks.onError.length; i++) {
      hooks.onError[i](error)
    }

    if (i <= 0) {
      throw error
    }
  }

  function init(container, group, lastName) {
    Object.keys(group).forEach(function (key) {
      if (!container[key]) {
        container[key] = {}
      }

      var name = lastName ? lastName + "." + key : key
      var action = group[key]

      if (typeof action === "function") {
        container[key] = function (data) {
          for (var i = 0; i < hooks.onAction.length; i++) {
            hooks.onAction[i](name, data)
          }

          var result = action(model, data, actions, onError)

          if (result == null || typeof result.then === "function") {
            return result

          } else {
            for (var i = 0; i < hooks.onUpdate.length; i++) {
              hooks.onUpdate[i](model, result, data)
            }

            model = merge(model, result)
            render(model, view)
          }
        }
      } else {
        init(container[key], action, name)
      }
    })
  }

  function load(fn) {
    if (document.readyState[0] !== "l") {
      fn()
    } else {
      document.addEventListener("DOMContentLoaded", fn)
    }
  }

  function render(model, view) {
    for (var i = 0; i < hooks.onRender.length; i++) {
      view = hooks.onRender[i](model, view)
    }

    element = patch(root, element, node, node = view(model, actions))

    for (var i = 0; i < batch.length; i++) {
      batch[i]()
    }

    batch = []
  }

  function merge(a, b) {
    var obj = {}

    if (typeof b !== "object" || Array.isArray(b)) {
      return b
    }

    for (var key in a) {
      obj[key] = a[key]
    }
    for (var key in b) {
      obj[key] = b[key]
    }

    return obj
  }

  function defer(fn, data) {
    setTimeout(function () {
      fn(data)
    }, 0)
  }

  function createElementFrom(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)

    } else {
      var element = (isSVG = isSVG || node.tag === "svg")
        ? document.createElementNS(SVG_NS, node.tag)
        : document.createElement(node.tag)

      for (var name in node.data) {
        if (name === "onCreate") {
          defer(node.data[name], element)
        } else {
          setElementData(element, name, node.data[name])
        }
      }

      for (var i = 0; i < node.children.length; i++) {
        element.appendChild(createElementFrom(node.children[i], isSVG))
      }
    }

    return element
  }

  function setElementData(element, name, value, oldValue) {
    name = name.toLowerCase()

    if (!value) {
      element[name] = value
      element.removeAttribute(name)

    } else if (name === "style") {
      for (var i in oldValue) {
        if (!(i in value)) {
          element.style[i] = ""
        }
      }

      for (var i in value) {
        element.style[i] = value[i]
      }
    } else {
      element.setAttribute(name, value)

      if (element.namespaceURI !== SVG_NS) {
        if (element.type === "text") {
          var oldSelStart = element.selectionStart
          var oldSelEnd = element.selectionEnd
        }

        element[name] = value

        if (oldSelStart >= 0) {
          element.setSelectionRange(oldSelStart, oldSelEnd)
        }
      }
    }
  }

  function updateElementData(element, data, oldData) {
    for (var name in merge(oldData, data)) {
      var value = data[name]
      var oldValue = oldData[name]
      var realValue = element[name]

      if (name === "onUpdate") {
        defer(value, element)

      } else if (value !== oldValue || realValue !== value) {
        setElementData(element, name, value, oldValue)
      }
    }
  }

  function patch(parent, element, oldNode, node) {
    if (oldNode == null) {
      element = parent.appendChild(createElementFrom(node))

    } else if (node == null) {
      batch.push(parent.removeChild.bind(parent, element))

      if (oldNode && oldNode.data && oldNode.data.onRemove) {
        defer(oldNode.data.onRemove, element)
      }

    } else if (
      node.tag !== oldNode.tag ||
      typeof node !== typeof oldNode ||
      typeof node === "string" && node !== oldNode
    ) {
      if (typeof node === "string") {
        element.textContent = node
      } else {
        var i = createElementFrom(node)
        parent.replaceChild(i, element)
        element = i
      }
    } else if (node.tag) {
      updateElementData(element, node.data, oldNode.data)

      var len = node.children.length
      var oldLen = oldNode.children.length

      for (var i = 0; i < len || i < oldLen; i++) {
        patch(element, element.childNodes[i], oldNode.children[i], node.children[i])
      }
    }

    return element
  }
}
<commit_msg>Refactor patch for less bytes.<commit_after>var SVG_NS = "http://www.w3.org/2000/svg"

export default function (app) {
  var view = app.view || function () {
    return ""
  }

  var model
  var actions = {}
  var subscriptions = []
  var hooks = {
    onError: [],
    onAction: [],
    onUpdate: [],
    onRender: []
  }

  var node
  var element
  var root
  var batch = []
  var plugins = app.plugins || []

  for (var i = -1; i < plugins.length; i++) {
    var plugin = i < 0 ? app : plugins[i](app)
    var obj = plugin.model

    if (obj != null) {
      model = merge(model, obj)
    }

    if (obj = plugin.actions) {
      init(actions, obj)
    }

    if (obj = plugin.subscriptions) {
      subscriptions = subscriptions.concat(obj)
    }

    if (obj = plugin.hooks) {
      Object.keys(obj).forEach(function (key) {
        hooks[key].push(obj[key])
      })
    }
  }

  load(function () {
    root = app.root || document.body

    render(model, view)

    for (var i = 0; i < subscriptions.length; i++) {
      subscriptions[i](model, actions, onError)
    }
  })

  function onError(error) {
    for (var i = 0; i < hooks.onError.length; i++) {
      hooks.onError[i](error)
    }

    if (i <= 0) {
      throw error
    }
  }

  function init(container, group, lastName) {
    Object.keys(group).forEach(function (key) {
      if (!container[key]) {
        container[key] = {}
      }

      var name = lastName ? lastName + "." + key : key
      var action = group[key]

      if (typeof action === "function") {
        container[key] = function (data) {
          for (var i = 0; i < hooks.onAction.length; i++) {
            hooks.onAction[i](name, data)
          }

          var result = action(model, data, actions, onError)

          if (result == null || typeof result.then === "function") {
            return result

          } else {
            for (var i = 0; i < hooks.onUpdate.length; i++) {
              hooks.onUpdate[i](model, result, data)
            }

            model = merge(model, result)
            render(model, view)
          }
        }
      } else {
        init(container[key], action, name)
      }
    })
  }

  function load(fn) {
    if (document.readyState[0] !== "l") {
      fn()
    } else {
      document.addEventListener("DOMContentLoaded", fn)
    }
  }

  function render(model, view) {
    for (var i = 0; i < hooks.onRender.length; i++) {
      view = hooks.onRender[i](model, view)
    }

    element = patch(root, element, node, node = view(model, actions))

    for (var i = 0; i < batch.length; i++) {
      batch[i]()
    }

    batch = []
  }

  function merge(a, b) {
    var obj = {}

    if (typeof b !== "object" || Array.isArray(b)) {
      return b
    }

    for (var key in a) {
      obj[key] = a[key]
    }
    for (var key in b) {
      obj[key] = b[key]
    }

    return obj
  }

  function defer(fn, data) {
    setTimeout(function () {
      fn(data)
    }, 0)
  }

  function createElementFrom(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)

    } else {
      var element = (isSVG = isSVG || node.tag === "svg")
        ? document.createElementNS(SVG_NS, node.tag)
        : document.createElement(node.tag)

      for (var name in node.data) {
        if (name === "onCreate") {
          defer(node.data[name], element)
        } else {
          setElementData(element, name, node.data[name])
        }
      }

      for (var i = 0; i < node.children.length; i++) {
        element.appendChild(createElementFrom(node.children[i], isSVG))
      }
    }

    return element
  }

  function setElementData(element, name, value, oldValue) {
    name = name.toLowerCase()

    if (!value) {
      element[name] = value
      element.removeAttribute(name)

    } else if (name === "style") {
      for (var i in oldValue) {
        if (!(i in value)) {
          element.style[i] = ""
        }
      }

      for (var i in value) {
        element.style[i] = value[i]
      }
    } else {
      element.setAttribute(name, value)

      if (element.namespaceURI !== SVG_NS) {
        if (element.type === "text") {
          var oldSelStart = element.selectionStart
          var oldSelEnd = element.selectionEnd
        }

        element[name] = value

        if (oldSelStart >= 0) {
          element.setSelectionRange(oldSelStart, oldSelEnd)
        }
      }
    }
  }

  function updateElementData(element, data, oldData) {
    for (var name in merge(oldData, data)) {
      var value = data[name]
      var oldValue = oldData[name]
      var realValue = element[name]

      if (name === "onUpdate") {
        defer(value, element)

      } else if (value !== oldValue || realValue !== value) {
        setElementData(element, name, value, oldValue)
      }
    }
  }

  function patch(parent, element, oldNode, node) {
    if (oldNode == null) {
      element = parent.appendChild(createElementFrom(node))

    } else if (node == null) {
      batch.push(parent.removeChild.bind(parent, element))

      if (oldNode && oldNode.data && oldNode.data.onRemove) {
        defer(oldNode.data.onRemove, element)
      }

    } else if (node.tag && node.tag === oldNode.tag) {
      updateElementData(element, node.data, oldNode.data)

      var len = node.children.length
      var oldLen = oldNode.children.length

      for (var i = 0; i < len || i < oldLen; i++) {
        patch(element, element.childNodes[i], oldNode.children[i], node.children[i])
      }

    } else if (node !== oldNode) {
      if (typeof node === "string") {
        element.textContent = node
      } else {
        var i = createElementFrom(node)
        parent.replaceChild(i, element)
        element = i
      }
    }

    return element
  }
}
<|endoftext|>


<commit_before>(function($) {
"use strict";

var sources = Echo.App.manifest("Echo.Apps.SocialSources");

if (Echo.App.isDefined(sources)) return;

sources.vars = {
	"chart": undefined,
	"sources": [],
	"visible": true,
	"watchers": {}
};

sources.config = {
	"targetURL": undefined,
	// amount of items to retrieve from StreamServer
	// 100 is the limitation on the amount of root items
	"maxItemsToRetrieve": 100,
	"chartUpdateDelay": 10000, // in ms
	"sources": {
		"twitter": {
			"id": "twitter",
			"color": "#00ACEA",
			"highlight": "#4CC5F1",
			"label": "Twitter"
		},
		"instagram": {
			"id": "instagram",
			"color": "#346A91",
			"highlight": "#7197B2",
			"label": "Instagram"
		},
		"facebook": {
			"id": "facebook",
			"color": "#3D5995",
			"highlight": "#778BB5",
			"label": "Facebook"
		},
		"youtube": {
			"id": "youtube",
			"color": "#FF0019",
			"highlight": "#FF4C5E",
			"label": "Youtube"
		},
		"native": {
			"id": "native",
			"color": "#D8D8D8",
			"highlight": "#E4E4E4",
			"label": "Website"
		},
		"other": {
			"id": "other",
			"color": "#EF4836",
			"highlight": "#F47F72",
			"label": "Other"
		}
	},
	"presentation": {
		"visualization": "doughnut", // or "pie"
		"maxWidth": 500 // in px
	},
	"chart": {
		"tooltipTemplate": "<%=label%>",
		"responsive": true
	},
	"dependencies": {
		"StreamServer": {
			"appkey": undefined,
			"apiBaseURL": "{%= apiBaseURLs.StreamServer.basic %}/",
			"liveUpdates": {
				"transport": "websockets",
				"enabled": true,
				"websockets": {
					"URL": "{%= apiBaseURLs.StreamServer.ws %}/"
				}
			}
		}
	}
};

sources.labels = {
	"noSources": "There are no items yet.
Stay tuned!"
};

sources.dependencies = [{
	"url": "{config:cdnBaseURL.sdk}/api.pack.js",
	"control": "Echo.StreamServer.API"
}, {
	"url": "{%= appBaseURLs.prod %}/third-party/chart.min.js",
	"loaded": function() { return !!window.Chart; }
}];

sources.init = function() {
	var app = this;

	// check for "targetURL" field, without
	// this field we are unable to retrieve any data
	if (!this.config.get("targetURL")) {
		this.showMessage({
			"type": "error",
			"message": "Unable to retrieve data, target URL is not specified."
		});
		return;
	}

	// spin up document visibility watcher to stop
	// gauge rendering in case a page is not active
	var watcher = app._createDocumentVisibilityWatcher();
	if (watcher) {
		watcher.start(function() {
			app.set("visible", true);
			app.refresh();
		}, function() {
			app.set("visible", false);
		});
		app.set("watchers.visibility", watcher);
	}

	// create chart update watcher to prevent
	// massive amount of chart update calls
	app.set("watchers.update", app._createUpdateWatcher());

	app.request = app._getRequestObject();

	var data = app.get("data");
	if ($.isEmptyObject(data)) {
		app.request.send();
	} else {
		app._getHandlerFor("onData")(data);
		app.request.send({
			"skipInitialRequest": true,
			"data": {
				"q": app._assembleQuery(),
				"appkey": app.config.get("dependencies.StreamServer.appkey"),
				"since": data.nextSince
			}
		});
	}
};

sources.destroy = function() {
	$.each(this.get("watchers"), function(_, watcher) {
		watcher.stop();
	});
};

sources.methods.template = function() {
	return this.templates[this.get("sources").length ? "graph" : "empty"];
};

sources.templates.graph =
	'
' +
		'<canvas class="{class:graph}"></canvas>' +
	'
';

sources.templates.empty =
	'
' +
		'{label:noSources}' +
	'
';

sources.renderers.container = function(element) {
	return element.css({
		"max-width": parseInt(this.config.get("presentation.maxWidth") + "px")
	});
};

sources.methods._initChart = function(target) {
	var ctx = target.get(0).getContext("2d");
	var type = this.config.get("presentation.visualization") === "pie"
		? "Pie"
		: "Doughnut";
	return new Chart(ctx)[type](this.get("sources"), this.config.get("chart"));
};

sources.methods._associateWithSources = function(entries, updateChart) {
	if (!entries || !entries.length) return;
	var app = this;
	var chart = this.get("chart");
	var configs = this.config.get("sources");
	var sources = this.get("sources", []);
	$.map(entries, function(entry) {
		if (entry.verbs[0] !== "http://activitystrea.ms/schema/1.0/post") return;
		var placed = false;
		var itemSource = app._classifySource(entry);
		$.each(sources, function(id, source) {
			if (source.id === itemSource) {
				source.value++;
				if (updateChart && chart.segments[id]) {
					chart.segments[id].value = source.value;
					app.get("watchers.update").start();
				}
				placed = true;
				return false; // break
			}
		});
		if (!placed) {
			var newSource = $.extend({"value": 1}, configs[itemSource]);
			sources.push(newSource);
			if (updateChart) {
				chart.addData(newSource);
			}
		}
	});
};

sources.methods._classifySource = function(entry) {
	var source = entry.source.name.toLowerCase();
	var configs = this.config.get("sources");
	if (!this.get("customerId")) {
		var canvasId = this.config.get("canvasId");
		this.set("customerId", canvasId.split("/")[0].toLowerCase());
	}
	return configs[source]
		? source
		: this.get("customerId") === source ? "native" : "other";
};

sources.methods._assembleQuery = function() {
	var query = "childrenof:{config:targetURL} " +
		"itemsPerPage:{config:maxItemsToRetrieve} children:0";
	return this.substitute({"template": query});
};

sources.methods._getRequestObject = function() {
	var ssConfig = this.config.get("dependencies.StreamServer");
	return Echo.StreamServer.API.request({
		"endpoint": "search",
		"apiBaseURL": ssConfig.apiBaseURL,
		"data": {
			"q": this._assembleQuery(),
			"appkey": ssConfig.appkey
		},
		"liveUpdates": $.extend(ssConfig.liveUpdates, {
			"onData": this._getHandlerFor("onUpdate")
		}),
		"onError": this._getHandlerFor("onError"),
		"onData": this._getHandlerFor("onData")
	});
};

// we prevent chart updates from super-fast calls in case of a huge
// new items flow, since chart update is quite CPU-intensive operation.
sources.methods._createUpdateWatcher = function() {
	var app = this, timeout;
	var stop = function() {
		clearTimeout(timeout);
		timeout = undefined;
	};
	var start = function() {
		if (timeout || !app.get("chart")) return;
		timeout = setTimeout(function() {
			if (app.get("visible")) {
				app.get("chart").update();
			}
			stop();
		}, app.config.get("chartUpdateDelay"));
	};
	return {"start": start, "stop": stop};
};

// maybe move to Echo.Utils later...
// note: the same function is located within Echo Historical Volume app!
// inspired by http://www.html5rocks.com/en/tutorials/pagevisibility/intro/
sources.methods._createDocumentVisibilityWatcher = function() {
	var prefix, handler;

	// if "hidden" is natively supported just return it
	if ("hidden" in document) {
		prefix = ""; // non-prefixed, i.e. natively supported
	} else {
		var prefixes = ["webkit", "moz", "ms", "o"];
		for (var i = 0; i < prefixes.length; i++) {
			if ((prefixes[i] + "Hidden") in document) {
				prefix = prefixes[i] + "Hidden";
				break;
			}
		}
	}

	// we were unable to locate "hidden" property,
	// which means this functionality is not supported
	if (prefix === undefined) return;

	var eventName = prefix + "visibilitychange";
	return {
		"start": function(onShow, onHide) {
			handler = function() {
				document[prefix ? prefix + "Hidden" : "hidden"]
					? onHide()
					: onShow();
			};
			$(document).on(eventName, handler);
		},
		"stop": function() {
			$(document).off(eventName, handler);
		}
	};
};

sources.methods._getHandlerFor = function(name) {
	return $.proxy(this.handlers[name], this);
};

sources.methods.handlers = {};

sources.methods.handlers.onData = function(data) {
	var app = this;

	// store initial data in the config as well,
	// so that we can access it later to refresh the gauge
	if ($.isEmptyObject(app.config.get("data"))) {
		app.config.set("data", data);
	}

	app._associateWithSources(data.entries);
	app.render();

	// we init graph *only* after a target is placed into DOM,
	// Chart.js doesn't like elements detached from DOM structure...
	if (app.get("sources").length) {
		app.set("chart", app._initChart(app.view.get("graph")));
	}

	app.ready();
};

sources.methods.handlers.onUpdate = function(data) {
	if (!data || !data.entries) return;

	if (this.get("visible")) {
		var chartInitialized = !!this.get("chart");
		this._associateWithSources(data.entries, chartInitialized);
		if (this.get("sources").length && !chartInitialized) {
			// rerender the whole app
			// to switch templates and init chart
			this.render();
			this.set("chart", this._initChart(this.view.get("graph")));
		}
	}

	if (data && data.entries) {
		var max = this.config.get("maxItemsToRetrieve");
		var entries = this.config.get("data.entries", []);
		data.entries = data.entries.concat(entries).slice(0, max);
		this.config.set("data", data);
	}
};

sources.methods.handlers.onError = function(data, options) {
	var isCriticalError =
		typeof options.critical === "undefined" ||
		options.critical && options.requestType === "initial";

	if (isCriticalError) {
		this.showError(data, $.extend(options, {
			"request": this.request
		}));
	}
};

sources.css =
	'.{class:empty} { border: 1px solid #d2d2d2; margin: 0 5px 10px 5px; padding: 30px 0; text-align: left; }' +
	'.{class:empty} .{class:message} { background: url("//cdn.echoenabled.com/apps/echo/conversations/v2/sdk-derived/images/info.png") no-repeat; margin: 0 auto; font-size: 14px; font-family: "Helvetica Neue", Helvetica, "Open Sans", sans-serif; padding-left: 40px; display: block; width: 180px; line-height: 16px; color: #7f7f7f; }' +
	'.{class:container} { margin: 0px auto; }' +
	'.{class:graph} { width: 100%; }';

Echo.App.create(sources);

})(Echo.jQuery);
<commit_msg>Updating 'no data' message UI<commit_after>(function($) {
"use strict";

var sources = Echo.App.manifest("Echo.Apps.SocialSources");

if (Echo.App.isDefined(sources)) return;

sources.vars = {
	"chart": undefined,
	"sources": [],
	"visible": true,
	"watchers": {}
};

sources.config = {
	"targetURL": undefined,
	// amount of items to retrieve from StreamServer
	// 100 is the limitation on the amount of root items
	"maxItemsToRetrieve": 100,
	"chartUpdateDelay": 10000, // in ms
	"sources": {
		"twitter": {
			"id": "twitter",
			"color": "#00ACEA",
			"highlight": "#4CC5F1",
			"label": "Twitter"
		},
		"instagram": {
			"id": "instagram",
			"color": "#346A91",
			"highlight": "#7197B2",
			"label": "Instagram"
		},
		"facebook": {
			"id": "facebook",
			"color": "#3D5995",
			"highlight": "#778BB5",
			"label": "Facebook"
		},
		"youtube": {
			"id": "youtube",
			"color": "#FF0019",
			"highlight": "#FF4C5E",
			"label": "Youtube"
		},
		"native": {
			"id": "native",
			"color": "#D8D8D8",
			"highlight": "#E4E4E4",
			"label": "Website"
		},
		"other": {
			"id": "other",
			"color": "#EF4836",
			"highlight": "#F47F72",
			"label": "Other"
		}
	},
	"presentation": {
		"visualization": "doughnut", // or "pie"
		"maxWidth": 500 // in px
	},
	"chart": {
		"tooltipTemplate": "<%=label%>",
		"responsive": true
	},
	"dependencies": {
		"StreamServer": {
			"appkey": undefined,
			"apiBaseURL": "{%= apiBaseURLs.StreamServer.basic %}/",
			"liveUpdates": {
				"transport": "websockets",
				"enabled": true,
				"websockets": {
					"URL": "{%= apiBaseURLs.StreamServer.ws %}/"
				}
			}
		}
	}
};

sources.labels = {
	"noSources": "No data yet.
Stay tuned!"
};

sources.dependencies = [{
	"url": "{config:cdnBaseURL.sdk}/api.pack.js",
	"control": "Echo.StreamServer.API"
}, {
	"url": "{%= appBaseURLs.prod %}/third-party/chart.min.js",
	"loaded": function() { return !!window.Chart; }
}];

sources.init = function() {
	var app = this;

	// check for "targetURL" field, without
	// this field we are unable to retrieve any data
	if (!this.config.get("targetURL")) {
		this.showMessage({
			"type": "error",
			"message": "Unable to retrieve data, target URL is not specified."
		});
		return;
	}

	// spin up document visibility watcher to stop
	// gauge rendering in case a page is not active
	var watcher = app._createDocumentVisibilityWatcher();
	if (watcher) {
		watcher.start(function() {
			app.set("visible", true);
			app.refresh();
		}, function() {
			app.set("visible", false);
		});
		app.set("watchers.visibility", watcher);
	}

	// create chart update watcher to prevent
	// massive amount of chart update calls
	app.set("watchers.update", app._createUpdateWatcher());

	app.request = app._getRequestObject();

	var data = app.get("data");
	if ($.isEmptyObject(data)) {
		app.request.send();
	} else {
		app._getHandlerFor("onData")(data);
		app.request.send({
			"skipInitialRequest": true,
			"data": {
				"q": app._assembleQuery(),
				"appkey": app.config.get("dependencies.StreamServer.appkey"),
				"since": data.nextSince
			}
		});
	}
};

sources.destroy = function() {
	$.each(this.get("watchers"), function(_, watcher) {
		watcher.stop();
	});
};

sources.methods.template = function() {
	return this.templates[this.get("sources").length ? "graph" : "empty"];
};

sources.templates.graph =
	'
' +
		'<canvas class="{class:graph}"></canvas>' +
	'
';

sources.templates.empty =
	'
' +
		'{label:noSources}' +
	'
';

sources.renderers.container = function(element) {
	return element.css({
		"max-width": parseInt(this.config.get("presentation.maxWidth") + "px")
	});
};

sources.methods._initChart = function(target) {
	var ctx = target.get(0).getContext("2d");
	var type = this.config.get("presentation.visualization") === "pie"
		? "Pie"
		: "Doughnut";
	return new Chart(ctx)[type](this.get("sources"), this.config.get("chart"));
};

sources.methods._associateWithSources = function(entries, updateChart) {
	if (!entries || !entries.length) return;
	var app = this;
	var chart = this.get("chart");
	var configs = this.config.get("sources");
	var sources = this.get("sources", []);
	$.map(entries, function(entry) {
		if (entry.verbs[0] !== "http://activitystrea.ms/schema/1.0/post") return;
		var placed = false;
		var itemSource = app._classifySource(entry);
		$.each(sources, function(id, source) {
			if (source.id === itemSource) {
				source.value++;
				if (updateChart && chart.segments[id]) {
					chart.segments[id].value = source.value;
					app.get("watchers.update").start();
				}
				placed = true;
				return false; // break
			}
		});
		if (!placed) {
			var newSource = $.extend({"value": 1}, configs[itemSource]);
			sources.push(newSource);
			if (updateChart) {
				chart.addData(newSource);
			}
		}
	});
};

sources.methods._classifySource = function(entry) {
	var source = entry.source.name.toLowerCase();
	var configs = this.config.get("sources");
	if (!this.get("customerId")) {
		var canvasId = this.config.get("canvasId");
		this.set("customerId", canvasId.split("/")[0].toLowerCase());
	}
	return configs[source]
		? source
		: this.get("customerId") === source ? "native" : "other";
};

sources.methods._assembleQuery = function() {
	var query = "childrenof:{config:targetURL} " +
		"itemsPerPage:{config:maxItemsToRetrieve} children:0";
	return this.substitute({"template": query});
};

sources.methods._getRequestObject = function() {
	var ssConfig = this.config.get("dependencies.StreamServer");
	return Echo.StreamServer.API.request({
		"endpoint": "search",
		"apiBaseURL": ssConfig.apiBaseURL,
		"data": {
			"q": this._assembleQuery(),
			"appkey": ssConfig.appkey
		},
		"liveUpdates": $.extend(ssConfig.liveUpdates, {
			"onData": this._getHandlerFor("onUpdate")
		}),
		"onError": this._getHandlerFor("onError"),
		"onData": this._getHandlerFor("onData")
	});
};

// we prevent chart updates from super-fast calls in case of a huge
// new items flow, since chart update is quite CPU-intensive operation.
sources.methods._createUpdateWatcher = function() {
	var app = this, timeout;
	var stop = function() {
		clearTimeout(timeout);
		timeout = undefined;
	};
	var start = function() {
		if (timeout || !app.get("chart")) return;
		timeout = setTimeout(function() {
			if (app.get("visible")) {
				app.get("chart").update();
			}
			stop();
		}, app.config.get("chartUpdateDelay"));
	};
	return {"start": start, "stop": stop};
};

// maybe move to Echo.Utils later...
// note: the same function is located within Echo Historical Volume app!
// inspired by http://www.html5rocks.com/en/tutorials/pagevisibility/intro/
sources.methods._createDocumentVisibilityWatcher = function() {
	var prefix, handler;

	// if "hidden" is natively supported just return it
	if ("hidden" in document) {
		prefix = ""; // non-prefixed, i.e. natively supported
	} else {
		var prefixes = ["webkit", "moz", "ms", "o"];
		for (var i = 0; i < prefixes.length; i++) {
			if ((prefixes[i] + "Hidden") in document) {
				prefix = prefixes[i] + "Hidden";
				break;
			}
		}
	}

	// we were unable to locate "hidden" property,
	// which means this functionality is not supported
	if (prefix === undefined) return;

	var eventName = prefix + "visibilitychange";
	return {
		"start": function(onShow, onHide) {
			handler = function() {
				document[prefix ? prefix + "Hidden" : "hidden"]
					? onHide()
					: onShow();
			};
			$(document).on(eventName, handler);
		},
		"stop": function() {
			$(document).off(eventName, handler);
		}
	};
};

sources.methods._getHandlerFor = function(name) {
	return $.proxy(this.handlers[name], this);
};

sources.methods.handlers = {};

sources.methods.handlers.onData = function(data) {
	var app = this;

	// store initial data in the config as well,
	// so that we can access it later to refresh the gauge
	if ($.isEmptyObject(app.config.get("data"))) {
		app.config.set("data", data);
	}

	app._associateWithSources(data.entries);
	app.render();

	// we init graph *only* after a target is placed into DOM,
	// Chart.js doesn't like elements detached from DOM structure...
	if (app.get("sources").length) {
		app.set("chart", app._initChart(app.view.get("graph")));
	}

	app.ready();
};

sources.methods.handlers.onUpdate = function(data) {
	if (!data || !data.entries) return;

	if (this.get("visible")) {
		var chartInitialized = !!this.get("chart");
		this._associateWithSources(data.entries, chartInitialized);
		if (this.get("sources").length && !chartInitialized) {
			// rerender the whole app
			// to switch templates and init chart
			this.render();
			this.set("chart", this._initChart(this.view.get("graph")));
		}
	}

	if (data && data.entries) {
		var max = this.config.get("maxItemsToRetrieve");
		var entries = this.config.get("data.entries", []);
		data.entries = data.entries.concat(entries).slice(0, max);
		this.config.set("data", data);
	}
};

sources.methods.handlers.onError = function(data, options) {
	var isCriticalError =
		typeof options.critical === "undefined" ||
		options.critical && options.requestType === "initial";

	if (isCriticalError) {
		this.showError(data, $.extend(options, {
			"request": this.request
		}));
	}
};

sources.css =
	'.{class:empty} { border: 1px solid #d2d2d2; background-color: #fff; margin: 0 5px 10px 5px; padding: 30px 20px; text-align: center; }' +
	'.{class:empty} .{class:message} { background: url("//cdn.echoenabled.com/apps/echo/conversations/v2/sdk-derived/images/info.png") no-repeat; margin: 0 auto; font-size: 14px; font-family: "Helvetica Neue", Helvetica, "Open Sans", sans-serif; padding-left: 40px; display: inline-block; text-align: left; line-height: 16px; color: #7f7f7f; }' +
	'.{class:container} { margin: 0px auto; }' +
	'.{class:graph} { width: 100%; }';

Echo.App.create(sources);

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


<commit_before>import React, {Component, PropTypes} from 'react';
import {IntlProvider, injectIntl} from 'react-intl';

export const decorateContainer = function(DecoratedComponent, options = {}) {
    const ConnectedDecoratedComponent = injectIntl(DecoratedComponent);

    const EnvelopedContainer = class extends Component {
        static get propTypes() {
            return options.propTypes;
        }

        static get contextTypes() {
            return {
                config: PropTypes.object
            };
        }

        createDispatch(props) {
            let {dispatch} = this.props;

            if (!dispatch) {
                dispatch = (action) => {
                    if (typeof action === 'function') {
                        action((action) => {
                            // Unwrap all the thunk layers
                            dispatch(action);
                        }, () => {
                            return props;
                        });
                    } else {
                        this.setState(action.payload || {});
                    }
                };
            }

            return dispatch;
        }

        calculateIntlProps() {
            const {messages} = this.props;
            const {locale = options.locale || 'en'} = this.props;

            const props = {locale};

            if (typeof messages !== 'undefined') {
                props.messages = messages;
            }

            return props;
        }

        mergeProps() {
            // TODO: Deep merge? Performance on render?
            const contextConfig = this.context ? (this.context.config || {}) : {};
            const propsConfig = this.props ? (this.props.config || {}) : {};
            const stateConfig = this.state ? (this.state.config || {}) : {};
            const config = {...options.config, ...contextConfig, ...propsConfig, ...stateConfig};

            const props = {
                ...this.props,
                ...this.state,
                ...{config: config}
            };

            return props;
        }

        render() {
            const intlProps = this.calculateIntlProps();
            const props = this.mergeProps();
            const dispatch = this.createDispatch(props);

            return (
                <IntlProvider {...intlProps}>
                    <ConnectedDecoratedComponent {...props} dispatch={dispatch} />
                </IntlProvider>
            );
        }
    };

    Object.defineProperty(EnvelopedContainer, 'name', {writable: true});
    EnvelopedContainer.name = DecoratedComponent.name;
    Object.defineProperty(EnvelopedContainer, 'name', {writable: false});
    EnvelopedContainer.Component = DecoratedComponent

    return EnvelopedContainer;
};

export default function Container(options = {}) {
    // `options` is the class being decorated
    let result;

    if (typeof options !== 'function') {
        result = (DecoratedComponent) => decorateContainer(DecoratedComponent, options);
    } else {
        result = decorateContainer(options);
    }

    return result;
}

<commit_msg>Changed Container name<commit_after>import React, {Component, PropTypes} from 'react';
import {IntlProvider, injectIntl} from 'react-intl';

export const decorateContainer = function(DecoratedComponent, options = {}) {
    const ConnectedDecoratedComponent = injectIntl(DecoratedComponent);

    const EnvelopedContainer = class extends Component {
        static get propTypes() {
            return options.propTypes;
        }

        static get contextTypes() {
            return {
                config: PropTypes.object
            };
        }

        createDispatch(props) {
            let {dispatch} = this.props;

            if (!dispatch) {
                dispatch = (action) => {
                    if (typeof action === 'function') {
                        action((action) => {
                            // Unwrap all the thunk layers
                            dispatch(action);
                        }, () => {
                            return props;
                        });
                    } else {
                        this.setState(action.payload || {});
                    }
                };
            }

            return dispatch;
        }

        calculateIntlProps() {
            const {messages} = this.props;
            const {locale = options.locale || 'en'} = this.props;

            const props = {locale};

            if (typeof messages !== 'undefined') {
                props.messages = messages;
            }

            return props;
        }

        mergeProps() {
            // TODO: Deep merge? Performance on render?
            const contextConfig = this.context ? (this.context.config || {}) : {};
            const propsConfig = this.props ? (this.props.config || {}) : {};
            const stateConfig = this.state ? (this.state.config || {}) : {};
            const config = {...options.config, ...contextConfig, ...propsConfig, ...stateConfig};

            const props = {
                ...this.props,
                ...this.state,
                ...{config: config}
            };

            return props;
        }

        render() {
            const intlProps = this.calculateIntlProps();
            const props = this.mergeProps();
            const dispatch = this.createDispatch(props);

            return (
                <IntlProvider {...intlProps}>
                    <ConnectedDecoratedComponent {...props} dispatch={dispatch} />
                </IntlProvider>
            );
        }
    };

    Object.defineProperty(EnvelopedContainer, 'name', {writable: true});
    EnvelopedContainer.name = `${DecoratedComponent.name}Container`;
    Object.defineProperty(EnvelopedContainer, 'name', {writable: false});
    EnvelopedContainer.Component = DecoratedComponent;

    return EnvelopedContainer;
};

export default function Container(options = {}) {
    // `options` is the class being decorated
    let result;

    if (typeof options !== 'function') {
        result = (DecoratedComponent) => decorateContainer(DecoratedComponent, options);
    } else {
        result = decorateContainer(options);
    }

    return result;
}

<|endoftext|>


<commit_before>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FS_CLIENT_SECRET = process.env.FS_CLIENT_SECRET;
const FS_CLIENT_ID = process.env.FS_CLIENT_ID;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

const foursquareVersion = '20160108';
const foursquareCategories = {
    food: 'food',
    drinks: 'drinks',
    coffee: 'coffee',
    shops: 'shops',
    arts: 'arts',
    topPicks: 'topPicks',
};

const actionFindVenue = 'findVenue';
const intentFindVenue = 'FindVenue';

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text:", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                var action = response.result.action;
                var intentName = response.result.metadata.intentName;
                var parameters = response.result.parameters;

                console.log(action);

                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        async.eachSeries(responseData.facebook, (facebookMessage, callback) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action, callback);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage, callback);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message}, callback);
                            }
                        });
                    }
                } else if (isDefined(responseText)) {
                    responseText(responseText);
                } else if (isDefined(action) && isDefined(intentName)) {
                    if (action === actionFindVenue && intentName == intentFindVenue) {
                        if (isDefined(parameters)) {
                            console.log('Found parameters');
                            var foursquareResponse = findVenue(parameters);
                            if (isDefined(foursquareResponse)) {
                                cardResponse(foursquareResponse);
                            }
                        }
                    }
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    }
}

function textResponse(str) {
    console.log('Response as text message');
        // facebook API limit for text length is 320,
        // so we must split message if needed
    var splittedText = splitResponse(responseText);

    async.eachSeries(splittedText, (textPart, callback) => {
        sendFBMessage(sender, {text: textPart}, callback);
    });
}

function cardResponse(res) {
    //respond with a card
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    return output;
}

function sendFBMessage(sender, messageData, callback) {
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();

function formatGETOptions(parameters) {

    var venueType = parameters.venue;
    var location = parameters.location.location;

    if (!isDefined(venueType)) {
        venueType = foursquareCategories.topPicks;
    }

    if (!isDefined(location)) {
        return null;
    }

    var options = {
        method: 'GET',
        url: 'http://api.foursquare.com/v2/venues/explore',
        qs: {
            client_id: FS_CLIENT_ID,
            client_secret: FS_CLIENT_SECRET,
            v: foursquareVersion,
            m: 'foursquare',
            near: location,
            section: venueType,
            limit: 5,
        },
        venuePhotos: 1,

    };

    console.log('Venue: ', options.qs.section);
    console.log('Location: ', options.qs.near);

    return options;
}

function findVenue(parameters) {

    var response;

    var options = formatGETOptions(parameters);

    if (isDefined(options)) {
        request(options, (error, res, body) => {
            if (error) {
                console.error('GET Error: ', error);
            } else {
                console.log(body);
                response = JSON.parse(body);
            }
        });
    };

    console.log(typeof(response));
    return response;
}<commit_msg>syntax<commit_after>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FS_CLIENT_SECRET = process.env.FS_CLIENT_SECRET;
const FS_CLIENT_ID = process.env.FS_CLIENT_ID;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

const foursquareVersion = '20160108';
const foursquareCategories = {
    food: 'food',
    drinks: 'drinks',
    coffee: 'coffee',
    shops: 'shops',
    arts: 'arts',
    topPicks: 'topPicks',
};

const actionFindVenue = 'findVenue';
const intentFindVenue = 'FindVenue';

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text:", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                var action = response.result.action;
                var intentName = response.result.metadata.intentName;
                var parameters = response.result.parameters;

                console.log(action);

                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        async.eachSeries(responseData.facebook, (facebookMessage, callback) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action, callback);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage, callback);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message}, callback);
                            }
                        });
                    }
                } else if (isDefined(responseText)) {
                    textRespond(responseText);
                } else if (isDefined(action) && isDefined(intentName)) {
                    if (action === actionFindVenue && intentName == intentFindVenue) {
                        if (isDefined(parameters)) {
                            console.log('Found parameters');
                            var foursquareResponse = findVenue(parameters);
                            if (isDefined(foursquareResponse)) {
                                cardResponse(foursquareResponse);
                            }
                        }
                    }
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    }
}

function textRespond(str) {
    console.log('Response as text message');
        // facebook API limit for text length is 320,
        // so we must split message if needed
    var splittedText = splitResponse(responseText);

    async.eachSeries(splittedText, (textPart, callback) => {
        sendFBMessage(sender, {text: textPart}, callback);
    });
}

function cardResponse(res) {
    //respond with a card
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    return output;
}

function sendFBMessage(sender, messageData, callback) {
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();

function formatGETOptions(parameters) {

    var venueType = parameters.venue;
    var location = parameters.location.location;

    if (!isDefined(venueType)) {
        venueType = foursquareCategories.topPicks;
    }

    if (!isDefined(location)) {
        return null;
    }

    var options = {
        method: 'GET',
        url: 'http://api.foursquare.com/v2/venues/explore',
        qs: {
            client_id: FS_CLIENT_ID,
            client_secret: FS_CLIENT_SECRET,
            v: foursquareVersion,
            m: 'foursquare',
            near: location,
            section: venueType,
            limit: 5,
        },
        venuePhotos: 1,

    };

    console.log('Venue: ', options.qs.section);
    console.log('Location: ', options.qs.near);

    return options;
}

function findVenue(parameters) {

    var response;

    var options = formatGETOptions(parameters);

    if (isDefined(options)) {
        request(options, (error, res, body) => {
            if (error) {
                console.error('GET Error: ', error);
            } else {
                console.log(body);
                response = JSON.parse(body);
            }
        });
    };

    console.log(typeof(response));
    return response;
}<|endoftext|>


<commit_before>require('array.prototype.find').shim()
require('array.prototype.findindex').shim()

import choo from 'choo'
import requestCamera from './effects/request-camera'
import requestFullscreen from './effects/request-fullscreen'
import snap from './effects/snap'
import translate from './effects/translate'
import baseView from './views/base-view'

const app = choo()

app.model({
  state: {
    activeView:  'main',
    cameraReady: false,
    cameraError: false,
    streamUrl:   null,
    video:       null,
    ctx:         null,
    canvas:      null,
    isSnapping:  false,
    firstTime:   true,
    fullscreen:  false,
    label:       '',
    translation: '',
    activeLang:  'spanish',
    targetLang:  'english',
    guesses:     '',
    rotateTerms: true
  },
  subscriptions: [
    (send, done) => document.addEventListener('DOMContentLoaded', _ => send('requestCamera', done))
  ],
  reducers: {
    showList:      _ => ({activeView: 'list'}),
    showMain:      _ => ({activeView: 'main'}),
    cameraError:   _ => ({cameraError: true}),
    startSnap:     _ => ({isSnapping: true, firstTime: false}),
    endSnap:       _ => ({isSnapping: false}),
    setFullscreen: _ => ({fullscreen: true}),
    setLabelPair:  (_, labels) => labels,
    changeLang:    (_, lang) => ({
      activeView:  'main',
      activeLang:  lang,
      label:       '',
      translation: ''
    }),
    setStream:     (_, {stream, video, ctx, canvas}) => ({
      cameraReady: true,
      streamUrl:   stream,
      video,
      ctx,
      canvas
    })
  },
  effects: {
    requestCamera,
    snap,
    translate,
    requestFullscreen
  }
})

app.router({default: '/'}, [['/', baseView]])
document.body.appendChild(app.start())
<commit_msg>update stream property naming<commit_after>require('array.prototype.find').shim()
require('array.prototype.findindex').shim()

import choo from 'choo'
import requestCamera from './effects/request-camera'
import requestFullscreen from './effects/request-fullscreen'
import snap from './effects/snap'
import translate from './effects/translate'
import baseView from './views/base-view'

const app = choo()

app.model({
  state: {
    activeView:  'main',
    cameraReady: false,
    cameraError: false,
    stream:      null,
    video:       null,
    ctx:         null,
    canvas:      null,
    isSnapping:  false,
    firstTime:   true,
    fullscreen:  false,
    label:       '',
    translation: '',
    activeLang:  'spanish',
    targetLang:  'english',
    guesses:     '',
    rotateTerms: true
  },
  subscriptions: [
    (send, done) => document.addEventListener('DOMContentLoaded', _ => send('requestCamera', done))
  ],
  reducers: {
    showList:      _ => ({activeView: 'list'}),
    showMain:      _ => ({activeView: 'main'}),
    cameraError:   _ => ({cameraError: true}),
    startSnap:     _ => ({isSnapping: true, firstTime: false}),
    endSnap:       _ => ({isSnapping: false}),
    setFullscreen: _ => ({fullscreen: true}),
    setLabelPair:  (_, labels) => labels,
    changeLang:    (_, lang) => ({
      activeView:  'main',
      activeLang:  lang,
      label:       '',
      translation: ''
    }),
    setStream:     (_, {stream, video, ctx, canvas}) => ({
      cameraReady: true,
      stream,
      video,
      ctx,
      canvas
    })
  },
  effects: {
    requestCamera,
    snap,
    translate,
    requestFullscreen
  }
})

app.router({default: '/'}, [['/', baseView]])
document.body.appendChild(app.start())
<|endoftext|>


<commit_before>const electron = require('electron');
const AutoLaunch = require('auto-launch');
const contribution = require('contribution');

const log = require('./logger');
const icon = require('./icon');
const pjson = require('../package.json');
const store = require('./store');

const { app, BrowserWindow, Tray, Menu, shell, ipcMain } = electron;

app.on('ready', () => {
  const streakerAutoLauncher = new AutoLaunch({
    name: pjson.name
  });

  const tray = new Tray(icon.done);
  let usernameWindow = null;

  function createUsernameWindow() {
    if (usernameWindow) {
      usernameWindow.focus();
      return;
    }
    usernameWindow = new BrowserWindow({
      title: `${pjson.name} - Set GitHub Username`,
      frame: false,
      width: 270,
      height: 60,
      resizable: false,
      maximizable: false,
      show: false
    });
    usernameWindow.loadURL(`file://${__dirname}/username.html`);
    usernameWindow.once('ready-to-show', () => {
      const screen = electron.screen.getDisplayNearestPoint(
        electron.screen.getCursorScreenPoint()
      );
      usernameWindow.setPosition(
        Math.floor(
          screen.bounds.x +
            screen.size.width / 2 -
            usernameWindow.getSize()[0] / 2
        ),
        Math.floor(
          screen.bounds.y +
            screen.size.height / 2 -
            usernameWindow.getSize()[1] / 2
        )
      );
      usernameWindow.show();
    });
    usernameWindow.on('closed', () => {
      usernameWindow = null;
    });
    usernameWindow.on('blur', () => {
      usernameWindow.close();
    });
  }

  function createTrayMenu(contributionCount, currentStreak, bestStreak) {
    const username = store.get('username') || 'username not set';
    const githubProfileUrl = `https://github.com/${username}`;
    const menuTemplate = [
      { label: username, enabled: false },
      { type: 'separator' },
      { label: `Current Streak: ${currentStreak}`, enabled: false },
      { label: `Best Streak: ${bestStreak}`, enabled: false },
      { label: `Contributions: ${contributionCount}`, enabled: false },
      { type: 'separator' },
      { label: 'Reload', accelerator: 'Cmd+R', click: requestContributionData },
      {
        label: 'Open GitHub Profile...',
        accelerator: 'Cmd+O',
        click: () => shell.openExternal(githubProfileUrl)
      },
      { type: 'separator' },
      {
        label: 'Set GitHub Username...',
        accelerator: 'Cmd+S',
        click: createUsernameWindow
      },
      {
        label: 'Preferences',
        submenu: [
          {
            label: `Launch ${pjson.name} at login`,
            type: 'checkbox',
            checked: store.get('autoLaunch'),
            click: checkbox => {
              store.set('autoLaunch', checkbox.checked);
              if (checkbox.checked) {
                streakerAutoLauncher.enable();
              } else {
                streakerAutoLauncher.disable();
              }
              log.info(`Store updated - autoLaunch=${checkbox.checked}`);
            }
          }
        ]
      },
      { type: 'separator' },
      {
        label: `About ${pjson.name}...`,
        click: () => shell.openExternal(pjson.homepage)
      },
      {
        label: 'Feedback && Support...',
        click: () => shell.openExternal(pjson.bugs.url)
      },
      { type: 'separator' },
      {
        label: `Quit ${pjson.name}`,
        accelerator: 'Cmd+Q',
        click: () => app.quit()
      }
    ];
    return Menu.buildFromTemplate(menuTemplate);
  }

  function requestContributionData() {

    const username = store.get('username');

    if (username === '') {
      tray.setImage(icon.fail);
      createUsernameWindow();
      return;
    }

    tray.setImage(icon.load);
    tray.setContextMenu(
      createTrayMenu('Loading...', 'Loading...', 'Loading...')
    );

    contribution(username)
      .then(data => {
        tray.setContextMenu(
          createTrayMenu(
            data.contributions,
            data.currentStreak,
            data.bestStreak
          )
        );
        tray.setImage(data.currentStreak > 0 ? icon.done : icon.todo);
        log.info(
          `Request successful - username=${username} streak=${
            data.currentStreak
          } today=${data.contributions > 0}`
        );
      })
      .catch(error => {
        tray.setContextMenu(createTrayMenu('Error', 'Error', 'Error'));
        tray.setImage(icon.fail);
        log.error(
          `Request failed - username=${username}) statusCode=${
            error.statusCode
          }`
        );
      });
  }

  function setUsername(event, username) {
    usernameWindow.close();
    if (username && username !== store.get('username')) {
      store.set('username', username);
      requestContributionData();
      log.info(`Store updated - username=${username}`);
    }
  }

  process.on('uncaughtException', (error) => {
      tray.setContextMenu(createTrayMenu('Error', 'Error', 'Error'));
      tray.setImage(icon.fail);
      log.error(error);
  })

  if (process.platform === 'darwin') {
    app.dock.hide();
  }

  app.on('window-all-closed', () => {});
  tray.on('right-click', requestContributionData);
  ipcMain.on('setUsername', setUsername);

  requestContributionData();
  setInterval(requestContributionData, 1000 * 60 * 15); // 15 Minutes
});
<commit_msg>Set contextMenu with '0' as value<commit_after>const electron = require('electron');
const AutoLaunch = require('auto-launch');
const contribution = require('contribution');

const log = require('./logger');
const icon = require('./icon');
const pjson = require('../package.json');
const store = require('./store');

const { app, BrowserWindow, Tray, Menu, shell, ipcMain } = electron;

app.on('ready', () => {
  const streakerAutoLauncher = new AutoLaunch({
    name: pjson.name
  });

  const tray = new Tray(icon.done);
  let usernameWindow = null;

  function createUsernameWindow() {
    if (usernameWindow) {
      usernameWindow.focus();
      return;
    }
    usernameWindow = new BrowserWindow({
      title: `${pjson.name} - Set GitHub Username`,
      frame: false,
      width: 270,
      height: 60,
      resizable: false,
      maximizable: false,
      show: false
    });
    usernameWindow.loadURL(`file://${__dirname}/username.html`);
    usernameWindow.once('ready-to-show', () => {
      const screen = electron.screen.getDisplayNearestPoint(
        electron.screen.getCursorScreenPoint()
      );
      usernameWindow.setPosition(
        Math.floor(
          screen.bounds.x +
            screen.size.width / 2 -
            usernameWindow.getSize()[0] / 2
        ),
        Math.floor(
          screen.bounds.y +
            screen.size.height / 2 -
            usernameWindow.getSize()[1] / 2
        )
      );
      usernameWindow.show();
    });
    usernameWindow.on('closed', () => {
      usernameWindow = null;
    });
    usernameWindow.on('blur', () => {
      usernameWindow.close();
    });
  }

  function createTrayMenu(contributionCount, currentStreak, bestStreak) {
    const username = store.get('username') || 'username not set';
    const githubProfileUrl = `https://github.com/${username}`;
    const menuTemplate = [
      { label: username, enabled: false },
      { type: 'separator' },
      { label: `Current Streak: ${currentStreak}`, enabled: false },
      { label: `Best Streak: ${bestStreak}`, enabled: false },
      { label: `Contributions: ${contributionCount}`, enabled: false },
      { type: 'separator' },
      { label: 'Reload', accelerator: 'Cmd+R', click: requestContributionData },
      {
        label: 'Open GitHub Profile...',
        accelerator: 'Cmd+O',
        click: () => shell.openExternal(githubProfileUrl)
      },
      { type: 'separator' },
      {
        label: 'Set GitHub Username...',
        accelerator: 'Cmd+S',
        click: createUsernameWindow
      },
      {
        label: 'Preferences',
        submenu: [
          {
            label: `Launch ${pjson.name} at login`,
            type: 'checkbox',
            checked: store.get('autoLaunch'),
            click: checkbox => {
              store.set('autoLaunch', checkbox.checked);
              if (checkbox.checked) {
                streakerAutoLauncher.enable();
              } else {
                streakerAutoLauncher.disable();
              }
              log.info(`Store updated - autoLaunch=${checkbox.checked}`);
            }
          }
        ]
      },
      { type: 'separator' },
      {
        label: `About ${pjson.name}...`,
        click: () => shell.openExternal(pjson.homepage)
      },
      {
        label: 'Feedback && Support...',
        click: () => shell.openExternal(pjson.bugs.url)
      },
      { type: 'separator' },
      {
        label: `Quit ${pjson.name}`,
        accelerator: 'Cmd+Q',
        click: () => app.quit()
      }
    ];
    return Menu.buildFromTemplate(menuTemplate);
  }

  function requestContributionData() {

    const username = store.get('username');

    if (username === '') {
      tray.setImage(icon.fail);
      createUsernameWindow();
      tray.setContextMenu(
        createTrayMenu('0', '0', '0')
      );
      return;
    }

    tray.setImage(icon.load);
    tray.setContextMenu(
      createTrayMenu('Loading...', 'Loading...', 'Loading...')
    );

    contribution(username)
      .then(data => {
        tray.setContextMenu(
          createTrayMenu(
            data.contributions,
            data.currentStreak,
            data.bestStreak
          )
        );
        tray.setImage(data.currentStreak > 0 ? icon.done : icon.todo);
        log.info(
          `Request successful - username=${username} streak=${
            data.currentStreak
          } today=${data.contributions > 0}`
        );
      })
      .catch(error => {
        tray.setContextMenu(createTrayMenu('Error', 'Error', 'Error'));
        tray.setImage(icon.fail);
        log.error(
          `Request failed - username=${username}) statusCode=${
            error.statusCode
          }`
        );
      });
  }

  function setUsername(event, username) {
    usernameWindow.close();
    if (username && username !== store.get('username')) {
      store.set('username', username);
      requestContributionData();
      log.info(`Store updated - username=${username}`);
    }
  }

  process.on('uncaughtException', (error) => {
      tray.setContextMenu(createTrayMenu('Error', 'Error', 'Error'));
      tray.setImage(icon.fail);
      log.error(error);
  })

  if (process.platform === 'darwin') {
    app.dock.hide();
  }

  app.on('window-all-closed', () => {});
  tray.on('right-click', requestContributionData);
  ipcMain.on('setUsername', setUsername);

  requestContributionData();
  setInterval(requestContributionData, 1000 * 60 * 15); // 15 Minutes
});
<|endoftext|>


<commit_before>var app = angular.module('codeLinks', []);
app.factory('post', [function() {
    var o = {
        posts: []
    };
    return 0;
}]);
app.controller('MainCtrl', [
    '$scope',
    function($scope) {
        $scope.test = 'Hello world!';
        $scope.posts = [
            {title: 'post 1', upvotes: 5},
            {title: 'post 2', upvotes: 2},
            {title: 'post 3', upvotes: 15},
            {title: 'post 4', upvotes: 9},
            {title: 'post 5', upvotes: 4}
        ];
        $scope.addPost = function() {
            if (!$scope.title || $scope.title === '') {
                return;
            }
            $scope.posts.push({
                title: $scope.title,
                link: $scope.link,
                upvotes: 0
            });
            $scope.title = '';
            $scope.link = '';
        };
        $scope.incrementUpvotes = function(post) {
            post.upvotes += 1;
        };
    }
]);<commit_msg>Inject posts service to MainCtrl.<commit_after>var app = angular.module('codeLinks', []);
app.factory('posts', [function() {
    var o = {
        posts: []
    };
    return o;
}]);
app.controller('MainCtrl', [
    '$scope',
    'posts',
    function($scope, posts) {
        $scope.test = 'Hello world!';
        $scope.posts = posts.posts;
        $scope.addPost = function() {
            if (!$scope.title || $scope.title === '') {
                return;
            }
            $scope.posts.push({
                title: $scope.title,
                link: $scope.link,
                upvotes: 0
            });
            $scope.title = '';
            $scope.link = '';
        };
        $scope.incrementUpvotes = function(post) {
            post.upvotes += 1;
        };
    }
]);<|endoftext|>