Custom Web Software Development

JavaScript, HTML 5, CSS 4, & PHP 8: Innovative Enterprise level Scripting for interactive sites, SaaS, & cross-platform desktop apps
  • Home
  • Automotive Dealer Website Development
  • Free desktop apps
  • Rainbow-Maker
  • MasterColorPicker
  • Free libraries: JavaScript and PHP
  • <input type="picker">
  • RGB_Calc color-space converter
  • FormFieldGenie fieldset multiplier
  • UniDOM.js x-browser solution +
  • eMail address validation scripts
  • PHP Utility Functions
  • SoftMoon Humane Use License
  • ActiveState Komodo Edit support
  • FormFieldGenie Text-Input Auto-Pop-Up

    This JavaScript powered multifunctional class allows you to create an end-user-manipulatable HTML form that requires an unknown number of textual-input / file-input form fields.  When you don’t want to limit your visitors to a minimal number of input options, but don’t want your page display to be bloated with a large number of empty user-input boxes, this is the utility you need.  An end-user can even cut, copy, and paste entire sections of an HTML form to/from multi-clip clipboards, if you set up your Genie and HTML to do so.  We use this class in our ►MasterColorPicker package, and you can see useful working integrated examples there (in the “MyPalette” and “Gradientor” sub-applications, and the “Color Filter” extension).

    ¿What do you use this for?  When you have to ask someone to list each of something you request, and they may need one (1) form field, or they may need fifty (50), or anywhere in between or even more!  Consider asking about someone’s children and grandchildren.  In one family there may be two children, each with one grandchild.  What about a large family with 7 children, each with an average of 5 grandchildren?  Do you create a form that accommodates the large family, forcing the small family to wade through your web page like trying to navigate swamp water?  And what about the family with 12 children and 78 grandchildren?  Of course there are functions that say “click this button to create a new form field”. Clicking once or twice may be OK.  I've seen that approach used to upload files, and it annoyed me when I had to upload two dozen, simply because it broke the “flow” of selecting each file; and that was all point and click.  When you have to actually type in a phrase, then switch to the mouse to click, then back to the keyboard... This is much easier — even when an end-user just needs to simply upload an unknown number of files.  And on the back-end, the FormFieldGenie can help you very simply organize complex data sent to the server as the end-user enters it

    The best way to understand this class is to exemplify it, so we created a ►working demo.  You should also view the highlighted ►demo source code to understand what is happening, why and how.  However, this instruction page provides a basic overall concept and details that make understanding the demo code easy.  You can download the JavaScript package from our ►download page or Git it from GitHub: ►FormFieldGenie.

    Using this class is simple and straight-forward, requiring only a few JavaScript event handlers be added to the form field(s) you want duplicated (cloned).  But it is no simple class: not only can it clone the form field that the event handlers are attached to, but any associated form fields, their labels, and any other DOM nodes that go along.  If the said form-fields are being sent to a server, then the other primary function this class serves is to update the names of the cloned form-fields and keep them in order when new fields are “popped,” “pasted,” “inserted,” or “deleted”.  We will attempt to explain its use in terms that a non-programmer can understand, so you can incorporate this class successfully in your site, but a basic amount of HTML understanding and a minimum amount of knowledge on using JavaScript in your HTML pages is required.  It has a list of options that allow you to fine-tune the way it works making it incredibly versatile.  Plus it allows plug-in callback functions that allow it infinite flexibility.  It intelligently updates the name associated with each form field cloned based on each individual name; if your given name is incompatible with this class’ capabilities, your custom plugin can handle it.

    To create a new FormFieldGenie instance:

    myGenie=new SoftMoon.WebWare.FormFieldGenie({…options object…}, HTML_clipMenu, genie_name);

    All parameters are optional.  Jumping ahead, the genie_name is only used in “advanced” forms in conjunction with the HTML attributes genie-tier-index and update-value-genie.  To be useful, the name should therefore be valid in an HTML attribute, so it may not contain line-endings.  It may contain any textual characters except the comma , (the comma is used to separate the names within the HTML attributes).  The Genie will throw an Error on these illegal characters if you try to use them.  Any whitespace at the beginning or end will be trimmed off and ignored, but whitespace in the middle is OK.  After creating a Genie instance, you can set or change the name like in this example:  myGenie.name='foo'

    When adding a new form-field or group of form-fields (using the popNewGroup() method), the FormFieldGenie can create (clone) one based on what already exists in the form (more on that below), or you can explicitly give it a form-field or group of form-fields to clone.  You may pass an explicit DOM node (form-field or group of form-fields) to clone when creating an instance of the FormFieldGenie;  for example:

    myGenie=new SoftMoon.WebWare.FormFieldGenie({clone: ……my DOM node to clone…… }); myGenie.popNewGroup(……)

    After creating an instance of the FormFieldGenie, you may also set the instance.config.clone (myGenie.config.clone in these examples) to the explicit DOM node (form-field or group of form-fields) you want to clone (if any).  An example of defining an explicit node to clone:

    myGenie=new SoftMoon.WebWare.FormFieldGenie; myGenie.config.clone= ……my DOM node to clone…… myGenie.popNewGroup(………)

    Using the FormFieldGenie

    The first parameter passed to most of this class’ methods (the “actionable” methods) is the entire DOM Element Node you want auto-regenerated, deleted, cut, copied, inserted before, or pasted-over.  A quick look at the source code of the demonstration example will help clarify this.  If you only need the form field itself repeated, pass the value of this to the method.  Remember, in an event handler attached to form fields, the keyword this refers to the DOM node of the form-field itself.  If the field has a <label> Element (or any other Element) around it that you want cloned also, simply pass this.parentNode.  If you want to clone a whole group of fields and associated text, simply repeat parentNode in a chain as many times as needed to move up the DOM tree.  Alternatively, you could use the .closest() method of a DOM Element, perhaps relating to the class-name you assign to your “group” (see also groupClass in the ConfigStack description).  Note that the FormFieldGenie started out as a single simple function back in the days of dinosaur browsers, when focusIn & focusOut events (which do bubble) were not standard.  You had to use focus & blur events (which do not bubble) directly on the <input><textarea> Elements, requiring an event-handler for each that needs to keep track of parentNodes and their “anscestors.”  The demo (test!) example was developed then.  These days you can put the event-handlers on parentNodes and their “ancestors” (as in the short simple example shown below), greatly simplifying the application of this class to your HTML.

    From there, the class makes one requirement: there must be an Element enclosing the group (see below) you want cloned.  The clones are added into this enclosing Element.  First, let’s be clear that any HTML “elements” may be used; it does not matter to the FormFieldGenie.  For the sake of discussion, we will refer to these these as follows:

    In the super-simple example shown below, the <input> is the fieldNode, the <label> is the group, and the <fieldset> is the batch.  The <legend> will be ignored (see more info in the next paragraph).  The number 1 will be updated in the <input>’s name attribute when the group is cloned and the clone is added to the batch.  This updated number will be kept in proper sequence (without gaps) with the order of <input>s in the HTML, which is generally and accordingly the sequence shown to the end-user.  (Alternatively, with PHP at least, the name could simply be user[file][] and the FormFieldGenie will do nothing to it, because it doesn’t need to! …more on that below…)  Also note that the event-handlers could be moved to the <label> and we could then use the keyword this instead of event.target.parentNode  If we keep them on the <fieldset> they could be attached using addEventListener() instead of using inline-attributes, as they will not need to be cloned; the <label> gets cloned, so it is easier to use inline-attributes there since they get cloned with the Element, whereas “attached EventListeners” do not get cloned.

    <fieldset onfocusin='myGenie.tabbedOut=false' onkeydown='myGenie.catchTab(event)' onfocusout='myGenie.popNewGroup(event.target.parentNode)'> <legend>select the files you want to upload</legend> <label>choose a file<input type='file' name='user[file][1]'></label> </fieldset>

    The batch may contain any other HTML besides the clones; but the clones will always be added to the batch directly following the last group.  See also the config.groupClass and config.groupTag options in the ConfigStack description below.  In this example above, myGenie.config.groupTag='LABEL' must be set.  Beyond this, you are free to develop your page the way you see fit. 

    Any time your keyboard cursor is focused on a fieldNode and then you leave it by pressing the Tab key or by clicking the mouse button somewhere else, the class looks at the “current” group (the one with keyboard focus within) and all the groups within the batch that follow the “current” group.  If any are empty (except for the last one), by default it deletes them (you can change this default action with options; see dumpEmpties() in the ConfigStack description below).  It then looks within the last group and by default (see checkForFilled and checkField in the ConfigStack description below) finds the first text-based <input> tag (see userDataInputTypes in the ConfigStack description below), or if there are no <input>s, by default finds the first <textarea>, then looks to see if anything was typed in (or similar for type='file').  If this fieldNode has been filled by the user, the class ‘pops’ a new group.

    If the end-user pressed the Tab key from within a fieldNode (to exit it) and a new group was popped, by default the cursor is focused within a specified fieldNode of the new group.  If nothing was popped, the cursor is controlled by the browser as usual, and typically is passed onto the next form field.  If the end-user (mouse-)“clicked” out of the fieldNode, the usual action is taken by the browser depending on where you click, whether or not a new group was popped.

    That sums up the main framework of the class.  Its true power begins to shine when you look at how it updates the names associated with each form field cloned, and how the available options you can pass to the class can modify the default actions of the main framework and the process of updating the field names.  This gives it the ability to work with virtually any HTML form of any complexity, and the corresponding supporting server side script.

    Updating fieldNode names

    When this class updates a name, it considers several things.  Is the name indexed, or not? (i.e. name[index] or name[index][index] etc.)  Remember in PHP, when a name is indexed, a “mapped-array” or nested mapped-arrays is/are created.  If it is indexed, is the last index virtual (“implicit”)? (i.e. name[] or name[index][] etc.)  Remember in PHP, when multiple form fields return the same virtual (“implicit”) indexed name they are automatically placed into a sequentially numbered array.  The individual “indexes” in the name may be numeric, or textual; for example: name='foo[1][bar][3][baz][7][bing][]'.  When multiple “indexes” are present, as in the above example, we are signifying “nested arrays”, and each “level” of nesting we call an “index tier”.  If the field’s name is not indexed, this function considers whether the last characters are numeric digits, or not (e.g. name='foo' or name='foo123' etc.).  Remember when a form returns multiple fields of the same name without using virtual (“implicit”) indexes, the last one overwrites all the others when using PHP.  It should be mentioned here that if these built-in processes for updating the name don’t suit your needs, you may write your own plug-in that can do anything you want!  (See updateName() in the ConfigStack description below.)  The one other thing to be considered when updating the name is a special case involving the value of checkboxes and radio-buttons; this involves reasons similar to those involved when updating virtually indexed names, and we will take a look at this further down… … …

    Data organization with the FormFieldGenie

    Perhaps the number one (#1) most important consideration when engineering software systems is data organization.  Do everything you can to organize your data to serve your codebase; don’t organize your codebase to serve your data, unless you don’t have control over the data.  ¡Organize your data so that your code can most easily and quickly process it, and that depends on the process!  Typically, data is organized in a flat-list or a hierarchical-tree format (tables are another good example).  When you create an HTML form to send data to a server to be processed, it is inherently sent by the browser to the server as a flat-list; each fieldNode’s name is paired with its value, and the pairs are sent sequentially.  As mentioned above, the PHP language on a server can interpret the name sent and create “arrays” … and arrays nested within arrays nested with arrays…  This automatically converts the flat-list data-format to a hierarchical-tree data-format (and that format can moonlight as a table-format if need be).  Quite often, dealing with complex data is better suited to the hierarchical-tree format, rather that trying to coordinate a bunch of flat-lists.  The FormFieldGenie was designed with the hierarchical-tree format in mind, by supporting “indexed names” with nested Genies…

    Fully Indexed Names

    The class by default looks for the last index in the name that is numeric and increases it by one for each new field popped.  If no index is found to be numeric, the name is left unchanged. You can specify which index in the name gets modified with the opts.indexTier parameter, or by using an HTML attribute.  The indexTier is perhaps the most important property of the FormFieldGenie ConfigStack, and you can find the technical specs behind this option in that section of these instructions.  At times, you may create a form with a complex data structure, such that having one indexTier value that applies to all fieldNode names in a group is insufficient; then you use the custom HTML attribute genie-tier-index.  This attribute allows you to specify the tierIndex for one or more Genies (when they are nested).  Check out the example below, noting that you must not place any characters, including whitespace, between the Genie “names” and the following colon : but whitespace elsewhere in the attribute data-value is ignored.

    // your Genie names: ↓↓↓ ↓↓↓ ↓↓↓ <input name='foo[1][bar][3][baz][7][bing][]' genie-tier-index='myGenie: 0, otherGenie: 1, etcGenie: -2'> // updates: [1] ↑ [3] ↑ [7] ↑↑ // remember only numeric indexes in the name are “counted” // the default indxTier value of (-1) points to the numerically-implicit index [] at the end

    Virtually Indexed (Numerically Implicit) Names

    These are names with an “empty” index-tier at the end: <input name='foo[bars][]'> As noted, PHP automatically “numbers” them into a sequential array as it receives them.  There is often no need to do anything to these names, because the server side script (PHP) handles them as they should be.  If your form is returning a “deep tree” of data to PHP with Genie batches nested within Genie batches nested within … … …, and you are using a name with “numeric indices” as well as the “virtual index” at the end, one or more Genie(s) may need to update a numeric index, while another leaves the virtual index alone.  Because of this need, a virtual index is “counted” as a numeric index when using opts.indxTier.

    Non-indexed Names

    Non-indexed names without digits at the end are simply left alone.  This is generally only useful for radio buttons or <button> Elements.  Names that end in a sequence of digits have that sequence numerically increased by one for each new field popped.  Pay close attention to the favorite cars example and the data it produces when the form is submitted.  Note how only the last sequence of digits is increased, and the rest are left alone.

    Special case: checkboxes and radio buttons with “numeric” values

    In some cases the need arises for the value of these form fields to be updated, not the name.  This exception occurs sometimes when you use checkboxes and radio buttons.  The favorite pets section in the demonstration exemplifies this.  Take a look at the source code of the demonstration example and also at the test-demo itself.  Notice how the nickname section also uses checkboxes within the group to be cloned.  If we simply clone these and leave their names unchanged, the resulting data no longer co-ordinates with its associate data.  You get a sequential array of all the checkboxes checked by the user, but there is no way to tell which array element corresponds to which nickname.  If an <input>’s value is a number it can be updated instead of the name.  You can control which <input> values are updated by listing the specific Genie(s) that do so with an HTML Element attribute (remember, this only works with checkboxes and radio-buttons).  This becomes necessary when groups are nested within each other.  For example: <input type='checkbox' name='user[1][pets][favorites][]' value='0' update-value-genie='thePetGenie'>.  The Genie with the name thePetGenie will update the value of this <input>; but when it updates another fieldNode in the group it is cloning, and said fieldNode has the name user[1][pets][0][name], it will (by default with the indxTier=(-1)) update the [0] index in the name; furthermore, the value='index' and the [index] updated in the names will all have matching indexes within the same cloned group (provided your seed-clone originally had matching indexes).  In other words, the checkbox/radio-button’s value will indicate the group it is in.  The content of this update-value-genie attribute should be the name you pass into the FormFieldGenie constructor (you can also set it directly after you construct your Genie using something like: myGenie.name='foobar';).  If you want more than one Genie to update the checkbox / radiobutton value, specify their names in your attribute separated by commas.  For this reason, your Genie name may contain any characters except the comma , character (also, whitespace is trimmed off the beginning and end of your name).  Then only the Genie(s) you specify will update the value; other Genies that operate on your checkbox or radiobutton will update the <input>’s name as usual.

    Coping, Cutting, Pasting, & Inserting

    You can paste two different ways using three different methods:

    ( see “clip” in “options” below for more info on yourClipReference )

    The difference between popNewGroup() and pasteGroup() is that pasteGroup() will return false if the clip is empty, while popNewGroup() will simply pop a new “blank” clone if the clip is empty.  After creating an instance of the FormFieldGenie, the clipboard Object may be accessed through instance.clipboard;  each clipboard Object property may contain an individual clip (DOM node).  For more details, see the “options” description below.

    The HTML_clipMenu option is used when you want to allow your end-users the ability to cut/copy/paste/delete entire form sections (groups — when copied/cut we call them “clips”) into a multi-clip clipboard.  It should be an actual snippet of HTML with a specific format.  The FormFieldGenie will update the contents of this HTML menu by automatically adding/deleting <li> Elements that reference copied/cut “clips.”  These “clips” are handled as special by the FormFieldGenie and they have their own Array in the clipboard Object: myGenie.clipboard.clips.  In addition, the FormFieldGenie can manage keyboard and mouse interactions with the menu, if it is “bound” to the Genie instance.  To “bind” the HTML_clipMenu to your Genie instance you either • pass it in as the second parameter when creating an instance of the FormFieldGenie and it will be automatically “bound”; or • “bind” it to the Genie instance using for example myGenie.bind_clipMenu().  If you want to customize management of the end-user interaction with the menu, but still want its contents updated when the end-user cuts/copies/pastes/deletes etc., do not bind it, instead set it as the property: myGenie.HTML_clipMenu of your Genie instance.  When updating the menu, new <li> Elements that are added can be automatically given the tabindex attribute.  “Bound” menus’ <li> Elements always get a tabindex="-1".  “Unbound” menus’ <li> Elements get a tabindex if the .HTML_clipMenu_tabindex of the Genie’s .config options object is set (for example myGenie.config.HTML_clipMenu_tabindex).  That allows you to customize the tabindex value to your needs, but typically it would be only "0" or "-1" (by default it is set to "-1").  Every time the menu’s contents is updated, a custom ffgeniemenuupdate Event is dispatched on the menu; you can listen for this event and further customize the menu if you need to. Two examples are shown below: the first is typical when binding the menu to the Genie; the second is a basic suggestion on how you could manage an unbound HTML_clipMenu, but you might customize it more to your specific needs.

    <menu id='myGenie_bound_popUpMenu'> <li class='insert' tabindex="-1" controls='myGenie_popUpMenu_insertA myGenie_popUpMenu_insertB'>insert: <span class='new' id='myGenie_popUpMenu_insertA' tabindex="-1">new group</span> <ul class='clips' id='myGenie_popUpMenu_insertB'> <li standard tabindex="-1">all clips</li> </ul> </li> <li class='copy' tabindex="-1" controls='myGenie_popUpMenu_copy'>copy to:<ul class='clips' id='myGenie_popUpMenu_copy'> <li standard tabindex="-1">new clip</li></ul></li> <li class='cut' tabindex="-1" controls='myGenie_popUpMenu_cut'>cut to:<ul class='clips' id='myGenie_popUpMenu_cut'> <li standard tabindex="-1">new clip</li></ul></li> <li class='paste' tabindex="-1" controls='myGenie_popUpMenu_paste'>paste from:<ul class='clips' id='myGenie_popUpMenu_paste'> </ul></li> <li class='delete' title='triple-click' tabindex="-1">delete</li> <li title='triple-click' tabindex="-1">clear clipboard</li> </menu> <script> myGenie=new SoftMoon.WebWare.FormFieldGenie({…options…}, document.getElementById('myGenie_bound_popUpMenu')); </script> <menu id='myGenie_unbound_popUpMenu'> <li>insert:<span onclick='myGenie.popNewGroup(this.closest("."+myGenie.config.groupClass), {doso:"insert"})'>empty field</span> <ul class='clips' onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.pasteGroup(this.closest("."+myGenie.config.groupClass), {doso:"insert", clip:event.target.innerText})'> <li standard>all clips</li> </ul></li> <li>copy to:<ul class='clips' onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.copyGroup(this.closest("."+myGenie.config.groupClass), {clip:event.target.innerText})'> <li standard>new clip</li> </ul></li> <li>cut to:<ul class='clips' onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.cutGroup(this.closest("."+myGenie.config.groupClass), {clip:event.target.innerText})'> <li standard>new clip</li> </ul></li> <li>paste from:<ul class='clips' onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.pasteGroup(this.closest("."+myGenie.config.groupClass), {clip:event.target.innerText})'> </ul></li> <li onclick='if (confirm("Do you want to delete this group?")) myGenie.deleteGroup(this.closest("."+myGenie.config.groupClass))'>delete</li> <li onclick='myGenie.clearClipboard();'>clear clipboard</li> </menu> <script> myGenie=new SoftMoon.WebWare.FormFieldGenie({…options…}); myGenie.HTML_clipMenu=document.getElementById('myGenie_unbound_popUpMenu')); </script>

    Note that the standard attribute signifies <li> Elements in the <ul>s that do not get deleted.  You may modify the TEXT in the lone <span> without hassles.  For unbound menus, you may modify the TEXT in the <li> elements that belong to the <menu> without hassles.  If you need to modify any other text, for clarity in your specific project, or to support another language, etc., you must update the .HTML_clipMenu_TEXT property of your Genie instance (for example: myGenie.HTML_ClipMenu_TEXT={…your specs…};).  For instance, with a list of names, your TEXT may become: “insert name”, “copy name”, “cut name”, “paste name”, “delete name”, and your confirm dialog may become “Do you want to delete this name?”.  See the FormfieldGenie source-code for a description of this Object’s format.  You can have multiple versions of the basic menu in different languages, and change languages on-the-fly:

    myGenie.bind_clipMenu(false); //unbind the current menu … perhaps it is English /* the above step removes the event handlers — * if you think you might re-instate this menu, you can skip this first step * and keep it bound, and just re-set myGenie.HTML_clipMenu directly in the future */ myGenie.bind_clipMenu(document.getElementById('myGenie_popUpMenu_French')); //bind the newly selected language menu // ¡ if you are not using “bound” menus, skip the preceding first two steps ! … instead use: // myGenie.HTML_clipMenu=document.getElementById('myGenie_popUpMenu_French'); myGenie.HTML_ClipMenu_TEXT=French_TEXT; // our French_TEXT object is predefined elsewhere myGenie.update_HTML_clipMenu(); //fill the menu with the appropriate “clip” references

    You may change/add any id or class names to the HTML of the menu for CSS handles; however, the existing class-names must remain.  The myGenie variable in the “unbound” menu should of course be changed to the name of the variable that you use for your specific Genie (multiple Genies require multiple variables to be assigned to, and multiple HTML_clipMenus).  Remember, the first parameter passed into the Genie methods is the “current” group.  The “unbound” example above assumes you are using the groupClass option (see ConfigStack below); maybe you should use the groupTag option, or perhaps better yet, just hard-code the .closest() spec into your script.  The FormFieldGenie will add clip references to the menu that look like these examples (with the default TEXT): “clip 1”, “clip 2”, “clip 3”, etc.  Using the .HTML_clipMenu_TEXT Object (see above), you can modify the word “clip” to the word (or language) you desire.  Embedded JavaScript event-handler attributes may also be modified and/or expanded as appropriate to your needs.  Since this menu would be typically activated by the end-user using the “context menu” keyboard-key or mouse-button, and since the “context menu” may need to do more than support the FormFieldGenie in your specific application, you may (with restrictions): • add additional <li> elements to the main <menu>, • add any other HTML Elements within your additions, • add embedded <ul class='clips'>s to be filled with the current list of “clips” when the menu is updated.  With “bound” menus, and except for <ul class='clips'>s, your menu additions are restricted from having the same class-names as any existing class-names on similar elements. If your menu is “bound” these additions will be ignored, and you must support them.  When the menu is updated, all <li> Elements that reference copied/cut “clips” are deleted, and then the menu is rebuilt from scratch.  After the menu is updated, a custom 'ffgeniemenuupdate' event is fired on the HTML_clipMenu so you can further customize the update if needed.  If you need event-handlers directly on the auto-generated <li> Elements that reference copied/cut “clips,” you will have to use this custom event and then attach your events to these <li> Elements using element.attachEventListener() or by setting their attributes.  As an example of a difference, you may want the end-user to double or even triple-click to delete, and then do it with no confirmation: onclick='if (event.detail===3) myGenie.deleteGroup(this.closest("."+myGenie.config.groupClass))'.  The Genie itself will confirm if you want to clear the whole clipboard unless you auto-confirm using .clearClipboard(true)

    Note that using the HTML_clipMenu is not required to cut/copy/paste/delete using the Genie; but if you do inform the Genie of this menu’s existence, it will update it automatically for you.  This menu interfaces with the Genie’s innate ability to handle a “multi-clip” clipboard; but you don’t need to integrate the “multi-clip” functionality in your project.  Perhaps you only want to allow your end-users to copy/cut/paste into a “single-clip” clipboard so they don’t get confused, or for a smaller menu with a smaller footprint on the screen, or for whatever reason.  Perhaps then you need a simple “pop-up” menu like the one shown below.  (¡Note again that we are assuming the use of the groupClass option like the above example!)  The TEXT in this demo example menu below is meaningless to the Genie and you may change it to anything you like.  Note that we don’t specify the “clip” instead of calculating it like in the above example demo menu; the Genie will use the _system_ clip spec as defined in the ConfigStack prototype.  Moreover, We tried not to box you in as a developer.  By simplifying the cut/copy/paste menu as shown below, it could be used with may different batches using the same Genie in each batch, but using a different “clip” spec for each batch; perhaps then you would add to the imbedded JavaScript something like {clip: this.closest(".genie-batch").getAttribute("clip-spec")}

    <menu id='myGenie_simpleMenu'> <li onclick='myGenie.copyGroup(this.closest("."+myGenie.config.groupClass))'>copy section</li> <li onclick='myGenie.cutGroup(this.closest("."+myGenie.config.groupClass))'>cut section</li> <li onclick='myGenie.pasteGroup(this.closest("."+myGenie.config.groupClass))'>paste copied/cut section</li> <li onclick='myGenie.pasteGroup(this.closest("."+myGenie.config.groupClass), {doso:"insert"})'>insert copied/cut section</li> <li onclick='myGenie.popNewGroup(this.closest("."+myGenie.config.groupClass), {doso:"insert"})'>insert new section</li> <li onclick='myGenie.deleteGroup(this.closest("."+myGenie.config.groupClass))'>delete section</li> </menu>

    The way the embedded JavaScript is written in both of these “menu” examples, your webpage JavaScript code should further manage this HTML menu, typically moving it “into” the group HTML when the end-user either •right-clicks (menu-clicks) in said group or •presses the “menu” key (looks kinda like this: when it is present on the keyboard) and keyboard-focus is “in” a fieldNode within said group, and style it with CSS so it “pops-up” (appears) at the right place at that time.  If your menu is “bound,” you simply use myGenie.show_clipMenu(event, group, container) where: • event is any event, but a keyboard event creates the opportunity for the end-user to return to the Element that had keyboard focus; • group is the FormFieldGenie group that the menu refers to; and • container is the DOM Element that the HTML_clipMenu is moved into when it “pops-up,” so it appears in the correct position on the screen (according to your CSS).  “Unbound” menus should also have full keyboard support, by including a tabindex attribute on all <li> Elements, and the .config.HTML_clipMenu_tabindex option will automatically add this attribute to the auto-generated <li> Elements that reference the clips.  Your custom JavaScript should support the arrow-keys (, , , ) to navigate the menu, the ↵Enter and Spacebar keys to make a selection, the Esc and maybe even the menu keys to exit without a selection made, etc.  It would typically need additional Event handlers so it “pops down” (disappears) when a selection is made; and/or maybe Event-handlers attached to the body to do the same thing if clicked elsewhere, or another form-field somewhere else gets focus, etc.  Technically speaking, you should also include full ARIA haspopup support, but implementing all that is beyond the scope of these instructions.  If you “bind” your menu, all that is handled for you.  “Bound” menus also get custom "popUp" and "popDown" events at the appropriate time, and the event that triggered the pop-state-change can be found in the custom-event’s .detail property.

    The publicly accessible properties of a FormFieldGenie instance are:

    .config
    An instance of a FormFieldGenie.ConfigStack.  This holds all the configuration values for a FormFieldGenie instance.  You can myGenie.config.stack() a new set of values (of your choice) onto the stack to temporarily change them, then myGenie.config.cull() off those modifications to restore the configuration to the previous values.  Any time you pass an opts (options) object to any method, those options are temporarily stacked onto this stack while the method is operating.
    .clipboard
    This Object holds all the clipboard data for your Genie instance.
    .HTML_clipMenu
    This points to the HTML that is the “menu” shown to the end-user with the “multi-clip” clipboard data for your Genie instance.  If you are not implementing a “multi-clip” interface, this property should be undefined.
    .HTML_clipMenu_TEXT
    This Object references the HTML’s TEXT in the “menu” and can be replaced with references to different customized text or languages.
    .name
    The name of your specific Genie.  It may not contain the comma , character or line-endings.  Whitespace at the beginning and end will be trimmed off.
    .tabbedOut
    this Boolean value indicates if the end-user pressed the Tab key to exit a form field.

    The publicly accessible user methods of a FormFieldGenie instance are:

    popNewGroup(group, opts)
    Returns true if a new group is ‘popped’ or false if not.
    deleteGroup(group, opts)
    Returns true if the group was deleted, false if not.
    cutGroup(group, opts)
    Returns true if the group was deleted, false if not.
    group will always be copied to the clipboard.
    copyGroup(group, opts)
    Returns null.
    group will always be copied to the clipboard.
    pasteGroup(group, opts)
    Returns false if the clipboard clip is empty, true if it is pasted.
    clearClipboard(confirmed)
    Does what it says.  It will manually confirm with the end-user if you don’t pass true for confirmed.
    getClip(clipID, doInit)
    This method is also called internally by other user-methods as a worker-method, but you may utilize it if manually working with the clipboard.
    update_HTML_clipMenu()
    This method is also called internally by other user-methods as a worker-method, but you may utilize it if manually updating the clipboard.
    show_clipMenu(event, group, container)
    If your menu is “bound”, call this method from your custom event hander that triggers opening the menu (“pop-up”).  Pass along the event from your event-handler, and custom-calculate/-specify the group and container for your HTML.  The group is the one associated with this menu’s operations, and the container is the DOM Element that the menu will be placed into as it “pops-up.”
    bind_clipMenu(Boolean true ‖ HTML_clipMenu)
    Passing a Boolean value (true is default) will either bind or unbind the current myGenie.HTML_clipMenu.  Passing an HTML Element will bind that element as the new .HTML_clipMenu.

    The publicly accessible (and user-replaceable) worker-methods of a FormFieldGenie instance are:

    isActiveField(fieldNode, cbParams)
    This is called internally by user methods.  You can replace it using something like: myGenie.isActiveField=function(fieldNode, cbParams) { /*…your customizing code…*/ } The standard function also recognizes myGenie.config.isActiveField which may be a function or Boolean value.  This can replace the standard function to check if a form field is currently active or not; i.e. is it disabled, or is it even displayed at all?  You may add/subtract your own rules, perhaps checking the status of another element.  Inactive elements will not be considered when deciding to pop a new group or dump an empty one.  The cbParams variable is the user’s call-back parameters (see cbParams below in the ConfigStack description below).  Your function should return true‖false.
    catchTab(event)
    This is to be utilized (called by) by your “onKeyDown” event handler.  It can also be the “onKeyDown” event handler if you attach it using Element.addEventListener() as opposed to the handler being an Element's attribute.
    catchKey(event)
    This is not defined natively, but is recognized by the standard catchTab() method of an instance.  Since the form field that the event-handler (i.e. catchTab(), which calls this method) is attached to is likely to be cloned, it is easier to attach event-handlers “inline”, i.e. a part of the HTML tag as an “onkeydown” attribute (e.g. <input type='text' onkeydown='myGenie.catchTab(event)'>), so said attribute that leads to calling this method is inclusively cloned, rather than using “unobtrusive” JavaScript addEventListener() methods every time the form field is cloned.  This conception, however, limits you to only one (1) “onkeydown” event handler, so the “catchKey” hook is internal to allow you to “use” additional “onkeydown” handlers without having to add them to the element.  Alternatively, you may utilize the “myGenie.config.eventRegistrar()” functional option (see the ConfigStack options below) to add on a handler that calls this method, or any additional handlers, to newly cloned form fields as needed.
    clipMenu_controller(event)
    handleEvent(event)
    These two properties are one-in-the-same aliases for the method that handles all end-user interaction with “bound” menus.
    handleKeydownInMenu(event)
    Does what it says and returns true if the menu should stay open, or false if the menu should close.
    onGenieMenuSelect(event)
    Manages an end-user selection and calls the copy/cut/paste/delete/etc. action.  It returns the value that the action returns.  In addition, the event Object has one or two additional properties modified: • .menuSelectReturnStatus is true if the menu should stay open, false if the menu should close; and • .opStatus indicates the meta-status of the user-action.

    The publicly accessible properties and default values of a FormFieldGenie.ConfigStack (myGenie.config) instance are:

    indxTier: integer-number -1
    Number of numeric index “tiers” to ignore counting from the beginning or end of a name; used to specify the specific tier when updating names.  Positive (+n) and zero (0) values skip over tiers starting from the beginning of the name progressing forwards (i.e. a zero-based count), while negative (−n) values count tiers starting from the end of the name progressing backwards (i.e. a −1 based count).  Example: name → myField[4][3][2] when indxTier=-3 the FormFieldGenie updates/modifies the index that contains “4” and when indxTier=1 the FormFieldGenie updates/modifies the index that contains “3”.  The Genie looks for the numeric indexes, so note the following example: name → myField[4][subsection][3][2] when indxTier=2 the FormFieldGenie updates/modifies the index that contains “2”; the “subsection” tier is ignored.  A virtual (“numerically implicit”) index at the end of the name is included in the “count,” so with a name like myField[4][], the indxTier values of “0” and “-2” both point to the [4] index, while “1” and “-1” both point to the virtual [] index.  The FormFieldGenie does nothing to the name when a virtual [] index is pointed to by the indxTier.  You can override this spec using attributes in your fieldNode; see Fully Indexed Names above.
    focusField: 0 +number 0
    This applies to pasteGroup() and popNewGroup() only. Pass the field number (counted from zero) of the text/filename field you want the cursor focused on, if the user pressed the Tab key or config.doFocus=true (see directly below), when popping or when pasting a new group.
    doFocus: true ‖ false ‖ null
    This applies to pasteGroup() and popNewGroup() only. If true, the focusField (see directly above) will receive focus, whether or not the Tab key was pressed.  If false, the focusField will not receive focus when the Tab key is pressed.  If null, then the Tab key will cause the focusField to receive focus when popping a new group.
    isActiveField: undefined ‖ true ‖ false ‖ function(groupInQuestion, cbParams) { /*…your custom code makes the distinction…*/ }
    This works in conjunction with the isActiveField() method of a FormFieldGenie instance.  If true, all form fields will be considered active; if false they will not.  If this value is a user-defined function, its Boolean return value will be respected.  The cbParams variable is the user’s options (see below).
    dumpEmpties: dumpEmpties() ‖ true ‖ false ‖ function(empty_groupInQuestion, deleteFlag) { /*…your custom code makes the distinction…*/ }
    Remove emptied groups on the fly? This applies to deleteGroup() and popNewGroup() only, and not when inserting or pasting. The supplied default function utilizes the minFields, groupTag, & groupClass ConfigStack values (see below) to make the distinction.  If a function is supplied, it should return true ‖ false ‖ null and if null is returned, the function should remove the field itself.  If you use deleteGroup(), the group will be removed even if dumpEmpties===false;  however, if dumpEmpties is a function, it will be called with the value of deleteFlag=true as its second parameter and its return value (true‖false) will be respected.
    minGroups: +number 1
    Min number of “groups” when checking to dump empties using the standard dumpEmpties() function (see directly above).
    maxGroups: +number 100
    Maximum number of clones (groups) in the batch.
    groupClass: string ‖ RegExp ""
    This is used to identify specific child-Elements in a batch that are groups.  If it is undefined, all children of the batch are groups if they match the ConfigStack.groupTag.
    groupTag: htmlTagNameString.toUpperCase() ‖ null
    This is used to identify specific child-Elements in a batch that are groups.  If it is undefined, all children of the batch are groups if they match the ConfigStack.groupClass.
    checkForFilled: "all" ‖ "some" ‖ "any" ‖ "one"
    This applies to deleteGroup() and popNewGroup() only, and not when inserting or pasting. If set, the corresponding specified input-fields in the group will be checked.  By default only the first one is checked.  If one or some or possibly any, the checkField option (see below) should be used also.
    'one'
    the “specified field” is indexed by checkField if the one specified field is full, a new group is popped. if the one specified field is empty, the group may be dumped (automatically deleted).
    'any'
    the “specified fields” are either all of the fields in the group, or if checkField is an array, it should contain the numeric indices of the fields if any one of the specified fields is full, a new group is popped. if all of the specified fields are empty, the group may be dumped (automatically deleted).
    'some'
    each of the -first- "checkField" number of fields specified will be checked, or if checkfield is an array, it should contain the numeric indices of the fields if all of the specified fields are full, a new group is popped. if all of the specified fields are empty, the group may be dumped (automatically deleted).
    'all'
    if all of the fields in the group are full, a new group is popped. if all of the fields in the group are empty, the group may be dumped (automatically deleted).
    checkField: 0, +numberArray 0
    This applies to deleteGroup() and popNewGroup() only, and not when inserting or pasting. Used in conjunction with checkForFilled (see above).
    • if checkField is a number (when checkForFilled = 'one' ‖ 'some') Pass the field number (counted from zero) of the field or fields you want checked for being “empty” when popping.  If checkForFilled='some' then each of the first number of fields will be checked.
    • if checkField is an Array (when checkForFilled = 'any' ‖ 'some') The array should contain numeric values that are the indices of the fields to check.  The fields are indexed (starting with 0) in the order that they appear in the HTML document.
    cbParams:variable undefined
    This is a user-defined object/value of any type.  This property is not defined (e.g. (cbParams in myGenie.config)===false)) unless defined by the user.  This will be passed through to the updateName plugin callback function as the fourth variable (cbParams), and to the isActiveField†, cloneCustomizer‡, eventRegistrar‡ and batchCustomizer‡ plugin callback functions as the †second or ‡third.  It may be any type as required by your plugin callback functions, but if they share you may want to use an object with separate properties.
    updateName: null ‖ function(field, indxOffset, group, batch, cbParams) { /*your plugin code*/ }
    A plugin callback function to handle the process of updating each name.  The function will be passed each individual form DOM object (<input> or <textarea> or <select> or <button>) one at a time in the field variable.  The indxOffset variable contains the numerical positional offset of the new field compared to the field passed in.  The cbParams variable is the user’s options (see above). The Function should pass back a string of the new name, or null. If a string is returned, the name attribute of the DOM Element will be set to that value; no need for your function to alter the name directly, unless returning null. If null is returned, the usual process of updating the name continues.  The updateName() function may do anything it needs from partial updating the name directly (to be continued by the usual process), to updating the value, to updating the parentNode text, or whatever you can imagine…
    cloneCustomizer: null ‖ function(group, pasteOver, cbParams) { /*your customizing code*/ }
    If there is something special you want to do to each nodeGroup cloned, you may pass a function to handle that.  All field names will have been updated, but the node will not yet have been added to the document.  The passed variable pasteOver will be (true ‖ false ‖ 'paste-over')  —  true if pasting and inserting, 'paste-over' if pasting over an existing group (the old existing one will be discarded).  The cbParams variable is the user’s call-back parameters (see cbParams above).  This Function is called only when a new group is being popped or pasted over.
    eventRegistrar: null ‖ function(group, pasteOver, cbParams) { /*your customizing code*/ }
    While HTML attributes including event handlers are cloned when a DOM node is cloned, DOM level 2+ event handlers are not cloned.  If you need event handlers registered for any elements in your cloned group, you must do them “by hand” through this function.  The function will be passed the group after it has been added to the document.  See cloneCustomizer (above) for info on pasteOver and params.  This Function is called only when a new group is being popped or pasted over.
    batchCustomizer: null ‖ function(batch, pasteOver, cbParams) { /*your customizing code*/ }
    This is called when a new group is being popped, pasted, or when a group is deleted or was empty and has been dumped.  It is called from a setTimeout() function, so the DOM will be fully updated.  Use it to do any final customizing.  Note it is passed the whole batch node containing all groups including the new one after it has been added to the document, not simply the newly cloned group.  See cloneCustomizer (above) for info on pasteOver and params.
    minPixWidth: 0, +number 4
    for an input to be "active"
    minPixHeight: 0, +number 4
    for an input to be "active"
    clone: HTMLElement null
    This is the Node (including children) that will be cloned when popping a new field/fieldset.  If null then the last group will be cloned instead.
    clip: string "_system_"
    Used if you cut/copy/paste w/out specifying the clipboard “clip” name.  See clip in the options section below for more information.
    clear_clips_whenRetrieved: Boolean false
    ¿Do we remove the clip from the clipboard when pasting it to unclutter the clipboard and/or to free up memory, or do we leave it there so we can paste it multiple times?
    HTML_clipMenu_tabindex: stringnull "-1"
    All auto-generated <li> items in an HTML_clipMenu will get a tabindex attribute with this value, unless this is ==null.
    only_clips_inAll: true ‖ false
    When using Genie.getClip('all clips'), ¿ only return the clipboard.clips array ? vs. said array with the other properties of clipboard appended.  This is an advanced feature.
    no_system_inAllClips: true ‖ false
    When using Genie.getClip('all clips'), ¿ avoid clips in clipboard with names that contain “_system_” ?  This does not apply to clips in the clipboard.clips array.  This is an advanced feature.
    namedElements: string "input, textarea, select, button"
    This string must work with querySelectorAll();  These are elements who’s names will be updated.  ¿Maybe you want to use "*[name]" instead to update other Elements with non-standard “name” attributes?
    userDataInputTypes: ['text', 'search', 'tel', 'url', 'email', 'password', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'file']
    An array that specifies which <input> “types” the FormFieldGenie recognizes as having values defined by the user.

    Passing Options to the FormFieldGenie

    Passing options to this class is simple: use an object with properties named accordingly.  Any properties may be included or not, making it simple to control any option without worrying about the others.  If you are not so familiar with JavaScript, pay close attention to the demonstration example and how to define an object right in the event handler text.  You can also define your object in a separate <script></script>, then simply pass it to the class by name.  If you don’t understand how to do this, please refer to a good book on JavaScript programming.

    Options may be defined or passed at three levels: global defaults, instance defaults, and when calling a method.  The FormFieldGenie class’ methods, popNewGroup(), cutGroup(), copyGroup(), pasteGroup(), and deleteGroup(), only take two parameters (the group and the opts options object).  By setting the option values of the FormFieldGenie.ConfigStack.prototype property, you can control the option defaults globally.  When creating an instance (myGenie = new SoftMoon.WebWare.FormFieldGenie( { ...options... } )) you can override the global defaults, and when calling instance methods you can override the instance defaults.  However, some options only apply to specific methods as they are being called; any similar option in the ConfigStack will be ignored.

    The options object may contain any properties found in the FormFieldGenie.ConfigStack (most others will be ignored).  Some (as noted) will be ignored if passed when creating a new FormFieldGenie instance and are only valid when calling a method; those “methodical” properties are noted below:

    doso: true ‖ 'insert' ‖ 'paste'
    This applies to popNewGroup() and pasteGroup() only. If you pass (Boolean) true when using popNewGroup(), a new group will be popped at the end of the batch regardless of whether the last group is empty;  but not exceeding maxGroups.  Empty groups may be removed as usual.  Empty groups will not be automatically removed if "insert" when using popNewGroup().  If you pass "insert" or "paste" when using popNewGroup(), a new field will be popped and inserted before the passed group, regardless of whether the last field is empty; but not exceeding maxGroups.
    With popNewGroup(), “insert” inserts an empty group.
    With pasteGroup(), “insert” inserts the selected clip.
    With popNewGroup(), “paste” inserts the selected clip.
    addTo: true
    This applies to popNewGroup() and pasteGroup() only. If you pass opts.addto=true, then the value that would be passed into popNewGroup() as group will be instead considered the batch.  This will allow you to add a new group to an empty batch but only if •the myGenie.config.clone is set; •or opts.doso='paste' while the clipboard has contents.  Passing opts.addto=true acts similar as passing opts.doso=true in that it will always pop a new field (unless as noted above the batch is empty and there is no clone and no paste).  Note that pasteGroup() with opts.doso='insert' internally calls calls popNewGroup(), and this option may then take effect.
    clip: numberstring
    Object-member/property-identifier — ( a.k.a. yourClipReference )
    This is a reference to the member or property of the clipboard object associated with an instance of the FormFieldGenie.  Each FormFieldGenie instance has its own clipboard, and each clipboard can hold an “unlimited” number of clips (limited by the machine).  You may copy, cut and paste into/from any clip.  The .update_HTML_clipMenu() method, in conjunction with the getClip() method and the internal functioning of other methods, recognizes the string pattern "clip num" (where num is a positive number) and this is the same as passing in a simple number; e.g. {clip:1} is the same as {clip:"clip 1"}.  These “numbered” clips are stored internally in the instance.clipboard.clips array.  Every other string value you pass in for clip refers to a property of instance.clipboard; the clipboard itself is an array, so oddly if you pass the string "1" like so: {clip:"1"} you will refer to instance.clipboard[1]

    Most of these options do not need much more explanation, especially if you study the demonstration example.  A few do, so we will touch on them here.

    By using the config.groupClass and/or config.groupTag options, you may include other DOM Elements besides groups in your batch, including form-inputs or tags with form-inputs as children, that are not to be duplicated, automatically cleared, or otherwise messed-with by the Genie.

    When you want to use a plugin function to update names, you may write one that accepts a set of parameters, and then pass different parameters to your plugin for different form fields.  There is an included plugin with the demonstration example and the downloadable source code that shows how this works.  The supplied standard demo plugin can accept two different parameters (passed in the one object).  You may create your own “order” and pass it through to this plugin, should you choose.  If you understand Perl compatible regular expressions, and can understand how this simple plugin functions, then you could also pass a custom RegExp to the plugin and have even greater control over how it updates the name.  We chose using the first index, because this works well with this plugin’s logic.  Pay attention to the fact that the logic requires matching a single character, then the “incremental word” follows.  Another example of a naming style that would work with this function’s logic is name_first  name_second using the RegExp /_([a-z]+)$/ or name_first[]  using /_([a-z]+)\[/