<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" href="tabs.css">
</head>

<body>
<p>Custom Tag</p>
<alerts></alerts>
<code id="target"></code>

<script src="alert.html" type="riot/tag"></script>
<script src="alerts.html" type="riot/tag"></script>
<script src="riotcompiler.js"></script>
<script>
    riot.mount('alerts', {
        items: [{
          player: 'Player 1',
          category: 'info'
        },{
          player: 'Player 2',
          category: 'warning'
        }]
        });

    
    setTimeout(function() {
      var el = document.getElementById('alerts')
      var pre = document.getElementById('target')
      console.log(el, pre)
      var sample_html = el.outerHTML;
      sample_html = sample_html.replace(/</g,"&lt;").replace(/>/g,"&gt;");      
      pre.innerHTML = sample_html;
    }, 4000)
</script>    
</body>

</html>
Riot JS Tabs Example
====================

Tutorial from http://www.robertwpearce.com/blog/riotjs-example/ updated for in browser compile

Also updated to pass tabs from script ex.

````javascript
var tabs = [{ title: 'Tab 1', ref: 'tab1', content: "...},...]
riot.mount('tabs', {"tabs": tabs})
 ````

routing added, not sure if this is the proper way
*, *:before, *:after {
  box-sizing: border-box; }

body {
  font-size: 16px;
  font-family: Helvetica;
  line-height: 1.5;
  font-weight: 300;
  margin: 0;
  padding: 0;
  color: #55AFA8; }

a {
  color: #B4EAE6;
  cursor: pointer;
  text-decoration: none;
  border-bottom: 1px dotted; }

iframe {
  border: none;
  width: 100%; }
  iframe.iframe--tabs {
    height: 550px; }
  iframe.iframe--posts {
    height: 400px; }

.list--bare, .post__locations, .listing__comments {
  list-style: none;
  margin: 0;
  padding: 0; }

.section, .section--main {
  padding: 75px 0;
  background-color: #FFF;
  border-bottom: 1px solid #B4EAE6; }

.section--main {
  background-color: #55AFA8;
  color: #FFF;
  border-bottom: none; }
  .section--main h1 {
    font-size: 3em; }

.layout--constrained {
  width: 400px;
  margin: 0 auto; }

tabs {
  display: block; }

.tab {
  display: inline-block;
  vertical-align: middle;
  width: 75px;
  height: 45px;
  line-height: 45px;
  text-align: center;
  cursor: pointer;
  border: 1px solid;
  background-color: #FFF;
  margin-bottom: -1px;
  margin-right: 5px; }
  .tab.is-active {
    border-bottom: none; }

.tabContent {
  border: 1px solid;
  padding: 20px; }

.tabContent__item {
  display: none; }
  .tabContent__item.is-active {
    display: block; }

posts {
  display: block; }

.listing {
  margin-bottom: 50px;
  width: 280px;
  padding: 15px 0;
  border: 1px solid #55AFA8;
  margin-bottom: 25px;
  font-size: 0; }

.listing__item {
  display: inline-block;
  vertical-align: middle;
  font-size: 1.25rem;
  width: 33.333333%;
  text-align: center;
  cursor: pointer;
  border-right: 1px solid #55AFA8; }
  .listing__item.is-inactive {
    opacity: 0.4; }
  .listing__item:last-of-type {
    border-right: none; }

.listing__author {
  display: inline-block;
  vertical-align: middle;
  cursor: pointer; }
  .listing__author img {
    display: inline-block;
    vertical-align: middle;
    width: 60px;
    height: 60px;
    border-radius: 50%; }

.listing__info {
  padding-top: 20px;
  height: 0;
  visibility: hidden;
  position: absolute;
  transition: all 0.3s ease-in-out; }
  .is-active .listing__info {
    height: auto;
    visibility: visible;
    position: relative; }

.post__location--start,
.post__location--via,
.post__location--stop {
  position: relative;
  padding-left: 32px;
  line-height: 1; }
  .post__location--start:before,
  .post__location--via:before,
  .post__location--stop:before {
    content: "";
    position: absolute;
    left: 12px;
    border-left: 1px solid #55AFA8;
    height: 100%;
    width: 1px; }
  .post__location--start:after,
  .post__location--via:after,
  .post__location--stop:after {
    position: absolute;
    left: 5px; }

.post__location--start,
.post__location--via {
  padding-bottom: 15px; }

.post__location--via {
  font-size: 0.813rem; }
  .post__location--via:after {
    content: "";
    position: absolute;
    top: 6px;
    left: 12px;
    border-bottom: 1px solid #55AFA8;
    width: 6px;
    height: 1px; }

.post__location--start:after,
.post__location--stop:after {
  content: "";
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border-width: 2px;
  border-style: solid;
  background-color: #82d0c9; }

.post__location--start:after {
  border-color: #55AFA8;
  top: 0; }

.post__location--stop:after {
  border-color: #55AFA8;
  bottom: 0; }

.listing__comments {
  border-top: 1px solid #55AFA8;
  margin-top: 20px; }
  .listing__comments > li {
    margin-top: 15px; }
  .listing__comments img, .listing__comments span {
    display: inline-block;
    vertical-align: top; }
  .listing__comments img {
    width: 30px;
    height: 30px;
    border-radius: 50%; }
  .listing__comments span {
    padding-top: 3px;
    padding-left: 5px;
    width: 200px; }

.listing__infoContact {
  margin-top: 10px;
  border: 1px solid #55AFA8;
  display: inline-block;
  padding: 5px 10px; }

/*# sourceMappingURL=app.css.map */
/* Custom versione related to: https://github.com/muut/riotjs/issues/295 
 * Modification 1 lines: 523
 * Modification 2 lines: 542 - 547
/* Riot 2.0.7, @license MIT, (c) 2015 Muut Inc. + contributors */

/* Riot 2.0.7, @license MIT, (c) 2015 Muut Inc. + contributors */

;(function() {

var riot = { version: 'v2.0.7' }

'use strict'

riot.observable = function(el) {

  el = el || {}

  var callbacks = {}

  el.on = function(events, fn) {
    if (typeof fn == 'function') {
      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 if (fn) {
      var arr = callbacks[events]
      for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
        if (cb == fn) { arr.splice(i, 1); i-- }
      }
    } else {
      events.replace(/\S+/g, function(name) {
        callbacks[name] = []
      })
    }
    return el
  }

  // only single event supported
  el.one = function(name, fn) {
    if (fn) fn.one = 1
    return el.on(name, fn)
  }

  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 (fn.one) { fns.splice(i, 1); i-- }
         else if (fns[i] !== fn) { i-- } // Makes self-removal possible during iteration
        fn.busy = 0
      }
    }

    return el
  }

  return el

}
;(function(riot, evt) {

  // browsers only
  if (!this.top) return

  var loc = location,
      fns = riot.observable(),
      current = hash(),
      win = window

  function hash() {
    return loc.hash.slice(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
  }

  win.addEventListener ? win.addEventListener(evt, emit, false) : win.attachEvent('on' + evt, emit)

})(riot, 'hashchange')
/*

//// 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"

*/

riot._tmpl = (function() {

  var cache = {},

      // find variable names
      re_vars = /("|').+?[^\\]\1|\.\w*|\w*:|\b(?:this|true|false|null|undefined|new|typeof|Number|String|Object|Array|Math|Date|JSON)\b|([a-z_]\w*)/gi
              // [ 1            ][ 2  ][ 3 ][ 4                                                                                        ][ 5       ]
              // 1. skip quoted strings: "a b", 'a b', 'a \'b\''
              // 2. skip object properties: .name
              // 3. skip object literals: name:
              // 4. skip reserved words
              // 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) {
    p = (s || '{}')

      // temporarily convert \{ and \} to a non-character
      .replace(/\\{/g, '\uFFF0')
      .replace(/\\}/g, '\uFFF1')

      // split string to expression and non-expresion parts
      .split(/({[\s\S]*?})/)

    return new Function('d', 'return ' + (

      // is it a single expression or a template? i.e. {x} or <b>{x}</b>
      !p[0] && !p[2]

        // 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, 1)

              // 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, '{')
      .replace(/\uFFF1/g, '}')

    )

  }


  // parse { ... } expression

  function expr(s, n) {
    s = s

      // convert new lines to spaces
      .replace(/\n/g, ' ')

      // trim whitespace, curly brackets, strip comments
      .replace(/^[{ ]+|[ }]+$|\/\*.+?\*\//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"
      ? '[' + s.replace(/\W*([\w-]+)\W*:([^,]+)/g, function(_, k, v) {

          // safely execute vars to prevent undefined value errors
          return v.replace(/\w[^,|& ]*/g, function(v) { return wrap(v, n) }) + '?"' + k + '":"",'

        }) + '].join(" ")'

      // if js expression, evaluate as javascript
      : wrap(s, n)

  }


  // execute js w/o breaking on errors or undefined vars

  function wrap(s, nonull) {
    return '(function(v){try{v='

        // prefix vars (name => data.name)
        + (s.replace(re_vars, function(s, _, v) { return v ? 'd.' + v : s })

          // break the expression if its empty (resulting in undefined value)
          || 'x')

      + '}finally{return '

        // default to empty string for falsy values except zero
        + (nonull ? '!v&&v!==0?"":v' : 'v')

      + '}}).call(d)'
  }

})()
;(function(riot, is_browser) {

  if (!is_browser) return

  var tmpl = riot._tmpl,
      all_tags = [],
      tag_impl = {},
      doc = document

  function each(nodes, fn) {
    for (var i = 0; i < (nodes || []).length; i++) {
      if (fn(nodes[i], i) === false) i--
    }
  }

  function extend(obj, from) {
    from && Object.keys(from).map(function(key) {
      obj[key] = from[key]
    })
    return obj
  }

  function diff(arr1, arr2) {
    return arr1.filter(function(el) {
      return arr2.indexOf(el) < 0
    })
  }

  function walk(dom, fn) {
    dom = fn(dom) === false ? dom.nextSibling : dom.firstChild

    while (dom) {
      walk(dom, fn)
      dom = dom.nextSibling
    }
  }


  function mkdom(tmpl) {
    var tag_name = tmpl.trim().slice(1, 3).toLowerCase(),
        root_tag = /td|th/.test(tag_name) ? 'tr' : tag_name == 'tr' ? 'tbody' : 'div'
        el = doc.createElement(root_tag)

    el.innerHTML = tmpl
    return el
  }


  function setEventHandler(name, handler, dom, instance) {

    dom[name] = function(e) {

      // cross browser event fix
      e = e || window.event
      e.which = e.which || e.charCode || e.keyCode
      e.target = e.target || e.srcElement
      e.currentTarget = dom

      // currently looped item
      e.item = instance.__item || instance

      // prevent default behaviour (by default)
      if (handler.call(instance, e) !== true) {
        e.preventDefault && e.preventDefault()
        e.returnValue = false
      }

      instance.update()
    }

  }


  function update(expressions, instance) {

    // allow recalculation of context data
    instance.trigger('update')

    each(expressions, function(expr) {
      var tag = expr.tag,
          dom = expr.dom

      function remAttr(name) {
        dom.removeAttribute(name)
      }

      // loops first: TODO remove from expressions arr
      if (expr.loop) {
        remAttr('each')
        return loop(expr, instance)
      }

      // custom tag
      if (tag) return tag.update ? tag.update() :
        expr.tag = createTag({ tmpl: tag[0], fn: tag[1], root: dom, parent: instance })


      var attr_name = expr.attr,
          value = tmpl(expr.expr, instance)

      if (value == null) value = ''

      // no change
      if (expr.value === value) return
      expr.value = value


      // text node
      if (!attr_name) return dom.nodeValue = value

      // attribute
      if (!value && expr.bool || /obj|func/.test(typeof value)) remAttr(attr_name)

      // event handler
      if (typeof value == 'function') {
        setEventHandler(attr_name, value, dom, instance)

      // show / hide / if
      } else if (/^(show|hide|if)$/.test(attr_name)) {
        remAttr(attr_name)
        if (attr_name == 'hide') value = !value
        dom.style.display = value ? '' : 'none'

      // normal attribute
      } else {
        if (expr.bool) {
          dom[attr_name] = value
          if (!value) return
          value = attr_name
        }

        dom.setAttribute(attr_name, value)
      }

    })

    instance.trigger('updated')

  }

  function parse(root) {

    var named_elements = {},
        expressions = []


    function addExpr(dom, value, data) {
      if (value ? value.indexOf('{') >= 0 : data) {
        var expr = { dom: dom, expr: value }
        expressions.push(extend(expr, data || {}))
      }
    }

    walk(root, function(dom) {

      var type = dom.nodeType,
          value = dom.nodeValue

      // text node
      if (type == 3 && dom.parentNode.tagName != 'STYLE') {
        addExpr(dom, value)

      // element
      } else if (type == 1) {

        // loop?
        value = dom.getAttribute('each')

        if (value) {
          addExpr(dom, value, { loop: 1 })
          return false
        }

        // custom tag?
        var tag = tag_impl[dom.tagName.toLowerCase()]

        // attributes
        each(dom.attributes, function(attr) {
          var name = attr.name,
              value = attr.value

          // named elements
          if (/^(name|id)$/.test(name)) named_elements[value] = dom

          // expressions
          if (!tag) {
            var bool = name.split('__')[1]
            addExpr(dom, value, { attr: bool || name, bool: bool })
            if (bool) {
              dom.removeAttribute(name)
              return false
            }
          }

        })

        if (tag) addExpr(dom, 0, { tag: tag })

      }

    })

    return { expr: expressions, elem: named_elements }

  }

  function moveChildren(src, dst) {
    while (src.firstChild) {
      dst.appendChild(src.removeChild(src.firstChild))
    }
    return dst
  }


  // create new custom tag (component)
  function createTag(conf) {

    var opts = conf.opts || {},
        dom = mkdom(conf.tmpl),
        mountNode = conf.root,
        parent = conf.parent,
        ast = parse(dom),
        tag = { root: mountNode, opts: opts, parent: parent, __item: conf.item },
        attributes = {}

    // named elements
    extend(tag, ast.elem)

    // attributes
    each(mountNode.attributes, function(attr) {
      attributes[attr.name] = attr.value
    })

    function updateOpts() {
      Object.keys(attributes).map(function(name) {
        var val = opts[name] = tmpl(attributes[name], parent || tag)
        if (typeof val == 'object') mountNode.removeAttribute(name)
      })
    }

    updateOpts()

    if (!tag.on) {
      riot.observable(tag)
      delete tag.off // off method not needed
    }

    if (conf.fn) conf.fn.call(tag, opts)

    tag.update = function(data, _system) {
      /*
        If loop is defined on the root of the HTML template
        the original parent is a temporary <div/> by mkdom()
      */
      if (parent && dom && !dom.firstChild) {
        mountNode = parent.root
        dom = null
      }

      if (_system || doc.body.contains(mountNode) || doc.querySelectorAll("[data-riot-tag='" + mountNode.nodeName + "']").length > 0) {
        extend(tag, data)
        extend(tag, tag.__item)

        if (opts.transclude) {
          // If transclude is enabled collect all the child elements of root node on a temporary node and save for later use
          opts.include = moveChildren(mountNode, doc.createElement('div'))
        }

        updateOpts()
        update(ast.expr, tag)

        // update parent
        !_system && tag.__item && parent.update()
        return true

      } else {
        tag.trigger('unmount')
      }

    }

    tag.update(0, true)

    if (opts.replacetag) {
      dom.firstChild.setAttribute("data-riot-tag", mountNode.nodeName);
      if ((mountNode.nodeName == 'INCLUDE') && (parent.opts.include)) {
        //if the parent has a stored include el and the current root node is INCLUDE
        moveChildren(parent.opts.include, dom.firstChild)
        mountNode = mountNode.parentNode.replaceChild(dom.firstChild, mountNode)
      } else {
        mountNode = mountNode.parentNode.replaceChild(dom.firstChild, mountNode)
      }
    }
    else {
        // append to root
        while (dom.firstChild) {
          if (conf.before) mountNode.insertBefore(dom.firstChild, conf.before)
          else mountNode.appendChild(dom.firstChild)
        }
    }

    tag.trigger('mount')

    all_tags.push(tag)

    return tag
  }


  function loop(expr, instance) {

    // initialize once
    if (expr.done) return
    expr.done = true

    var dom = expr.dom,
        prev = dom.previousSibling,
        root = dom.parentNode,
        template = dom.outerHTML,
        val = expr.expr,
        els = val.split(/\s+in\s+/),
        rendered = [],
        checksum,
        keys


    if (els[1]) {
      val = '{ ' + els[1]
      keys = els[0].slice(1).trim().split(/,\s*/)
    }

    // clean template code
    instance.one('mount', function() {
      var p = dom.parentNode
      if (p) {
        root = p
        root.removeChild(dom)
      }
    })

    function startPos() {
      return Array.prototype.indexOf.call(root.childNodes, prev) + 1
    }

    instance.on('updated', function() {

      var items = tmpl(val, instance),
          is_array = Array.isArray(items)

      if (is_array) items = items.slice(0)

      else {

        if (!items) return // some IE8 issue

        // detect Object changes
        var testsum = JSON.stringify(items)
        if (testsum == checksum) return
        checksum = testsum

        items = Object.keys(items).map(function(key, i) {
          var item = {}
          item[keys[0]] = key
          item[keys[1]] = items[key]
          return item
        })

      }

      // remove redundant
      diff(rendered, items).map(function(item) {
        var pos = rendered.indexOf(item)
        root.removeChild(root.childNodes[startPos() + pos])
        rendered.splice(pos, 1)
      })

      // add new
      diff(items, rendered).map(function(item, i) {
        var pos = items.indexOf(item)

        // string array
        if (keys && !checksum) {
          var obj = {}
          obj[keys[0]] = item
          obj[keys[1]] = pos
          item = obj
        }

        var tag = createTag({
          before: root.childNodes[startPos() + pos],
          parent: instance,
          tmpl: template,
          item: item,
          root: root
        })

        instance.on('update', function() {
          tag.update(0, true)
        })

      })

      // assign rendered
      rendered = items

    })

  }

  riot.tag = function(name, tmpl, fn) {
    fn = fn || noop,
    tag_impl[name] = [tmpl, fn]
  }

  riot.mountTo = function(node, tagName, opts) {
    var tag = tag_impl[tagName]
    return tag && createTag({ tmpl: tag[0], fn: tag[1], root: node, opts: opts })
  }

  riot.mount = function(selector, opts) {
    if (selector == '*') selector = Object.keys(tag_impl).join(', ')

    var instances = []

    each(doc.querySelectorAll(selector), function(node) {
      if (node.riot) return

      var tagName = node.tagName.toLowerCase(),
          instance = riot.mountTo(node, tagName, opts)

      if (instance) {
        instances.push(instance)
        node.riot = 1
      }
    })

    return instances
  }

  // update everything
  riot.update = function() {
    return all_tags = all_tags.filter(function(tag) {
      return !!tag.update()
    })
  }

  riot.tag('include', '<span></span>', function(opts) {
    opts.replacetag = true
  });


})(riot, this.top)

// support CommonJS
if (typeof exports === 'object')
  module.exports = riot

// support AMD
else if (typeof define === 'function' && define.amd)
  define(function() { return riot })

// support browser
else
  this.riot = riot

})();

(function(e){var t=("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(",");var r="area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",");var n={jade:v};var i={coffeescript:p,none:g,cs:p,es6:d,typescript:m};var o=/^<([\w\-]+)>([^\x00]*[\w\/]>$)?([^\x00]*?)^<\/\1>/gim,a=/<script(\s+type=['"]?([^>'"]+)['"]?)?>([^\x00]*?)<\/script>/gm,c=/<!--.*?-->/g,u=/<([\w\-]+)([^\/]*)\/\s*>/g,f=/^\s*\/\/.*$/gm,s=/\/\*[^\x00]*?\*\//gm;function l(e,n,i){e=e.replace(/\s+/g," ");e=e.trim().replace(c,"");e=e.replace(/=(\{[^\}]+\})([\s\>])/g,'="$1"$2');e=e.replace(/([\w\-]+)=["'](\{[^\}]+\})["']/g,function(e,r,n){if(t.indexOf(r.toLowerCase())>=0)r="__"+r;return r+'="'+n+'"'});if(n.expr){e=e.replace(/\{\s*([^\}]+)\s*\}/g,function(e,t){return"{"+w(t,n,i).trim()+"}"})}e=e.replace(u,function(e,t,n){var i="<"+t+(n?" "+n.trim():"")+">";if(r.indexOf(t.toLowerCase())==-1)i+="</"+t+">";return i});e=e.replace(/'/g,"\\'");e=e.replace(/\\[{}]/g,"\\$&");if(n.compact)e=e.replace(/> </g,"><");return e}function p(e){return require("coffee-script").compile(e,{bare:true})}function d(e){return require("6to5").transform(e).code}function m(e){return require("typescript-simple")(e)}function g(e){return e}function v(e){return require("jade").render(e,{pretty:true})}function h(e){e=e.replace(f,"").replace(s,"");var t=e.split("\n"),r="";t.forEach(function(e,n){var i=e.trim();if(i[0]!="}"&&i.indexOf("(")>0&&i.slice(-1)=="{"&&i.indexOf("function")==-1){var o=/(\s+)([\w]+)\s*\(([\w,\s]*)\)\s*\{/.exec(e);if(o&&!/^(if|while|switch|for)$/.test(o[2])){t[n]=o[1]+"this."+o[2]+" = function("+o[3]+") {";r=o[1]}}if(e.slice(0,r.length+1)==r+"}"){t[n]+=".bind(this);";r=""}});return t.join("\n")}function w(e,t,r){var n=t.parser||(r?i[r]:h);if(!n)throw new Error('Parser not found "'+r+'"');return n(e,t)}function y(e,t){var r=n[e];if(!r)throw new Error('Template parser not found "'+e+'"');return r(t)}function x(e,t){t=t||{};if(t.template)e=y(t.template,e);return e.replace(o,function(e,r,n,i){n=n||"";var o=t.type;if(!i.trim()){n=n.replace(a,function(e,t,r,n){if(r)o=r.replace("text/","");i=n;return""})}return"riot.tag('"+r+"', '"+l(n,t,o)+"', function(opts) {"+w(i,t,o)+"\n});"})}if(e){return module.exports={html:l,compile:x}}var b=document,q,E;function k(e,t){var r=new XMLHttpRequest;r.onreadystatechange=function(){if(r.readyState==4&&r.status==200)t(r.responseText)};r.open("GET",e,true);r.send("")}function T(e){var t=/[ \t]+/.exec(e);if(t)e=e.replace(new RegExp("^"+t[0],"gm"),"");return e}function $(e){var t=b.createElement("script"),r=b.documentElement;t.text=x(e);r.appendChild(t);r.removeChild(t)}function C(e){var t=b.querySelectorAll('script[type="riot/tag"]');[].map.call(t,function(r,n){var i=r.getAttribute("src");function o(i){r.parentNode.removeChild(r);$(i);if(n+1==t.length){q.trigger("ready");E=true;e&&e()}}return i?k(i,o):o(T(r.innerHTML))})}function L(e,t){if(typeof e=="string"){var r=T(x(e));if(!t)$(r);return r}if(typeof e!="function")e=undefined;if(E)return e&&e();if(q){e&&q.on("ready",e)}else{q=riot.observable();C(e)}}var O=riot.mount,j=riot.mountTo;riot.mount=function(e,t){L(function(){O(e,t)})};riot.mountTo=function(e,t,r){L(function(){j(e,t,r)})};riot._compile=function(e){return L(e,true)}})(!this.top);
<alert>
  <div class="alert alert-{category}" role="alert">...<include></div>

  opts.replacetag = true
  opts.transclude = true
  this.category = opts.category || "info"
</alert>
<alerts>
  <ul><li each={ items }><alert category={ category }> { player } </alert></li></ul>

  this.items = opts.items
</alerts>