text: /* Riot v2.2.2, @license MIT, (c) 2015 Muut Inc. + contributors */
  
  ;(function(window, undefined) {
    'use strict'
    var riot = { version: 'v2.2.2', settings: {} }
  
    // This globals 'const' helps code size reduction
  
    // for typeof == '' comparisons
    var T_STRING = 'string',
        T_OBJECT = 'object',
        T_UNDEF  = 'undefined'
  
    // for IE8 and rest of the world
    /* istanbul ignore next */
    var isArray = Array.isArray || (function () {
      var _ts = Object.prototype.toString
      return function (v) { return _ts.call(v) === '[object Array]' }
    })()
  
    // Version# for IE 8-11, 0 for others
    var ieVersion = (function (win) {
      return (window && window.document || {}).documentMode | 0
    })()
  
  riot.observable = function(el) {
  
    el = el || {}
  
    var callbacks = {},
        _id = 0
  
    el.on = function(events, fn) {
      if (isFunction(fn)) {
        if (typeof fn.id === T_UNDEF) fn._id = _id++
  
        events.replace(/\S+/g, function(name, pos) {
          (callbacks[name] = callbacks[name] || []).push(fn)
          fn.typed = pos > 0
        })
      }
      return el
    }
  
    el.off = function(events, fn) {
      if (events == '*') callbacks = {}
      else {
        events.replace(/\S+/g, function(name) {
          if (fn) {
            var arr = callbacks[name]
            for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
              if (cb._id == fn._id) arr.splice(i--, 1)
            }
          } else {
            callbacks[name] = []
          }
        })
      }
      return el
    }
  
    // only single event supported
    el.one = function(name, fn) {
      function on() {
        el.off(name, on)
        fn.apply(el, arguments)
      }
      return el.on(name, on)
    }
  
    el.trigger = function(name) {
      var args = [].slice.call(arguments, 1),
          fns = callbacks[name] || []
  
      for (var i = 0, fn; (fn = fns[i]); ++i) {
        if (!fn.busy) {
          fn.busy = 1
          fn.apply(el, fn.typed ? [name].concat(args) : args)
          if (fns[i] !== fn) { i-- }
          fn.busy = 0
        }
      }
  
      if (callbacks.all && name != 'all') {
        el.trigger.apply(el, ['all', name].concat(args))
      }
  
      return el
    }
  
    return el
  
  }
  riot.mixin = (function() {
    var mixins = {}
  
    return function(name, mixin) {
      if (!mixin) return mixins[name]
      mixins[name] = mixin
    }
  
  })()
  
  ;(function(riot, evt, win) {
  
    // browsers only
    if (!win) return
  
    var loc = win.location,
        fns = riot.observable(),
        started = false,
        current
  
    function hash() {
      return loc.href.split('#')[1] || ''
    }
  
    function parser(path) {
      return path.split('/')
    }
  
    function emit(path) {
      if (path.type) path = hash()
  
      if (path != current) {
        fns.trigger.apply(null, ['H'].concat(parser(path)))
        current = path
      }
    }
  
    var r = riot.route = function(arg) {
      // string
      if (arg[0]) {
        loc.hash = arg
        emit(arg)
  
      // function
      } else {
        fns.on('H', arg)
      }
    }
  
    r.exec = function(fn) {
      fn.apply(null, parser(hash()))
    }
  
    r.parser = function(fn) {
      parser = fn
    }
  
    r.stop = function () {
      if (!started) return
      win.removeEventListener ? win.removeEventListener(evt, emit, false) : win.detachEvent('on' + evt, emit)
      fns.off('*')
      started = false
    }
  
    r.start = function () {
      if (started) return
      win.addEventListener ? win.addEventListener(evt, emit, false) : win.attachEvent('on' + evt, emit)
      started = true
    }
  
    // autostart the router
    r.start()
  
  })(riot, 'hashchange', window)
  /*
  
  //// How it works?
  
  
  Three ways:
  
  1. Expressions: tmpl('{ value }', data).
     Returns the result of evaluated expression as a raw object.
  
  2. Templates: tmpl('Hi { name } { surname }', data).
     Returns a string with evaluated expressions.
  
  3. Filters: tmpl('{ show: !done, highlight: active }', data).
     Returns a space separated list of trueish keys (mainly
     used for setting html classes), e.g. "show highlight".
  
  
  // Template examples
  
  tmpl('{ title || "Untitled" }', data)
  tmpl('Results are { results ? "ready" : "loading" }', data)
  tmpl('Today is { new Date() }', data)
  tmpl('{ message.length > 140 && "Message is too long" }', data)
  tmpl('This item got { Math.round(rating) } stars', data)
  tmpl('<h1>{ title }</h1>{ body }', data)
  
  
  // Falsy expressions in templates
  
  In templates (as opposed to single expressions) all falsy values
  except zero (undefined/null/false) will default to empty string:
  
  tmpl('{ undefined } - { false } - { null } - { 0 }', {})
  // will return: " - - - 0"
  
  */
  
  
  var brackets = (function(orig) {
  
    var cachedBrackets,
        r,
        b,
        re = /[{}]/g
  
    return function(x) {
  
      // make sure we use the current setting
      var s = riot.settings.brackets || orig
  
      // recreate cached vars if needed
      if (cachedBrackets !== s) {
        cachedBrackets = s
        b = s.split(' ')
        r = b.map(function (e) { return e.replace(/(?=.)/g, '\\') })
      }
  
      // if regexp given, rewrite it with current brackets (only if differ from default)
      return x instanceof RegExp ? (
          s === orig ? x :
          new RegExp(x.source.replace(re, function(b) { return r[~~(b === '}')] }), x.global ? 'g' : '')
        ) :
        // else, get specific bracket
        b[x]
    }
  })('{ }')
  
  
  var tmpl = (function() {
  
    var cache = {},
        reVars = /(['"\/]).*?[^\\]\1|\.\w*|\w*:|\b(?:(?:new|typeof|in|instanceof) |(?:this|true|false|null|undefined)\b|function *\()|([a-z_$]\w*)/gi
                // [ 1               ][ 2  ][ 3 ][ 4                                                                                  ][ 5       ]
                // find variable names:
                // 1. skip quoted strings and regexps: "a b", 'a b', 'a \'b\'', /a b/
                // 2. skip object properties: .name
                // 3. skip object literals: name:
                // 4. skip javascript keywords
                // 5. match var name
  
    // build a template (or get it from cache), render with data
    return function(str, data) {
      return str && (cache[str] = cache[str] || tmpl(str))(data)
    }
  
  
    // create a template instance
  
    function tmpl(s, p) {
  
      // default template string to {}
      s = (s || (brackets(0) + brackets(1)))
  
        // temporarily convert \{ and \} to a non-character
        .replace(brackets(/\\{/g), '\uFFF0')
        .replace(brackets(/\\}/g), '\uFFF1')
  
      // split string to expression and non-expresion parts
      p = split(s, extract(s, brackets(/{/), brackets(/}/)))
  
      return new Function('d', 'return ' + (
  
        // is it a single expression or a template? i.e. {x} or <b>{x}</b>
        !p[0] && !p[2] && !p[3]
  
          // if expression, evaluate it
          ? expr(p[1])
  
          // if template, evaluate all expressions in it
          : '[' + p.map(function(s, i) {
  
              // is it an expression or a string (every second part is an expression)
            return i % 2
  
                // evaluate the expressions
                ? expr(s, true)
  
                // process string parts of the template:
                : '"' + s
  
                    // preserve new lines
                    .replace(/\n/g, '\\n')
  
                    // escape quotes
                    .replace(/"/g, '\\"')
  
                  + '"'
  
          }).join(',') + '].join("")'
        )
  
        // bring escaped { and } back
        .replace(/\uFFF0/g, brackets(0))
        .replace(/\uFFF1/g, brackets(1))
  
      + ';')
  
    }
  
  
    // parse { ... } expression
  
    function expr(s, n) {
      s = s
  
        // convert new lines to spaces
        .replace(/\n/g, ' ')
  
        // trim whitespace, brackets, strip comments
        .replace(brackets(/^[{ ]+|[ }]+$|\/\*.+?\*\//g), '')
  
      // is it an object literal? i.e. { key : value }
      return /^\s*[\w- "']+ *:/.test(s)
  
        // if object literal, return trueish keys
        // e.g.: { show: isOpen(), done: item.done } -> "show done"
        ? '[' +
  
            // extract key:val pairs, ignoring any nested objects
            extract(s,
  
                // name part: name:, "name":, 'name':, name :
                /["' ]*[\w- ]+["' ]*:/,
  
                // expression part: everything upto a comma followed by a name (see above) or end of line
                /,(?=["' ]*[\w- ]+["' ]*:)|}|$/
                ).map(function(pair) {
  
                  // get key, val parts
                  return pair.replace(/^[ "']*(.+?)[ "']*: *(.+?),? *$/, function(_, k, v) {
  
                    // wrap all conditional parts to ignore errors
                    return v.replace(/[^&|=!><]+/g, wrap) + '?"' + k + '":"",'
  
                  })
  
                }).join('')
  
          + '].join(" ").trim()'
  
        // if js expression, evaluate as javascript
        : wrap(s, n)
  
    }
  
  
    // execute js w/o breaking on errors or undefined vars
  
    function wrap(s, nonull) {
      s = s.trim()
      return !s ? '' : '(function(v){try{v='
  
          // prefix vars (name => data.name)
          + (s.replace(reVars, function(s, _, v) { return v ? '(d.'+v+'===undefined?'+(typeof window == 'undefined' ? 'global.' : 'window.')+v+':d.'+v+')' : s })
  
            // break the expression if its empty (resulting in undefined value)
            || 'x')
        + '}catch(e){'
        + '}finally{return '
  
          // default to empty string for falsy values except zero
          + (nonull === true ? '!v&&v!==0?"":v' : 'v')
  
        + '}}).call(d)'
    }
  
  
    // split string by an array of substrings
  
    function split(str, substrings) {
      var parts = []
      substrings.map(function(sub, i) {
  
        // push matched expression and part before it
        i = str.indexOf(sub)
        parts.push(str.slice(0, i), sub)
        str = str.slice(i + sub.length)
      })
  
      // push the remaining part
      return parts.concat(str)
    }
  
  
    // match strings between opening and closing regexp, skipping any inner/nested matches
  
    function extract(str, open, close) {
  
      var start,
          level = 0,
          matches = [],
          re = new RegExp('('+open.source+')|('+close.source+')', 'g')
  
      str.replace(re, function(_, open, close, pos) {
  
        // if outer inner bracket, mark position
        if (!level && open) start = pos
  
        // in(de)crease bracket level
        level += open ? 1 : -1
  
        // if outer closing bracket, grab the match
        if (!level && close != null) matches.push(str.slice(start, pos+close.length))
  
      })
  
      return matches
    }
  
  })()
  
  // { key, i in items} -> { key, i, items }
  function loopKeys(expr) {
    var b0 = brackets(0),
        els = expr.slice(b0.length).match(/^\s*(\S+?)\s*(?:,\s*(\S+))?\s+in\s+(.+)$/)
    return els ? { key: els[1], pos: els[2], val: b0 + els[3] } : { val: expr }
  }
  
  function mkitem(expr, key, val) {
    var item = {}
    item[expr.key] = key
    if (expr.pos) item[expr.pos] = val
    return item
  }
  
  
  /* Beware: heavy stuff */
  function _each(dom, parent, expr) {
  
    remAttr(dom, 'each')
  
    var tagName = getTagName(dom),
        template = dom.outerHTML,
        hasImpl = !!tagImpl[tagName],
        impl = tagImpl[tagName] || {
          tmpl: template
        },
        root = dom.parentNode,
        placeholder = document.createComment('riot placeholder'),
        tags = [],
        child = getTag(dom),
        checksum
  
    root.insertBefore(placeholder, dom)
  
    expr = loopKeys(expr)
  
    // clean template code
    parent
      .one('premount', function () {
        if (root.stub) root = parent.root
        // remove the original DOM node
        dom.parentNode.removeChild(dom)
      })
      .on('update', function () {
        var items = tmpl(expr.val, parent)
  
        // object loop. any changes cause full redraw
        if (!isArray(items)) {
  
          checksum = items ? JSON.stringify(items) : ''
  
          items = !items ? [] :
            Object.keys(items).map(function (key) {
              return mkitem(expr, key, items[key])
            })
        }
  
        var frag = document.createDocumentFragment(),
            i = tags.length,
            j = items.length
  
        // unmount leftover items
        while (i > j) {
          tags[--i].unmount()
          tags.splice(i, 1)
        }
  
        for (i = 0; i < j; ++i) {
          var _item = !checksum && !!expr.key ? mkitem(expr, items[i], i) : items[i]
  
          if (!tags[i]) {
            // mount new
            (tags[i] = new Tag(impl, {
                parent: parent,
                isLoop: true,
                hasImpl: hasImpl,
                root: hasImpl ? dom.cloneNode() : root,
                item: _item
              }, dom.innerHTML)
            ).mount()
  
            frag.appendChild(tags[i].root)
          } else
            tags[i].update(_item)
  
          tags[i]._item = _item
  
        }
  
        root.insertBefore(frag, placeholder)
  
        if (child) parent.tags[tagName] = tags
  
      }).one('updated', function() {
        var keys = Object.keys(parent)// only set new values
        walk(root, function(node) {
          // only set element node and not isLoop
          if (node.nodeType == 1 && !node.isLoop && !node._looped) {
            node._visited = false // reset _visited for loop node
            node._looped = true // avoid set multiple each
            setNamed(node, parent, keys)
          }
        })
      })
  
  }
  
  
  function parseNamedElements(root, parent, childTags) {
  
    walk(root, function(dom) {
      if (dom.nodeType == 1) {
        dom.isLoop = dom.isLoop || (dom.parentNode && dom.parentNode.isLoop || dom.getAttribute('each')) ? 1 : 0
  
        // custom child tag
        var child = getTag(dom)
  
        if (child && !dom.isLoop) {
          var tag = new Tag(child, { root: dom, parent: parent }, dom.innerHTML),
              tagName = getTagName(dom),
              ptag = parent,
              cachedTag
  
          while (!getTag(ptag.root)) {
            if (!ptag.parent) break
            ptag = ptag.parent
          }
  
          // fix for the parent attribute in the looped elements
          tag.parent = ptag
  
          cachedTag = ptag.tags[tagName]
  
          // if there are multiple children tags having the same name
          if (cachedTag) {
            // if the parent tags property is not yet an array
            // create it adding the first cached tag
            if (!isArray(cachedTag))
              ptag.tags[tagName] = [cachedTag]
            // add the new nested tag to the array
            ptag.tags[tagName].push(tag)
          } else {
            ptag.tags[tagName] = tag
          }
  
          // empty the child node once we got its template
          // to avoid that its children get compiled multiple times
          dom.innerHTML = ''
          childTags.push(tag)
        }
  
        if (!dom.isLoop)
          setNamed(dom, parent, [])
      }
  
    })
  
  }
  
  function parseExpressions(root, tag, expressions) {
  
    function addExpr(dom, val, extra) {
      if (val.indexOf(brackets(0)) >= 0) {
        var expr = { dom: dom, expr: val }
        expressions.push(extend(expr, extra))
      }
    }
  
    walk(root, function(dom) {
      var type = dom.nodeType
  
      // text node
      if (type == 3 && dom.parentNode.tagName != 'STYLE') addExpr(dom, dom.nodeValue)
      if (type != 1) return
  
      /* element */
  
      // loop
      var attr = dom.getAttribute('each')
  
      if (attr) { _each(dom, tag, attr); return false }
  
      // attribute expressions
      each(dom.attributes, function(attr) {
        var name = attr.name,
          bool = name.split('__')[1]
  
        addExpr(dom, attr.value, { attr: bool || name, bool: bool })
        if (bool) { remAttr(dom, name); return false }
  
      })
  
      // skip custom tags
      if (getTag(dom)) return false
  
    })
  
  }
  function Tag(impl, conf, innerHTML) {
  
    var self = riot.observable(this),
        opts = inherit(conf.opts) || {},
        dom = mkdom(impl.tmpl),
        parent = conf.parent,
        isLoop = conf.isLoop,
        hasImpl = conf.hasImpl,
        item = cleanUpData(conf.item),
        expressions = [],
        childTags = [],
        root = conf.root,
        fn = impl.fn,
        tagName = root.tagName.toLowerCase(),
        attr = {},
        propsInSyncWithParent = [],
        loopDom,
        TAG_ATTRIBUTES = /([\w\-]+)\s?=\s?['"]([^'"]+)["']/gim
  
  
    if (fn && root._tag) {
      root._tag.unmount(true)
    }
  
    // not yet mounted
    this.isMounted = false
    root.isLoop = isLoop
  
    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|select|optgroup|tbody|tr/.test(tagName))
      // replace all the yield tags with the tag inner html
      dom.innerHTML = replaceYield(dom.innerHTML, innerHTML)
  
    // options
    function updateOpts() {
      var ctx = hasImpl && isLoop ? self : parent || self
      // update opts from current DOM attributes
      each(root.attributes, function(el) {
        opts[el.name] = tmpl(el.value, ctx)
      })
      // recover those with expressions
      each(Object.keys(attr), function(name) {
        opts[name] = tmpl(attr[name], ctx)
      })
    }
  
    function normalizeData(data) {
      for (var key in item) {
        if (typeof self[key] !== T_UNDEF)
          self[key] = data[key]
      }
    }
  
    function inheritFromParent () {
      if (!self.parent || !isLoop) return
      each(Object.keys(self.parent), function(k) {
        // some properties must be always in sync with the parent tag
        var mustSync = ~propsInSyncWithParent.indexOf(k)
        if (typeof self[k] === T_UNDEF || mustSync) {
          // track the property to keep in sync
          // so we can keep it updated
          if (!mustSync) propsInSyncWithParent.push(k)
          self[k] = self.parent[k]
        }
      })
    }
  
    this.update = function(data) {
      // make sure the data passed will not override
      // the component core methods
      data = cleanUpData(data)
      // inherit properties from the parent
      inheritFromParent()
      // normalize the tag properties in case an item object was initially passed
      if (typeof item === T_OBJECT || isArray(item)) {
        normalizeData(data)
        item = data
      }
      extend(self, data)
      updateOpts()
      self.trigger('update', data)
      update(expressions, self)
      self.trigger('updated')
    }
  
    this.mixin = function() {
      each(arguments, function(mix) {
        mix = typeof mix === T_STRING ? riot.mixin(mix) : mix
        each(Object.keys(mix), function(key) {
          // bind methods to self
          if (key != 'init')
            self[key] = isFunction(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 || hasImpl) parseExpressions(self.root, self, expressions) // top level before update, empty root
  
      if (!self.parent || isLoop) self.update(item)
  
      // internal use only, fixes #403
      self.trigger('premount')
  
      if (isLoop && !hasImpl) {
        // update the root attribute for the looped elements
        self.root = root = loopDom = dom.firstChild
  
      } else {
        while (dom.firstChild) root.appendChild(dom.firstChild)
        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.parent.isMounted) {
        self.isMounted = true
        self.trigger('mount')
      }
      // otherwise we need to wait that the parent event gets triggered
      else self.parent.one('mount', function() {
        // avoid to trigger the `mount` event for the tags
        // not visible included in an if statement
        if (!isInStub(self.root)) {
          self.parent.isMounted = self.isMounted = true
          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 (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'
  
        // the loop tags will be always in sync with the parent automatically
        if (isLoop)
          parent[evt]('unmount', self.unmount)
        else
          parent[evt]('update', self.update)[evt]('unmount', self.unmount)
      }
    }
  
    // named elements available for fn
    parseNamedElements(dom, this, childTags)
  
  
  }
  
  function setEventHandler(name, handler, dom, tag) {
  
    dom[name] = function(e) {
  
      var item = tag._item,
          ptag = tag.parent
  
      if (!item)
        while (ptag) {
          item = ptag._item
          ptag = item ? false : ptag.parent
        }
  
      // cross browser event fix
      e = e || window.event
  
      // ignore error on some browsers
      try {
        e.currentTarget = dom
        if (!e.target) e.target = e.srcElement
        if (!e.which) e.which = e.charCode || e.keyCode
      } catch (ignored) { '' }
  
      e.item = item
  
      // prevent default behaviour (by default)
      if (handler.call(tag, e) !== true && !/radio|check/.test(dom.type)) {
        e.preventDefault && e.preventDefault()
        e.returnValue = false
      }
  
      if (!e.preventUpdate) {
        var el = item ? tag.parent : tag
        el.update()
      }
  
    }
  
  }
  
  // used by if- attribute
  function insertTo(root, node, before) {
    if (root) {
      root.insertBefore(before, node)
      root.removeChild(node)
    }
  }
  
  function update(expressions, tag) {
  
    each(expressions, function(expr, i) {
  
      var dom = expr.dom,
          attrName = expr.attr,
          value = tmpl(expr.expr, tag),
          parent = expr.dom.parentNode
  
      if (value == null) value = ''
  
      // leave out riot- prefixes from strings inside textarea
      if (parent && parent.tagName == 'TEXTAREA') value = value.replace(/riot-/g, '')
  
      // no change
      if (expr.value === value) return
      expr.value = value
  
      // text node
      if (!attrName) return dom.nodeValue = value.toString()
  
      // remove original attribute
      remAttr(dom, attrName)
  
      // event handler
      if (isFunction(value)) {
        setEventHandler(attrName, value, dom, tag)
  
      // if- conditional
      } else if (attrName == 'if') {
        var stub = expr.stub
  
        // add to DOM
        if (value) {
          if (stub) {
            insertTo(stub.parentNode, stub, dom)
            dom.inStub = false
            // avoid to trigger the mount event if the tags is not visible yet
            // maybe we can optimize this avoiding to mount the tag at all
            if (!isInStub(dom)) {
              walk(dom, function(el) {
                if (el._tag && !el._tag.isMounted) el._tag.isMounted = !!el._tag.trigger('mount')
              })
            }
          }
        // remove from DOM
        } else {
          stub = expr.stub = stub || document.createTextNode('')
          insertTo(dom.parentNode, dom, stub)
          dom.inStub = true
        }
      // show / hide
      } else if (/^(show|hide)$/.test(attrName)) {
        if (attrName == 'hide') value = !value
        dom.style.display = value ? '' : 'none'
  
      // field value
      } else if (attrName == 'value') {
        dom.value = value
  
      // <img src="{ expr }">
      } else if (attrName.slice(0, 5) == 'riot-' && attrName != 'riot-tag') {
        attrName = attrName.slice(5)
        value ? dom.setAttribute(attrName, value) : remAttr(dom, attrName)
  
      } else {
        if (expr.bool) {
          dom[attrName] = value
          if (!value) return
          value = attrName
        }
  
        if (typeof value !== T_OBJECT) dom.setAttribute(attrName, value)
  
      }
  
    })
  
  }
  
  function each(els, fn) {
    for (var i = 0, len = (els || []).length, el; i < len; i++) {
      el = els[i]
      // return false -> remove current item during loop
      if (el != null && fn(el, i) === false) i--
    }
    return els
  }
  
  function isFunction(v) {
    return typeof v === 'function' || false   // avoid IE problems
  }
  
  function remAttr(dom, name) {
    dom.removeAttribute(name)
  }
  
  function fastAbs(nr) {
    return (nr ^ (nr >> 31)) - (nr >> 31)
  }
  
  function getTag(dom) {
    var tagName = dom.tagName.toLowerCase()
    return tagImpl[dom.getAttribute(RIOT_TAG) || tagName]
  }
  
  function getTagName(dom) {
    var child = getTag(dom),
      namedTag = dom.getAttribute('name'),
      tagName = namedTag && namedTag.indexOf(brackets(0)) < 0 ? namedTag : child ? child.name : dom.tagName.toLowerCase()
  
    return tagName
  }
  
  function extend(src) {
    var obj, args = arguments
    for (var i = 1; i < args.length; ++i) {
      if ((obj = args[i])) {
        for (var key in obj) {      // eslint-disable-line guard-for-in
          src[key] = obj[key]
        }
      }
    }
    return src
  }
  
  // with this function we avoid that the current Tag methods get overridden
  function cleanUpData(data) {
    if (!(data instanceof Tag)) return data
  
    var o = {},
        blackList = ['update', 'root', 'mount', 'unmount', 'mixin', 'isMounted', 'isloop', 'tags', 'parent', 'opts']
    for (var key in data) {
      if (!~blackList.indexOf(key))
        o[key] = data[key]
    }
    return o
  }
  
  function mkdom(template) {
    var checkie = ieVersion && ieVersion < 10,
        matches = /^\s*<([\w-]+)/.exec(template),
        tagName = matches ? matches[1].toLowerCase() : '',
        rootTag = (tagName === 'th' || tagName === 'td') ? 'tr' :
                  (tagName === 'tr' ? 'tbody' : 'div'),
        el = mkEl(rootTag)
  
    el.stub = true
  
    if (checkie) {
      if (tagName === 'optgroup')
        optgroupInnerHTML(el, template)
      else if (tagName === 'option')
        optionInnerHTML(el, template)
      else if (rootTag !== 'div')
        tbodyInnerHTML(el, template, tagName)
      else
        checkie = 0
    }
    if (!checkie) el.innerHTML = template
  
    return el
  }
  
  function walk(dom, fn) {
    if (dom) {
      if (fn(dom) === false) walk(dom.nextSibling, fn)
      else {
        dom = dom.firstChild
  
        while (dom) {
          walk(dom, fn)
          dom = dom.nextSibling
        }
      }
    }
  }
  
  function isInStub(dom) {
    while (dom) {
      if (dom.inStub) return true
      dom = dom.parentNode
    }
    return false
  }
  
  function mkEl(name) {
    return document.createElement(name)
  }
  
  function replaceYield (tmpl, innerHTML) {
    return tmpl.replace(/<(yield)\/?>(<\/\1>)?/gim, innerHTML || '')
  }
  
  function $$(selector, ctx) {
    return (ctx || document).querySelectorAll(selector)
  }
  
  function $(selector, ctx) {
    return (ctx || document).querySelector(selector)
  }
  
  function inherit(parent) {
    function Child() {}
    Child.prototype = parent
    return new Child()
  }
  
  function setNamed(dom, parent, keys) {
    each(dom.attributes, function(attr) {
      if (dom._visited) return
      if (attr.name === 'id' || attr.name === 'name') {
        dom._visited = true
        var p, v = attr.value
        if (~keys.indexOf(v)) return
  
        p = parent[v]
        if (!p)
          parent[v] = dom
        else
          isArray(p) ? p.push(dom) : (parent[v] = [p, dom])
      }
    })
  }
  /**
   *
   * Hacks needed for the old internet explorer versions [lower than IE10]
   *
   */
  /* istanbul ignore next */
  function tbodyInnerHTML(el, html, tagName) {
    var div = mkEl('div'),
        loops = /td|th/.test(tagName) ? 3 : 2,
        child
  
    div.innerHTML = '<table>' + html + '</table>'
    child = div.firstChild
  
    while (loops--) child = child.firstChild
  
    el.appendChild(child)
  
  }
  /* istanbul ignore next */
  function optionInnerHTML(el, html) {
    var opt = mkEl('option'),
        valRegx = /value=[\"'](.+?)[\"']/,
        selRegx = /selected=[\"'](.+?)[\"']/,
        eachRegx = /each=[\"'](.+?)[\"']/,
        ifRegx = /if=[\"'](.+?)[\"']/,
        innerRegx = />([^<]*)</,
        valuesMatch = html.match(valRegx),
        selectedMatch = html.match(selRegx),
        innerValue = html.match(innerRegx),
        eachMatch = html.match(eachRegx),
        ifMatch = html.match(ifRegx)
  
    if (innerValue) opt.innerHTML = innerValue[1]
    else opt.innerHTML = html
  
    if (valuesMatch) opt.value = valuesMatch[1]
    if (selectedMatch) opt.setAttribute('riot-selected', selectedMatch[1])
    if (eachMatch) opt.setAttribute('each', eachMatch[1])
    if (ifMatch) opt.setAttribute('if', ifMatch[1])
  
    el.appendChild(opt)
  }
  /* istanbul ignore next */
  function optgroupInnerHTML(el, html) {
    var opt = mkEl('optgroup'),
        labelRegx = /label=[\"'](.+?)[\"']/,
        elementRegx = /^<([^>]*)>/,
        tagRegx = /^<([^ \>]*)/,
        labelMatch = html.match(labelRegx),
        elementMatch = html.match(elementRegx),
        tagMatch = html.match(tagRegx),
        innerContent = html
  
    if (elementMatch) {
      var options = html.slice(elementMatch[1].length+2, -tagMatch[1].length-3).trim()
      innerContent = options
    }
  
    if (labelMatch) opt.setAttribute('riot-label', labelMatch[1])
  
    if (innerContent) {
      var innerOpt = mkEl('div')
  
      optionInnerHTML(innerOpt, innerContent)
  
      opt.appendChild(innerOpt.firstChild)
    }
  
    el.appendChild(opt)
  }
  
  /*
   Virtual dom is an array of custom tags on the document.
   Updates and unmounts propagate downwards from parent to children.
  */
  
  var virtualDom = [],
      tagImpl = {},
      styleNode
  
  var RIOT_TAG = 'riot-tag'
  
  function injectStyle(css) {
  
    styleNode = styleNode || mkEl('style')
  
    if (!document.head) return
  
    if (styleNode.styleSheet)
      styleNode.styleSheet.cssText += css
    else
      styleNode.innerHTML += css
  
    if (!styleNode._rendered)
      if (styleNode.styleSheet) {
        document.body.appendChild(styleNode)
      } else {
        var rs = $('style[type=riot]')
        if (rs) {
          rs.parentNode.insertBefore(styleNode, rs)
          rs.parentNode.removeChild(rs)
        } else document.head.appendChild(styleNode)
  
      }
  
    styleNode._rendered = true
  
  }
  
  function mountTo(root, tagName, opts) {
    var tag = tagImpl[tagName],
        // cache the inner HTML to fix #855
        innerHTML = root._innerHTML = root._innerHTML || root.innerHTML
  
    // clear the inner html
    root.innerHTML = ''
  
    if (tag && root) tag = new Tag(tag, { root: root, opts: opts }, innerHTML)
  
    if (tag && tag.mount) {
      tag.mount()
      virtualDom.push(tag)
      return tag.on('unmount', function() {
        virtualDom.splice(virtualDom.indexOf(tag), 1)
      })
    }
  
  }
  
  riot.tag = function(name, html, css, attrs, fn) {
    if (isFunction(attrs)) {
      fn = attrs
      if (/^[\w\-]+\s?=/.test(css)) {
        attrs = css
        css = ''
      } else attrs = ''
    }
    if (css) {
      if (isFunction(css)) fn = css
      else injectStyle(css)
    }
    tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
    return name
  }
  
  riot.mount = function(selector, tagName, opts) {
  
    var els,
        allTags,
        tags = []
  
    // helper functions
  
    function addRiotTags(arr) {
      var list = ''
      each(arr, function (e) {
        list += ', *[riot-tag="'+ e.trim() + '"]'
      })
      return list
    }
  
    function selectAllTags() {
      var keys = Object.keys(tagImpl)
      return keys + addRiotTags(keys)
    }
  
    function pushTags(root) {
      if (root.tagName) {
        if (tagName && !root.getAttribute(RIOT_TAG))
          root.setAttribute(RIOT_TAG, tagName)
  
        var tag = mountTo(root,
          tagName || root.getAttribute(RIOT_TAG) || root.tagName.toLowerCase(), opts)
  
        if (tag) tags.push(tag)
      }
      else if (root.length) {
        each(root, pushTags)   // assume nodeList
      }
    }
  
    // ----- mount code -----
  
    if (typeof tagName === T_OBJECT) {
      opts = tagName
      tagName = 0
    }
  
    // crawl the DOM to find the tag
    if (typeof selector === T_STRING) {
      if (selector === '*')
        // select all the tags registered
        // and also the tags found with the riot-tag attribute set
        selector = allTags = selectAllTags()
      else
        // or just the ones named like the selector
        selector += addRiotTags(selector.split(','))
  
      els = $$(selector)
    }
    else
      // probably you have passed already a tag or a NodeList
      els = selector
  
    // select all the registered and mount them inside their root elements
    if (tagName === '*') {
      // get all custom tags
      tagName = allTags || selectAllTags()
      // if the root els it's just a single tag
      if (els.tagName)
        els = $$(tagName, els)
      else {
        // select all the children for all the different root elements
        var nodeList = []
        each(els, function (_el) {
          nodeList.push($$(tagName, _el))
        })
        els = nodeList
      }
      // get rid of the tagName
      tagName = 0
    }
  
    if (els.tagName)
      pushTags(els)
    else
      each(els, pushTags)
  
    return tags
  }
  
  // update everything
  riot.update = function() {
    return each(virtualDom, function(tag) {
      tag.update()
    })
  }
  
  // @deprecated
  riot.mountTo = riot.mount
  
  var parsers = {
    html: {},
    css: {},
    js: {
      coffee: function(js) {
        return CoffeeScript.compile(js, { bare: true })
      },
      es6: function(js) {
        return babel.transform(js, { blacklist: ['useStrict'] }).code
      },
      none: function(js) {
        return js
      }
    }
  }
  
  // fix 913
  parsers.js.javascript = parsers.js.none
  // 4 the nostalgics
  parsers.js.coffeescript = parsers.js.coffee
  
  riot.parsers = parsers
  
  
  var BOOL_ATTR = ('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,'+
    'defaultchecked,defaultmuted,defaultselected,defer,disabled,draggable,enabled,formnovalidate,hidden,'+
    'indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,'+
    'pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,spellcheck,translate,truespeed,'+
    'typemustmatch,visible').split(','),
    // these cannot be auto-closed
    VOID_TAGS = 'area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr'.split(','),
    /*
      Following attributes give error when parsed on browser with { exrp_values }
  
      'd' describes the SVG <path>, Chrome gives error if the value is not valid format
      https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
    */
    PREFIX_ATTR = ['style', 'src', 'd'],
  
    LINE_TAG = /^<([\w\-]+)>(.*)<\/\1>/gim,
    QUOTE = /=({[^}]+})([\s\/\>])/g,
    SET_ATTR = /([\w\-]+)=(["'])([^\2]+?)\2/g,
    EXPR = /{\s*([^}]+)\s*}/g,
    // (tagname) (html) (javascript) endtag
    CUSTOM_TAG = /^<([\w\-]+)\s?([^>]*)>([^\x00]*[\w\/}"']>$)?([^\x00]*?)^<\/\1>/gim,
    SCRIPT = /<script(\s+type=['"]?([^>'"]+)['"]?)?>([^\x00]*?)<\/script>/gm,
    STYLE = /<style(\s+type=['"]?([^>'"]+)['"]?|\s+scoped)?>([^\x00]*?)<\/style>/gm,
    CSS_SELECTOR = /(^|\}|\{)\s*([^\{\}]+)\s*(?=\{)/g,
    CSS_COMMENT = /\/\*[^\x00]*?\*\//gm,
    HTML_COMMENT = /<!--.*?-->/g,
    CLOSED_TAG = /<([\w\-]+)([^>]*)\/\s*>/g,
    LINE_COMMENT = /^\s*\/\/.*$/gm,
    JS_COMMENT = /\/\*[^\x00]*?\*\//gm,
    INPUT_NUMBER = /(<input\s[^>]*?)type=['"]number['"]/gm
  
  function mktag(name, html, css, attrs, js) {
    return 'riot.tag(\''
      + name + '\', \''
      + html + '\''
      + (css ? ', \'' + css + '\'' : '')
      + (attrs ? ', \'' + attrs.replace(/'/g, "\\'") + '\'' : '')
      + ', function(opts) {' + js + '\n});'
  }
  
  function compileHTML(html, opts, type) {
  
    var brackets = riot.util.brackets
  
    // foo={ bar } --> foo="{ bar }"
    html = html.replace(brackets(QUOTE), '="$1"$2')
  
    // whitespace
    html = opts.whitespace ? html.replace(/\n/g, '\\n') : html.replace(/\s+/g, ' ')
  
    // strip comments
    html = html.trim().replace(HTML_COMMENT, '')
  
    // input type=numbr
    html = html.replace(INPUT_NUMBER, '$1riot-type='+brackets(0)+'"number"'+brackets(1)) // fake expression
  
    // alter special attribute names
    html = html.replace(SET_ATTR, function(full, name, _, expr) {
      if (expr.indexOf(brackets(0)) >= 0) {
        name = name.toLowerCase()
  
        if (PREFIX_ATTR.indexOf(name) >= 0) name = 'riot-' + name
  
        // IE8 looses boolean attr values: `checked={ expr }` --> `__checked={ expr }`
        else if (BOOL_ATTR.indexOf(name) >= 0) name = '__' + name
      }
  
      return name + '="' + expr + '"'
    })
  
    // run expressions trough parser
    if (opts.expr) {
      html = html.replace(brackets(EXPR), function(_, expr) {
        var ret = compileJS(expr, opts, type).trim().replace(/\r?\n|\r/g, '').trim()
        if (ret.slice(-1) == ';') ret = ret.slice(0, -1)
        return brackets(0) + ret + brackets(1)
      })
    }
  
    // <foo/> -> <foo></foo>
    html = html.replace(CLOSED_TAG, function(_, name, attr) {
      var tag = '<' + name + (attr ? ' ' + attr.trim() : '') + '>'
  
      // Do not self-close HTML5 void tags
      if (VOID_TAGS.indexOf(name.toLowerCase()) == -1) tag += '</' + name + '>'
      return tag
    })
  
    // escape single quotes
    html = html.replace(/'/g, "\\'")
  
    // \{ jotain \} --> \\{ jotain \\}
    html = html.replace(brackets(/\\{|\\}/g), '\\$&')
  
    // compact: no whitespace between tags
    if (opts.compact) html = html.replace(/> </g, '><')
  
    return html
  
  }
  
  
  function riotjs(js) {
  
    // strip comments
    js = js.replace(LINE_COMMENT, '').replace(JS_COMMENT, '')
  
    // ES6 method signatures
    var lines = js.split('\n'),
        es6Ident = ''
  
    lines.forEach(function(line, i) {
      var l = line.trim()
  
      // method start
      if (l[0] != '}' && l.indexOf('(') > 0 && l.indexOf('function') == -1) {
        var end = /[{}]/.exec(l.slice(-1)),
            m = end && /(\s+)([\w]+)\s*\(([\w,\s]*)\)\s*\{/.exec(line)
  
        if (m && !/^(if|while|switch|for|catch)$/.test(m[2])) {
          lines[i] = m[1] + 'this.' + m[2] + ' = function(' + m[3] + ') {'
  
          // foo() { }
          if (end[0] == '}') {
            lines[i] += ' ' + l.slice(m[0].length - 1, -1) + '}.bind(this)'
  
          } else {
            es6Ident = m[1]
          }
        }
  
      }
  
      // method end
      if (line.slice(0, es6Ident.length + 1) == es6Ident + '}') {
        lines[i] = es6Ident + '}.bind(this);'
        es6Ident = ''
      }
  
    })
  
    return lines.join('\n')
  
  }
  
  function scopedCSS (tag, style, type) {
    return style.replace(CSS_COMMENT, '').replace(CSS_SELECTOR, function (m, p1, p2) {
      return p1 + ' ' + p2.split(/\s*,\s*/g).map(function(sel) {
        var s = sel.trim().replace(/:scope\s*/, '')
        return s[0] == '@' || s == 'from' || s == 'to' || /%$/.test(s) ? s :
          tag + ' ' + s + ', [riot-tag="' + tag + '"] ' + s
      }).join(',')
    }).trim()
  }
  
  function compileJS(js, opts, type) {
    var parser = opts.parser || (type ? riot.parsers.js[type] : riotjs)
    if (!parser) throw new Error('Parser not found "' + type + '"')
    return parser(js, opts)
  }
  
  function compileTemplate(lang, html) {
    var parser = riot.parsers.html[lang]
    if (!parser) throw new Error('Template parser not found "' + lang + '"')
    return parser(html)
  }
  
  function compileCSS(style, tag, type) {
    if (type == 'scoped-css') style = scopedCSS(tag, style)
    else if (riot.parsers.css[type]) style = riot.parsers.css[type](tag, style)
    return style.replace(/\s+/g, ' ').replace(/\\/g, '\\\\').replace(/'/g, "\\'").trim()
  }
  
  function compile(src, opts) {
  
    opts = opts || {}
  
    if (opts.brackets) riot.settings.brackets = opts.brackets
  
    if (opts.template) src = compileTemplate(opts.template, src)
  
    src = src.replace(LINE_TAG, function(_, tagName, html) {
      return mktag(tagName, compileHTML(html, opts), '', '', '')
    })
  
    return src.replace(CUSTOM_TAG, function(_, tagName, attrs, html, js) {
      html = html || ''
      attrs = compileHTML(attrs, '', '')
  
      // js wrapped inside <script> tag
      var type = opts.type
  
      if (!js.trim()) {
        html = html.replace(SCRIPT, function(_, fullType, _type, script) {
          if (_type) type = _type.replace('text/', '')
          js = script
          return ''
        })
      }
  
      // styles in <style> tag
      var styleType = 'css',
          style = ''
  
      html = html.replace(STYLE, function(_, fullType, _type, _style) {
        if (fullType && fullType.trim() == 'scoped') styleType = 'scoped-css'
          else if (_type) styleType = _type.replace('text/', '')
        style = _style
        return ''
      })
  
      return mktag(
        tagName,
        compileHTML(html, opts, type),
        compileCSS(style, tagName, styleType),
        attrs,
        compileJS(js, opts, type)
      )
  
    })
  
  }
  var doc = window.document,
      promise,
      ready
  
  
  function GET(url, fn) {
    var req = new XMLHttpRequest()
  
    req.onreadystatechange = function() {
      if (req.readyState == 4 && req.status == 200) fn(req.responseText)
    }
    req.open('GET', url, true)
    req.send('')
  }
  
  function unindent(src) {
    var ident = /[ \t]+/.exec(src)
    if (ident) src = src.replace(new RegExp('^' + ident[0], 'gm'), '')
    return src
  }
  
  function globalEval(js) {
    var node = doc.createElement('script'),
        root = doc.documentElement
  
    node.text = compile(js)
    root.appendChild(node)
    root.removeChild(node)
  }
  
  function compileScripts(fn) {
    var scripts = doc.querySelectorAll('script[type="riot/tag"]'),
        scriptsAmount = scripts.length
  
    function done() {
      promise.trigger('ready')
      ready = true
      fn && fn()
    }
  
    if (!scriptsAmount) {
      done()
    } else {
      [].map.call(scripts, function(script) {
        var url = script.getAttribute('src')
  
        function compileTag(source) {
          globalEval(source)
          scriptsAmount--
          if (!scriptsAmount) {
            done()
          }
        }
  
        return url ? GET(url, compileTag) : compileTag(unindent(script.innerHTML))
      })
    }
  }
  
  
  riot.compile = function(arg, fn) {
  
    // string
    if (typeof arg === T_STRING) {
  
      // compile & return
      if (arg.trim()[0] == '<') {
        var js = unindent(compile(arg))
        if (!fn) globalEval(js)
        return js
  
      // URL
      } else {
        return GET(arg, function(str) {
          var js = unindent(compile(str))
          globalEval(js)
          fn && fn(js, str)
        })
      }
    }
  
    // must be a function
    if (typeof arg !== 'function') arg = undefined
  
    // all compiled
    if (ready) return arg && arg()
  
    // add to queue
    if (promise) {
      arg && promise.on('ready', arg)
  
    // grab riot/tag elements + load & execute them
    } else {
      promise = riot.observable()
      compileScripts(arg)
    }
  
  }
  
  // reassign mount methods
  var mount = riot.mount
  
  riot.mount = function(a, b, c) {
    var ret
    riot.compile(function() { ret = mount(a, b, c) })
    return ret
  }
  
  // @deprecated
  riot.mountTo = riot.mount
  
    // share methods for other riot parts, e.g. compiler
    riot.util = { brackets: brackets, tmpl: tmpl }
  
    // support CommonJS, AMD & browser
    /* istanbul ignore next */
    if (typeof exports === T_OBJECT)
      module.exports = riot
    else if (typeof define === 'function' && define.amd)
      define(function() { return window.riot = riot })
    else
      window.riot = riot
  
  })(typeof window != 'undefined' ? window : undefined);
  