# GvaScript.Repeat

Support for repeated sections

### Table of contents

NAME
SYNOPSIS
DESCRIPTION
Terminology
HTML
Markup of repeat elements
String substitutions within repeat elements
METHODS
init
add
remove
EVENTS
onAdd
onRemove
SEE ALSO

## NAME

GvaScript.Repeat - Support for repeated sections

## SYNOPSIS

      <div repeat="foo">
        <h2>Foo #{foo.count}</h2>
        This is the repeated foo section
        <table>
          <tr repeat="bar">
            <td>Item #{bar.count}</td>
            <td><input name="#{bar.path}.buz"></td>
            <td><button onclick="GvaScript.Repeat.remove('#{bar.path}')">
                  Remove this row
                </button></td>
          </tr>
        </table>
         <button onclick="GvaScript.Repeat.add('#{foo.path}.bar')">
            Add a bar
         </button>
      </div>
      <button onclick="GvaScript.Repeat.add('foo')">Add a foo</button>

      <script>
        GvaScript.Repeat.init(document.body);
      </script

## DESCRIPTION

This module of Alien::GvaScript implements a mechanism for repeated elements within an HTML page. The main use is within forms, for dynamic expansion of subforms, so it is quite likely that you will want to use the Alien::GvaScript::Form API instead of calling the present module directly. However, the repeat mechanism will work for any kind of HTML element.

The design is partially inspired by the `Web Forms 2.0` proposal (http://www.whatwg.org/specs/web-forms/current-work/), but is not an attempt to implement the proposed specification: there are some differences both in syntax and in semantics.

### Terminology

At initialization stage, the DOM is inspected for finding repeat elements (elements having a `repeat` attribute). These elements are removed from the DOM and replaced by placeholders (empty DOM elements, just marking where the repetition blocks should be inserted). Each placeholder stores a template, which is a plain string representation of the repeat element, with special markup for template instanciation. The template may then be dynamically instanciated into repetition blocks by calling the /add method.

## HTML

### Markup of repeat elements

      <div repeat="foo" repeat-start="3" repeat-min="2" repeat-max="6"
                        repeat-prefix="">
        <!-- content to be repeated, using #{foo.ix}, #{foo.path}, etc. -->
      </div>

Any element marked with a non-empty `repeat` attribute is a repeat element; the value of that attribute defines the repeat name. Repeat elements can be nested, and their repeat names are used to distinguish at which level a repetition block should be instanciated.

Other attributes of the repetion model all start with the `repeat` prefix, as listed below; these attributes are optional.

- repeat

  marks the element as a repeat element, and defines the repeat name.

- repeat-start

  defines how many repetition blocks should be automatically created as initialization time. Default is 1.

- repeat-min

  minimum number of repetition blocks. Prevents deletion of repetition blocks under that number. Default is 0.

- repeat-max

  maximum number of repetition blocks. Prevents addition of repetition blocks over that number. Default is 99.

- repeat-prefix

  defines the initial fragment of the `repeat path` (see explanation below). Only valid for top-level repetition elements, because nested repetition elements are automatically prefixed by their parent repetition element. Default is the empty string.

### String substitutions within repeat elements

The content of a repeat element may contain special markup for repetition variables, which will be replaced by values when the element is instanciated into a repetition block.

Variables are written `#{<repetition name>.<variable>}`, like for example `#{foo.ix}`, `#{foo.count}` or `#{bar.path}`. The repetition name is the value of the `repeat` attribute of the repeat element, as explained above; in this namespace, the following variables are defined

- count

  current number of repetition blocks for that repeat element, starting at 1.

- ix

  integer index of the current repetition block, starting at 0.

- path

  cumulated list of repetition name and repetition indices, separated by dots.

- min

  value of the `repeat-min` attribute.

- max

  value of the `repeat-max` attribute.

The `path` is unique within any nesting of repeat elements, and therefore is typically used for ids or for names of input elements :

      <tr repeat="bar">
        <td>Item #{bar.count}</td>
        <td><input name="#{bar.path}.buz"></td>
      </tr>

Within the 3rd repetition block of `bar`, nested within the 2nd repetition block of `foo`, the value of `#{bar.path}` will be `foo.2.bar.3` and therefore in this example the input name would be `foo.2.bar.3.buz`.

Implementation note: the `#{...}` syntax for variables is borrowed from `prototype.js`'s `Template` class; however, the implementation of substitutions is done in plain javascript, without calling `Template.evaluate()` method.

## METHODS

### init

      GvaScript.Repeat.init(element)

Walks down the DOM under the given element, finds all elements having a `repeat` attribute, replaces these by placeholders.

The `element` argument will be fed to `prototype.js`'s `$()` function, so it can be either a DOM element or an id string.

### add

      GvaScript.Repeat.add(repeat_name, count)

Creates one or several new repetition blocks in the DOM, after the current blocks, by instanciating the repeat element `repeat_name` (including prefix). The `count` argument is optional and states how many blocks should be added; default is 1. Returns the final number of repetition blocks for that repeat element.

If the repeat structure looks like

      <div repeat="foo" repeat-prefix="top">
         <div repeat="bar">

then a new `foo` repetition block is instanciated through `add('top.foo')`, and a new `bar` repetition block is instanciated through `add('#{foo.path}.bar')`.

### remove

      GvaScript.Repeat.remove(repetition_block)

Removes a repetition block from the DOM. The argument is either a DOM element or a string containing the element id.

All repetition blocks above the removed block are renumbered, leaving no hole in the index sequence. To do so, these blocks are also removed from the DOM, and then added again through the /add method.

If the repetition block is within a form, you probably want to call Alien::GvaScript::Form/remove instead of the present method, in order to properly preserve current form data.

## EVENTS

For a general explanation on registering handlers for GvaScript events, see the event documentation.

### onAdd

      <div repeat="Foo"
           onAdd="alert('a new Foo is born with index #{Foo.ix}')">

This event is triggered whenever a new repetition block is created through the /add method (including when the /init method instantiates `repeat-start` initial repetition blocks).

### onRemove

      <div repeat="Foo"
           onRemove="alert(event.target.id + ' is removed')">

This event is triggered whenever a repetition block is removed through the /remove method. Since this method also removes all repetition blocks above the requested block, the event will be triggered once for each block.

## SEE ALSO

Alien::GvaScript