<commit_before>define(
  'ephox.alloy.demo.FormDemo',

  [
    'ephox.alloy.api.Gui',
    'ephox.alloy.api.behaviour.Keying',
    'ephox.alloy.api.behaviour.Representing',
    'ephox.alloy.api.behaviour.Tabstopping',
    'ephox.alloy.api.ui.Form',
    'ephox.alloy.api.ui.FormChooser',
    'ephox.alloy.api.ui.FormField',
    'ephox.alloy.api.ui.Input',
    'ephox.alloy.demo.HtmlDisplay',
    'ephox.alloy.registry.Tagger',
    'ephox.boulder.api.Objects',
    'ephox.highway.Merger',
    'ephox.knoch.future.Future',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option',
    'ephox.perhaps.Result',
    'ephox.sugar.api.Class',
    'ephox.sugar.api.Element',
    'ephox.sugar.api.Insert',
    'global!document',
    'global!setTimeout'
  ],

  function (Gui, Keying, Representing, Tabstopping, Form, FormChooser, FormField, Input, HtmlDisplay, Tagger, Objects, Merger, Future, Fun, Option, Result, Class, Element, Insert, document, setTimeout) {
    return function () {
      var gui = Gui.create();
      var body = Element.fromDom(document.body);
      Class.add(gui.element(), 'gui-root-demo-container');
      Insert.append(body, gui.element());

      var textMunger = function (spec) {
        var invalidUid = Tagger.generate('demo-invalid-uid');
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div'
          },
          parts: {
            field: {
              invalidating: {
                invalidClass: 'invalid-input',
                notify: {
                  getContainer: function (input) {
                    return input.getSystem().getByUid(invalidUid).fold(Option.none, function (c) {
                      return Option.some(c.element());
                    });
                  }
                },
                validator: {
                  validate: function (input) {
                    var v = Representing.getValue(input);
                    return Future.nu(function (callback) {
                      setTimeout(function () {
                        var res = v.indexOf('a') === 0 ? Result.error('Do not start with a!') : Result.value({ });
                        callback(res);
                      }, 1000);  
                    });
                  },
                  onEvent: 'input'
                }
              }
            },
            label: {
              dom: { tag: 'label', innerHtml: spec.label }
            }
          },
          components: [
            FormField.parts(Input).label(),
            { uiType: 'container', uid: invalidUid },
            FormField.parts(Input).field()
          ]
        });
      };

      var coupledTextMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div'
          },
          parts: {
            'field-1': spec.field1,
            'field-2': spec.field2,
            lock: { uiType: 'button', dom: { tag: 'button', innerHtml: 'x' }, tabstopping: true }
          },
          markers: {
            lockClass: 'demo-selected'
          },
          onLockedChange: function (primary, current, other) {
            var cValue = Representing.getValue(current);
            Representing.setValue(other, cValue);
          },

          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-1>', owner: 'coupled-text-input' },
            { uiType: 'placeholder', name: '<alloy.form.field-2>', owner: 'coupled-text-input' },
            { uiType: 'placeholder', name: '<alloy.form.lock>', owner: 'coupled-text-input' }
          ]
        });
      };

      var selectMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div',
            styles: {
              border: '1px solid blue'
            }
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-label>', owner: 'formlabel' },
            {
              uiType: 'container',
              dom: {
                classes: [ 'wrapper' ]
              },
              components: [
                { uiType: 'placeholder', name: '<alloy.form.field-input>', owner: 'formlabel' }
              ]
            }
          ],
          members: {
            option: {
              munge: function (spec) {
                return {
                  dom: {
                    attributes: {
                      value: spec.value
                    },
                    innerHtml: spec.text
                  }
                };
              }
            }
          },
          parts: {
            field: {
              dom: {
                classes: [ 'ephox-select-wrapper' ]
              },
              tabstopping: true,
              focusing: true
            }
          }
        });
      };

      var radioMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'fieldset'
          },
          label: 'Radio',
          parts: {
            legend: { },
            fields: { }
          },
          markers: {
            radioSelector: 'input[type="radio"]'
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.radio-fields>', owner: 'radio-group' },
            { uiType: 'placeholder', name: '<alloy.form.field-legend>', owner: 'radio-group' }
          ]
        });
      };

      var customRadioMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'fieldset'
          },
          label: 'Radio',
          parts: {
            legend: { },
            fields: { }
          },
          markers: {
            itemClass: 'ephox-pastry-independent-button',
            selectedClass: 'demo-selected'
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-legend>', owner: 'radio-group' },
            {
              uiType: 'container',
              components: [
                { uiType: 'placeholder', name: '<alloy.form.radio-fields>', owner: 'radio-group' }
              ]
            }
          ]
        });
      };

      var mungers = {
        'text-input': textMunger,
        'select-input': selectMunger,
        'radio-group': radioMunger,
        'custom-radio-group': customRadioMunger,
        'coupled-text-input': coupledTextMunger
      };

      var fieldParts = {
        omega: {
          type: 'coupled-text-input',
          field1: { type: 'text-input', label: 'Omega.1' },
          field2: { type: 'text-input', label: 'Omega.2' }
        },
        alpha: FormField.build(Input, textMunger({ label: 'Alpha', inline: false })),
        beta: FormField.build(Input, textMunger({ label: 'Beta', inline: false })),
        gamma: FormField.build(Input, textMunger({ label: 'Gamma', inline: false })),
        delta: FormField.build(Input, textMunger({ label: 'Delta', inline: false })),
        epsilon: FormField.build(Input, textMunger({ label: 'Epsilon' })),
        rho: FormChooser.build({
          radioStyle: 'icons',

          parts: {
            legend: {
              dom: {
                innerHtml: 'Rho'
              }
            },
            choices: { }
          },

          markers: {
            choiceClass: 'ephox-pastry-independent-button',
            selectedClass: 'demo-selected'
          },

          dom: {
            tag: 'div'
          },
          components: [
            FormChooser.parts().legend(),
            FormChooser.parts().choices()
          ],
          behaviours: Objects.wrapAll([
            Tabstopping.config(true)
          ]),
          members: {
            choice: {
              munge: function (data) {
                return {
                  uiType: 'custom',
                  dom: {
                    tag: 'span',
                    classes: [ 'ephox-pastry-independent-button' ],
                    attributes: {
                      title: data.text
                    },
                    styles: {
                      display: 'flex'
                    }
                  },

// <span class="ephox-pastry-independent-button ephox-pastry-button ephox-polish-dialog-float-container ephox-polish-dialog-float-selected" unselectable="on" tabindex="-1" data-float-value="none" 
//   title="Align None" aria-label="Align None" role="radio" aria-checked="true" style="-webkit-user-select: none;">
//                 <div role="presentation" aria-hidden="true" class="ephox-polish-dialog-float-icon">
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-left"></div>
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-center"></div>
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-right"></div>
//                 </div>
//               </span>


                  label: 'Alignment',
                  parts: {
                    legend: { },
                    fields: { }
                  },
                  markers: {
                    radioSelector: 'input[type="radio"]'
                  }
                };
              }
            }
          },
          name: 'rho',
          choices: [
            { value: 'left', text: 'Left' },
            { value: 'middle', text: 'Middle' },
            { value: 'right', text: 'Right' }
          ]
        }),
        theta: {
          type: 'select-input',
          label: 'AA',
          options: [
            { value: 'a.a', text: 'A.A' },
            { value: 'b.b', text: 'B.B' },
            { value: 'c.c', text: 'C.C' },
            { value: 'd.d', text: 'D.D' }
          ]        
        }
      };

      var form = HtmlDisplay.section(
        gui,
        'This form has many fields',
        Form.build({
          dom: {
            tag: 'div',
            classes: [ 'outside-form' ]
          },
          members: {
            ui: {
              munge: function (spec) {
                return mungers[spec.type](spec);
              }
            }
          },
          parts: Objects.narrow(fieldParts, [ 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'rho' ]),

          components: [
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.alpha>' },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.beta>' },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.gamma>' },
            {
              uiType: 'container',
              components: [
                { uiType: 'placeholder', owner: 'form', name: '<alloy.field.delta>' },
                { uiType: 'placeholder', owner: 'form', name: '<alloy.field.epsilon>' }
              ]
            },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.rho>' }

          ],

          behaviours: {
            keying: {
              mode: 'cyclic'
            }
          }
        })
      );

      Representing.setValue(form, {
        alpha: { value: 'doggy', text : 'doggy' },<commit_msg>VAN-44-wheel-hackery-apis-2: removing mungers<commit_after>define(
  'ephox.alloy.demo.FormDemo',

  [
    'ephox.alloy.api.Gui',
    'ephox.alloy.api.behaviour.Keying',
    'ephox.alloy.api.behaviour.Representing',
    'ephox.alloy.api.behaviour.Tabstopping',
    'ephox.alloy.api.ui.Form',
    'ephox.alloy.api.ui.FormChooser',
    'ephox.alloy.api.ui.FormField',
    'ephox.alloy.api.ui.Input',
    'ephox.alloy.demo.HtmlDisplay',
    'ephox.alloy.registry.Tagger',
    'ephox.boulder.api.Objects',
    'ephox.highway.Merger',
    'ephox.knoch.future.Future',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option',
    'ephox.perhaps.Result',
    'ephox.sugar.api.Class',
    'ephox.sugar.api.Element',
    'ephox.sugar.api.Insert',
    'global!document',
    'global!setTimeout'
  ],

  function (Gui, Keying, Representing, Tabstopping, Form, FormChooser, FormField, Input, HtmlDisplay, Tagger, Objects, Merger, Future, Fun, Option, Result, Class, Element, Insert, document, setTimeout) {
    return function () {
      var gui = Gui.create();
      var body = Element.fromDom(document.body);
      Class.add(gui.element(), 'gui-root-demo-container');
      Insert.append(body, gui.element());

      var textMunger = function (spec) {
        var invalidUid = Tagger.generate('demo-invalid-uid');
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div'
          },
          parts: {
            field: {
              invalidating: {
                invalidClass: 'invalid-input',
                notify: {
                  getContainer: function (input) {
                    return input.getSystem().getByUid(invalidUid).fold(Option.none, function (c) {
                      return Option.some(c.element());
                    });
                  }
                },
                validator: {
                  validate: function (input) {
                    var v = Representing.getValue(input);
                    return Future.nu(function (callback) {
                      setTimeout(function () {
                        var res = v.indexOf('a') === 0 ? Result.error('Do not start with a!') : Result.value({ });
                        callback(res);
                      }, 1000);  
                    });
                  },
                  onEvent: 'input'
                }
              }
            },
            label: {
              dom: { tag: 'label', innerHtml: spec.label }
            }
          },
          components: [
            FormField.parts(Input).label(),
            { uiType: 'container', uid: invalidUid },
            FormField.parts(Input).field()
          ]
        });
      };

      var coupledTextMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div'
          },
          parts: {
            'field-1': spec.field1,
            'field-2': spec.field2,
            lock: { uiType: 'button', dom: { tag: 'button', innerHtml: 'x' }, tabstopping: true }
          },
          markers: {
            lockClass: 'demo-selected'
          },
          onLockedChange: function (primary, current, other) {
            var cValue = Representing.getValue(current);
            Representing.setValue(other, cValue);
          },

          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-1>', owner: 'coupled-text-input' },
            { uiType: 'placeholder', name: '<alloy.form.field-2>', owner: 'coupled-text-input' },
            { uiType: 'placeholder', name: '<alloy.form.lock>', owner: 'coupled-text-input' }
          ]
        });
      };

      var selectMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'div',
            styles: {
              border: '1px solid blue'
            }
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-label>', owner: 'formlabel' },
            {
              uiType: 'container',
              dom: {
                classes: [ 'wrapper' ]
              },
              components: [
                { uiType: 'placeholder', name: '<alloy.form.field-input>', owner: 'formlabel' }
              ]
            }
          ],
          members: {
            option: {
              munge: function (spec) {
                return {
                  dom: {
                    attributes: {
                      value: spec.value
                    },
                    innerHtml: spec.text
                  }
                };
              }
            }
          },
          parts: {
            field: {
              dom: {
                classes: [ 'ephox-select-wrapper' ]
              },
              tabstopping: true,
              focusing: true
            }
          }
        });
      };

      var radioMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'fieldset'
          },
          label: 'Radio',
          parts: {
            legend: { },
            fields: { }
          },
          markers: {
            radioSelector: 'input[type="radio"]'
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.radio-fields>', owner: 'radio-group' },
            { uiType: 'placeholder', name: '<alloy.form.field-legend>', owner: 'radio-group' }
          ]
        });
      };

      var customRadioMunger = function (spec) {
        return Merger.deepMerge(spec, {
          dom: {
            tag: 'fieldset'
          },
          label: 'Radio',
          parts: {
            legend: { },
            fields: { }
          },
          markers: {
            itemClass: 'ephox-pastry-independent-button',
            selectedClass: 'demo-selected'
          },
          components: [
            { uiType: 'placeholder', name: '<alloy.form.field-legend>', owner: 'radio-group' },
            {
              uiType: 'container',
              components: [
                { uiType: 'placeholder', name: '<alloy.form.radio-fields>', owner: 'radio-group' }
              ]
            }
          ]
        });
      };

      var mungers = {
        'text-input': textMunger,
        'select-input': selectMunger,
        'radio-group': radioMunger,
        'custom-radio-group': customRadioMunger,
        'coupled-text-input': coupledTextMunger
      };

      var fieldParts = {
        omega: {
          type: 'coupled-text-input',
          field1: { type: 'text-input', label: 'Omega.1' },
          field2: { type: 'text-input', label: 'Omega.2' }
        },
        alpha: FormField.build(Input, textMunger({ label: 'Alpha', inline: false })),
        beta: FormField.build(Input, textMunger({ label: 'Beta', inline: false })),
        gamma: FormField.build(Input, textMunger({ label: 'Gamma', inline: false })),
        delta: FormField.build(Input, textMunger({ label: 'Delta', inline: false })),
        epsilon: FormField.build(Input, textMunger({ label: 'Epsilon' })),
        rho: FormChooser.build({
          radioStyle: 'icons',

          parts: {
            legend: {
              dom: {
                innerHtml: 'Rho'
              }
            },
            choices: { }
          },

          markers: {
            choiceClass: 'ephox-pastry-independent-button',
            selectedClass: 'demo-selected'
          },

          dom: {
            tag: 'div'
          },
          components: [
            FormChooser.parts().legend(),
            FormChooser.parts().choices()
          ],
          behaviours: Objects.wrapAll([
            Tabstopping.config(true)
          ]),
          members: {
            choice: {
              munge: function (data) {
                return {
                  uiType: 'custom',
                  dom: {
                    tag: 'span',
                    classes: [ 'ephox-pastry-independent-button' ],
                    attributes: {
                      title: data.text
                    },
                    styles: {
                      display: 'flex'
                    }
                  },

// <span class="ephox-pastry-independent-button ephox-pastry-button ephox-polish-dialog-float-container ephox-polish-dialog-float-selected" unselectable="on" tabindex="-1" data-float-value="none" 
//   title="Align None" aria-label="Align None" role="radio" aria-checked="true" style="-webkit-user-select: none;">
//                 <div role="presentation" aria-hidden="true" class="ephox-polish-dialog-float-icon">
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-left"></div>
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-center"></div>
//                   <div role="presentation" class="ephox-polish-dialog-float-icon-inner-right"></div>
//                 </div>
//               </span>


                  label: 'Alignment',
                  parts: {
                    legend: { },
                    fields: { }
                  },
                  markers: {
                    radioSelector: 'input[type="radio"]'
                  }
                };
              }
            }
          },
          name: 'rho',
          choices: [
            { value: 'left', text: 'Left' },
            { value: 'middle', text: 'Middle' },
            { value: 'right', text: 'Right' }
          ]
        }),
        theta: {
          type: 'select-input',
          label: 'AA',
          options: [
            { value: 'a.a', text: 'A.A' },
            { value: 'b.b', text: 'B.B' },
            { value: 'c.c', text: 'C.C' },
            { value: 'd.d', text: 'D.D' }
          ]        
        }
      };

      var form = HtmlDisplay.section(
        gui,
        'This form has many fields',
        Form.build({
          dom: {
            tag: 'div',
            classes: [ 'outside-form' ]
          },
       
          parts: Objects.narrow(fieldParts, [ 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'rho' ]),

          components: [
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.alpha>' },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.beta>' },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.gamma>' },
            {
              uiType: 'container',
              components: [
                { uiType: 'placeholder', owner: 'form', name: '<alloy.field.delta>' },
                { uiType: 'placeholder', owner: 'form', name: '<alloy.field.epsilon>' }
              ]
            },
            { uiType: 'placeholder', owner: 'form', name: '<alloy.field.rho>' }

          ],

          behaviours: {
            keying: {
              mode: 'cyclic'
            }
          }
        })
      );

      Representing.setValue(form, {
        alpha: { value: 'doggy', text : 'doggy' },
{'fraction_non_alphanumeric': 0.14345568813188814, 'fraction_numerical': 0.0016850350669459877, 'mean_word_length': 1.3129344849378555, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2480472', 'n_tokens_mistral': 6325, 'n_tokens_neox': 6134, 'n_words': 1107}