lib/compiler.js
function(walk) {
    var i, item;
    var ctl = this.createCtl('associativeGroup');
    ctl.items = [];
    // Tweak selector to use the generated id
    this.selector.id = ctl.id;
    // Also append `associativeGroup` class
    if (!Array.isArray(this.selector.classes))
      this.selector.classes = [];
    this.selector.classes.push('associativeGroup');
    // Emit root element
    this.out.push('<fieldset');
    this.emitSelector();
    this.out.push('>');
    // Parse categories and items
    var startIdx = walk.position;
    while(walk.hasCurrent()) {
      walk.scrollToEol().skipWhitespaces();
      if (walk.at('@ ') || walk.at('* ')) {
        this.parseAssociativeItem(ctl, new SubWalker(walk, startIdx, walk.position));
        startIdx = walk.position;
      }
    }
    // Parse the last one
    this.parseAssociativeItem(ctl, new SubWalker(walk, startIdx, walk.position));
    // Emit categories
    this.out.push('<div class="categories">');
    for (i = 0; i < ctl.items.length; i++) {
      item = ctl.items[i];
      if (!item.category)
        continue;
      this.out.push('<div');
      this.emitAttrs({
        'id': 'category-' + item.id,
        'class': 'category'
      });
      this.out.push('>');
      this.out.push('<input');
      this.emitAttrs({
        'id': item.id,
        'name': ctl.id,
        'type': 'hidden',
        'value': item.id
      });
      this.out.push('/>');
      this.out.push('<label>');
      this.out.push(item.label);
      this.out.push('</label>');
      this.out.push('<div class="dropTarget"></div>');
      this.out.push('</div>');
    }
    // Include input for leftover items
    this.out.push('<input');
    this.emitAttrs({
      'id': item.id,
      'name': ctl.id,
      'type': 'hidden',
      'value': 'leftovers'
    });
    this.out.push('/>');
    this.out.push('</div>');
    // Emit select items
    this.out.push('<div class="items">');
    for (i = 0; i < ctl.items.length; i++) {
      item = ctl.items[i];
      if (item.category)
        continue;
      this.out.push('<div');
      this.emitAttrs({
        'id': 'item-' + item.id,
        'class': 'item'
      });
      this.out.push('>');
      this.out.push('<input');
      this.emitAttrs({
        'id': item.id,
        'name': ctl.id,
        'type': 'hidden',
        'value': item.id
      });
      this.out.push('/>');
      this.out.push('<label>');
      this.out.push(item.label);
      this.out.push('</label>');
      this.out.push('</div>');
    }
    this.out.push('</div>');
    // Finish
    this.out.push('</fieldset>');
  }
function(walk) {
    var i, item;
    var ctl = this.createCtl('associativeGroup');
    ctl.items = [];
    // Tweak selector to use the generated id
    this.selector.id = ctl.id;
    // Also append `associativeGroup` class
    if (!Array.isArray(this.selector.classes))
      this.selector.classes = [];
    this.selector.classes.push('associativeGroup');
    // Emit root element
    this.out.push('<fieldset');
    this.emitSelector();
    this.out.push('>');
    // Parse categories and items
    var startIdx = walk.position;
    while(walk.hasCurrent()) {
      walk.scrollToEol().skipWhitespaces();
      if (walk.at('@ ') || walk.at('* ')) {
        this.parseAssociativeItem(ctl, new SubWalker(walk, startIdx, walk.position));
        startIdx = walk.position;
      }
    }
    // Parse the last one
    this.parseAssociativeItem(ctl, new SubWalker(walk, startIdx, walk.position));
    // Emit categories
    this.out.push('<div class="categories">');
    for (i = 0; i < ctl.items.length; i++) {
      item = ctl.items[i];
      if (!item.category)
        continue;
      this.out.push('<div');
      this.emitAttrs({
        'id': 'category-' + item.id,
        'class': 'category'
      });
      this.out.push('>');
      this.out.push('<input');
      this.emitAttrs({
        'id': item.id,
        'name': ctl.id,
        'type': 'hidden',
        'value': item.id
      });
      this.out.push('/>');
      this.out.push('<label>');
      this.out.push(item.label);
      this.out.push('</label>');
      this.out.push('<div class="dropTarget"></div>');
      this.out.push('</div>');
    }
    // Include input for leftover items
    this.out.push('<input');
    this.emitAttrs({
      'id': item.id,
      'name': ctl.id,
      'type': 'hidden',
      'value': 'leftovers'
    });
    this.out.push('/>');
    this.out.push('</div>');
    // Emit select items
    this.out.push('<div class="items">');
    for (i = 0; i < ctl.items.length; i++) {
      item = ctl.items[i];
      if (item.category)
        continue;
      this.out.push('<div');
      this.emitAttrs({
        'id': 'item-' + item.id,
        'class': 'item'
      });
      this.out.push('>');
      this.out.push('<input');
      this.emitAttrs({
        'id': item.id,
        'name': ctl.id,
        'type': 'hidden',
        'value': item.id
      });
      this.out.push('/>');
      this.out.push('<label>');
      this.out.push(item.label);
      this.out.push('</label>');
      this.out.push('</div>');
    }
    this.out.push('</div>');
    // Finish
    this.out.push('</fieldset>');
  }
Emits an associative group, e.g.

```
<fieldset id="{ctl.id}" class="associativeGroup">
<div class="categories>
<div id="category-#{item.id}" class="category">
<input id="{item.id}"
type="hidden"
name="{ctl.id}"
value="{item.id}"/>
<label>Category 1</label>
<div class="dropTarget"></div>
</div>
...
</div>
<div class="items">
<div id="item-{item.id}" class="item">
<input id="{item.id}"
type="hidden"
name="{ctl.id}"
value="{item.id}"/>
<label>{item.label}</label>
</div>
...
</div>
</fieldset>
```