<commit_before>import forEach from '../core/for-each'
import forElements from '../core/for-elements'

import remove from '../dom/remove'
import data from '../dom/data'
import find from '../dom/find'
import createElement from '../../dom/create-element'
import append from '../dom/append'
import clone from '../dom/clone'

import style from '../styles/style'
import screenPosition from '../styles/screen-position'
import height from '../styles/height'
import transform from '../styles/transform'
import size from '../styles/size'

import resize from '../events/resize'
import unresize from '../events/unresize'
import load from '../events/load'

import defaultify from '../utils/defaultify'

import ScrollEvents from './scroll-events'

let defaults = {
  debug: false,
  offset: 0,
  ease: 0.2,
  stepMinSize: 5,
  snapOffset: 0,
  handle: {
    top: 'top',
    bottom: 'bottom'
  }
}

function randomColor () {
    return '#' + Math.floor(Math.random()*16777215).toString(16)
}

//Scroll manager
export class Wasabi {
  constructor(config) {
    this.config = defaultify(config, defaults)

    this.wrapper = this.scroller ? this.scroller.wrapper : document.body

    if (!this.config.scroller) {
      this.wrapper = document.body
      this.scrollTop = this.previousScrollTop = screenPosition(this.wrapper).top

      this.scrollEventsManager = new ScrollEvents()
      this.scrollEventsCallback = this.onScrollEvent.bind(this)
      this.scrollEventsManager.on(this.scrollEventsCallback)
    }
    else {
        this.scroller = this.config.scroller

        this.wrapper = this.scroller.wrapper
        this.scrollTop = this.previousScrollTop = this.scroller.scroll.y

        this.scrollerCallback = this.onScroller.bind(this)
        this.scroller.on('update', this.scrollerCallback)
    }

    this.resizeCallback = resize(() => {
        requestAnimationFrame(this.refresh.bind(this))
    })

    if (this.config.debug) {
      this.debugWrapper = createElement('

')
      style(this.debugWrapper, {
        'z-index': 10000,
        width: 25,
        height: height(this.wrapper),
        position: 'absolute',
        top: 0,
        right: 0,
        background: '#2d2d2d'
      })
      append(this.wrapper, this.debugWrapper)
    }

    this.currentSnapIndex = 0
    this.running = true
    this.refresh()
    this.update()

    load(find(this.wrapper, 'img'), this.refresh.bind(this))
  }

  refresh() {
    if (this.config.debug) console.log('%c WASABI DEBUG ', 'background: #2d2d2d color: #b0dd44')

    this.zones = []
    this.snaps = []

    this.windowHeight = window.innerHeight
    this.halfHeight = this.windowHeight/2

    if (this.wrapper) this.wrapperHeight = height(this.wrapper)
    if (this.config.debug) remove('#wasabi-debug .wasabi-marker')

    if (typeof this.config.zones == 'string') {
      forElements(this.config.zones, (element) => {
        this.addZone({}, element)
      })
    }
    else {
      let i = this.config.zones.length
      while (i--) {
        let zoneConfig = this.config.zones[i]

        if (zoneConfig.selector) {
          forElements(zoneConfig.selector, (element) => {
            this.addZone(zoneConfig, element)
          })
        }
        else {
          this.addZone(zoneConfig)
        }
      }
    }

    if (this.snaps.length) {
      if (!this.scroller) {
        console.error('snap option needs a SmoothScroller instance')
      }

      this.snaps.sort((a, b) => {
        return a.top - b.top
      })

      this.currentSnap = this.snaps[this.currentSnapIndex]
    }
  }

  addZone(zoneConfig, element) {
    let zone = {},
        top, bottom

    if (element) {
      zone.element = element
      zone.selector = zoneConfig.selector
      top = screenPosition(element).top + this.scrollTop
      bottom = top + height(element)

      zone.parallax = []
      forElements(find(element, '[data-wasabi]'), (pxElement) => {
        let options = eval('('+data(pxElement, 'wasabi')+')')

        let toX = (typeof options.x !== 'undefined') ? options.x : ((options.to && options.to.x) || 0),
            toY = (typeof options.y !== 'undefined') ? options.y : ((options.to && options.to.y) || 0),
            fromX = (options.from && options.from.x) || 0,
            fromY = (options.from && options.from.y) || 0,
            parentSize = size(element.parentNode)

        if (typeof toX == 'string' && toX.indexOf('%') != -1)
            toX = parseInt(toX, 10) * parentSize.width

        if (typeof toY == 'string' && toY.indexOf('%') != -1)
            toY = parseInt(toY, 10) * parentSize.height

        if (typeof fromX == 'string' && fromX.indexOf('%') != -1)
            fromX = parseInt(fromX, 10) * parentSize.width

        if (typeof fromY == 'string' && fromY.indexOf('%') != -1)
            fromY = parseInt(fromY, 10) * parentSize.height

        zone.parallax.push({
            element: pxElement,
            toX: toX,
            toY: toY,
            fromX: fromX,
            fromY: fromY
        })
      })
    }
    else {
      top = zoneConfig.top
      bottom = zoneConfig.bottom
    }

    let offset = defaultify(zoneConfig.offset, this.config.offset)
    zone.offset = {
        top: defaultify(offset.top, offset),
        bottom: defaultify(offset.bottom, offset)
    }

    zone.top = top - zone.offset.top
    zone.bottom = bottom + zone.offset.bottom

    if (this.config.debug) {
      let color = randomColor()

      console.log(zone.selector +' %c ' + color, 'color:'+color)
      console.log(zone.element)

      let topDebug = createElement(`<div class="wasabi-marker"></div>`)
      append(this.debugWrapper, topDebug)
      style(topDebug, {
        position: 'absolute',
        top: zone.top,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })

      let bottomDebug = clone(topDebug)
      append(this.debugWrapper, bottomDebug)
      style(bottomDebug, {
        'z-index': 9999,
        position: 'absolute',
        top: zone.bottom,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })
    }

    zone.size = zone.bottom - zone.top
    zone.handle = zoneConfig.handle || this.config.handle
    zone.progress = zoneConfig.progress || this.config.progress
    zone.snap = zoneConfig.snap || this.config.snap
    zone.handler = zoneConfig.handler || this.config.handler
    zone.enter = zoneConfig.enter || this.config.enter
    zone.leave = zoneConfig.leave || this.config.leave

    if (zoneConfig.tween) {
      zone.tween = zoneConfig.tween
      if (zone.tween.pause) zone.tween.pause()
    }

    if (zoneConfig.progressTween) {
      zone.progressTween = zoneConfig.progressTween
      if (zone.progressTween.pause) zone.progressTween.pause()
    }

    let handles = {}
    let handleForward = zone.handle.forward || zone.handle
    handles.forward = {
      top: handleForward.top || handleForward,
      bottom: handleForward.bottom || handleForward
    }
    let handleBackward = zone.handle.forward || zone.handle
    handles.backward = {
      top: handleBackward.top || handleBackward,
      bottom: handleBackward.bottom || handleBackward
    }

    if (handles.forward.top == 'middle') {
      zone.forwardTop = zone.top + this.halfHeight
    }
    else if (handles.forward.top == 'bottom') {
      zone.forwardTop = zone.top - this.windowHeight
    }
    else {
      zone.forwardTop = zone.top
    }

    if (handles.forward.bottom == 'middle') {
      zone.forwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.forward.bottom == 'bottom') {
      zone.forwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.forwardBottom = zone.bottom
    }

    zone.forwardSize = Math.max(this.config.stepMinSize, zone.forwardBottom - zone.forwardTop)

    if (handles.backward.top == 'middle') {
      zone.backwardTop = zone.top + this.halfHeight
    }
    else if (handles.backward.top == 'bottom') {
      zone.backwardTop = zone.top - this.windowHeight
    }
    else {
      zone.backwardTop = zone.top
    }

    if (handles.backward.bottom == 'middle') {
      zone.backwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.backward.bottom == 'bottom') {
      zone.backwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.backwardBottom = zone.bottom
    }

    zone.backwardSize = Math.max(this.config.stepMinSize, zone.backwardBottom - zone.backwardTop)

    if (zone.snap) this.snaps.push(zone)

    this.zones.push(zone)
  }

  testSnapping(scrollTarget) {
    if (this.scroller.scrollDisabled || !this.snaps.length) return

    let previous = this.snaps[this.currentSnapIndex-1],
        next = this.snaps[this.currentSnapIndex+1]

    if (previous && scrollTarget.y < this.currentSnap.top) {
      --this.currentSnapIndex
      this.currentSnap = previous

      this.scroller.scrollTarget.y = this.currentSnap.top + this.currentSnap.offset.top
      this.scroller.scrollTo({
        x: 0,
        y: (previous.bottom - this.currentSnap.offset.bottom) - Math.min(previous.size, this.windowHeight)-1
      })
    }
    else if (next && scrollTarget.y > this.currentSnap.bottom - this.windowHeight) {
      ++this.currentSnapIndex
      this.currentSnap = next

      this.scroller.scrollTarget.y = this.currentSnap.bottom - this.windowHeight - this.currentSnap.offset.bottom
      this.scroller.scrollTo({
        x: 0,
        y: (next.top + this.currentSnap.offset.top)+1
      })
    }
  }

  onScroller(scrollTarget) {
    this.scrollTop = this.scroller.scroll.y
    this.testSnapping(scrollTarget)
  }

  onScrollEvent(event) {
    this.scrollTop = screenPosition(this.wrapper).top
  }

  update() {
    if (!this.running) return

    let i = this.zones.length,
        direction = this.previousScrollTop < this.scrollTop ? 'forward' : 'backward'

    while (i--) {
      let zone = this.zones[i], entered, progress

      progress = (this.scrollTop - zone[direction+'Top'])/zone[direction+'Size']
      entered = progress >= 0 && progress <= 1

      if (!zone.entered && entered) {
        if (zone.tween) zone.tween.resume()
        if(zone.handler) zone.handler('enter', direction, zone.selector, zone.element)
        if(zone.enter) zone.enter(direction, zone.selector, zone.element)

        if(zone.snap) {
            this.currentSnapIndex = this.snaps.indexOf(zone)
            this.currentSnap = this.snaps[this.currentSnapIndex]
        }
      }
      else if (zone.entered && !entered) {
          if(zone.handler) zone.handler('leave', direction, zone.selector, zone.element)
        if(zone.leave) zone.leave(direction, zone.selector, zone.element)
      }

      forEach(zone.parallax, (item) => {
        transform(item.element, {
          x: item.fromX + (item.toX - item.fromX) * progress,
          y: item.fromY + (item.toY - item.fromY) * progress
        })
      })

      zone.entered = entered
      if (zone.entered) {
        if (zone.progress) zone.progress(direction, progress, zone.selector)
        if (zone.progressTween && zone.progressTween.progress) zone.progressTween.progress(progress)
      }
    }

    this.previousScrollTop = this.scrollTop

    this.updateRequest = requestAnimationFrame(this.update.bind(this))
  }

  kill() {
    this.running = false

    unresize(this.resizeCallback)
    cancelAnimationFrame(this.updateRequest)

    remove(this.debugWrapper)

    if (this.scrollEventsCallback) {
      this.scrollEventsManager.off(this.scrollEventsCallback)
    }
    else if (this.scrollerCallback) {
      this.scroller.off('update', this.scrollerCallback)
    }

    let i = this.zones.length
    while(i--) {
      let zone = this.zones[i]

      if (zone.tween) this.killTimeline(zone.tween)
      if (zone.progressTween) this.killTimeline(zone.progressTween)
    }

    this.zones = this.snaps = null
  }

  concatenateVars(object) {
    if (!object) return

    let keys = Object.keys(object),
        i = keys.length,
        vars = []

    while(i--) {
        if (typeof object[keys[i]] == 'object') {
            vars = vars.concat(this.concatenateVars(object[keys[i]]))
        }
        else {
            let key = keys[i]
            vars.push(key == 'x' || key == 'y' || key == 'scale' || key == 'rotate' ? 'transform' : key)
        }
    }

    return vars
  }

  killTimeline(timeline) {
    let tweens = timeline.getChildren()
    timeline.kill()

    let i = tweens.length, tween
    while(i--) {
      tween = tweens[i]

      if (tween.target) {
        TweenLite.set(tween.target, {
          clearProps: this.concatenateVars(tween.vars).join(',')
        })
      }
    }
  }
}

export default Wasabi
<commit_msg>fix wasabi imports<commit_after>import forEach from '../core/for-each'
import forElements from '../core/for-elements'

import remove from '../dom/remove'
import data from '../dom/data'
import find from '../dom/find'
import createElement from '../dom/create-element'
import append from '../dom/append'
import clone from '../dom/clone'

import style from '../styles/style'
import screenPosition from '../styles/screen-position'
import height from '../styles/height'
import transform from '../styles/transform'
import size from '../styles/size'

import resize from '../events/resize'
import unresize from '../events/unresize'
import load from '../events/load'

import defaultify from '../utils/defaultify'

import ScrollEvents from './scroll-events'

let defaults = {
  debug: false,
  offset: 0,
  ease: 0.2,
  stepMinSize: 5,
  snapOffset: 0,
  handle: {
    top: 'top',
    bottom: 'bottom'
  }
}

function randomColor () {
    return '#' + Math.floor(Math.random()*16777215).toString(16)
}

//Scroll manager
export class Wasabi {
  constructor(config) {
    this.config = defaultify(config, defaults)

    this.wrapper = this.scroller ? this.scroller.wrapper : document.body

    if (!this.config.scroller) {
      this.wrapper = document.body
      this.scrollTop = this.previousScrollTop = screenPosition(this.wrapper).top

      this.scrollEventsManager = new ScrollEvents()
      this.scrollEventsCallback = this.onScrollEvent.bind(this)
      this.scrollEventsManager.on(this.scrollEventsCallback)
    }
    else {
        this.scroller = this.config.scroller

        this.wrapper = this.scroller.wrapper
        this.scrollTop = this.previousScrollTop = this.scroller.scroll.y

        this.scrollerCallback = this.onScroller.bind(this)
        this.scroller.on('update', this.scrollerCallback)
    }

    this.resizeCallback = resize(() => {
        requestAnimationFrame(this.refresh.bind(this))
    })

    if (this.config.debug) {
      this.debugWrapper = createElement('

')
      style(this.debugWrapper, {
        'z-index': 10000,
        width: 25,
        height: height(this.wrapper),
        position: 'absolute',
        top: 0,
        right: 0,
        background: '#2d2d2d'
      })
      append(this.wrapper, this.debugWrapper)
    }

    this.currentSnapIndex = 0
    this.running = true
    this.refresh()
    this.update()

    load(find(this.wrapper, 'img'), this.refresh.bind(this))
  }

  refresh() {
    if (this.config.debug) console.log('%c WASABI DEBUG ', 'background: #2d2d2d color: #b0dd44')

    this.zones = []
    this.snaps = []

    this.windowHeight = window.innerHeight
    this.halfHeight = this.windowHeight/2

    if (this.wrapper) this.wrapperHeight = height(this.wrapper)
    if (this.config.debug) remove('#wasabi-debug .wasabi-marker')

    if (typeof this.config.zones == 'string') {
      forElements(this.config.zones, (element) => {
        this.addZone({}, element)
      })
    }
    else {
      let i = this.config.zones.length
      while (i--) {
        let zoneConfig = this.config.zones[i]

        if (zoneConfig.selector) {
          forElements(zoneConfig.selector, (element) => {
            this.addZone(zoneConfig, element)
          })
        }
        else {
          this.addZone(zoneConfig)
        }
      }
    }

    if (this.snaps.length) {
      if (!this.scroller) {
        console.error('snap option needs a SmoothScroller instance')
      }

      this.snaps.sort((a, b) => {
        return a.top - b.top
      })

      this.currentSnap = this.snaps[this.currentSnapIndex]
    }
  }

  addZone(zoneConfig, element) {
    let zone = {},
        top, bottom

    if (element) {
      zone.element = element
      zone.selector = zoneConfig.selector
      top = screenPosition(element).top + this.scrollTop
      bottom = top + height(element)

      zone.parallax = []
      forElements(find(element, '[data-wasabi]'), (pxElement) => {
        let options = eval('('+data(pxElement, 'wasabi')+')')

        let toX = (typeof options.x !== 'undefined') ? options.x : ((options.to && options.to.x) || 0),
            toY = (typeof options.y !== 'undefined') ? options.y : ((options.to && options.to.y) || 0),
            fromX = (options.from && options.from.x) || 0,
            fromY = (options.from && options.from.y) || 0,
            parentSize = size(element.parentNode)

        if (typeof toX == 'string' && toX.indexOf('%') != -1)
            toX = parseInt(toX, 10) * parentSize.width

        if (typeof toY == 'string' && toY.indexOf('%') != -1)
            toY = parseInt(toY, 10) * parentSize.height

        if (typeof fromX == 'string' && fromX.indexOf('%') != -1)
            fromX = parseInt(fromX, 10) * parentSize.width

        if (typeof fromY == 'string' && fromY.indexOf('%') != -1)
            fromY = parseInt(fromY, 10) * parentSize.height

        zone.parallax.push({
            element: pxElement,
            toX: toX,
            toY: toY,
            fromX: fromX,
            fromY: fromY
        })
      })
    }
    else {
      top = zoneConfig.top
      bottom = zoneConfig.bottom
    }

    let offset = defaultify(zoneConfig.offset, this.config.offset)
    zone.offset = {
        top: defaultify(offset.top, offset),
        bottom: defaultify(offset.bottom, offset)
    }

    zone.top = top - zone.offset.top
    zone.bottom = bottom + zone.offset.bottom

    if (this.config.debug) {
      let color = randomColor()

      console.log(zone.selector +' %c ' + color, 'color:'+color)
      console.log(zone.element)

      let topDebug = createElement(`<div class="wasabi-marker"></div>`)
      append(this.debugWrapper, topDebug)
      style(topDebug, {
        position: 'absolute',
        top: zone.top,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })

      let bottomDebug = clone(topDebug)
      append(this.debugWrapper, bottomDebug)
      style(bottomDebug, {
        'z-index': 9999,
        position: 'absolute',
        top: zone.bottom,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })
    }

    zone.size = zone.bottom - zone.top
    zone.handle = zoneConfig.handle || this.config.handle
    zone.progress = zoneConfig.progress || this.config.progress
    zone.snap = zoneConfig.snap || this.config.snap
    zone.handler = zoneConfig.handler || this.config.handler
    zone.enter = zoneConfig.enter || this.config.enter
    zone.leave = zoneConfig.leave || this.config.leave

    if (zoneConfig.tween) {
      zone.tween = zoneConfig.tween
      if (zone.tween.pause) zone.tween.pause()
    }

    if (zoneConfig.progressTween) {
      zone.progressTween = zoneConfig.progressTween
      if (zone.progressTween.pause) zone.progressTween.pause()
    }

    let handles = {}
    let handleForward = zone.handle.forward || zone.handle
    handles.forward = {
      top: handleForward.top || handleForward,
      bottom: handleForward.bottom || handleForward
    }
    let handleBackward = zone.handle.forward || zone.handle
    handles.backward = {
      top: handleBackward.top || handleBackward,
      bottom: handleBackward.bottom || handleBackward
    }

    if (handles.forward.top == 'middle') {
      zone.forwardTop = zone.top + this.halfHeight
    }
    else if (handles.forward.top == 'bottom') {
      zone.forwardTop = zone.top - this.windowHeight
    }
    else {
      zone.forwardTop = zone.top
    }

    if (handles.forward.bottom == 'middle') {
      zone.forwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.forward.bottom == 'bottom') {
      zone.forwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.forwardBottom = zone.bottom
    }

    zone.forwardSize = Math.max(this.config.stepMinSize, zone.forwardBottom - zone.forwardTop)

    if (handles.backward.top == 'middle') {
      zone.backwardTop = zone.top + this.halfHeight
    }
    else if (handles.backward.top == 'bottom') {
      zone.backwardTop = zone.top - this.windowHeight
    }
    else {
      zone.backwardTop = zone.top
    }

    if (handles.backward.bottom == 'middle') {
      zone.backwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.backward.bottom == 'bottom') {
      zone.backwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.backwardBottom = zone.bottom
    }

    zone.backwardSize = Math.max(this.config.stepMinSize, zone.backwardBottom - zone.backwardTop)

    if (zone.snap) this.snaps.push(zone)

    this.zones.push(zone)
  }

  testSnapping(scrollTarget) {
    if (this.scroller.scrollDisabled || !this.snaps.length) return

    let previous = this.snaps[this.currentSnapIndex-1],
        next = this.snaps[this.currentSnapIndex+1]

    if (previous && scrollTarget.y < this.currentSnap.top) {
      --this.currentSnapIndex
      this.currentSnap = previous

      this.scroller.scrollTarget.y = this.currentSnap.top + this.currentSnap.offset.top
      this.scroller.scrollTo({
        x: 0,
        y: (previous.bottom - this.currentSnap.offset.bottom) - Math.min(previous.size, this.windowHeight)-1
      })
    }
    else if (next && scrollTarget.y > this.currentSnap.bottom - this.windowHeight) {
      ++this.currentSnapIndex
      this.currentSnap = next

      this.scroller.scrollTarget.y = this.currentSnap.bottom - this.windowHeight - this.currentSnap.offset.bottom
      this.scroller.scrollTo({
        x: 0,
        y: (next.top + this.currentSnap.offset.top)+1
      })
    }
  }

  onScroller(scrollTarget) {
    this.scrollTop = this.scroller.scroll.y
    this.testSnapping(scrollTarget)
  }

  onScrollEvent(event) {
    this.scrollTop = screenPosition(this.wrapper).top
  }

  update() {
    if (!this.running) return

    let i = this.zones.length,
        direction = this.previousScrollTop < this.scrollTop ? 'forward' : 'backward'

    while (i--) {
      let zone = this.zones[i], entered, progress

      progress = (this.scrollTop - zone[direction+'Top'])/zone[direction+'Size']
      entered = progress >= 0 && progress <= 1

      if (!zone.entered && entered) {
        if (zone.tween) zone.tween.resume()
        if(zone.handler) zone.handler('enter', direction, zone.selector, zone.element)
        if(zone.enter) zone.enter(direction, zone.selector, zone.element)

        if(zone.snap) {
            this.currentSnapIndex = this.snaps.indexOf(zone)
            this.currentSnap = this.snaps[this.currentSnapIndex]
        }
      }
      else if (zone.entered && !entered) {
          if(zone.handler) zone.handler('leave', direction, zone.selector, zone.element)
        if(zone.leave) zone.leave(direction, zone.selector, zone.element)
      }

      forEach(zone.parallax, (item) => {
        transform(item.element, {
          x: item.fromX + (item.toX - item.fromX) * progress,
          y: item.fromY + (item.toY - item.fromY) * progress
        })
      })

      zone.entered = entered
      if (zone.entered) {
        if (zone.progress) zone.progress(direction, progress, zone.selector)
        if (zone.progressTween && zone.progressTween.progress) zone.progressTween.progress(progress)
      }
    }

    this.previousScrollTop = this.scrollTop

    this.updateRequest = requestAnimationFrame(this.update.bind(this))
  }

  kill() {
    this.running = false

    unresize(this.resizeCallback)
    cancelAnimationFrame(this.updateRequest)

    remove(this.debugWrapper)

    if (this.scrollEventsCallback) {
      this.scrollEventsManager.off(this.scrollEventsCallback)
    }
    else if (this.scrollerCallback) {
      this.scroller.off('update', this.scrollerCallback)
    }

    let i = this.zones.length
    while(i--) {
      let zone = this.zones[i]

      if (zone.tween) this.killTimeline(zone.tween)
      if (zone.progressTween) this.killTimeline(zone.progressTween)
    }

    this.zones = this.snaps = null
  }

  concatenateVars(object) {
    if (!object) return

    let keys = Object.keys(object),
        i = keys.length,
        vars = []

    while(i--) {
        if (typeof object[keys[i]] == 'object') {
            vars = vars.concat(this.concatenateVars(object[keys[i]]))
        }
        else {
            let key = keys[i]
            vars.push(key == 'x' || key == 'y' || key == 'scale' || key == 'rotate' ? 'transform' : key)
        }
    }

    return vars
  }

  killTimeline(timeline) {
    let tweens = timeline.getChildren()
    timeline.kill()

    let i = tweens.length, tween
    while(i--) {
      tween = tweens[i]

      if (tween.target) {
        TweenLite.set(tween.target, {
          clearProps: this.concatenateVars(tween.vars).join(',')
        })
      }
    }
  }
}

export default Wasabi
<|endoftext|>


<commit_before>
var assert = require('assert')

describe('drafts', function(){
  var haiku = require('../')
    , path = require('path')

  before(function(){
    h = haiku(path.resolve(__dirname, 'fixtures', 'drafts'))
  })

  it('is findable', function(done){
    h.get('draft.md', function(err, page){
      if (err) return done(err)
      assert.ok(page)
      done()
    })
  })

  it('is not included in the collections', function(done){
    h.get('draft.md', function(err, page){
      if (err) return done()
      assert.ok(done)
      assert.equal(h.context.content.length, 1)
      done()
    })
  })

})
<commit_msg>Fixes global leak in test<commit_after>
var assert = require('assert')

describe('drafts', function(){
  var haiku = require('../')
    , path = require('path')
    , h

  before(function(){
    h = haiku(path.resolve(__dirname, 'fixtures', 'drafts'))
  })

  it('is findable', function(done){
    h.get('draft.md', function(err, page){
      if (err) return done(err)
      assert.ok(page)
      done()
    })
  })

  it('is not included in the collections', function(done){
    h.get('draft.md', function(err, page){
      if (err) return done()
      assert.ok(done)
      assert.equal(h.context.content.length, 1)
      done()
    })
  })

})
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var vfs = require("vinyl-fs");
var through = require("through2");
var streamHelpers = require("../helpers/stream");
var cheerio = require("cheerio");

var Plugin = function(registry) {
  registry.before("load", "images:move", _.bind(this.moveImages, this));
  registry.after(
    "markdown:convert",
    "images:replace",
    _.bind(this.replaceImages, this)
  );
};

// through2 pipe function that creates a hasmap of
// old -> image names.
function mapImages(imageMap, srcFolder, destFolder) {
  return through.obj(function(file, enc, cb) {
    // find the relative path to image. If any pipe has changed the filename,
    // it's the original is set in orgRelative, so we look at that first.
    var relativeFrom = file.orgRelative || file.relative;
    var relativeTo = path.join(destFolder, file.relative);
    imageMap[relativeFrom] = relativeTo;
    cb(null, file);
  });
}

// through2 pipe function that replaces images src attributes
// based on values in hashmap.
function replaceSrc(imageMap) {
  return through.obj(function(file, enc, cb) {
    file.$el = file.$el || cheerio.load(file.contents.toString());

    var changed = false;

    // loop over each image
    file.$el("img").each(function(i, el) {
      // convert el to cheerio el
      var jel = file.$el(this);
      var src = jel.attr("src");

      // if this image exists in source folder,
      // replace src with new source
      if (imageMap[src]) {
        // this file has changed
        changed = true;

        // find the relative path from the image to the file
        var srcRelative = path.relative(
          path.dirname(file.relative),
          imageMap[src]
        );

        jel.attr("src", srcRelative);
      } else if (!src.match(/^http/)) {
        console.log("image not found in source folder: " + src);
      }
    });

    // only if we find an image, replace contents in file
    if (changed) {
      file.contents = new Buffer(file.$el("body").html());
    }

    cb(null, file);
  });
}

Plugin.prototype = {
  moveImages: function(config, extras, callback) {
    this.imageMap = {};

    // load all files in the source folder
    var imagesStream = vfs.src(config.images.files);

    // digest
    if (_.get(config, "images.digest")) {
      imagesStream = imagesStream.pipe(streamHelpers.digest());
    }

    // save map of old and new file names
    imagesStream
      .pipe(
        mapImages(this.imageMap, config.images.files, config.images.destination)
      )

      // vinyl-fs dest automatically determines whether a file
      // should be updated or not, based on the mtime timestamp.
      // so we don't need to do that manually.
      .pipe(vfs.dest(path.join(extras.destination, config.images.destination)))
      .on("finish", function() {
        callback(null, config, extras);
      });
  },

  replaceImages: function(config, stream, extras, callback) {
    stream = stream.pipe(replaceSrc(this.imageMap));
    callback(null, config, stream, extras);
  }
};

module.exports = Plugin;
<commit_msg>fixes #29; img.src attribute not correctly resolved on Windows<commit_after>var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var vfs = require("vinyl-fs");
var through = require("through2");
var streamHelpers = require("../helpers/stream");
var cheerio = require("cheerio");

var Plugin = function(registry) {
  registry.before("load", "images:move", _.bind(this.moveImages, this));
  registry.after(
    "markdown:convert",
    "images:replace",
    _.bind(this.replaceImages, this)
  );
};

// through2 pipe function that creates a hasmap of
// old -> image names.
function mapImages(imageMap, srcFolder, destFolder) {
  return through.obj(function(file, enc, cb) {
    // find the relative path to image. If any pipe has changed the filename,
    // it's the original is set in orgRelative, so we look at that first.
    var relativeFrom = file.orgRelative || file.relative;
    var relativeTo = path.join(destFolder, file.relative);
    imageMap[relativeFrom] = relativeTo;
    cb(null, file);
  });
}

// through2 pipe function that replaces images src attributes
// based on values in hashmap.
function replaceSrc(imageMap) {
  return through.obj(function(file, enc, cb) {
    file.$el = file.$el || cheerio.load(file.contents.toString());

    var changed = false;

    // loop over each image
    file.$el("img").each(function(i, el) {
      // convert el to cheerio el
      var jel = file.$el(this);
      var src = jel.attr("src");

      //make sure to convert the src attribute to the specific OS format, so they are correctly matched in the imageMap
      //e.g. on windows slashes are converted to backslashes like for instance images/fig01.png -> images\fig01.png
      src = src ? path.normalize(src) : src;

      // if this image exists in source folder,
      // replace src with new source
      if (imageMap[src]) {
        // this file has changed
        changed = true;

        // find the relative path from the image to the file
        var srcRelative = path.relative(
          path.dirname(file.relative),
          imageMap[src]
        );

        jel.attr("src", srcRelative);
      } else if (!src.match(/^http/)) {
        console.log("image not found in source folder: " + src);
      }
    });

    // only if we find an image, replace contents in file
    if (changed) {
      file.contents = new Buffer(file.$el("body").html());
    }

    cb(null, file);
  });
}

Plugin.prototype = {
  moveImages: function(config, extras, callback) {
    this.imageMap = {};

    // load all files in the source folder
    var imagesStream = vfs.src(config.images.files);

    // digest
    if (_.get(config, "images.digest")) {
      imagesStream = imagesStream.pipe(streamHelpers.digest());
    }

    // save map of old and new file names
    imagesStream
      .pipe(
        mapImages(this.imageMap, config.images.files, config.images.destination)
      )

      // vinyl-fs dest automatically determines whether a file
      // should be updated or not, based on the mtime timestamp.
      // so we don't need to do that manually.
      .pipe(vfs.dest(path.join(extras.destination, config.images.destination)))
      .on("finish", function() {
        callback(null, config, extras);
      });
  },

  replaceImages: function(config, stream, extras, callback) {
    stream = stream.pipe(replaceSrc(this.imageMap));
    callback(null, config, stream, extras);
  }
};

module.exports = Plugin;
<|endoftext|>


<commit_before>// single field tests
describe("A url field", function() {
    var field = document.createElement("input"),
        v = fieldValidator.validate;

    field.setAttribute("type", "url");

    document.body.appendChild(field);

    it("should flag \"minlength\" errors", function() {
        field.setAttribute("minlength", "20");

        // below minlength
        field.value = "http://sebweb.ca";
        expect(v(field)).toEqual([{field: field, errors: ["minlength"], isValid: false}]);

        // exact minlength
        field.value = "http://www.sebweb.ca";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);

        // above minlength
        field.value = "http://www.sebweb.ca/my-blog-page/";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should flag \"maxlength\" errors", function() {
        field.setAttribute("maxlength", "10");
        field.value = "sebastien daniel";
        expect(v(field)).toEqual([{field: field, errors: ["maxlength"], isValid: false}]);

        field.value = "0123456789";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);

        field.value = "sebastien";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should flag \"required\" errors", function() {
        field.setAttribute("required", "required");

        field.value = "";
        expect(v(field)).toEqual([{field: field, errors: ["required"], isValid: false}]);

        field.value = "sebastien";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should flag \"pattern\" errors", function() {
        field.setAttribute("pattern", "^[a-z,A-Z]*$");

        field.value = "123sebas";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "seb123as";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "sebas123";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "sebastien";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should not flag errors for invalid string-type validation attributes", function() {
        field.setAttribute("min", "6");
        field.setAttribute("max", "10");
        field.setAttribute("step", "3");

        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });
});
<commit_msg>new url-specific test suit<commit_after>// single field tests
describe("A url field", function() {
    var field = document.createElement("input"),
        v = fieldValidator.validate;

    field.setAttribute("type", "url");

    document.body.appendChild(field);

    it("should flag \"minlength\" errors", function() {
        field.setAttribute("minlength", "20");

        // below minlength
        field.value = "http://sebweb.ca";
        expect(v(field)).toEqual([{field: field, errors: ["minlength"], isValid: false}]);

        // exact minlength
        field.value = "http://www.sebweb.ca";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);

        // above minlength
        field.value = "http://www.sebweb.ca/my-blog-page/";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should flag \"maxlength\" errors", function() {
        field.setAttribute("maxlength", "30");
        field.value = "https://sebastiendaniel.ca/this-is-my-blog/its-a-long-link/";
        expect(v(field)).toEqual([{field: field, errors: ["maxlength"], isValid: false}]);

        field.value = "https://sebastiendaniel.ca/";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);

        field.value = "https://sebastie.ca/";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    it("should flag \"required\" errors", function() {
        field.setAttribute("required", "required");

        field.value = "";
        expect(v(field)).toEqual([{field: field, errors: ["required"], isValid: false}]);

        field.value = "http://www.sebast.ca/";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });

    /*
    it("should flag \"pattern\" errors", function() {
        field.setAttribute("pattern", "^[a-z,A-Z]*$");

        field.value = "123sebas";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "seb123as";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "sebas123";
        expect(v(field)).toEqual([{field: field, errors: ["pattern"], isValid: false}]);

        field.value = "sebastien";
        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });
    */

    it("should not flag errors for invalid string-type validation attributes", function() {
        field.setAttribute("min", "6");
        field.setAttribute("max", "10");
        field.setAttribute("step", "3");

        expect(v(field)).toEqual([{field: field, errors: [], isValid: true}]);
    });
});
<|endoftext|>


<commit_before>var url = $(location).attr('href');
var id = url.substring(url.lastIndexOf('/') + 1);
//console.log(id);
$(document).ready(function(){
    $("#topic").upvote();
        $.post('http://localhost:8000/problem/'+id +'?token=' + $.cookie("token"),null)
        .done(function(data){
                //  console.log($.cookie("token"));
                $('#title').text(data.title);
                $('#description').text(data.description);
                data.tags.forEach(function(item){
                    $('#tags').append('
' + '' + item + '
');
                });
        });

            $('#comment').click(function(){
                $.post('http://localhost:8000/comment/'+ id +'?token=' + $.cookie("token"),{comment: $('#comment-textarea').val(),user_id:$.cookie("user_id")})
                .done(function(data){
                    if (data.status == "success"){
                        console.log("comment added successfuly");
                        $('#comment-textarea').val('');
                    }
                });
            });
            str = "";
            var userCommentIds=[];
            $.get('http://localhost:8000/comment/'+ id +'?token=' + $.cookie("token"),function(data){

                console.log(data);
                var i=1;
                var comments = data[0].comments;
                comments.forEach(function(item){
              // place span here
                    userCommentIds.push(item.user_id);
                    str+= ' ';
                    str+= '
' + item.body + '

';
                    i++;
                });
                $('#comments').append(str);
            });

            $.get('http://localhost:8000/answers/'+ id +'?token=' + $.cookie("token"),function(data){
                str = "";
                console.log(data);
                var answers = data.answers;
                answers.forEach(function(item){
                    str+= '
' + item.body + '

';
                });
                $('#answers').append(str);
            })

            $('#answer').click(function(){
                $.post('http://localhost:8000/answers/'+ id +'?token=' + $.cookie("token"),{answer: $('#answer-textarea').val()})
                .done(function(data){
                    if (data.status == "success"){
                        console.log("answer added successfuly");
                        $('#answer-textarea').val('');
                    }
                });
            });
            var i = 1;
            userCommentIds.forEach(function(item){
              $.get('http://localhost:8000/user/'+item+'?token=' + $.cookie("token"),function(data){
                  console.log(data.profile_pic);
                $('#comment_pic'+i).attr('src',data.profile_pic);

              });
              i++;
            });

            console.log(userCommentIds);
});
<commit_msg>test commit<commit_after>var url = $(location).attr('href');
var id = url.substring(url.lastIndexOf('/') + 1);
//console.log(id);
$(document).ready(function(){
    $("#topic").upvote();
        $.post('http://localhost:8000/problem/'+id +'?token=' + $.cookie("token"),null)
        .done(function(data){
                //  console.log($.cookie("token"));
                $('#title').text(data.title);
                $('#description').text(data.description);
                data.tags.forEach(function(item){
                    $('#tags').append('
' + '' + item + '
');
                });
        });

            $('#comment').click(function(){
                $.post('http://localhost:8000/comment/'+ id +'?token=' + $.cookie("token"),{comment: $('#comment-textarea').val(),user_id:$.cookie("user_id")})
                .done(function(data){
                    if (data.status == "success"){
                        console.log("comment added successfuly");
                        $('#comment-textarea').val('');
                    }
                });
            });
            str = "";
            var userCommentIds=[];
            $.get('http://localhost:8000/comment/'+ id +'?token=' + $.cookie("token"),function(data){

                console.log(data);
                var i=1;
                var comments = data[0].comments;
                comments.forEach(function(item){
              // place span here
                    userCommentIds.push(item.user_id);
                    str+= ' ';
                    str+= '
' + item.body + '

';
                    i++;
                });
                $('#comments').append(str);
            });

            $.get('http://localhost:8000/answers/'+ id +'?token=' + $.cookie("token"),function(data){
                str = "";
                console.log(data);
                var answers = data.answers;
                answers.forEach(function(item){
                    str+= '
' + item.body + '

';
                });
                $('#answers').append(str);
            })

            $('#answer').click(function(){
                $.post('http://localhost:8000/answers/'+ id +'?token=' + $.cookie("token"),{answer: $('#answer-textarea').val()})
                .done(function(data){
                    if (data.status == "success"){
                        console.log("answer added successfuly");
                        $('#answer-textarea').val('');
                    }
                });
            });
            var i = 1;
            userCommentIds.forEach(function(item){
              $.get('http://localhost:8000/user/'+item+'?token=' + $.cookie("token"),function(data){
                  console.log(data.profile_pic);
                $('#comment_pic'+i).attr('src',data.profile_pic);

              });
              i++;
            });

            console.log(userCommentIds);
});
<|endoftext|>


<commit_before>import Bulma from '../core';
import Plugin from '../plugin';

/**
 * @module Navbar
 * @since  0.1.0
 * @author  Thomas Erbe <vizuaalog@gmail.com>
 */
export class Navbar extends Plugin {
    /**
     * Handle parsing the DOMs data attribute API.
     * @param {HTMLElement} element The root element for this instance
     * @return {undefined}
     */
    static parseDocument(context) {
        let elements = context.querySelectorAll('.navbar');

        Bulma.each(elements, (element) => {

            Bulma(element)
                .data('navbar', new Navbar({
                    root: element,
                    sticky: element.hasAttribute('data-sticky') ? true : false,
                    stickyOffset: element.hasAttribute('data-sticky-offset') ? element.getAttribute('data-sticky-offset') : 0,
                    hideOnScroll: element.hasAttribute('data-hide-on-scroll') ? true : false,
                    tolerance: element.hasAttribute('data-tolerance') ? element.getAttribute('data-tolerance') : 0,
                    hideOffset: element.hasAttribute('data-hide-offset') ? element.getAttribute('data-hide-offset') : 0,
                    shadow: element.hasAttribute('data-sticky-shadow') ? true : false
                }))
                .data('navbar');
        });
    }

    /**
     * Returns an object containing the default config for this plugin.
     * @returns {object} The default config object.
     */
    static defaultconfig() {
        return {
            sticky: false,
            stickyOffset: 0,
            hideOnScroll: false,
            tolerance: 0,
            hideOffset: 0,
            shadow: false
        };
    }

    /**
     * Plugin constructor
     * @param  {Object} config The config object for this plugin
     * @return {this} The newly created plugin instance
     */
    constructor(config) {
        super(config);

        // Work out the parent if it hasn't been supplied as an option.
        if(this.parent === null) {
            this.parent = this.config.get('root').parentNode;
        }

        /**
         * The root navbar element.
         * @type {HTMLElement}
         */
        this.root = this.config.get('root');

        /**
         * The element used for the trigger.
         * @type {HTMLElement}
         */
        this.triggerElement = this.root.querySelector('.navbar-burger'),

        /**
         * The target element.
         * @type {HTMLELement}
         */
        this.target = this.root.querySelector('.navbar-menu');

        /**
         * Should this navbar stick to the top of the page?
         * @type {boolean}
         */
        this.sticky = !!this.config.get('sticky');

        /**
         * The offset in pixels before the navbar will stick to the top of the page
         * @type {number}
         */
        this.stickyOffset = parseInt(this.config.get('stickyOffset'));

        /**
         * Should the navbar hide when scrolling? Note: this just applies a 'is-hidden-scroll' class.
         * @type {boolean}
         */
        this.hideOnScroll = !!this.config.get('hideOnScroll');

        /**
         * The amount of tolerance required before checking the navbar should hide/show
         * @type {number}
         */
        this.tolerance = parseInt(this.config.get('tolerance'));

        /**
         * Add a shadow when the navbar is sticky?
         * @type {boolean}
         */
        this.shadow = !!this.config.get('shadow');

        /**
         * The offset in pixels before the navbar will be hidden, defaults to the height of the navbar
         * @type {number}
         */
        this.hideOffset = parseInt(this.config.get('hideOffset', Math.max(this.root.scrollHeight, this.stickyOffset)));

        /**
         * The last scroll Y known, this is used to calculate scroll direction
         * @type {number}
         */
        this.lastScrollY = 0;

        Bulma(this.root).data('navbar', this);

        this.registerEvents();
    }

    /**
     * Register all the events this module needs.
     * @return {undefined}
     */
    registerEvents() {
        this.triggerElement.addEventListener('click', this.handleTriggerClick.bind(this));

        if(this.sticky) {
            window.addEventListener('scroll', this.handleScroll.bind(this));
        }
    }

    /**
     * Handle the click event on the trigger.
     * @return {undefined}
     */
    handleTriggerClick() {
        if(this.target.classList.contains('is-active')) {
            this.target.classList.remove('is-active');
            this.triggerElement.classList.remove('is-active');
        } else {
            this.target.classList.add('is-active');
            this.triggerElement.classList.add('is-active');
        }
    }

    /**
     * Handle the scroll event
     * @return {undefined}
     */
    handleScroll() {
        this.toggleSticky(window.pageYOffset);
    }

    /**
     * Toggle the navbar's sticky state
     * @param {number} scrollY The amount of pixels that has been scrolled
     * @return {undefined}
     */
    toggleSticky(scrollY) {
        if(scrollY > this.stickyOffset) {
            this.root.classList.add('is-fixed-top');
            document.body.classList.add('has-navbar-fixed-top');

            if(this.shadow) {
                this.root.classList.add('has-shadow');
            }
        } else {
            this.root.classList.remove('is-fixed-top');
            document.body.classList.remove('has-navbar-fixed-top');

            if(this.shadow) {
                this.root.classList.remove('has-shadow');
            }
        }

        if(this.hideOnScroll) {
            let scrollDirection = this.calculateScrollDirection(scrollY, this.lastScrollY);
            let triggeredTolerance = this.difference(scrollY, this.lastScrollY) >= this.tolerance;

            if (scrollDirection === 'down') {
                // only hide the navbar at the top if we reach a certain offset so the hiding is more smooth
                let isBeyondTopOffset = scrollY > this.hideOffset;
                if (triggeredTolerance && isBeyondTopOffset) {
                    this.element.classList.add('is-hidden-scroll');
                }
            } else {
                // if scrolling up to the very top where the navbar would be by default always show it
                let isAtVeryTop = scrollY < this.hideOffset;
                if (triggeredTolerance || isAtVeryTop) {
                    this.element.classList.remove('is-hidden-scroll');
                }
            }

            this.lastScrollY = scrollY;
        }
    }

    difference(a, b) {
        if (a > b) {
            return a - b;
        } else {
            return b - a;
        }
    }

    calculateScrollDirection(currentY, lastY) {
        return currentY >= lastY ? 'down' : 'up';
    }
}

Bulma.registerPlugin('navbar', Navbar);

export default Bulma;
<commit_msg>Update the navbar for the new API.<commit_after>import Bulma from '../core';
import Plugin from '../plugin';

/**
 * @module Navbar
 * @since  0.1.0
 * @author  Thomas Erbe <vizuaalog@gmail.com>
 */
export class Navbar extends Plugin {
    /**
     * Handle parsing the DOMs data attribute API.
     * @param {HTMLElement} element The root element for this instance
     * @return {undefined}
     */
    static parseDocument(context) {
        let elements = context.querySelectorAll('.navbar');

        Bulma.each(elements, (element) => {
            Bulma(element).navbar({
                    sticky: element.hasAttribute('data-sticky') ? true : false,
                    stickyOffset: element.hasAttribute('data-sticky-offset') ? element.getAttribute('data-sticky-offset') : 0,
                    hideOnScroll: element.hasAttribute('data-hide-on-scroll') ? true : false,
                    tolerance: element.hasAttribute('data-tolerance') ? element.getAttribute('data-tolerance') : 0,
                    hideOffset: element.hasAttribute('data-hide-offset') ? element.getAttribute('data-hide-offset') : 0,
                    shadow: element.hasAttribute('data-sticky-shadow') ? true : false
                });
        });
    }

    /**
     * Returns an object containing the default config for this plugin.
     * @returns {object} The default config object.
     */
    static defaultconfig() {
        return {
            sticky: false,
            stickyOffset: 0,
            hideOnScroll: false,
            tolerance: 0,
            hideOffset: 0,
            shadow: false
        };
    }

    /**
     * Plugin constructor
     * @param  {Object} config The config object for this plugin
     * @return {this} The newly created plugin instance
     */
    constructor(config, root) {
        super(config, root);

        // Work out the parent if it hasn't been supplied as an option.
        if(this.parent === null) {
            this.parent = this.config.get('root').parentNode;
        }

        /**
         * The root navbar element.
         * @type {HTMLElement}
         */
        this.root = this.config.get('root');

        /**
         * The element used for the trigger.
         * @type {HTMLElement}
         */
        this.triggerElement = this.root.querySelector('.navbar-burger'),

        /**
         * The target element.
         * @type {HTMLELement}
         */
        this.target = this.root.querySelector('.navbar-menu');

        /**
         * Should this navbar stick to the top of the page?
         * @type {boolean}
         */
        this.sticky = !!this.config.get('sticky');

        /**
         * The offset in pixels before the navbar will stick to the top of the page
         * @type {number}
         */
        this.stickyOffset = parseInt(this.config.get('stickyOffset'));

        /**
         * Should the navbar hide when scrolling? Note: this just applies a 'is-hidden-scroll' class.
         * @type {boolean}
         */
        this.hideOnScroll = !!this.config.get('hideOnScroll');

        /**
         * The amount of tolerance required before checking the navbar should hide/show
         * @type {number}
         */
        this.tolerance = parseInt(this.config.get('tolerance'));

        /**
         * Add a shadow when the navbar is sticky?
         * @type {boolean}
         */
        this.shadow = !!this.config.get('shadow');

        /**
         * The offset in pixels before the navbar will be hidden, defaults to the height of the navbar
         * @type {number}
         */
        this.hideOffset = parseInt(this.config.get('hideOffset', Math.max(this.root.scrollHeight, this.stickyOffset)));

        /**
         * The last scroll Y known, this is used to calculate scroll direction
         * @type {number}
         */
        this.lastScrollY = 0;

        Bulma(this.root).data('navbar', this);

        this.registerEvents();
    }

    /**
     * Register all the events this module needs.
     * @return {undefined}
     */
    registerEvents() {
        this.triggerElement.addEventListener('click', this.handleTriggerClick.bind(this));

        if(this.sticky) {
            window.addEventListener('scroll', this.handleScroll.bind(this));
        }
    }

    /**
     * Handle the click event on the trigger.
     * @return {undefined}
     */
    handleTriggerClick() {
        if(this.target.classList.contains('is-active')) {
            this.target.classList.remove('is-active');
            this.triggerElement.classList.remove('is-active');
        } else {
            this.target.classList.add('is-active');
            this.triggerElement.classList.add('is-active');
        }
    }

    /**
     * Handle the scroll event
     * @return {undefined}
     */
    handleScroll() {
        this.toggleSticky(window.pageYOffset);
    }

    /**
     * Toggle the navbar's sticky state
     * @param {number} scrollY The amount of pixels that has been scrolled
     * @return {undefined}
     */
    toggleSticky(scrollY) {
        if(scrollY > this.stickyOffset) {
            this.root.classList.add('is-fixed-top');
            document.body.classList.add('has-navbar-fixed-top');

            if(this.shadow) {
                this.root.classList.add('has-shadow');
            }
        } else {
            this.root.classList.remove('is-fixed-top');
            document.body.classList.remove('has-navbar-fixed-top');

            if(this.shadow) {
                this.root.classList.remove('has-shadow');
            }
        }

        if(this.hideOnScroll) {
            let scrollDirection = this.calculateScrollDirection(scrollY, this.lastScrollY);
            let triggeredTolerance = this.difference(scrollY, this.lastScrollY) >= this.tolerance;

            if (scrollDirection === 'down') {
                // only hide the navbar at the top if we reach a certain offset so the hiding is more smooth
                let isBeyondTopOffset = scrollY > this.hideOffset;
                if (triggeredTolerance && isBeyondTopOffset) {
                    this.root.classList.add('is-hidden-scroll');
                }
            } else {
                // if scrolling up to the very top where the navbar would be by default always show it
                let isAtVeryTop = scrollY < this.hideOffset;
                if (triggeredTolerance || isAtVeryTop) {
                    this.root.classList.remove('is-hidden-scroll');
                }
            }

            this.lastScrollY = scrollY;
        }
    }

    difference(a, b) {
        if (a > b) {
            return a - b;
        } else {
            return b - a;
        }
    }

    calculateScrollDirection(currentY, lastY) {
        return currentY >= lastY ? 'down' : 'up';
    }
}

Bulma.registerPlugin('navbar', Navbar);

export default Bulma;
<|endoftext|>


<commit_before>var expect = require("expect.js");

describe("macro expander", function() {
  it("should expand a macro with pattern `$x:lit`", function() {
    macro id {
      case ($x:lit) => {
        $x
      }
    }
    var z = id(4);
    expect(z).to.equal(4);
  });

  it("should expand a macro with pattern `=> $x:lit`", function() {
    macro litid {
      case (=> $x:lit) => {
        $x
      }
    }
    var z = litid(=> 42);
    expect(z).to.equal(42);
  });

  it("should expand a macro with a pattern `$x:lit <+> $y:lit`", function() {
    macro oddadd {
      case ($x:lit <+> $y:lit) => {
        $x + $y
      }
    }
    var z = oddadd(2 <+> 4);
    expect(z).to.be(6);
  });

  it("should expand a macro with a pattern `($x:lit) <+> $y:lit`", function() {
    macro oddadd {
      case (($x:lit) <+> $y:lit) => {
        $x + $y
      }
    }
    var z = oddadd((2) <+> 4);
    expect(z).to.be(6);

  });

  it("should match primary expressions", function() {
    macro expr {
      case ($x:expr) => { $x }
    }
    expect(expr(this)).to.be(this);

    var foo = 42;
    expect(expr(foo)).to.be(42);

    expect(expr(42)).to.be(42);

    expect(expr([1,2,3])).to.eql([1,2,3]);
  });

  it("should match simple ternary expression", function() {
    macro m {
      case ($x:expr) => {$x}
    }

    var x = m (true ? 42 : "foo")
    expect(x).to.be(42);
  });

  it("should match a complex ternary expression", function() {
    macro m {
      case ($x:expr) => {$x}
    }

    var x = m (2+4 > 0 ? 20 + 22 : "foo" + "bar")
    expect(x).to.be(42);

  });

  it("should match binary expressions", function() {
    macro expr {
      case ($x:expr) => {
        $x
      }
    }
    var z1 = expr(2 + 2);
    var z2 = expr(2 * 2);
    var z3 = expr(2 + 2 * 2);
    expect(z1).to.be(4);
    expect(z2).to.be(4);
    expect(z3).to.be(6);
  });

  it("should expand a macro with a pattern `$x:expr plus! $y:expr`", function() {
    macro expr {
      case ($x:expr plus! $y:expr) => {
        $x + $y
      }
    }
    var z = expr(2 * 4 plus! 2 * 2);
    expect(z).to.be(12);

  });

  it("should expand a thunk macro", function() {
    macro thunk {
      case ($x:expr) => {
        function() { return $x; }
      }
    }
    var z = thunk(2 * 4);
    expect(z()).to.be(8);
  });

  it("should expand multiple macro body types", function() {
    macro assign {
      case ($x:ident) {$y:expr} => {
        var $x = $y;
      }
    }

    assign (z) {2+2}

    expect(z).to.be(4);
  });

  it("should expand literal braces", function() {
    macro paren {
      case ({$x:lit}) => {
        [$x]
      }
    }
    var z = paren ({4});

    expect(z[0]).to.be(4);
  });

  it("should expand literal parens", function() {
    macro paren {
      case (($x:lit)) => {
        [$x]
      }
    }
    var z = paren ((4));

    expect(z[0]).to.be(4);
  });

  it("should distinguish between commas and no commas in a repeat", function() {
    macro m {
      case ($p ...) => {
        "no commas"
      }
      case ($p (,) ...) => {
        "comma"
      }
    }
    expect(m (a b)).to.be("no commas");
  });

  it("should match as much of the pattern as possible if not in a delimiter even when more syntax follows", function() {
      macro m {
        case $p (,) ... => {
          [$p (,) ...];
        }
      }
      var res = m 1, 2, 3 "trailing";
      expect(res).to.eql([1,2,3]);
  });

  it("should expand with ellipses", function() {
    macro paren {
      case ( $x:lit (,) ...) => {
        [$x (,) ...]
      }
    }
    var z = paren (4, 3);

    expect(z[0]).to.be(4);
    expect(z[1]).to.be(3);
  });

  it("should expand literal parens with ellipses", function() {
    macro paren {
      case ( ($x:lit) (,) ...) => {
        [$x (,) ...]
      }
    }
    var z = paren ((4), (3));

    expect(z[0]).to.be(4);
    expect(z[1]).to.be(3);
  });

  it("should expand a simple let macro", function() {
    macro let {
      case ($x:ident = $v:expr) {$y:expr} => {
        (function($x) { return $y; })($v);
      }
    }

    var foo = let (z = 1) {z+2}

    expect(foo).to.be(3)
  });

  it("should expand a complex let macro", function() {
    macro let {
      case ( $($x:ident = $v:expr) (,) ...) {$y:expr} => {
        (function($x (,) ...) { return $y; })($v (,) ...);
      }
    }

    var foo = let (z = 1, t = 5 + 2) {z+t}

    expect(foo).to.be(8)
  });

  it("should handle ellipses in output delimiters", function() {
    macro m {
      case ( $x:lit (,) ...) => {
        [[$x] (,) ...]
      }
    }
    var x = m ( 1, 2, 3 );
    expect(x).to.eql([[1],[2],[3]]);
  });

  it("should work", function() {
    macro m {
      case { $($a $b) ... } => {
        [$([$a, $b]) (,) ...];
      }
    }

    var x = m { 1 2 3 4 }

    expect(x).to.eql([[1, 2], [3, 4]])
  });

  it("should expand simple nested ellipses", function() {
    macro nest {
      case ( ($x:lit (,) ...) (,) ... ) => {
        [ [$x (,) ...] (,) ...]
      }
    }
    var x = nest ( (1, 2, 3), (4, 5, 6) );
  });

  it("should expand a nested ellipses macro", function() {
    macro nest {
      case ( ($x:lit ; $y:lit (,) ...) (,) ...) => {
        [ [$x (,) ...], [$y (,) ...] (,) ... ]
      }
    }

    var foo = nest ( (1; 2,3,4,5), (10; 11, 12, 13, 14, 15) );

    expect(foo[0]).to.eql([1,10]);
    expect(foo[1]).to.eql([2,3,4,5]);
    expect(foo[2]).to.eql([11, 12, 13, 14, 15]);
  });

  it("should expand an ellipsis with a ; delimiter", function() {
    macro semi {
      case ( $x:lit (;) ...) => {
        [$x (,) ...]
      }
    }
    var a = semi(1;2;3;4);
    expect(a.length).to.be(4);
    expect(a[1]).to.be(2);
  });

  it("should expand an ellipsis with no separator", function() {
    macro semi {
      case ($x:ident ...) => {
        var $($x = 2) (,) ...
      }
    }
    semi(w x y z);
    expect(w).to.be(2);
    expect(x).to.be(2);
    expect(y).to.be(2);
    expect(z).to.be(2);
  });

  it("should handle def macro", function() {
    macro def {
      case $name:ident ($params:ident (,) ...) $body => {
        function $name ($params (,) ...) {
          $body
        }
      }
    }
    def add (a, b) {
      return a + b;
    }

    expect(add(2,2)).to.be(4);
  });

  it("should handle multiple cases", function() {
    macro m {
      case four => {
        4
      }
      case two => {
        2
      }
    }
    var n = m four;
    expect(n).to.be(4)
    var n = m two;
    expect(n).to.be(2)
  });

  it("should handle multiple cases when matching different length patterns", function() {
    macro arrid {
      case [$x:lit] => {
        [$x]
      }
      case [$x:lit, $y:lit] => {
        [$x, $y]
      }
    }

    var l = arrid [1]
    expect(l).to.eql([1])

    var ll = arrid [1,2]
    expect(ll).to.eql([1, 2])
  });

  it("should handle more multiple cases", function() {
    macro m {
      case $n:ident => {42}
      case $n:ident < $m:ident {} => {42}
    }
    var z = m foo;
    var zz = m foo < foo {};
    expect(z).to.be(42);
    expect(zz).to.be(42);
  });

  it("should handle recursive macros", function() {
    macro rot {
      case [$x:lit] => {
        [$x]
      }
      case [$x:lit, $y:lit] => {
        [rot [$y], $x]
      }
    }

    var l = rot [1]
    expect(l).to.eql([1])

    var ll = rot [1,2]
    expect(ll).to.eql([[2], 1])
  });

  it("should handle mutually recursive macros", function() {
    macro a {
      case [$x:lit] => {
        b ($x)
      }
    }
    macro b {
      case ($x:lit) => {
        [$x]
      }
      case ($x:lit, $y:lit) => {
        a [$x]
      }
    }
    var z = b (1, 2);
    expect(z).to.eql([1])
  });

  it("should allow macro defining macros", function() {
    macro mm {
      case ($x:lit) => {
        macro m {
          case ($y:lit) => {
            [$x, $y]
          }
        }
      }
    }

    mm (42)
    var z = m (24);
    expect(z).to.eql([42,24])
  });

  it("should allow matching of unparsed tokens", function() {
    macro m {
      case ($x) => {
        $x
      }
    }
    var z = m ([1,2,3]);
    expect(z).to.eql([1,2,3])
  });

  it("should allow literal syntax", function() {
    macro m {
        case $x => {
            macro $x {
                case ($y $[...]) => {
                    [$y (,) $[...]];
                }
            }
        }
    }

    m n;
    var x = n (42 24);
    expect(x[0]).to.be(42);
    expect(x[1]).to.be(24);
  });

  it("should allow literal syntax with pattern var literals", function() {
    macro $test {
      case ($op (|) ...) => {
        macro rel {
          case $x => { $x }
          $(case ($x $op $y) => { 1 }) ...
        }
      }
    }

    $test (<|>)
    rel(1 < 2 < 3)
  });

  it("should do the right thing", function() {
    macro m {
        case { $y:expr DONTMATCH } => {
          $y
        }
        case { $y:expr } => {
            $y
        }
    }

    function c() {
      return 42;
    }

    var myobj = { baz: function(param) { return param; }};

    var x = m { myobj.baz(c()) }
    expect(x).to.be(42);
  });

  it("should not fail with tokens that are on an object's prototype chain", function() {
    // (had been using an object naively as a dictionary so make sure we don't regress)
    macro m {
      case () => {
        this.constructor
      }
    }

    m ();
  });

  it("should allow fn calls as an :expr", function() {
    macro m {
      case ($x:expr) => {$x}
    }
    function id (x) { return x; }
    var x = m( id(4) );
  });

});<commit_msg>naming<commit_after>var expect = require("expect.js");

describe("macro expander", function() {
  it("should expand a macro with pattern `$x:lit`", function() {
    macro id {
      case ($x:lit) => {
        $x
      }
    }
    var z = id(4);
    expect(z).to.equal(4);
  });

  it("should expand a macro with pattern `=> $x:lit`", function() {
    macro litid {
      case (=> $x:lit) => {
        $x
      }
    }
    var z = litid(=> 42);
    expect(z).to.equal(42);
  });

  it("should expand a macro with a pattern `$x:lit <+> $y:lit`", function() {
    macro oddadd {
      case ($x:lit <+> $y:lit) => {
        $x + $y
      }
    }
    var z = oddadd(2 <+> 4);
    expect(z).to.be(6);
  });

  it("should expand a macro with a pattern `($x:lit) <+> $y:lit`", function() {
    macro oddadd {
      case (($x:lit) <+> $y:lit) => {
        $x + $y
      }
    }
    var z = oddadd((2) <+> 4);
    expect(z).to.be(6);

  });

  it("should match primary expressions", function() {
    macro expr {
      case ($x:expr) => { $x }
    }
    expect(expr(this)).to.be(this);

    var foo = 42;
    expect(expr(foo)).to.be(42);

    expect(expr(42)).to.be(42);

    expect(expr([1,2,3])).to.eql([1,2,3]);
  });

  it("should match simple ternary expression", function() {
    macro m {
      case ($x:expr) => {$x}
    }

    var x = m (true ? 42 : "foo")
    expect(x).to.be(42);
  });

  it("should match a complex ternary expression", function() {
    macro m {
      case ($x:expr) => {$x}
    }

    var x = m (2+4 > 0 ? 20 + 22 : "foo" + "bar")
    expect(x).to.be(42);

  });

  it("should match binary expressions", function() {
    macro expr {
      case ($x:expr) => {
        $x
      }
    }
    var z1 = expr(2 + 2);
    var z2 = expr(2 * 2);
    var z3 = expr(2 + 2 * 2);
    expect(z1).to.be(4);
    expect(z2).to.be(4);
    expect(z3).to.be(6);
  });

  it("should expand a macro with a pattern `$x:expr plus! $y:expr`", function() {
    macro expr {
      case ($x:expr plus! $y:expr) => {
        $x + $y
      }
    }
    var z = expr(2 * 4 plus! 2 * 2);
    expect(z).to.be(12);

  });

  it("should expand a thunk macro", function() {
    macro thunk {
      case ($x:expr) => {
        function() { return $x; }
      }
    }
    var z = thunk(2 * 4);
    expect(z()).to.be(8);
  });

  it("should expand multiple macro body types", function() {
    macro assign {
      case ($x:ident) {$y:expr} => {
        var $x = $y;
      }
    }

    assign (z) {2+2}

    expect(z).to.be(4);
  });

  it("should expand literal braces", function() {
    macro paren {
      case ({$x:lit}) => {
        [$x]
      }
    }
    var z = paren ({4});

    expect(z[0]).to.be(4);
  });

  it("should expand literal parens", function() {
    macro paren {
      case (($x:lit)) => {
        [$x]
      }
    }
    var z = paren ((4));

    expect(z[0]).to.be(4);
  });

  it("should distinguish between commas and no commas in a repeat", function() {
    macro m {
      case ($p ...) => {
        "no commas"
      }
      case ($p (,) ...) => {
        "comma"
      }
    }
    expect(m (a b)).to.be("no commas");
  });

  it("should match as much of the pattern as possible if not in a delimiter even when more syntax follows", function() {
      macro m {
        case $p (,) ... => {
          [$p (,) ...];
        }
      }
      var res = m 1, 2, 3 "trailing";
      expect(res).to.eql([1,2,3]);
  });

  it("should expand with ellipses", function() {
    macro paren {
      case ( $x:lit (,) ...) => {
        [$x (,) ...]
      }
    }
    var z = paren (4, 3);

    expect(z[0]).to.be(4);
    expect(z[1]).to.be(3);
  });

  it("should expand literal parens with ellipses", function() {
    macro paren {
      case ( ($x:lit) (,) ...) => {
        [$x (,) ...]
      }
    }
    var z = paren ((4), (3));

    expect(z[0]).to.be(4);
    expect(z[1]).to.be(3);
  });

  it("should expand a simple let macro", function() {
    macro let {
      case ($x:ident = $v:expr) {$y:expr} => {
        (function($x) { return $y; })($v);
      }
    }

    var foo = let (z = 1) {z+2}

    expect(foo).to.be(3)
  });

  it("should expand a complex let macro", function() {
    macro let {
      case ( $($x:ident = $v:expr) (,) ...) {$y:expr} => {
        (function($x (,) ...) { return $y; })($v (,) ...);
      }
    }

    var foo = let (z = 1, t = 5 + 2) {z+t}

    expect(foo).to.be(8)
  });

  it("should handle ellipses in output delimiters", function() {
    macro m {
      case ( $x:lit (,) ...) => {
        [[$x] (,) ...]
      }
    }
    var x = m ( 1, 2, 3 );
    expect(x).to.eql([[1],[2],[3]]);
  });

  it("should work", function() {
    macro m {
      case { $($a $b) ... } => {
        [$([$a, $b]) (,) ...];
      }
    }

    var x = m { 1 2 3 4 }

    expect(x).to.eql([[1, 2], [3, 4]])
  });

  it("should expand simple nested ellipses", function() {
    macro nest {
      case ( ($x:lit (,) ...) (,) ... ) => {
        [ [$x (,) ...] (,) ...]
      }
    }
    var x = nest ( (1, 2, 3), (4, 5, 6) );
  });

  it("should expand a nested ellipses macro", function() {
    macro nest {
      case ( ($x:lit ; $y:lit (,) ...) (,) ...) => {
        [ [$x (,) ...], [$y (,) ...] (,) ... ]
      }
    }

    var foo = nest ( (1; 2,3,4,5), (10; 11, 12, 13, 14, 15) );

    expect(foo[0]).to.eql([1,10]);
    expect(foo[1]).to.eql([2,3,4,5]);
    expect(foo[2]).to.eql([11, 12, 13, 14, 15]);
  });

  it("should expand an ellipsis with a ; delimiter", function() {
    macro semi {
      case ( $x:lit (;) ...) => {
        [$x (,) ...]
      }
    }
    var a = semi(1;2;3;4);
    expect(a.length).to.be(4);
    expect(a[1]).to.be(2);
  });

  it("should expand an ellipsis with no separator", function() {
    macro semi {
      case ($x:ident ...) => {
        var $($x = 2) (,) ...
      }
    }
    semi(w x y z);
    expect(w).to.be(2);
    expect(x).to.be(2);
    expect(y).to.be(2);
    expect(z).to.be(2);
  });

  it("should handle def macro", function() {
    macro def {
      case $name:ident ($params:ident (,) ...) $body => {
        function $name ($params (,) ...) {
          $body
        }
      }
    }
    def add (a, b) {
      return a + b;
    }

    expect(add(2,2)).to.be(4);
  });

  it("should handle multiple cases", function() {
    macro m {
      case four => {
        4
      }
      case two => {
        2
      }
    }
    var n = m four;
    expect(n).to.be(4)
    var n = m two;
    expect(n).to.be(2)
  });

  it("should handle multiple cases when matching different length patterns", function() {
    macro arrid {
      case [$x:lit] => {
        [$x]
      }
      case [$x:lit, $y:lit] => {
        [$x, $y]
      }
    }

    var l = arrid [1]
    expect(l).to.eql([1])

    var ll = arrid [1,2]
    expect(ll).to.eql([1, 2])
  });

  it("should handle more multiple cases", function() {
    macro m {
      case $n:ident => {42}
      case $n:ident < $m:ident {} => {42}
    }
    var z = m foo;
    var zz = m foo < foo {};
    expect(z).to.be(42);
    expect(zz).to.be(42);
  });

  it("should handle recursive macros", function() {
    macro rot {
      case [$x:lit] => {
        [$x]
      }
      case [$x:lit, $y:lit] => {
        [rot [$y], $x]
      }
    }

    var l = rot [1]
    expect(l).to.eql([1])

    var ll = rot [1,2]
    expect(ll).to.eql([[2], 1])
  });

  it("should handle mutually recursive macros", function() {
    macro a {
      case [$x:lit] => {
        b ($x)
      }
    }
    macro b {
      case ($x:lit) => {
        [$x]
      }
      case ($x:lit, $y:lit) => {
        a [$x]
      }
    }
    var z = b (1, 2);
    expect(z).to.eql([1])
  });

  it("should allow macro defining macros", function() {
    macro mm {
      case ($x:lit) => {
        macro m {
          case ($y:lit) => {
            [$x, $y]
          }
        }
      }
    }

    mm (42)
    var z = m (24);
    expect(z).to.eql([42,24])
  });

  it("should allow matching of unparsed tokens", function() {
    macro m {
      case ($x) => {
        $x
      }
    }
    var z = m ([1,2,3]);
    expect(z).to.eql([1,2,3])
  });

  it("should allow literal syntax", function() {
    macro m {
        case $x => {
            macro $x {
                case ($y $[...]) => {
                    [$y (,) $[...]];
                }
            }
        }
    }

    m n;
    var x = n (42 24);
    expect(x[0]).to.be(42);
    expect(x[1]).to.be(24);
  });

  it("should allow literal syntax with pattern var literals", function() {
    macro $test {
      case ($op (|) ...) => {
        macro rel {
          case $x => { $x }
          $(case ($x $op $y) => { 1 }) ...
        }
      }
    }

    $test (<|>)
    rel(1 < 2 < 3)
  });

  it("should allow a case to fail while not purtubing the remaining cases", function() {
    macro m {
        case { $y:expr DONTMATCH } => {
          $y
        }
        case { $y:expr } => {
            $y
        }
    }

    function c() {
      return 42;
    }

    var myobj = { baz: function(param) { return param; }};
    var x = m { myobj.baz(c()) }
    expect(x).to.be(42);
  });

  it("should not fail with tokens that are on an object's prototype chain", function() {
    // (had been using an object naively as a dictionary so make sure we don't regress)
    macro m {
      case () => {
        this.constructor
      }
    }

    m ();
  });

  it("should allow fn calls as an :expr", function() {
    macro m {
      case ($x:expr) => {$x}
    }
    function id (x) { return x; }
    var x = m( id(4) );
  });

});<|endoftext|>


<commit_before>var vumigo = require('vumigo_v02');
var App = vumigo.App;
var AppTester = vumigo.AppTester;
var EndState = vumigo.states.EndState;
var MetricsHelper = require('../lib');
var assert = require('assert');

describe('MetricsHelper', function() {
    var app;
    var tester;
    var metricsH;

    beforeEach(function() {
        app = new App('states:test');
        tester = new AppTester(app);

        app.init = function() {
            metricsH = new MetricsHelper(app.im);
            metricsH
                .add.totalUniqueUsers('uniqueUsers')
                .add.totalUniqueUsers('uniqueUsers2');
       };

        app.states.add('states:test', function(name) {
            return new EndState(name, {
                text: 'This is the end state.'
            });
        });

        tester
            .setup.config.app({
                name: 'metricsHelper-tester'
            });
    });

    describe('When a new user accesses the service', function() {

        it('should display the first state text', function() {
            return tester
                .start()
                .check.interaction({
                    state: 'states:test',
                    reply: 'This is the end state.'
                })
                .run();
        });

        it('should fire the new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire both new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics1 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics2 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers2;
                    assert.deepEqual(metrics1, {agg: 'sum', values: [ 1 ]});
                    assert.deepEqual(metrics2, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new user', function() {
            return tester
                .inputs(
                    {from_addr: '+271234', content: null},
                    {from_addr: '+274321', content: null})
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1, 1 ]});
                })
                .run();
        });

        it('should not fire the metrics for existing users', function() {
            return tester
                .setup.user({addr: '+271234', state:'states:test'})
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics.stores;
                    assert.deepEqual(metrics, []);
                })
                .run();
        });

    });
});
<commit_msg>Added tests for totalSessions metric<commit_after>var vumigo = require('vumigo_v02');
var App = vumigo.App;
var AppTester = vumigo.AppTester;
var EndState = vumigo.states.EndState;
var MetricsHelper = require('../lib');
var assert = require('assert');

describe('MetricsHelper', function() {
    var app;
    var tester;
    var metricsH;

    beforeEach(function() {
        app = new App('states:test');
        tester = new AppTester(app);

        app.init = function() {
            metricsH = new MetricsHelper(app.im);
            metricsH
                .add.totalUniqueUsers('uniqueUsers')
                .add.totalUniqueUsers('uniqueUsers2')
                .add.totalSessions('sessions')
                .add.totalSessions('sessions2');
       };

        app.states.add('states:test', function(name) {
            return new EndState(name, {
                text: 'This is the end state.'
            });
        });

        tester
            .setup.config.app({
                name: 'metricsHelper-tester'
            });
    });

    describe('When a new user accesses the service', function() {

        it('should display the first state text', function() {
            return tester
                .start()
                .check.interaction({
                    state: 'states:test',
                    reply: 'This is the end state.'
                })
                .run();
        });

        it('should fire the new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire both new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics1 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics2 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers2;
                    assert.deepEqual(metrics1, {agg: 'sum', values: [ 1 ]});
                    assert.deepEqual(metrics2, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new user', function() {
            return tester
                .inputs(
                    {from_addr: '+271234', content: null},
                    {from_addr: '+274321', content: null})
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1, 1 ]});
                })
                .run();
        });

        it('should not fire the metrics for existing users', function() {
            return tester
                .setup.user({addr: '+271234', state:'states:test'})
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.deepEqual(metrics.uniqueUsers, null);
                })
                .run();
        });

    });

    describe('when a new session is started', function() {

        it('should display the first state text', function() {
            return tester
                .start()
                .check.interaction({
                    state: 'states:test',
                    reply: 'This is the end state.'
                })
                .run();
        });

        it('should fire the totalSessions metric', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire both totalSessions metrics', function() {
            return tester
                .start()
                .check(function(api, im, app) {
                    metric1 = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    metric2 = api.metrics
                        .stores['metricsHelper-tester'].sessions2;
                    assert.deepEqual(metric1, {agg: 'sum', values: [ 1 ]});
                    assert.deepEqual(metric2, {agg: 'sum', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new session', function() {
            return tester
                .inputs(null, null)
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    assert.deepEqual(metrics, {agg: 'sum', values: [ 1, 1 ]});
                })
                .run();
        });

        it('should not fire when resuming a session', function() {
            return tester
                .input('resume')
                .check(function(api, im, app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.deepEqual(metrics.sessions, null);
                })
                .run();
        });

    });

});
<|endoftext|>


<commit_before>'use strict';

// Initiate require()
var Require = load( PATH + '/lib/require.js' );
var require = Require( PATH , [ 'libpath1', 'libpath2' ] );

// Load globals
var File  = java.io.File;
var files = new File( PATH + './global' ).listFiles();

for ( var fileIndex in files ) {
	var properties = require( files[ fileIndex ] );

	for ( var property in properties ) {
		global[ property ] = properties[ property ];
	}
}

// Run main.js file
require( './main.js' );
<commit_msg>Refactor: remove unnecessary variable<commit_after>'use strict';

// Initiate require()
var Require = load( PATH + '/lib/require.js' );
var require = Require( PATH , [ 'libpath1', 'libpath2' ] );

// Load globals
var files = new java.io.File( PATH + './global' ).listFiles();

for ( var fileIndex in files ) {
	var properties = require( files[ fileIndex ] );

	for ( var property in properties ) {
		global[ property ] = properties[ property ];
	}
}

// Run main.js file
require( './main.js' );
<|endoftext|>


<commit_before>export default class WeatherSummaryService {
    static get(weatherData) {
        return 'Weather forecast summarized';
    }
}
<commit_msg>Implemented the weather summary service<commit_after>export default class WeatherSummaryService {
    static get(modelList) {
        let changesMap = [{
                min: 0,
                max: 0,
                weatherCode: modelList[0].weather[0].id,
                description: modelList[0].weather[0].description
            }],
            subList = modelList.slice(1, 3),
            minSum = 0,
            maxSum = 0,
            summary = '';

        subList.forEach((model, index) => {
            changesMap.push({
                min: model.temp.min - modelList[index].temp.min,
                max: model.temp.max - modelList[index].temp.max,
                weatherCode: model.weather[0].id,
                description: model.weather[0].description
            })
        });

        changesMap.forEach((model) => {
            minSum += model.min;
            maxSum += model.max;
        });

        if (minSum < 0 && maxSum < 0) {
            summary += 'Getting colder';
        } else if (minSum > 0 && maxSum > 0) {
            summary += 'Getting warmer';
        } else {
            summary += 'Rather stable';
        }

        if (changesMap[0].weatherCode !== changesMap[1].weatherCode) {
            summary += ' ' + changesMap[1].description;
        }

        return summary;
    }
}
<|endoftext|>