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 = [];
    
    this.selector.id = ctl.id;
    
    if (!Array.isArray(this.selector.classes))
      this.selector.classes = [];
    this.selector.classes.push('associativeGroup');
    
    this.out.push('<fieldset');
    this.emitSelector();
    this.out.push('>');
    
    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;
      }
    }
    
    this.parseAssociativeItem(ctl, new SubWalker(walk, startIdx, walk.position));
    
    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>');
    }
    
    this.out.push('<input');
    this.emitAttrs({
      'id': item.id,
      'name': ctl.id,
      'type': 'hidden',
      'value': 'leftovers'
    });
    this.out.push('/>');
    this.out.push('</div>');
    
    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>');
    
    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>
```
Emits an associative group e . g .