<commit_before>function Tag(impl, conf, innerHTML) {

  var self = riot.observable(this),
      opts = inherit(conf.opts) || {},
      dom = mkdom(impl.tmpl),
      item = conf.item,
      parent = conf.parent,
      frag = conf.frag,
      expressions = [],
      childTags = [],
      root = conf.root,
      fn = impl.fn,
      tagName = root.tagName.toLowerCase(),
      attr = {},
      loopDom,
      TAG_ATTRIBUTES = /([\w\-]+)\s?=\s?['"]([^'"]+)["']/gim

  if (fn && root._tag) {
    root._tag.unmount(true)
  }

  if(impl.attrs) {
    var attrs = impl.attrs.match(TAG_ATTRIBUTES)

    each(attrs, function(a) {
      var kv = a.split(/\s?=\s?/)
      root.setAttribute(kv[0], kv[1].replace(/['"]/g, ''))
    })

  }
  // keep a reference to the tag just created
  // so we will be able to mount this tag multiple times
  root._tag = this

  // create a unique id to this tag
  // it could be handy to use it also to improve the virtual dom rendering speed
  this._id = fastAbs(~~(new Date().getTime() * Math.random()))

  extend(this, { parent: parent, root: root, opts: opts, tags: {} }, item)

  // grab attributes
  each(root.attributes, function(el) {
    var val = el.value
    // remember attributes with expressions only
    if (/\{.*\}/.test(val)) attr[el.name] = val
  })

  if (dom.innerHTML && !/select/.test(tagName) && !/tbody/.test(tagName) && !/tr/.test(tagName))
    // replace all the yield tags with the tag inner html
    dom.innerHTML = replaceYield(dom.innerHTML, innerHTML)

  // options
  function updateOpts() {
    // update opts from current DOM attributes
    each(root.attributes, function(el) {
      opts[el.name] = tmpl(el.value, parent || self)
    })
    // recover those with expressions
    each(Object.keys(attr), function(name) {
      opts[name] = tmpl(attr[name], parent || self)
    })
  }

  this.update = function(data) {
    extend(self, data, item)
    updateOpts()
    self.trigger('update', item)
    update(expressions, self, item)
    self.trigger('updated')
  }

  this.mixin = function() {
    each(arguments, function(mix) {
      mix = 'string' == typeof mix ? riot.mixin(mix) : mix
      each(Object.keys(mix), function(key) {
        // bind methods to self
        if ('init' != key)
          self[key] = 'function' == typeof mix[key] ? mix[key].bind(self) : mix[key]
      })
      // init method will be called automatically
      if (mix.init) mix.init.bind(self)()
    })
  }

  this.mount = function() {

    updateOpts()

    // initialiation
    fn && fn.call(self, opts)

    toggle(true)

    // parse layout after init. fn may calculate args for nested custom tags
    parseExpressions(dom, self, expressions)

    if (!self.parent) self.update()

    // internal use only, fixes #403
    self.trigger('premount')

    if (fn) {
      while (dom.firstChild) root.appendChild(dom.firstChild)
    } else {
      loopDom = dom.firstChild
      frag.appendChild(loopDom)
    }

    if (root.stub) self.root = root = parent.root

    // if it's not a child tag we can trigger its mount event
    if (!self.parent) self.trigger('mount')
    // otherwise we need to wait that the parent event gets triggered
    else self.parent.one('mount', function() { self.trigger('mount') })
  }

  this.unmount = function(keepRootTag) {
    var el = loopDom || root,
        p = el.parentNode

    if (p) {

      if (parent) {
        // remove this tag from the parent tags object
        // if there are multiple nested tags with same name..
        // remove this element form the array
        if (Array.isArray(parent.tags[tagName])) {
          each(parent.tags[tagName], function(tag, i) {
            if (tag._id == self._id)
              parent.tags[tagName].splice(i, 1)
          })
        } else
          // otherwise just delete the tag instance
          parent.tags[tagName] = undefined
      } else {
        while (el.firstChild) el.removeChild(el.firstChild)
      }

      if (!keepRootTag)
        p.removeChild(el)

    }

    self.trigger('unmount')
    toggle()
    self.off('*')
    // somehow ie8 does not like `delete root._tag`
    root._tag = null

  }

  function toggle(isMount) {

    // mount/unmount children
    each(childTags, function(child) { child[isMount ? 'mount' : 'unmount']() })

    // listen/unlisten parent (events flow one way from parent to children)
    if (parent) {
      var evt = isMount ? 'on' : 'off'
      parent[evt]('update', self.update)[evt]('unmount', self.unmount)
    }
  }

  // named elements available for fn
  parseNamedElements(dom, this, childTags)

}
<commit_msg>Use brackets() for expressions.<commit_after>function Tag(impl, conf, innerHTML) {

  var self = riot.observable(this),
      opts = inherit(conf.opts) || {},
      dom = mkdom(impl.tmpl),
      item = conf.item,
      parent = conf.parent,
      frag = conf.frag,
      expressions = [],
      childTags = [],
      root = conf.root,
      fn = impl.fn,
      tagName = root.tagName.toLowerCase(),
      attr = {},
      loopDom,
      TAG_ATTRIBUTES = /([\w\-]+)\s?=\s?['"]([^'"]+)["']/gim

  if (fn && root._tag) {
    root._tag.unmount(true)
  }

  if(impl.attrs) {
    var attrs = impl.attrs.match(TAG_ATTRIBUTES)

    each(attrs, function(a) {
      var kv = a.split(/\s?=\s?/)
      root.setAttribute(kv[0], kv[1].replace(/['"]/g, ''))
    })

  }
  // keep a reference to the tag just created
  // so we will be able to mount this tag multiple times
  root._tag = this

  // create a unique id to this tag
  // it could be handy to use it also to improve the virtual dom rendering speed
  this._id = fastAbs(~~(new Date().getTime() * Math.random()))

  extend(this, { parent: parent, root: root, opts: opts, tags: {} }, item)

  // grab attributes
  each(root.attributes, function(el) {
    var val = el.value
    // remember attributes with expressions only
    if (brackets(/\{.*\}/).test(val)) attr[el.name] = val
  })

  if (dom.innerHTML && !/select/.test(tagName) && !/tbody/.test(tagName) && !/tr/.test(tagName))
    // replace all the yield tags with the tag inner html
    dom.innerHTML = replaceYield(dom.innerHTML, innerHTML)

  // options
  function updateOpts() {
    // update opts from current DOM attributes
    each(root.attributes, function(el) {
      opts[el.name] = tmpl(el.value, parent || self)
    })
    // recover those with expressions
    each(Object.keys(attr), function(name) {
      opts[name] = tmpl(attr[name], parent || self)
    })
  }

  this.update = function(data) {
    extend(self, data, item)
    updateOpts()
    self.trigger('update', item)
    update(expressions, self, item)
    self.trigger('updated')
  }

  this.mixin = function() {
    each(arguments, function(mix) {
      mix = 'string' == typeof mix ? riot.mixin(mix) : mix
      each(Object.keys(mix), function(key) {
        // bind methods to self
        if ('init' != key)
          self[key] = 'function' == typeof mix[key] ? mix[key].bind(self) : mix[key]
      })
      // init method will be called automatically
      if (mix.init) mix.init.bind(self)()
    })
  }

  this.mount = function() {

    updateOpts()

    // initialiation
    fn && fn.call(self, opts)

    toggle(true)

    // parse layout after init. fn may calculate args for nested custom tags
    parseExpressions(dom, self, expressions)

    if (!self.parent) self.update()

    // internal use only, fixes #403
    self.trigger('premount')

    if (fn) {
      while (dom.firstChild) root.appendChild(dom.firstChild)
    } else {
      loopDom = dom.firstChild
      frag.appendChild(loopDom)
    }

    if (root.stub) self.root = root = parent.root

    // if it's not a child tag we can trigger its mount event
    if (!self.parent) self.trigger('mount')
    // otherwise we need to wait that the parent event gets triggered
    else self.parent.one('mount', function() { self.trigger('mount') })
  }

  this.unmount = function(keepRootTag) {
    var el = loopDom || root,
        p = el.parentNode

    if (p) {

      if (parent) {
        // remove this tag from the parent tags object
        // if there are multiple nested tags with same name..
        // remove this element form the array
        if (Array.isArray(parent.tags[tagName])) {
          each(parent.tags[tagName], function(tag, i) {
            if (tag._id == self._id)
              parent.tags[tagName].splice(i, 1)
          })
        } else
          // otherwise just delete the tag instance
          parent.tags[tagName] = undefined
      } else {
        while (el.firstChild) el.removeChild(el.firstChild)
      }

      if (!keepRootTag)
        p.removeChild(el)

    }

    self.trigger('unmount')
    toggle()
    self.off('*')
    // somehow ie8 does not like `delete root._tag`
    root._tag = null

  }

  function toggle(isMount) {

    // mount/unmount children
    each(childTags, function(child) { child[isMount ? 'mount' : 'unmount']() })

    // listen/unlisten parent (events flow one way from parent to children)
    if (parent) {
      var evt = isMount ? 'on' : 'off'
      parent[evt]('update', self.update)[evt]('unmount', self.unmount)
    }
  }

  // named elements available for fn
  parseNamedElements(dom, this, childTags)

}
<|endoftext|>


<commit_before>import observable from 'riot-observable'
import { parseExpressions, parseAttributes } from './parse'
import RefExpr from './ref'
import update from './update'
import mkdom from './mkdom'
import { mixin } from './core'
import settings from '../../settings'
import { setAttr, remAttr, walkAttrs } from './../common/util/dom'

import {
  isFunction,
  isString
} from './../common/util/check'

import {
  extend,
  each,
  startsWith,
  toCamel,
  contains,
  defineProperty
} from './../common/util/misc'

import {
  cleanUpData,
  unmountAll,
  arrayishRemove,
  inheritFrom,
  getTagName,
  getImmediateCustomParentTag
} from './../common/util/tags'

import {
  GLOBAL_MIXIN,
  __TAGS_CACHE,
  ATTRS_PREFIX,
  IS_DIRECTIVE
} from './../common/global-variables'

// counter to give a unique id to all the Tag instances
var __uid = 0

/**
 * We need to update opts for this tag. That requires updating the expressions
 * in any attributes on the tag, and then copying the result onto opts.
 * @this Tag
 * @param   {Boolean} isLoop - is it a loop tag?
 * @param   { Tag }  parent - parent tag node
 * @param   { Boolean }  isAnonymous - is it a tag without any impl? (a tag not registered)
 * @param   { Object }  opts - tag options
 * @param   { Array }  instAttrs - tag attributes array
 */
function updateOpts(isLoop, parent, isAnonymous, opts, instAttrs) {
  // isAnonymous `each` tags treat `dom` and `root` differently. In this case
  // (and only this case) we don't need to do updateOpts, because the regular parse
  // will update those attrs. Plus, isAnonymous tags don't need opts anyway
  if (isLoop && isAnonymous) return

  var ctx = !isAnonymous && isLoop ? this : parent || this
  each(instAttrs, (attr) => {
    if (attr.expr) update.call(ctx, [attr.expr])
    opts[toCamel(attr.name)] = attr.expr ? attr.expr.value : attr.value
  })
}

/**
 * Tag class
 * @constructor
 * @param { Object } impl - it contains the tag template, and logic
 * @param { Object } conf - tag options
 * @param { String } innerHTML - html that eventually we need to inject in the tag
 */
export default function Tag(impl = {}, conf = {}, innerHTML) {

  var opts = extend({}, conf.opts),
    parent = conf.parent,
    isLoop = conf.isLoop,
    isAnonymous = !!conf.isAnonymous,
    skipAnonymous = settings.skipAnonymousTags && isAnonymous,
    item = cleanUpData(conf.item),
    index = conf.index, // available only for the looped nodes
    instAttrs = [], // All attributes on the Tag when it's first parsed
    implAttrs = [], // expressions on this type of Tag
    expressions = [],
    root = conf.root,
    tagName = conf.tagName || getTagName(root),
    isVirtual = tagName === 'virtual',
    propsInSyncWithParent = [],
    dom

  // make this tag observable
  if (!skipAnonymous) observable(this)
  // only call unmount if we have a valid __TAG_IMPL (has name property)
  if (impl.name && root._tag) root._tag.unmount(true)

  // not yet mounted
  this.isMounted = false

  defineProperty(this, '__', {
    isAnonymous,
    instAttrs,
    innerHTML,
    tagName,
    index,
    isLoop,
    // these vars will be needed only for the virtual tags
    virts: [],
    tail: null,
    head: null,
    parent: null,
    item: null
  })

  // create a unique id to this tag
  // it could be handy to use it also to improve the virtual dom rendering speed
  defineProperty(this, '_riot_id', ++__uid) // base 1 allows test !t._riot_id
  defineProperty(this, 'root', root)
  extend(this, { opts }, item)
  // protect the "tags" and "refs" property from being overridden
  defineProperty(this, 'parent', parent || null)
  defineProperty(this, 'tags', {})
  defineProperty(this, 'refs', {})

  dom = isLoop && isAnonymous ? root : mkdom(impl.tmpl, innerHTML, isLoop)

  /**
   * Update the tag expressions and options
   * @param   { * }  data - data we want to use to extend the tag properties
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'update', function tagUpdate(data) {
    const nextOpts = {},
      canTrigger = this.isMounted && !skipAnonymous

    // make sure the data passed will not override
    // the component core methods
    data = cleanUpData(data)
    extend(this, data)
    updateOpts.apply(this, [isLoop, parent, isAnonymous, nextOpts, instAttrs])
    if (this.isMounted && isFunction(this.shouldUpdate) && !this.shouldUpdate(data, nextOpts)) return this

    // inherit properties from the parent, but only for isAnonymous tags
    if (isLoop && isAnonymous) inheritFrom.apply(this, [this.parent, propsInSyncWithParent])
    extend(opts, nextOpts)
    if (canTrigger) this.trigger('update', data)
    update.call(this, expressions)
    if (canTrigger) this.trigger('updated')

    return this

  }.bind(this))

  /**
   * Add a mixin to this tag
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'mixin', function tagMixin() {
    each(arguments, (mix) => {
      let instance, obj
      let props = []

      // properties blacklisted and will not be bound to the tag instance
      const propsBlacklist = ['init', '__proto__']

      mix = isString(mix) ? mixin(mix) : mix

      // check if the mixin is a function
      if (isFunction(mix)) {
        // create the new mixin instance
        instance = new mix()
      } else instance = mix

      var proto = Object.getPrototypeOf(instance)

      // build multilevel prototype inheritance chain property list
      do props = props.concat(Object.getOwnPropertyNames(obj || instance))
      while (obj = Object.getPrototypeOf(obj || instance))

      // loop the keys in the function prototype or the all object keys
      each(props, (key) => {
        // bind methods to this
        // allow mixins to override other properties/parent mixins
        if (!contains(propsBlacklist, key)) {
          // check for getters/setters
          var descriptor = Object.getOwnPropertyDescriptor(instance, key) || Object.getOwnPropertyDescriptor(proto, key)
          var hasGetterSetter = descriptor && (descriptor.get || descriptor.set)

          // apply method only if it does not already exist on the instance
          if (!this.hasOwnProperty(key) && hasGetterSetter) {
            Object.defineProperty(this, key, descriptor)
          } else {
            this[key] = isFunction(instance[key]) ?
              instance[key].bind(this) :
              instance[key]
          }
        }
      })

      // init method will be called automatically
      if (instance.init)
        instance.init.bind(this)()
    })
    return this
  }.bind(this))

  /**
   * Mount the current tag instance
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'mount', function tagMount() {
    root._tag = this // keep a reference to the tag just created

    // Read all the attrs on this instance. This give us the info we need for updateOpts
    parseAttributes.apply(parent, [root, root.attributes, (attr, expr) => {
      if (!isAnonymous && RefExpr.isPrototypeOf(expr)) expr.tag = this
      attr.expr = expr
      instAttrs.push(attr)
    }])

    // update the root adding custom attributes coming from the compiler
    implAttrs = []
    walkAttrs(impl.attrs, (k, v) => { implAttrs.push({name: k, value: v}) })
    parseAttributes.apply(this, [root, implAttrs, (attr, expr) => {
      if (expr) expressions.push(expr)
      else setAttr(root, attr.name, attr.value)
    }])

    // initialiation
    updateOpts.apply(this, [isLoop, parent, isAnonymous, opts, instAttrs])

    // add global mixins
    var globalMixin = mixin(GLOBAL_MIXIN)

    if (globalMixin && !skipAnonymous) {
      for (var i in globalMixin) {
        if (globalMixin.hasOwnProperty(i)) {
          this.mixin(globalMixin[i])
        }
      }
    }

    if (impl.fn) impl.fn.call(this, opts)

    if (!skipAnonymous) this.trigger('before-mount')

    // parse layout after init. fn may calculate args for nested custom tags
    parseExpressions.apply(this, [dom, expressions, isAnonymous])

    this.update(item)

    if (!isAnonymous) {
      while (dom.firstChild) root.appendChild(dom.firstChild)
    }

    defineProperty(this, 'root', root)
    defineProperty(this, 'isMounted', true)

    if (skipAnonymous) return

    // if it's not a child tag we can trigger its mount event
    if (!this.parent) {
      this.trigger('mount')
    }
    // otherwise we need to wait that the parent "mount" or "updated" event gets triggered
    else {
      const p = getImmediateCustomParentTag(this.parent)
      p.one(!p.isMounted ? 'mount' : 'updated', () => {
        this.trigger('mount')
      })
    }

    return this

  }.bind(this))

  /**
   * Unmount the tag instance
   * @param { Boolean } mustKeepRoot - if it's true the root node will not be removed
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'unmount', function tagUnmount(mustKeepRoot) {
    var el = this.root,
      p = el.parentNode,
      ptag,
      tagIndex = __TAGS_CACHE.indexOf(this)

    if (!skipAnonymous) this.trigger('before-unmount')

    // clear all attributes coming from the mounted tag
    walkAttrs(impl.attrs, (name) => {
      if (startsWith(name, ATTRS_PREFIX))
        name = name.slice(ATTRS_PREFIX.length)
      remAttr(root, name)
    })

    // remove this tag instance from the global virtualDom variable
    if (tagIndex !== -1)
      __TAGS_CACHE.splice(tagIndex, 1)

    if (p || isVirtual) {
      if (parent) {
        ptag = getImmediateCustomParentTag(parent)

        if (isVirtual) {
          Object.keys(this.tags).forEach(tagName => {
            arrayishRemove(ptag.tags, tagName, this.tags[tagName])
          })
        } else {
          arrayishRemove(ptag.tags, tagName, this)
          if(parent !== ptag) // remove from _parent too
            arrayishRemove(parent.tags, tagName, this)
        }
      } else {
        while (el.firstChild) el.removeChild(el.firstChild)
      }

      if (p)
        if (!mustKeepRoot) {
          p.removeChild(el)
        } else {
          // the riot-tag and the data-is attributes aren't needed anymore, remove them
          remAttr(p, IS_DIRECTIVE)
        }
    }

    if (this.__.virts) {
      each(this.__.virts, (v) => {
        if (v.parentNode) v.parentNode.removeChild(v)
      })
    }

    // allow expressions to unmount themselves
    unmountAll(expressions)
    each(instAttrs, a => a.expr && a.expr.unmount && a.expr.unmount())

    // custom internal unmount function to avoid relying on the observable
    if (this.__.onUnmount) this.__.onUnmount()

    if (!skipAnonymous) {
      this.trigger('unmount')
      this.off('*')
    }

    defineProperty(this, 'isMounted', false)

    delete this.root._tag

    return this

  }.bind(this))
}
<commit_msg>updated: small optimization avoiding the shouldUpdate check for the anonymous tags<commit_after>import observable from 'riot-observable'
import { parseExpressions, parseAttributes } from './parse'
import RefExpr from './ref'
import update from './update'
import mkdom from './mkdom'
import { mixin } from './core'
import settings from '../../settings'
import { setAttr, remAttr, walkAttrs } from './../common/util/dom'

import {
  isFunction,
  isString
} from './../common/util/check'

import {
  extend,
  each,
  startsWith,
  toCamel,
  contains,
  defineProperty
} from './../common/util/misc'

import {
  cleanUpData,
  unmountAll,
  arrayishRemove,
  inheritFrom,
  getTagName,
  getImmediateCustomParentTag
} from './../common/util/tags'

import {
  GLOBAL_MIXIN,
  __TAGS_CACHE,
  ATTRS_PREFIX,
  IS_DIRECTIVE
} from './../common/global-variables'

// counter to give a unique id to all the Tag instances
var __uid = 0

/**
 * We need to update opts for this tag. That requires updating the expressions
 * in any attributes on the tag, and then copying the result onto opts.
 * @this Tag
 * @param   {Boolean} isLoop - is it a loop tag?
 * @param   { Tag }  parent - parent tag node
 * @param   { Boolean }  isAnonymous - is it a tag without any impl? (a tag not registered)
 * @param   { Object }  opts - tag options
 * @param   { Array }  instAttrs - tag attributes array
 */
function updateOpts(isLoop, parent, isAnonymous, opts, instAttrs) {
  // isAnonymous `each` tags treat `dom` and `root` differently. In this case
  // (and only this case) we don't need to do updateOpts, because the regular parse
  // will update those attrs. Plus, isAnonymous tags don't need opts anyway
  if (isLoop && isAnonymous) return

  var ctx = !isAnonymous && isLoop ? this : parent || this
  each(instAttrs, (attr) => {
    if (attr.expr) update.call(ctx, [attr.expr])
    opts[toCamel(attr.name)] = attr.expr ? attr.expr.value : attr.value
  })
}

/**
 * Tag class
 * @constructor
 * @param { Object } impl - it contains the tag template, and logic
 * @param { Object } conf - tag options
 * @param { String } innerHTML - html that eventually we need to inject in the tag
 */
export default function Tag(impl = {}, conf = {}, innerHTML) {

  var opts = extend({}, conf.opts),
    parent = conf.parent,
    isLoop = conf.isLoop,
    isAnonymous = !!conf.isAnonymous,
    skipAnonymous = settings.skipAnonymousTags && isAnonymous,
    item = cleanUpData(conf.item),
    index = conf.index, // available only for the looped nodes
    instAttrs = [], // All attributes on the Tag when it's first parsed
    implAttrs = [], // expressions on this type of Tag
    expressions = [],
    root = conf.root,
    tagName = conf.tagName || getTagName(root),
    isVirtual = tagName === 'virtual',
    propsInSyncWithParent = [],
    dom

  // make this tag observable
  if (!skipAnonymous) observable(this)
  // only call unmount if we have a valid __TAG_IMPL (has name property)
  if (impl.name && root._tag) root._tag.unmount(true)

  // not yet mounted
  this.isMounted = false

  defineProperty(this, '__', {
    isAnonymous,
    instAttrs,
    innerHTML,
    tagName,
    index,
    isLoop,
    // these vars will be needed only for the virtual tags
    virts: [],
    tail: null,
    head: null,
    parent: null,
    item: null
  })

  // create a unique id to this tag
  // it could be handy to use it also to improve the virtual dom rendering speed
  defineProperty(this, '_riot_id', ++__uid) // base 1 allows test !t._riot_id
  defineProperty(this, 'root', root)
  extend(this, { opts }, item)
  // protect the "tags" and "refs" property from being overridden
  defineProperty(this, 'parent', parent || null)
  defineProperty(this, 'tags', {})
  defineProperty(this, 'refs', {})

  dom = isLoop && isAnonymous ? root : mkdom(impl.tmpl, innerHTML, isLoop)

  /**
   * Update the tag expressions and options
   * @param   { * }  data - data we want to use to extend the tag properties
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'update', function tagUpdate(data) {
    const nextOpts = {},
      canTrigger = this.isMounted && !skipAnonymous

    // make sure the data passed will not override
    // the component core methods
    data = cleanUpData(data)
    extend(this, data)
    updateOpts.apply(this, [isLoop, parent, isAnonymous, nextOpts, instAttrs])

    if (canTrigger && this.isMounted && isFunction(this.shouldUpdate) && !this.shouldUpdate(data, nextOpts)) {
      return this
    }

    // inherit properties from the parent, but only for isAnonymous tags
    if (isLoop && isAnonymous) inheritFrom.apply(this, [this.parent, propsInSyncWithParent])
    extend(opts, nextOpts)
    if (canTrigger) this.trigger('update', data)
    update.call(this, expressions)
    if (canTrigger) this.trigger('updated')

    return this

  }.bind(this))

  /**
   * Add a mixin to this tag
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'mixin', function tagMixin() {
    each(arguments, (mix) => {
      let instance, obj
      let props = []

      // properties blacklisted and will not be bound to the tag instance
      const propsBlacklist = ['init', '__proto__']

      mix = isString(mix) ? mixin(mix) : mix

      // check if the mixin is a function
      if (isFunction(mix)) {
        // create the new mixin instance
        instance = new mix()
      } else instance = mix

      var proto = Object.getPrototypeOf(instance)

      // build multilevel prototype inheritance chain property list
      do props = props.concat(Object.getOwnPropertyNames(obj || instance))
      while (obj = Object.getPrototypeOf(obj || instance))

      // loop the keys in the function prototype or the all object keys
      each(props, (key) => {
        // bind methods to this
        // allow mixins to override other properties/parent mixins
        if (!contains(propsBlacklist, key)) {
          // check for getters/setters
          var descriptor = Object.getOwnPropertyDescriptor(instance, key) || Object.getOwnPropertyDescriptor(proto, key)
          var hasGetterSetter = descriptor && (descriptor.get || descriptor.set)

          // apply method only if it does not already exist on the instance
          if (!this.hasOwnProperty(key) && hasGetterSetter) {
            Object.defineProperty(this, key, descriptor)
          } else {
            this[key] = isFunction(instance[key]) ?
              instance[key].bind(this) :
              instance[key]
          }
        }
      })

      // init method will be called automatically
      if (instance.init)
        instance.init.bind(this)()
    })
    return this
  }.bind(this))

  /**
   * Mount the current tag instance
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'mount', function tagMount() {
    root._tag = this // keep a reference to the tag just created

    // Read all the attrs on this instance. This give us the info we need for updateOpts
    parseAttributes.apply(parent, [root, root.attributes, (attr, expr) => {
      if (!isAnonymous && RefExpr.isPrototypeOf(expr)) expr.tag = this
      attr.expr = expr
      instAttrs.push(attr)
    }])

    // update the root adding custom attributes coming from the compiler
    implAttrs = []
    walkAttrs(impl.attrs, (k, v) => { implAttrs.push({name: k, value: v}) })
    parseAttributes.apply(this, [root, implAttrs, (attr, expr) => {
      if (expr) expressions.push(expr)
      else setAttr(root, attr.name, attr.value)
    }])

    // initialiation
    updateOpts.apply(this, [isLoop, parent, isAnonymous, opts, instAttrs])

    // add global mixins
    var globalMixin = mixin(GLOBAL_MIXIN)

    if (globalMixin && !skipAnonymous) {
      for (var i in globalMixin) {
        if (globalMixin.hasOwnProperty(i)) {
          this.mixin(globalMixin[i])
        }
      }
    }

    if (impl.fn) impl.fn.call(this, opts)

    if (!skipAnonymous) this.trigger('before-mount')

    // parse layout after init. fn may calculate args for nested custom tags
    parseExpressions.apply(this, [dom, expressions, isAnonymous])

    this.update(item)

    if (!isAnonymous) {
      while (dom.firstChild) root.appendChild(dom.firstChild)
    }

    defineProperty(this, 'root', root)
    defineProperty(this, 'isMounted', true)

    if (skipAnonymous) return

    // if it's not a child tag we can trigger its mount event
    if (!this.parent) {
      this.trigger('mount')
    }
    // otherwise we need to wait that the parent "mount" or "updated" event gets triggered
    else {
      const p = getImmediateCustomParentTag(this.parent)
      p.one(!p.isMounted ? 'mount' : 'updated', () => {
        this.trigger('mount')
      })
    }

    return this

  }.bind(this))

  /**
   * Unmount the tag instance
   * @param { Boolean } mustKeepRoot - if it's true the root node will not be removed
   * @returns { Tag } the current tag instance
   */
  defineProperty(this, 'unmount', function tagUnmount(mustKeepRoot) {
    var el = this.root,
      p = el.parentNode,
      ptag,
      tagIndex = __TAGS_CACHE.indexOf(this)

    if (!skipAnonymous) this.trigger('before-unmount')

    // clear all attributes coming from the mounted tag
    walkAttrs(impl.attrs, (name) => {
      if (startsWith(name, ATTRS_PREFIX))
        name = name.slice(ATTRS_PREFIX.length)
      remAttr(root, name)
    })

    // remove this tag instance from the global virtualDom variable
    if (tagIndex !== -1)
      __TAGS_CACHE.splice(tagIndex, 1)

    if (p || isVirtual) {
      if (parent) {
        ptag = getImmediateCustomParentTag(parent)

        if (isVirtual) {
          Object.keys(this.tags).forEach(tagName => {
            arrayishRemove(ptag.tags, tagName, this.tags[tagName])
          })
        } else {
          arrayishRemove(ptag.tags, tagName, this)
          if(parent !== ptag) // remove from _parent too
            arrayishRemove(parent.tags, tagName, this)
        }
      } else {
        while (el.firstChild) el.removeChild(el.firstChild)
      }

      if (p)
        if (!mustKeepRoot) {
          p.removeChild(el)
        } else {
          // the riot-tag and the data-is attributes aren't needed anymore, remove them
          remAttr(p, IS_DIRECTIVE)
        }
    }

    if (this.__.virts) {
      each(this.__.virts, (v) => {
        if (v.parentNode) v.parentNode.removeChild(v)
      })
    }

    // allow expressions to unmount themselves
    unmountAll(expressions)
    each(instAttrs, a => a.expr && a.expr.unmount && a.expr.unmount())

    // custom internal unmount function to avoid relying on the observable
    if (this.__.onUnmount) this.__.onUnmount()

    if (!skipAnonymous) {
      this.trigger('unmount')
      this.off('*')
    }

    defineProperty(this, 'isMounted', false)

    delete this.root._tag

    return this

  }.bind(this))
}
<|endoftext|>


<commit_before>var State = require('buildy').State,
    Crypto = require('crypto'),
    mime = require('mime');

/**
 * Send content to s3. Simple Buildy wrapper for knox.
 * See https://github.com/LearnBoost/knox
 *
 * Example:
 *
 * var Registry = require('../../node_modules/buildy').Registry,
 *     Queue = require('../../node_modules/buildy').Queue,
 *     reg = new Registry(),
 *     knox_config = {
            key: '<api-key-here>',
            secret: '<secret-here>',
            bucket: 'shaker'
 *     };
 *
 * reg.load(__dirname + '/s3.js'); // s3 task
 *
 * new Queue('deploy', {registry: reg})
 *     .task('files', ['s3.js'])
 *     .task('concat')
 *     .task('jsminify')
 *     .task('s3', {name: '/foo/bar/baz_{checksum}.js', client: knox_config})
 *     .task('write', {name: 'baz.js'})
 *     .task('inspect')
 *     .run();
 *
 * @method s3Task
 * @param options {Object} s3 task options.
 * @param options.name {String} Resource name.
 * @param options.client {Object} s3 client (key, secret, bucket).
 * @param status {EventEmitter} Status object, handles 'complete' and 'failed' task exit statuses.
 * @param logger {winston.Logger} Logger instance, if additional logging required (other than task exit status)
 * @return {undefined}
 * @public
 */
function s3Task(options, status, logger) {
    var self = this,
        name = options.name,
        root = (options && options.root) || '',
        knox,
        client;

    try {
        knox = require('knox');
    } catch (exception) {
        console.log('Failed to find knox module, please install.');
        throw exception;
    }

    client = knox.createClient(options.client);

    // Send content to s3.
    function storeFile(filename, data) {
        var md5sum, req;

        if (filename.indexOf('{checksum}') > -1) {  // Replace {checksum} with md5 string
            md5sum = Crypto.createHash('md5');
            md5sum.update(data);
            filename = filename.replace('{checksum}', md5sum.digest('hex'));
        }

        filename = root + filename;

        req = client.put(filename, {'Content-Length': data.length, 'Content-Type': mime.lookup(filename)});

        req.on('response', function (res) {
            if (res.statusCode === 200) {
                self._state.set(State.TYPES.STRING, data);
                status.emit('complete', 's3', req.url);
            } else {
                status.emit('failed', 's3', 'error sending file: ' + res.statusCode);
            }
        });
        req.end(data);
    }

    switch (this._state.get().type) {

    case State.TYPES.FILES:
        storeFile(name, self._state.get().value.join("\n"));
        break;

    case State.TYPES.STRING:
        storeFile(name, self._state.get().value);
        break;

    case State.TYPES.STRINGS:
        storeFile(name, self._state.get().value.join(""));
        break;

    case State.TYPES.UNDEFINED:
        storeFile(name, "");
        break;

    default:
        status.emit('failed', 's3', 'unrecognised input type: ' + this._type);
        break;
    }
}

exports.tasks = {
    's3' : {
        callback: s3Task
    }
};
<commit_msg>updated description<commit_after>var State = require('buildy').State,
    Crypto = require('crypto'),
    mime = require('mime');

/**
 * Send content to s3 with an optional checksum in the filename. Uses
 * knox S3 client.
 * See https://github.com/LearnBoost/knox
 *
 * Example:
 *
 * var Registry = require('../../node_modules/buildy').Registry,
 *     Queue = require('../../node_modules/buildy').Queue,
 *     reg = new Registry(),
 *     knox_config = {
            key: '<api-key-here>',
            secret: '<secret-here>',
            bucket: 'shaker'
 *     };
 *
 * reg.load(__dirname + '/s3.js'); // s3 task
 *
 * new Queue('deploy', {registry: reg})
 *     .task('files', ['s3.js'])
 *     .task('concat')
 *     .task('jsminify')
 *     .task('s3', {name: '/foo/bar/baz_{checksum}.js', client: knox_config})
 *     .task('write', {name: 'baz.js'})
 *     .task('inspect')
 *     .run();
 *
 * @method s3Task
 * @param options {Object} s3 task options.
 * @param options.name {String} Resource name.
 * @param options.client {Object} s3 client (key, secret, bucket).
 * @param status {EventEmitter} Status object, handles 'complete' and 'failed' task exit statuses.
 * @param logger {winston.Logger} Logger instance, if additional logging required (other than task exit status)
 * @return {undefined}
 * @public
 */
function s3Task(options, status, logger) {
    var self = this,
        name = options.name,
        root = (options && options.root) || '',
        knox,
        client;

    try {
        knox = require('knox');
    } catch (exception) {
        console.log('Failed to find knox module, please install.');
        throw exception;
    }

    client = knox.createClient(options.client);

    // Send content to s3.
    function storeFile(filename, data) {
        var md5sum, req;

        if (filename.indexOf('{checksum}') > -1) {  // Replace {checksum} with md5 string
            md5sum = Crypto.createHash('md5');
            md5sum.update(data);
            filename = filename.replace('{checksum}', md5sum.digest('hex'));
        }

        filename = root + filename;

        req = client.put(filename, {'Content-Length': data.length, 'Content-Type': mime.lookup(filename)});

        req.on('response', function (res) {
            if (res.statusCode === 200) {
                self._state.set(State.TYPES.STRING, data);
                status.emit('complete', 's3', req.url);
            } else {
                status.emit('failed', 's3', 'error sending file: ' + res.statusCode);
            }
        });
        req.end(data);
    }

    switch (this._state.get().type) {

    case State.TYPES.FILES:
        storeFile(name, self._state.get().value.join("\n"));
        break;

    case State.TYPES.STRING:
        storeFile(name, self._state.get().value);
        break;

    case State.TYPES.STRINGS:
        storeFile(name, self._state.get().value.join(""));
        break;

    case State.TYPES.UNDEFINED:
        storeFile(name, "");
        break;

    default:
        status.emit('failed', 's3', 'unrecognised input type: ' + this._type);
        break;
    }
}

exports.tasks = {
    's3' : {
        callback: s3Task
    }
};
<|endoftext|>


<commit_before>const db = require('./db')
const _ = require('lodash/fp')
const uuid = require('uuid')

function createCashboxBatch (deviceId, cashboxCount) {
  if (_.isEqual(0, cashboxCount)) throw new Error('Cashbox is empty. Cashbox batch could not be created.')
  const sql = `INSERT INTO cashbox_batches (id, device_id, created, operation_type) VALUES ($1, $2, now(), 'cash-in-empty') RETURNING *`
  const sql2 = `
    UPDATE bills SET cashbox_batch_id=$1
    FROM cash_in_txs
    WHERE bills.cash_in_txs_id = cash_in_txs.id AND
          cash_in_txs.device_id = $2 AND
          bills.cashbox_batch_id IS NULL
  `
  return db.tx(async t => {
    const newBatch = await t.oneOrNone(sql, [uuid.v4(), deviceId])
    return t.oneOrNone(sql2, [newBatch.id, newBatch.device_id])
  })
}

function getBatches () {
  const sql = `SELECT cb.id, cb.device_id, cb.created, cb.operation_type, cb.bill_count_override, cb.performed_by,
  json_agg(b.*) AS bills FROM cashbox_batches cb LEFT JOIN bills b ON cb.id=b.cashbox_batch_id GROUP BY cb.id`
  return db.any(sql).then(res => _.map(it => _.mapKeys(ite => _.camelCase(ite), it), res))
}

function editBatchById (id, performedBy) {
  const sql = `UPDATE cashbox_batches SET performed_by=$1 WHERE id=$2`
  return db.none(sql, [performedBy, id])
}

function getBillsByBatchId (id) {
  const sql = `SELECT * FROM bills WHERE cashbox_batch_id=$1`
  return db.any(sql, [id])
}

module.exports = { createCashboxBatch, getBatches, getBillsByBatchId, editBatchById }
<commit_msg>fix: sort cashbox batches by date<commit_after>const db = require('./db')
const _ = require('lodash/fp')
const uuid = require('uuid')

function createCashboxBatch (deviceId, cashboxCount) {
  if (_.isEqual(0, cashboxCount)) throw new Error('Cashbox is empty. Cashbox batch could not be created.')
  const sql = `INSERT INTO cashbox_batches (id, device_id, created, operation_type) VALUES ($1, $2, now(), 'cash-in-empty') RETURNING *`
  const sql2 = `
    UPDATE bills SET cashbox_batch_id=$1
    FROM cash_in_txs
    WHERE bills.cash_in_txs_id = cash_in_txs.id AND
          cash_in_txs.device_id = $2 AND
          bills.cashbox_batch_id IS NULL
  `
  return db.tx(async t => {
    const newBatch = await t.oneOrNone(sql, [uuid.v4(), deviceId])
    return t.oneOrNone(sql2, [newBatch.id, newBatch.device_id])
  })
}

function getBatches () {
  const sql = `
    SELECT cb.id, cb.device_id, cb.created, cb.operation_type, cb.bill_count_override, cb.performed_by, json_agg(b.*) AS bills
    FROM cashbox_batches AS cb
    LEFT JOIN bills AS b ON cb.id = b.cashbox_batch_id
    GROUP BY cb.id
    ORDER BY cb.created DESC
  `
  return db.any(sql).then(res => _.map(it => _.mapKeys(ite => _.camelCase(ite), it), res))
}

function editBatchById (id, performedBy) {
  const sql = `UPDATE cashbox_batches SET performed_by=$1 WHERE id=$2`
  return db.none(sql, [performedBy, id])
}

function getBillsByBatchId (id) {
  const sql = `SELECT * FROM bills WHERE cashbox_batch_id=$1`
  return db.any(sql, [id])
}

module.exports = { createCashboxBatch, getBatches, getBillsByBatchId, editBatchById }
<|endoftext|>


<commit_before>// Toggle main sections
$(".docs-sidebar-section-title").click(function () {
    $('.docs-sidebar-section').not(this).closest('.docs-sidebar-section').removeClass('active');
    $(this).closest('.docs-sidebar-section').toggleClass('active');
});

// Bootstrap stuff
$('.docs-actions i').tooltip();
$('.docs-sidebar-home').tooltip();

/* BB 150310
 *
 * openstackdocstheme provides three types of admonitions, important, note
 * and warning. We decorate their title paragraphs with Font Awesome icons
 * by adding the appropriate FA classes.
 *
 * We also insert a space between the icon and the admonition title
 * ("Note", "Warning", "Important" or their i18n equivalents). This could be
 * done with a single clause - $('p.admonition-title').... - affecting all
 * types of admonitions. I play it safe here and explicitly work on the three
 * openstackdocstheme admonitions.
 */
$('div.important > p.admonition-title').prepend('

');
$('div.note > p.admonition-title').prepend('

');
$('div.seealso > p.admonition-title').prepend('

');
$('div.warning > p.admonition-title').prepend('

');
$('div.versionadded > p').prepend('

');
$('div.versionchanged > p').prepend('

');
$('div.deprecated > p').prepend('

');

function logABug(bugTitle, bugProject, fieldComment, fieldTags, repositoryName, useStoryboard) {
    /* Gives the log a bug icon the information it needs to generate the bug in
     * Launchpad with pre-filled information such as git SHA, opendev.org
     * source URL, published document URL and tag.
     */
    var lineFeed = "%0A";

    var bugChecklist = "This bug tracker is for errors with the documentation, " +
        "use the following as a template and remove or add fields as " +
        "you see fit. Convert [ ] into [x] to check boxes:" + lineFeed + lineFeed +
        "- [ ] This doc is inaccurate in this way: ______" + lineFeed +
        "- [ ] This is a doc addition request." + lineFeed +
        "- [ ] I have a fix to the document that I can paste below including example: " +
        "input and output. " + lineFeed + lineFeed +
        "If you have a troubleshooting or support issue, use the following " +
        " resources:" + lineFeed + lineFeed +
        " - The mailing list: https://lists.openstack.org" + lineFeed +
        " - IRC: 'openstack' channel on Freenode"+ lineFeed;

    var urlBase = "https://bugs.launchpad.net/" + bugProject + "/+filebug?field.title=";
    var currentURL = "URL: " + window.location.href;
    var bugLink = "";
    if (useStoryboard) {
        var urlBase = "https://storyboard.openstack.org/#!/project/";
        bugLink = urlBase + repositoryName;
    } else {
        bugLink = urlBase  + encodeURIComponent(bugTitle) +
        "&field.tags=" + fieldTags +
        "&field.comment=" + lineFeed + lineFeed +  lineFeed +
        bugChecklist + lineFeed + "-----------------------------------" + lineFeed + fieldComment +
        lineFeed + currentURL;
    }
    document.getElementById("logABugLink1").href = bugLink;
    document.getElementById("logABugLink2").href = bugLink;
    document.getElementById("logABugLink3").href = bugLink;
}

function pdfLink(currentSourceFile, pdfFileName) {
    /* Create link to PDF file which is in top-level of document.  */

    /* We know the local path of the html page, so substitute that in
       the URL with the path to the PDF file. */
    /* We do not want any #subanchors, so do not use window.location.href. */
    var currentLink = window.location.protocol + "//" + window.location.hostname + "/" + window.location.pathname;
    if (currentLink.endsWith("/")) {
        currentLink = currentLink + "index.html";
    }
    var file = currentSourceFile + ".html";
    var pdfLink = currentLink.replace(file, pdfFileName);
    document.getElementById("pdfLink1").href = pdfLink;
    document.getElementById("pdfLink2").href = pdfLink;
}
<commit_msg>Change to OFTC<commit_after>// Toggle main sections
$(".docs-sidebar-section-title").click(function () {
    $('.docs-sidebar-section').not(this).closest('.docs-sidebar-section').removeClass('active');
    $(this).closest('.docs-sidebar-section').toggleClass('active');
});

// Bootstrap stuff
$('.docs-actions i').tooltip();
$('.docs-sidebar-home').tooltip();

/* BB 150310
 *
 * openstackdocstheme provides three types of admonitions, important, note
 * and warning. We decorate their title paragraphs with Font Awesome icons
 * by adding the appropriate FA classes.
 *
 * We also insert a space between the icon and the admonition title
 * ("Note", "Warning", "Important" or their i18n equivalents). This could be
 * done with a single clause - $('p.admonition-title').... - affecting all
 * types of admonitions. I play it safe here and explicitly work on the three
 * openstackdocstheme admonitions.
 */
$('div.important > p.admonition-title').prepend('

');
$('div.note > p.admonition-title').prepend('

');
$('div.seealso > p.admonition-title').prepend('

');
$('div.warning > p.admonition-title').prepend('

');
$('div.versionadded > p').prepend('

');
$('div.versionchanged > p').prepend('

');
$('div.deprecated > p').prepend('

');

function logABug(bugTitle, bugProject, fieldComment, fieldTags, repositoryName, useStoryboard) {
    /* Gives the log a bug icon the information it needs to generate the bug in
     * Launchpad with pre-filled information such as git SHA, opendev.org
     * source URL, published document URL and tag.
     */
    var lineFeed = "%0A";

    var bugChecklist = "This bug tracker is for errors with the documentation, " +
        "use the following as a template and remove or add fields as " +
        "you see fit. Convert [ ] into [x] to check boxes:" + lineFeed + lineFeed +
        "- [ ] This doc is inaccurate in this way: ______" + lineFeed +
        "- [ ] This is a doc addition request." + lineFeed +
        "- [ ] I have a fix to the document that I can paste below including example: " +
        "input and output. " + lineFeed + lineFeed +
        "If you have a troubleshooting or support issue, use the following " +
        " resources:" + lineFeed + lineFeed +
        " - The mailing list: https://lists.openstack.org" + lineFeed +
        " - IRC: 'openstack' channel on OFTC"+ lineFeed;

    var urlBase = "https://bugs.launchpad.net/" + bugProject + "/+filebug?field.title=";
    var currentURL = "URL: " + window.location.href;
    var bugLink = "";
    if (useStoryboard) {
        var urlBase = "https://storyboard.openstack.org/#!/project/";
        bugLink = urlBase + repositoryName;
    } else {
        bugLink = urlBase  + encodeURIComponent(bugTitle) +
        "&field.tags=" + fieldTags +
        "&field.comment=" + lineFeed + lineFeed +  lineFeed +
        bugChecklist + lineFeed + "-----------------------------------" + lineFeed + fieldComment +
        lineFeed + currentURL;
    }
    document.getElementById("logABugLink1").href = bugLink;
    document.getElementById("logABugLink2").href = bugLink;
    document.getElementById("logABugLink3").href = bugLink;
}

function pdfLink(currentSourceFile, pdfFileName) {
    /* Create link to PDF file which is in top-level of document.  */

    /* We know the local path of the html page, so substitute that in
       the URL with the path to the PDF file. */
    /* We do not want any #subanchors, so do not use window.location.href. */
    var currentLink = window.location.protocol + "//" + window.location.hostname + "/" + window.location.pathname;
    if (currentLink.endsWith("/")) {
        currentLink = currentLink + "index.html";
    }
    var file = currentSourceFile + ".html";
    var pdfLink = currentLink.replace(file, pdfFileName);
    document.getElementById("pdfLink1").href = pdfLink;
    document.getElementById("pdfLink2").href = pdfLink;
}
<|endoftext|>


<commit_before>// ---
// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
// ---

/**
 * Trigo module provides some math stuff for moving objects in a
 * direction or following a round path.
 */
var trigo={
	ANGLE_RIGHT:0,
	ANGLE_DOWN:Math.PI*0.5,
	ANGLE_LEFT:Math.PI,
	ANGLE_UP:Math.PI*1.5555555,
	addAngle:function(a,add) {
		a=(a+add)%(Math.PI*2);
		if (a<0) return (Math.PI*2)+a; else return a;
	},
	getDistance:function(p1,p2) {
		return Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2))
	},
	getAngle:function(p1,p2,transl) {
		return this.addAngle(Math.atan2(p2.y-p1.y,p2.x-p1.x),(transl?transl:0));
	},
	translate:function(p1,a,d) {
		p1.x=p1.x+Math.cos(a)*d;
		p1.y=p1.y+Math.sin(a)*d;
	},
	translateX:function(x1,a,d) {
		return x1+Math.cos(a)*d
	},
	translateY:function(y1,a,d) {
		return y1+Math.sin(a)*d;
	}
}
<commit_msg>Updated trigo.js with JSDoc style comments<commit_after>// ---
// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
// ---

/**
 * @namespace Trigo module provides some math stuff for moving objects in a
 * direction or following a round path.
 */
var trigo={
	ANGLE_RIGHT:0,
	ANGLE_DOWN:Math.PI*0.5,
	ANGLE_LEFT:Math.PI,
	ANGLE_UP:Math.PI*1.5555555,

 /**
 * Adds two angles together (radians).
 * @param {Float} a Base angle.
 * @param {Float} add The angle you're adding to the base angle.
 * @returns The resultant angle, always between 0 and 2*pi.
 */
	addAngle:function(a,add) {
		a=(a+add)%(Math.PI*2);
		if (a<0) return (Math.PI*2)+a; else return a;
	},
  /**
  * Gets the distance between two points.
  * @param {Object} p1 This is an object containing x and y params for the first point.
  * @param {Object} p2 This is an object containing x and y params for the second point.
  * @returns The distance between p1 and p2.
  */
	getDistance:function(p1,p2) {
		return Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2))
	},

  /**
  * Calculates the angle between two points.
  * @param {Object} p1 This is an object containing x and y params for the first point.
  * @param {Object} p2 This is an object containing x and y params for the second point.
  * @param {Float} transl (Optional) Adds an angle (in radians) to the result. Defaults to 0.
  * @returns The angle between points p1 and p2, plus transl.
  */
	getAngle:function(p1,p2,transl) {
		return this.addAngle(Math.atan2(p2.y-p1.y,p2.x-p1.x),(transl?transl:0));
	},

  /**
  * Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
  * @param {Object} p1 This is an object containing x and y params for the point.
  * @param {Float} a The angle of translation (rad).
  * @param {Float} d The distance of translation.
  */
	translate:function(p1,a,d) {
		p1.x=p1.x+Math.cos(a)*d;
		p1.y=p1.y+Math.sin(a)*d;
	},

  /**
  * Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
  * @param {Float} x1 This is an x coordinate.
  * @param {Float} a The angle of translation (rad).
  * @param {Float} d The distance of translation.
  */
	translateX:function(x1,a,d) {
		return x1+Math.cos(a)*d
	},

  /**
  * Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
  * @param {Float} y1 This is a y coordinate.
  * @param {Float} a The angle of translation (rad).
  * @param {Float} d The distance of translation.
  */
	translateY:function(y1,a,d) {
		return y1+Math.sin(a)*d;
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var Cookies = require("cookies");
var Proxy_ = (typeof Proxy !== 'undefined') ? Proxy : require("node-proxy");
var Handler = require("./ProxyHandler.js");
var crypto = require("crypto");

const COOKIE_NAME_SEP = '=';
const ACTIVE_DURATION = 1000 * 60 * 5;

function base64urlencode(arg) {
  var s = arg.toString('base64');
  s = s.split('=')[0]; // Remove any trailing '='s
  s = s.replace(/\+/g, '-'); // 62nd char of encoding
  s = s.replace(/\//g, '_'); // 63rd char of encoding
  // TODO optimize this; we can do much better
  return s;
}

function base64urldecode(arg) {
  var s = arg;
  s = s.replace(/-/g, '+'); // 62nd char of encoding
  s = s.replace(/_/g, '/'); // 63rd char of encoding
  switch (s.length % 4) // Pad with trailing '='s
  {
  case 0: break; // No pad chars in this case
  case 2: s += "=="; break; // Two pad chars
  case 3: s += "="; break; // One pad char
  default: throw new Error("Illegal base64url string!");
  }
  return new Buffer(s, 'base64'); // Standard base64 decoder
}

function deriveKey(master, type) {
  // eventually we want to use HKDF. For now we'll do something simpler.
  var hmac = crypto.createHmac('sha256', master);
  hmac.update(type);
  return hmac.digest('binary');
}

function constantTimeEquals(a, b) {
  // Ideally this would be a native function, so it's less sensitive to how the
  // JS engine might optimize.
  if (a.length != b.length)
    return false;
  var ret = 0;
  for (var i = 0; i < a.length; i++) {
    ret |= a.readUInt8(i) ^ b.readUInt8(i);
  }
  return ret == 0;
}

function encode(opts, content, duration, createdAt){
    // format will be:
    // iv.ciphertext.createdAt.duration.hmac

    if (!opts.cookieName) {
      throw new Error('cookieName option required');
    } else if (String(opts.cookieName).indexOf(COOKIE_NAME_SEP) != -1) {
      throw new Error('cookieName cannot include "="');
    }

    if (!opts.encryptionKey) {
      opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
    }

    if (!opts.signatureKey) {
      opts['signatureKey'] = deriveKey(opts.secret, 'cookiesession-signature');
    }

    duration = duration || 24*60*60*1000;
    createdAt = createdAt || new Date().getTime();

    // generate iv
    var iv = crypto.randomBytes(16);

    // encrypt with encryption key
    var plaintext = opts.cookieName + COOKIE_NAME_SEP + JSON.stringify(content);
    var cipher = crypto.createCipheriv('aes256', opts.encryptionKey, iv);
    var ciphertext = cipher.update(plaintext, 'utf8', 'binary');
    ciphertext += cipher.final('binary');
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    ciphertext = new Buffer(ciphertext, 'binary');

    // hmac it
    var hmacAlg = crypto.createHmac('sha256', opts.signatureKey);
    hmacAlg.update(iv);
    hmacAlg.update(".");
    hmacAlg.update(ciphertext);
    hmacAlg.update(".");
    hmacAlg.update(createdAt.toString());
    hmacAlg.update(".");
    hmacAlg.update(duration.toString());

    var hmac = hmacAlg.digest();
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    hmac = new Buffer(hmac, 'binary');

    return base64urlencode(iv) + "." + base64urlencode(ciphertext) + "." + createdAt + "." + duration + "." + base64urlencode(hmac);
}

function decode(opts, content) {

    // stop at any time if there's an issue
    var components = content.split(".");
    if (components.length != 5)
      return;

    if (!opts.cookieName) {
      throw new Error("cookieName option required");
    }

    if (!opts.encryptionKey) {
      opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
    }

    if (!opts.signatureKey) {
      opts['signatureKey'] = deriveKey(opts.secret, 'cookiesession-signature');
    }

    var iv = base64urldecode(components[0]);
    var ciphertext = base64urldecode(components[1]);
    var createdAt = parseInt(components[2], 10);
    var duration = parseInt(components[3], 10);
    var hmac = base64urldecode(components[4]);

    // make sure IV is right length
    if (iv.length != 16)
      return;

    // check hmac
    var hmacAlg = crypto.createHmac('sha256', opts.signatureKey);
    hmacAlg.update(iv);
    hmacAlg.update(".");
    hmacAlg.update(ciphertext);
    hmacAlg.update(".");
    hmacAlg.update(createdAt.toString());
    hmacAlg.update(".");
    hmacAlg.update(duration.toString());

    var expected_hmac = hmacAlg.digest();
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    expected_hmac = new Buffer(expected_hmac, 'binary');

    if (!constantTimeEquals(hmac, expected_hmac))
      return;

    // decrypt
    var cipher = crypto.createDecipheriv('aes256', opts.encryptionKey, iv);
    var plaintext = cipher.update(ciphertext, 'binary', 'utf8');
    plaintext += cipher.final('utf8');

    var cookieName = plaintext.substring(0, plaintext.indexOf(COOKIE_NAME_SEP));
    if (cookieName !== opts.cookieName) {
      return;
    }

    try {
      return {
        content: JSON.parse(plaintext.substring(plaintext.indexOf(COOKIE_NAME_SEP) + 1)),
        createdAt: createdAt,
        duration: duration
      };
    } catch (x) {
      return;
    }
}

/*
 * Session object
 *
 * this should be implemented with proxies at some point
 */
function Session(req, res, cookies, opts) {
  this.req = req;
  this.res = res;
  this.cookies = cookies;
  this.opts = opts;
  if (opts.cookie.ephemeral && opts.cookie.maxAge) {
    throw new Error("you cannot have an ephemeral cookie with a maxAge.");
  }

  this.content = {};
  this.loaded = false;
  this.dirty = false;

  // no need to initialize it, loadFromCookie will do
  // via reset() or unbox()
  this.createdAt = null;
  this.duration = opts.duration;
  this.activeDuration = opts.activeDuration;

  // support for maxAge
  if (opts.cookie.maxAge) {
    this.expires = new Date(new Date().getTime() + opts.cookie.maxAge);
  } else {
    this.updateDefaultExpires();
  }

  // here, we check that the security bits are set correctly
  var secure = (res.socket && res.socket.encrypted) || (req.connection && req.connection.proxySecure);
  if (opts.cookie.secure && !secure)
    throw new Error("you cannot have a secure cookie unless the socket is secure or you declare req.connection.proxySecure to be true.");
}

Session.prototype = {
  updateDefaultExpires: function() {
    if (this.opts.cookie.maxAge) return;

    if (this.opts.cookie.ephemeral) {
      this.expires = null;
    } else {
      var time = this.createdAt || new Date().getTime();
      // the cookie should expire when it becomes invalid
      // we add an extra second because the conversion to a date truncates the milliseconds
      this.expires = new Date(time + this.duration + 1000);
    }
  },

  clearContent: function(keysToPreserve) {
    var self = this;
    Object.keys(this.content).forEach(function(k) {
      // exclude this key if it's meant to be preserved
      if (keysToPreserve && (keysToPreserve.indexOf(k) > -1))
        return;

      delete self.content[k];
    });
  },

  reset: function(keysToPreserve) {
    this.clearContent(keysToPreserve);
    this.createdAt = new Date().getTime();
    this.duration = this.opts.duration;
    this.updateDefaultExpires();
    this.dirty = true;
    this.loaded = true;
  },

  setDuration: function(newDuration, ephemeral) {
    if (ephemeral && this.opts.cookie.maxAge) {
      throw new Error("you cannot have an ephemeral cookie with a maxAge.");
    }
    if (!this.loaded)
      this.loadFromCookie(true);
    this.dirty = true;
    this.duration = newDuration;
    this.createdAt = new Date().getTime();
    this.opts.cookie.ephemeral = ephemeral;
    this.updateDefaultExpires();
  },

  // take the content and do the encrypt-and-sign
  // boxing builds in the concept of createdAt
  box: function() {
    return encode(this.opts, this.content, this.duration, this.createdAt);
  },

  unbox: function(content) {
    this.clearContent();

    var unboxed = decode(this.opts, content);
    if (!unboxed) return;

    var self = this;

    Object.keys(unboxed.content).forEach(function(k) {
      self.content[k] = unboxed.content[k];
    });

    this.createdAt = unboxed.createdAt;
    this.duration = unboxed.duration;
    this.updateDefaultExpires();
  },

  updateCookie: function() {
    if (this.dirty) {
      // support for adding/removing cookie expires
      this.opts.cookie.expires = this.expires;

      try {
        this.cookies.set(this.opts.cookieName, this.box(), this.opts.cookie);
      } catch (x) {
        // this really shouldn't happen. Right now it happens if secure is set
        // but cookies can't determine that the connection is secure.
      }
    }
  },

  loadFromCookie: function(force_reset) {
    var cookie = this.cookies.get(this.opts.cookieName);
    if (cookie) {
      this.unbox(cookie);

      var expiresAt = this.createdAt + this.duration;
      var now = Date.now();
      // should we reset this session?
      if (expiresAt < now)
        this.reset();
      // if expiration is soon, push back a few minutes to not interrupt user
      else if (expiresAt - now < this.activeDuration) {
        this.createdAt += this.activeDuration;
        this.dirty = true;
        this.updateDefaultExpires();
      }
    } else {
      if (force_reset) {
        this.reset();
      } else {
        return false; // didn't actually load the cookie
      }
    }

    this.loaded = true;
    return true;
  },

  // called to create a proxy that monitors the session
  // for new properties being set
  monitor: function() {
    // the target for the proxy will be the content
    // variable, to simplify the proxying
    var sessionHandler = new Handler(this.content);
    var raw_session = this;

    // all values from content except special values
    sessionHandler.get = function(rcvr, name) {
      if (['reset'].indexOf(name) > -1) {
        return raw_session[name].bind(raw_session);
      } else if (['setDuration'].indexOf(name) > -1) {
        return raw_session[name].bind(raw_session);
      } else {
        if (!raw_session.loaded) {
          var didLoad = raw_session.loadFromCookie();
          if (!didLoad) return undefined;
        }
        return this.target[name];
      }
    };

    // set all values to content
    sessionHandler.set = function(rcvr, name, value) {
      // we have to load existing content, otherwise it will later override
      // the content that is written.
      if (!raw_session.loaded)
        raw_session.loadFromCookie(true);

      this.target[name] = value;
      raw_session.dirty = true;
    };

    // if key is deleted
    sessionHandler.delete = function(name) {
      // we have to load existing content, otherwise it will later override
      // the content that is written.
      if (!raw_session.loaded) {
        var didLoad = raw_session.loadFromCookie();
        if (!didLoad) return;
      }

      delete this.target[name];
      raw_session.dirty = true;
    };

    var proxySession = Proxy_.create(sessionHandler);
    return proxySession;
  }
};

var cookieSession = function(opts) {
  if (!opts)
    throw "no options provided, some are required"; // XXX rename opts?

  if (!opts.secret)
    throw "cannot set up sessions without a secret";

  // defaults
  opts.cookieName = opts.cookieName || "session_state";
  opts.duration = opts.duration || 24*60*60*1000;
  opts.activeDuration = 'activeDuration' in opts ? opts.activeDuration : ACTIVE_DURATION;

  // set up cookie defaults
  opts.cookie = opts.cookie || {};
  if (typeof(opts.cookie.httpOnly) == 'undefined')
    opts.cookie.httpOnly = true;

  // let's not default to secure just yet,
  // as this depends on the socket being secure,
  // which is tricky to determine if proxied.
  /*
  if (typeof(opts.cookie.secure) == 'undefined')
    opts.cookie.secure = true;
    */

  // derive two keys, one for signing one for encrypting, from the secret.
  opts.encryptionKey = deriveKey(opts.secret, 'cookiesession-encryption');
  opts.signatureKey = deriveKey(opts.secret, 'cookiesession-signature');

  const propertyName = opts.requestKey || opts.cookieName;

  return function(req, res, next) {
    if (req[propertyName]) {
      return next(); //self aware
    }

    var cookies = new Cookies(req, res);
    var raw_session;
    try {
      raw_session = new Session(req, res, cookies, opts);
    } catch (x) {
      // this happens only if there's a big problem
      process.nextTick(function() {next("client-sessions error: " + x.toString());});
      return;
    }

    req[propertyName] = raw_session.monitor();

    var writeHead = res.writeHead.bind(res)
    res.writeHead = function () {
      raw_session.updateCookie();
      return writeHead.apply(res, arguments)
    }

    next();
  };
};

module.exports = cookieSession;

// Expose encode and decode method

module.exports.util = {
  encode: encode,
  decode: decode
};
<commit_msg>Remove uncessary bind, add semicolons for style.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var Cookies = require("cookies");
var Proxy_ = (typeof Proxy !== 'undefined') ? Proxy : require("node-proxy");
var Handler = require("./ProxyHandler.js");
var crypto = require("crypto");

const COOKIE_NAME_SEP = '=';
const ACTIVE_DURATION = 1000 * 60 * 5;

function base64urlencode(arg) {
  var s = arg.toString('base64');
  s = s.split('=')[0]; // Remove any trailing '='s
  s = s.replace(/\+/g, '-'); // 62nd char of encoding
  s = s.replace(/\//g, '_'); // 63rd char of encoding
  // TODO optimize this; we can do much better
  return s;
}

function base64urldecode(arg) {
  var s = arg;
  s = s.replace(/-/g, '+'); // 62nd char of encoding
  s = s.replace(/_/g, '/'); // 63rd char of encoding
  switch (s.length % 4) // Pad with trailing '='s
  {
  case 0: break; // No pad chars in this case
  case 2: s += "=="; break; // Two pad chars
  case 3: s += "="; break; // One pad char
  default: throw new Error("Illegal base64url string!");
  }
  return new Buffer(s, 'base64'); // Standard base64 decoder
}

function deriveKey(master, type) {
  // eventually we want to use HKDF. For now we'll do something simpler.
  var hmac = crypto.createHmac('sha256', master);
  hmac.update(type);
  return hmac.digest('binary');
}

function constantTimeEquals(a, b) {
  // Ideally this would be a native function, so it's less sensitive to how the
  // JS engine might optimize.
  if (a.length != b.length)
    return false;
  var ret = 0;
  for (var i = 0; i < a.length; i++) {
    ret |= a.readUInt8(i) ^ b.readUInt8(i);
  }
  return ret == 0;
}

function encode(opts, content, duration, createdAt){
    // format will be:
    // iv.ciphertext.createdAt.duration.hmac

    if (!opts.cookieName) {
      throw new Error('cookieName option required');
    } else if (String(opts.cookieName).indexOf(COOKIE_NAME_SEP) != -1) {
      throw new Error('cookieName cannot include "="');
    }

    if (!opts.encryptionKey) {
      opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
    }

    if (!opts.signatureKey) {
      opts['signatureKey'] = deriveKey(opts.secret, 'cookiesession-signature');
    }

    duration = duration || 24*60*60*1000;
    createdAt = createdAt || new Date().getTime();

    // generate iv
    var iv = crypto.randomBytes(16);

    // encrypt with encryption key
    var plaintext = opts.cookieName + COOKIE_NAME_SEP + JSON.stringify(content);
    var cipher = crypto.createCipheriv('aes256', opts.encryptionKey, iv);
    var ciphertext = cipher.update(plaintext, 'utf8', 'binary');
    ciphertext += cipher.final('binary');
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    ciphertext = new Buffer(ciphertext, 'binary');

    // hmac it
    var hmacAlg = crypto.createHmac('sha256', opts.signatureKey);
    hmacAlg.update(iv);
    hmacAlg.update(".");
    hmacAlg.update(ciphertext);
    hmacAlg.update(".");
    hmacAlg.update(createdAt.toString());
    hmacAlg.update(".");
    hmacAlg.update(duration.toString());

    var hmac = hmacAlg.digest();
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    hmac = new Buffer(hmac, 'binary');

    return base64urlencode(iv) + "." + base64urlencode(ciphertext) + "." + createdAt + "." + duration + "." + base64urlencode(hmac);
}

function decode(opts, content) {

    // stop at any time if there's an issue
    var components = content.split(".");
    if (components.length != 5)
      return;

    if (!opts.cookieName) {
      throw new Error("cookieName option required");
    }

    if (!opts.encryptionKey) {
      opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
    }

    if (!opts.signatureKey) {
      opts['signatureKey'] = deriveKey(opts.secret, 'cookiesession-signature');
    }

    var iv = base64urldecode(components[0]);
    var ciphertext = base64urldecode(components[1]);
    var createdAt = parseInt(components[2], 10);
    var duration = parseInt(components[3], 10);
    var hmac = base64urldecode(components[4]);

    // make sure IV is right length
    if (iv.length != 16)
      return;

    // check hmac
    var hmacAlg = crypto.createHmac('sha256', opts.signatureKey);
    hmacAlg.update(iv);
    hmacAlg.update(".");
    hmacAlg.update(ciphertext);
    hmacAlg.update(".");
    hmacAlg.update(createdAt.toString());
    hmacAlg.update(".");
    hmacAlg.update(duration.toString());

    var expected_hmac = hmacAlg.digest();
    // Before 0.10, crypto returns binary-encoded strings. Remove when
    // dropping 0.8 support.
    expected_hmac = new Buffer(expected_hmac, 'binary');

    if (!constantTimeEquals(hmac, expected_hmac))
      return;

    // decrypt
    var cipher = crypto.createDecipheriv('aes256', opts.encryptionKey, iv);
    var plaintext = cipher.update(ciphertext, 'binary', 'utf8');
    plaintext += cipher.final('utf8');

    var cookieName = plaintext.substring(0, plaintext.indexOf(COOKIE_NAME_SEP));
    if (cookieName !== opts.cookieName) {
      return;
    }

    try {
      return {
        content: JSON.parse(plaintext.substring(plaintext.indexOf(COOKIE_NAME_SEP) + 1)),
        createdAt: createdAt,
        duration: duration
      };
    } catch (x) {
      return;
    }
}

/*
 * Session object
 *
 * this should be implemented with proxies at some point
 */
function Session(req, res, cookies, opts) {
  this.req = req;
  this.res = res;
  this.cookies = cookies;
  this.opts = opts;
  if (opts.cookie.ephemeral && opts.cookie.maxAge) {
    throw new Error("you cannot have an ephemeral cookie with a maxAge.");
  }

  this.content = {};
  this.loaded = false;
  this.dirty = false;

  // no need to initialize it, loadFromCookie will do
  // via reset() or unbox()
  this.createdAt = null;
  this.duration = opts.duration;
  this.activeDuration = opts.activeDuration;

  // support for maxAge
  if (opts.cookie.maxAge) {
    this.expires = new Date(new Date().getTime() + opts.cookie.maxAge);
  } else {
    this.updateDefaultExpires();
  }

  // here, we check that the security bits are set correctly
  var secure = (res.socket && res.socket.encrypted) || (req.connection && req.connection.proxySecure);
  if (opts.cookie.secure && !secure)
    throw new Error("you cannot have a secure cookie unless the socket is secure or you declare req.connection.proxySecure to be true.");
}

Session.prototype = {
  updateDefaultExpires: function() {
    if (this.opts.cookie.maxAge) return;

    if (this.opts.cookie.ephemeral) {
      this.expires = null;
    } else {
      var time = this.createdAt || new Date().getTime();
      // the cookie should expire when it becomes invalid
      // we add an extra second because the conversion to a date truncates the milliseconds
      this.expires = new Date(time + this.duration + 1000);
    }
  },

  clearContent: function(keysToPreserve) {
    var self = this;
    Object.keys(this.content).forEach(function(k) {
      // exclude this key if it's meant to be preserved
      if (keysToPreserve && (keysToPreserve.indexOf(k) > -1))
        return;

      delete self.content[k];
    });
  },

  reset: function(keysToPreserve) {
    this.clearContent(keysToPreserve);
    this.createdAt = new Date().getTime();
    this.duration = this.opts.duration;
    this.updateDefaultExpires();
    this.dirty = true;
    this.loaded = true;
  },

  setDuration: function(newDuration, ephemeral) {
    if (ephemeral && this.opts.cookie.maxAge) {
      throw new Error("you cannot have an ephemeral cookie with a maxAge.");
    }
    if (!this.loaded)
      this.loadFromCookie(true);
    this.dirty = true;
    this.duration = newDuration;
    this.createdAt = new Date().getTime();
    this.opts.cookie.ephemeral = ephemeral;
    this.updateDefaultExpires();
  },

  // take the content and do the encrypt-and-sign
  // boxing builds in the concept of createdAt
  box: function() {
    return encode(this.opts, this.content, this.duration, this.createdAt);
  },

  unbox: function(content) {
    this.clearContent();

    var unboxed = decode(this.opts, content);
    if (!unboxed) return;

    var self = this;

    Object.keys(unboxed.content).forEach(function(k) {
      self.content[k] = unboxed.content[k];
    });

    this.createdAt = unboxed.createdAt;
    this.duration = unboxed.duration;
    this.updateDefaultExpires();
  },

  updateCookie: function() {
    if (this.dirty) {
      // support for adding/removing cookie expires
      this.opts.cookie.expires = this.expires;

      try {
        this.cookies.set(this.opts.cookieName, this.box(), this.opts.cookie);
      } catch (x) {
        // this really shouldn't happen. Right now it happens if secure is set
        // but cookies can't determine that the connection is secure.
      }
    }
  },

  loadFromCookie: function(force_reset) {
    var cookie = this.cookies.get(this.opts.cookieName);
    if (cookie) {
      this.unbox(cookie);

      var expiresAt = this.createdAt + this.duration;
      var now = Date.now();
      // should we reset this session?
      if (expiresAt < now)
        this.reset();
      // if expiration is soon, push back a few minutes to not interrupt user
      else if (expiresAt - now < this.activeDuration) {
        this.createdAt += this.activeDuration;
        this.dirty = true;
        this.updateDefaultExpires();
      }
    } else {
      if (force_reset) {
        this.reset();
      } else {
        return false; // didn't actually load the cookie
      }
    }

    this.loaded = true;
    return true;
  },

  // called to create a proxy that monitors the session
  // for new properties being set
  monitor: function() {
    // the target for the proxy will be the content
    // variable, to simplify the proxying
    var sessionHandler = new Handler(this.content);
    var raw_session = this;

    // all values from content except special values
    sessionHandler.get = function(rcvr, name) {
      if (['reset'].indexOf(name) > -1) {
        return raw_session[name].bind(raw_session);
      } else if (['setDuration'].indexOf(name) > -1) {
        return raw_session[name].bind(raw_session);
      } else {
        if (!raw_session.loaded) {
          var didLoad = raw_session.loadFromCookie();
          if (!didLoad) return undefined;
        }
        return this.target[name];
      }
    };

    // set all values to content
    sessionHandler.set = function(rcvr, name, value) {
      // we have to load existing content, otherwise it will later override
      // the content that is written.
      if (!raw_session.loaded)
        raw_session.loadFromCookie(true);

      this.target[name] = value;
      raw_session.dirty = true;
    };

    // if key is deleted
    sessionHandler.delete = function(name) {
      // we have to load existing content, otherwise it will later override
      // the content that is written.
      if (!raw_session.loaded) {
        var didLoad = raw_session.loadFromCookie();
        if (!didLoad) return;
      }

      delete this.target[name];
      raw_session.dirty = true;
    };

    var proxySession = Proxy_.create(sessionHandler);
    return proxySession;
  }
};

var cookieSession = function(opts) {
  if (!opts)
    throw "no options provided, some are required"; // XXX rename opts?

  if (!opts.secret)
    throw "cannot set up sessions without a secret";

  // defaults
  opts.cookieName = opts.cookieName || "session_state";
  opts.duration = opts.duration || 24*60*60*1000;
  opts.activeDuration = 'activeDuration' in opts ? opts.activeDuration : ACTIVE_DURATION;

  // set up cookie defaults
  opts.cookie = opts.cookie || {};
  if (typeof(opts.cookie.httpOnly) == 'undefined')
    opts.cookie.httpOnly = true;

  // let's not default to secure just yet,
  // as this depends on the socket being secure,
  // which is tricky to determine if proxied.
  /*
  if (typeof(opts.cookie.secure) == 'undefined')
    opts.cookie.secure = true;
    */

  // derive two keys, one for signing one for encrypting, from the secret.
  opts.encryptionKey = deriveKey(opts.secret, 'cookiesession-encryption');
  opts.signatureKey = deriveKey(opts.secret, 'cookiesession-signature');

  const propertyName = opts.requestKey || opts.cookieName;

  return function(req, res, next) {
    if (req[propertyName]) {
      return next(); //self aware
    }

    var cookies = new Cookies(req, res);
    var raw_session;
    try {
      raw_session = new Session(req, res, cookies, opts);
    } catch (x) {
      // this happens only if there's a big problem
      process.nextTick(function() {next("client-sessions error: " + x.toString());});
      return;
    }

    req[propertyName] = raw_session.monitor();

    var writeHead = res.writeHead;
    res.writeHead = function () {
      raw_session.updateCookie();
      return writeHead.apply(res, arguments);
    }

    next();
  };
};

module.exports = cookieSession;

// Expose encode and decode method

module.exports.util = {
  encode: encode,
  decode: decode
};
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

function compact(obj) {
  return _.transform(obj, function(result, val, key) {
    if (val) {
      result[key] = val;
    }
  });
}

function Response(payload) {
  this.content = payload.content;
  this.result = this.content.result;
  this.headers = payload.properties.headers;
  this.properties = compact(_.omit(payload.properties, 'headers'));
}

module.exports = Response;
<commit_msg>client response decodes data from a buffer<commit_after>'use strict';

var _ = require('lodash');

function compact(obj) {
  return _.transform(obj, function(result, val, key) {
    if (val) {
      result[key] = val;
    }
  });
}

function Response(payload) {
  this.content = payload.content;

  if (Buffer.isBuffer(this.content)) {
    this.content = this.content.toString('utf8');

    try {
      this.content = JSON.parse(this.content);
    } catch (err) {}
  }

  this.result = this.content.result;
  this.headers = payload.properties.headers;
  this.properties = compact(_.omit(payload.properties, 'headers'));
}

module.exports = Response;
<|endoftext|>


<commit_before>var CloudCmd, Util, DOM, CloudFunc, Terminal;
(function(CloudCmd, Util, DOM, CloudFunc) {
    'use strict';

    CloudCmd.Terminal = TerminalProto;

    function TerminalProto(CallBack) {
        var Name            = 'Terminal',
            Loading,
            Element,
            MouseBinded,
            Term,
            Cell,
            Key             = CloudCmd.Key,
            ESC             = Key.ESC,
            Images          = DOM.Images,
            Notify          = DOM.Notify,

            Size            = {
                cols: 0,
                rows: 0
            },

            CHANNEL         = CloudFunc.CHANNEL_TERMINAL,
            CHANNEL_RESIZE  = CloudFunc.CHANNEL_TERMINAL_RESIZE,

            CloudTerm       = this;

        function init() {
            Loading = true;

            Util.loadOnLoad([
                DOM.jqueryLoad,
                CloudCmd.View,
                /* rm view keys, it ruin terminal */
                function(callback) {
                    CloudCmd.View.rmKeys(),
                    Util.exec(callback);
                },
                load,
                CloudCmd.Socket,
                CloudTerm.show,
                addListeners
            ]);
        }

        CloudTerm.show      = show;
        CloudTerm.write     = write;

        function show(callback) {
            var options = {
                    onUpdate: onResize,
                },

                socket  = CloudCmd.Socket;

            if (!Loading) {
                Images.showLoad({top:true});

                if (!Element) {
                    Element         = DOM.anyload({
                        name    : 'div',
                        id      : 'terminal',
                        style   : 'height :100%'
                    });

                   /* hack to determine console size
                     * inspired with
                     *
                     * https://github.com/petethepig/devtools-terminal
                     */
                    Cell            = DOM.anyload({
                        name    : 'div',
                        inner   : '&nbsp',
                        parent  : Element,
                        style   : 'position: absolute;' +
                                  'top      : -1000px;'
                    });

                    DOM.cssSet({
                        id      : 'terminal-css',
                        inner   : '#terminal, .terminal, #view {'   +
                                    'height'            + ': 100%;' +
                                '}'                                 +
                                  '.terminal-cursor {'              +
                                    'background'        + ': gray'  +
                                '}'
                    });

                    Term    = new Terminal({
                        screenKeys: true,
                        cursorBlink: false,
                    });

                    Term.open(Element);
                }

                CloudCmd.View.show(Element, function() {
                    Element.focus();
                    Terminal.brokenBold = true;

                    Util.exec(callback);
                }, options);
            }
        }

        function write(data) {
            Term.write(data);
        }

        function addListeners(callback) {
            var socket  = CloudCmd.Socket;

            socket.on(CHANNEL, write);

            Term.on('data', function(data) {
                socket.emit(CHANNEL, data);
            });

            Term.on('resize', function(size) {
                socket.emit(CHANNEL_RESIZE, size);
            });

            Util.exec(callback);
        }

        function getSize() {
            var wSubs   = Element.offsetWidth - Element.clientWidth,
                w       = Element.clientWidth - wSubs,

                hSubs   = Element.offsetHeight - Element.clientHeight,
                h       = Element.clientHeight - hSubs,

                x       = Cell.clientWidth,
                y       = Cell.clientHeight,

                cols    = Math.max(Math.floor(w / x), 10),
                rows    = Math.max(Math.floor(h / y), 10),

                size    = {
                    cols: cols,
                    rows: rows
                };

            return size;
        }

        function onResize() {
            var size    = getSize(),
                cols    = size.cols,
                rows    = size.rows;

            if (Size.cols !== cols || Size.rows !== rows) {
                Size = size;

                Term.resize(size.cols, size.rows);
                Term.emit('resize', size);
            }
        }

        function load(pCallBack) {
            var dir     =  CloudCmd.LIBDIRCLIENT + 'terminal/',
                path    = dir + 'term.js';

            Util.time(Name + ' load');

            DOM.jsload(path, function() {
                Util.timeEnd(Name + ' load');
                Loading = false;

                Util.exec(pCallBack);
            });
        }

        init();
    }

})(CloudCmd, Util, DOM, CloudFunc);
<commit_msg>feature(terminal) add connect/disconnect events<commit_after>var CloudCmd, Util, DOM, CloudFunc, Terminal;
(function(CloudCmd, Util, DOM, CloudFunc) {
    'use strict';

    CloudCmd.Terminal = TerminalProto;

    function TerminalProto(CallBack) {
        var Name            = 'Terminal',
            Loading,
            Element,
            MouseBinded,
            Term,
            Cell,
            Key             = CloudCmd.Key,
            ESC             = Key.ESC,
            Images          = DOM.Images,
            Notify          = DOM.Notify,

            Size            = {
                cols: 0,
                rows: 0
            },

            CHANNEL         = CloudFunc.CHANNEL_TERMINAL,
            CHANNEL_RESIZE  = CloudFunc.CHANNEL_TERMINAL_RESIZE,

            CloudTerm       = this;

        function init() {
            Loading = true;

            Util.loadOnLoad([
                DOM.jqueryLoad,
                CloudCmd.View,
                /* rm view keys, it ruin terminal */
                function(callback) {
                    CloudCmd.View.rmKeys(),
                    Util.exec(callback);
                },
                load,
                CloudCmd.Socket,
                CloudTerm.show,
                addListeners
            ]);
        }

        CloudTerm.show      = show;
        CloudTerm.write     = write;

        function show(callback) {
            var options = {
                    onUpdate: onResize,
                },

                socket  = CloudCmd.Socket;

            if (!Loading) {
                Images.showLoad({top:true});

                if (!Element) {
                    Element         = DOM.anyload({
                        name    : 'div',
                        id      : 'terminal',
                        style   : 'height :100%'
                    });

                   /* hack to determine console size
                     * inspired with
                     *
                     * https://github.com/petethepig/devtools-terminal
                     */
                    Cell            = DOM.anyload({
                        name    : 'div',
                        inner   : '&nbsp',
                        parent  : Element,
                        style   : 'position: absolute;' +
                                  'top      : -1000px;'
                    });

                    DOM.cssSet({
                        id      : 'terminal-css',
                        inner   : '#terminal, .terminal, #view {'   +
                                    'height'            + ': 100%;' +
                                '}'                                 +
                                  '.terminal-cursor {'              +
                                    'background'        + ': gray'  +
                                '}'
                    });

                    Term    = new Terminal({
                        screenKeys: true,
                        cursorBlink: false,
                    });

                    Term.open(Element);
                }

                CloudCmd.View.show(Element, function() {
                    Element.focus();
                    Terminal.brokenBold = true;

                    Util.exec(callback);
                }, options);
            }
        }

        function write(data) {
            Term.write(data);
        }

        function addListeners(callback) {
            var socket  = CloudCmd.Socket,
                options = {
                    'connect': function() {
                        write(socket.CONNECTED + '\r');
                    },
                    'disconnect': function() {
                        write(socket.DISCONNECTED +'\r');
                    },
                };

            options[CHANNEL] = write;

            socket.on(options);

            Term.on('data', function(data) {
                socket.emit(CHANNEL, data);
            });

            Term.on('resize', function(size) {
                socket.emit(CHANNEL_RESIZE, size);
            });

            Util.exec(callback);
        }

        function getSize() {
            var wSubs   = Element.offsetWidth - Element.clientWidth,
                w       = Element.clientWidth - wSubs,

                hSubs   = Element.offsetHeight - Element.clientHeight,
                h       = Element.clientHeight - hSubs,

                x       = Cell.clientWidth,
                y       = Cell.clientHeight,

                cols    = Math.max(Math.floor(w / x), 10),
                rows    = Math.max(Math.floor(h / y), 10),

                size    = {
                    cols: cols,
                    rows: rows
                };

            return size;
        }

        function onResize() {
            var size    = getSize(),
                cols    = size.cols,
                rows    = size.rows;

            if (Size.cols !== cols || Size.rows !== rows) {
                Size = size;

                Term.resize(size.cols, size.rows);
                Term.emit('resize', size);
            }
        }

        function load(pCallBack) {
            var dir     =  CloudCmd.LIBDIRCLIENT + 'terminal/',
                path    = dir + 'term.js';

            Util.time(Name + ' load');

            DOM.jsload(path, function() {
                Util.timeEnd(Name + ' load');
                Loading = false;

                Util.exec(pCallBack);
            });
        }

        init();
    }

})(CloudCmd, Util, DOM, CloudFunc);
<|endoftext|>


<commit_before>import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
import { htmlDecode } from '@vue-storefront/core/filters/html-decode'
import Composite from '@vue-storefront/core/mixins/composite'

export default {
  name: 'CmsPage',
  mixins: [Composite],
  computed: {
    pageTitle () {
      return this.$store.state.cms_page.current ? this.$store.state.cms_page.current.title : ''
    }
  },
  watch: {
    '$route': 'validateRoute'
  },
  asyncData ({ store, route, context }) { // this is for SSR purposes to prefetch data
    return new Promise((resolve, reject) => {
      if (context) context.output.cacheTags.add(`cmsPage`)
      store.dispatch('cms_page/single', {
        value: route.params.slug
      }).then(res => {
        if (res) {
          store.state.cms_page.current = res
          EventBus.$emitFilter('cms_page-after-load', { store: store, route: route }).then(results => {
            return resolve()
          }).catch(err => {
            console.error(err)
            reject(err)
          })
        }
      }).catch(err => {
        console.error(err)
        reject(err)
      })
    })
  },
  methods: {
    validateRoute () {
      this.$store.dispatch('cms_page/single', { key: 'slug', value: this.$route.params.slug }).then(cmsPage => {
        if (!cmsPage) {
          this.$router.push('/')
        } else {
          this.$bus.$emit('cms_page-changed', this.$store.state.cms_page.current_path)
          EventBus.$emitFilter('cms_page-after-load', { store: this.$store, route: this.$route })
        }
      })
    }
  },
  metaInfo () {
    return {
      title: htmlDecode(this.pageTitle || this.$route.meta.title),
      meta: this.$route.meta.description ? [{ vmid: 'description', description: htmlDecode(this.$route.meta.description) }] : []
    }
  }
}
<commit_msg>Remove using deprecated EventBus<commit_after>import { htmlDecode } from '@vue-storefront/core/filters/html-decode'
import Composite from '@vue-storefront/core/mixins/composite'

export default {
  name: 'CmsPage',
  mixins: [Composite],
  computed: {
    pageTitle () {
      return this.$store.state.cms_page.current ? this.$store.state.cms_page.current.title : ''
    }
  },
  watch: {
    '$route': 'validateRoute'
  },
  asyncData ({ store, route, context }) { // this is for SSR purposes to prefetch data
    return new Promise((resolve, reject) => {
      if (context) context.output.cacheTags.add(`cmsPage`)
      store.dispatch('cms_page/single', {
        value: route.params.slug
      }).then(res => {
        if (res) {
          store.state.cms_page.current = res
          return resolve(res)
        }
      }).catch(err => {
        console.error(err)
        reject(err)
      })
    })
  },
  methods: {
    validateRoute () {
      this.$store.dispatch('cms_page/single', { key: 'slug', value: this.$route.params.slug }).then(cmsPage => {
        if (!cmsPage) {
          this.$router.push('/')
        }
      })
    }
  },
  metaInfo () {
    return {
      title: htmlDecode(this.pageTitle || this.$route.meta.title),
      meta: this.$route.meta.description ? [{ vmid: 'description', description: htmlDecode(this.$route.meta.description) }] : []
    }
  }
}
<|endoftext|>