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
  • ActiveState Komodo Edit support
  • FormFieldGenie-4 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.  Since the version 2.2 release, an end-user can even cut, copy, and paste entire sections of an HTML form to/from multi-clip clipboards.  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.

    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.  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.  And it does all this without any additional markup to your HTML.

    To create a new FormFieldGenie instance:

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

    When adding a new form-field or group of form-fields (using the popNewField() 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.popNewField(……)

    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({……my options……}); myGenie.config.clone= ……my DOM node to clone…… myGenie.popNewField(………)

    Using the FormFieldGenie

    The first parameter passed to this class’ methods is the entire DOM 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 function.  Remember, in an event handler for form fields, the keyword this refers to the DOM node of the form-field itself.  If the field has a <label> tag (or any other tag) 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.  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 simplyfying the application of this class to your HTML.

    From there, the class makes one requirement: there must be a tag enclosing the fieldNodeGroup (see below) you want cloned.  The clones are added into this enclosing tag.  First, let’s be clear that any HTML “element” tags may be used; it does not matter to the class.  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 fieldNodeGroup, and the <fieldset> is the fieldNodeGroupFieldset.  The number 1 will be updated in the <input>’s name attribute when the fieldNodeGroup is cloned and the clone is added to the fieldNodeGroupFieldset.  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[pets][] 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  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.popNewField('> <label>pet name<input type='text' name='user[pets][1]'></label> </fieldset>

    The fieldNodeGroupFieldset may contain any other HTML besides the clones; but the clones will always be added to the fieldNodeGroupFieldset directly following the last fieldNodeGroup.  See also the config.groupClass and config.groupTag options in the ConfigStack description below.  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 all the fieldNodeGroups within fieldNodeGroupFieldset.  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 fieldNodeGroup and by default 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 fieldNodeGroup.

    If the end-user pressed the Tab key from within a fieldNode (to exit it) and a new fieldNodeGroup was popped, by default the cursor is focused within a specified fieldNode of the new fieldNodeGroup.  If nothing was popped, the cursor is passed onto the next form field as usual.  If the end-user clicked out of the fieldNode, the usual action is taken depending on where you click, whether or not a new fieldNodeGroup 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.

    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.)  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.  If the field’s name is not indexed, this function considers whether the last characters are numeric digits, or not (e.g. name or name123 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...

    Virtually Indexed Names

    Lets look at these first.  There is generally no need to do anything to these names.  The server side script (PHP) handles them as they should be.  The exception is when you use checkboxes and radio buttons.  Take a look at the source code of the demonstration example and also at the test-demo itself.  Notice how the nickname section uses checkboxes within the fieldNodeGroup 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.  So checkboxes and radio buttons with virtually indexed names are instead treated as fully indexed names.

    Fully Indexed Names

    The class looks for the last index in the name that is numeric and increases it by one for each new field popped.  Note this also applies to checkboxes and radio buttons with virtually indexed names (see the paragraph above).  If no index is found to be numeric, the name is left unchanged.

    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 “indexed” values

    In some cases the need arises for the value of these form fields to be updated, not the name.  The favorite pets section in the demonstration exemplifies this.  If the value is a number within square brackets (i.e. [0] or [1] or [1756] etc.) it can be updated instead of the name.  Furthermore, you can control what style name (virtual, indexed, non-indexed, or combinations) allows updating the value; while allowing flexibility, this becomes truly invaluable when fieldNodeGroups are nested within each other.  To control this using user-options, see updateValue in the FormFieldGenie.ConfigStack description.

    Coping, Cutting, Pasting, & Inserting

    You can paste two different ways using three different methods:

    ( see “clip” in “options” below for more info on %%your-clip-reference%% )

    The difference between popNewField() and pasteField() is that pasteField() will return false if the clip is empty, while popNewField() 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 is passed in as the second parameter when creating an instance of the FormFieldGenie.  Find it / modify it as the property: myGenie.HTML_clipMenu of your FormFieldGenie instance.  It should be an actual snippet of HTML.  In the example shown below, we would do something like:

    myGenie=new SoftMoon.WebWare.FormFieldGenie({…options…}, document.getElementById('myGenie_popUpMenu'));

    It is used when you want to allow your end-users the ability to cut/copy/paste/delete entire form sections (fieldNodeGroups — when copied/cut we call them “clips”) and would look something like the example below.  Note that the standardItems attribute holds the classname given to <li> Elements that do not get deleted.  The FormFieldGenie will update the HTML by adding/deleting <li> Elements that reference copied/cut “clips.”

    You may copy and paste this snippit of HTML into your web-page – one copy per Genie instance.  You may change/add any id or class names to this HTML for CSS handles.  You may modify the <li>TEXT</li> in the outer <menu> but not the inner <ul>s.  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?”.  Embedded JavaScript event-handler attributes may also be modified and/or expanded as appropriate to your needs; however, note the FormFieldGenie copies these “onclick” and all other attributes as it auto-creates new <li> items for each new “clip” the end-user copies/cuts to.  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.deleteField(this.closest("."+myGenie.config.groupClass))'.

    <menu id='myGenie_popUpMenu' standardItems='genie'> <li>insert:<span onclick='myGenie.popNewField(this.closest("."+myGenie.config.groupClass), {doso:"insert"})'>empty field</span> <ul onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.pasteField(this.closest("."+myGenie.config.groupClass), {doso:"insert",})'> <li class='genie'>all clips</li> </ul></li> <li>copy to:<ul onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.copyField(this.closest("."+myGenie.config.groupClass), {})'> <li class='genie'>new clip</li> </ul></li> <li>cut to:<ul onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.cutField(this.closest("."+myGenie.config.groupClass), {})'> <li class='genie'>new clip</li> </ul></li> <li>paste from:<ul onclick='if (event.phase===Event.BUBBLING_PHASE) myGenie.pasteField(this.closest("."+myGenie.config.groupClass), {})'> </ul></li> <li onclick='if (confirm("Do you want to delete this fieldNodeGroup?")) myGenie.deleteField(this.closest("."+myGenie.config.groupClass))'>delete</li> <li onclick='myGenie.clearClipboard();'>clear clipboard</li> </menu>

    Your webpage JavaScript code should further manage this HTML menu, typically moving it “into” the fieldNodeGroup HTML when the end-user right-clicks (menu-clicks) in said fieldNodeGroup, and style it with CSS so it “pops-up” (appears) at the right place at that time.  It would typically need additional Event handlers added to it so it “pops down” (disappears) when a selection is made; and/or Event-handlers attached to the body to do the same thing if clicked elsewhere, or another form-field somewhere else gets focus, etc.

    The publicly accessible properties of a FormFieldGenie instance are:

    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.
    This holds all the clipboard data for your Genie instance.
    This holds the HTML that is the “menu” shown to the end-user with the clipboard data for your Genie instance.
    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:

    popNewField(fieldNodeGroup, opts)
    Returns true if a new fieldNodeGroup is ‘popped’ or false if not.
    deleteField(fieldNodeGroup, opts)
    Returns true if the fieldNodeGroup was deleted, false if not.
    cutField(fieldNodeGroup, opts)
    Returns true if the fieldNodeGroup was deleted, false if not.
    fieldNodeGroup will always be copied to the clipboard.
    copyField(fieldNodeGroup, opts)
    Returns null.
    fieldNodeGroup will always be copied to the clipboard.
    pasteField(fieldNodeGroup, opts)
    Returns false if the clipboard clip is empty, true if it is pasted.
    Does what it says.
    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.
    This method is also called internally by other user-methods as a worker-method, but you may utilize it if manually updating the clipboard.

    The publicly accessible (and user-replacable) 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 fieldNodeGroup 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.
    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.
    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.

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

    indxTier: 0 +number 0
    Number of index “tiers” to ignore at the end of a name; used to skip over tier(s) when updating names.  climbTiers must be true (see directly below).  Example: name → myField[4][3][2] when indxTier=2 the FormFieldGenie updates/modifies the index that contains “4”.  Note the Genie looks for the next numeric index, so note the following example: name → myField[4][subsection][3][2] when indxTier=2 the FormFieldGenie updates/modifies the index that contains “4”.
    climbTiers: true ‖ false
    ¿Check all levels (tiers) of indices for a numeric value, or only the last?  See indxTier directly above.
    updateValue: "non-implicit" ‖ "non-indexed" ‖ "indexed" ‖ "implicit" ‖ "all"
    Controls the application of updating values instead of names in checkbox and radio-button fields that have values formatted similar to “[0]”  Any other (string value) condition passed yields no values updated. 
    ===↓ examples ↓===
    		all             name  name[string]  name[number]  name[]
    		non-implicit    name  name[string]  name[number]
    		non-indexed     name
    		indexed         name[string]  name[number]
    		implicit        name[]
    ===↑ examples only show final indices or lack of; indexed names may have additional indices ↑===
    focusField: 0 +number 0
    This applies to pasteField() and popNewField() 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 fieldNodeGroup.
    doFocus: true ‖ false ‖ null
    This applies to pasteField() and popNewField() 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 fieldNodeGroup.
    isActiveField: undefined ‖ true ‖ false ‖ function(fieldNodeGroupInQuestion, 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_fieldNodeGroupInQuestion, deleteFlag) { /*…your custom code makes the distinction…*/ }
    Remove emptied fieldNodeGroups on the fly? This applies to deleteField() and popNewField() 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 deleteField(), the fieldNodeGroup 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 “fieldNodeGroups” when checking to dump empties using the standard dumpEmpties() function (see directly above).
    maxGroups: +number 100
    Maximum number of clones (fieldNodeGroups) in the fieldNodeGroupFieldset.
    groupClass: string ‖ RegExp ""
    This is used to identify specific Elements in a fieldNodeGroupFieldset that are fieldNodeGroups.  If it is undefined, all children of the fieldNodeGroupFieldset are fieldNodeGroups if they match the ConfigStack.groupTag.
    groupTag: htmlTagNameString.toUpperCase() ‖ null
    This is used to identify specific Elements in a fieldNodeGroupFieldset that are fieldNodeGroups.  If it is undefined, all children of the fieldNodeGroupFieldset are fieldNodeGroups if they match the ConfigStack.groupClass.
    checkForFilled: "all" ‖ "some" ‖ "any" ‖ "one"
    This applies to deleteField() and popNewField() only, and not when inserting or pasting. If set, the corresponding specified input-fields in the fieldNodeGroup 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.
    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).
    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).
    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).
    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 deleteField() and popNewField() 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 checkForEmpty='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 fieldsetCusomizer‡ 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, fieldNodeGroupFieldset, 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 object 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(fieldNodeGroup, 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 fieldNodeGroup (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 fieldNodeGroup is being popped or pasted over.
    eventRegistrar: null ‖ function(fieldNodeGroup, 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 fieldNodeGroup, you must do them “by hand” through this function.  The function will be passed the fieldNodeGroup 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 fieldNodeGroup is being popped or pasted over.
    fieldsetCustomizer: null ‖ function(fieldNodeGroupFieldset, pasteOver, cbParams) { /*your customizing code*/ }
    This is called when a new fieldNodeGroup is being popped, pasted, or when a fieldNodeGroup 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 fieldNodeGroupFieldset node containing all fieldNodeGroups 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 fieldNodeGroup will be cloned instead.
    clip: string "_FormFieldGenie_system_"
    Used if you cut/copy/paste w/out specifying the clipboard “clip” name.
    only_clips_inAll: true ‖ false
    When using Genie.getClip('all clips'), ¿ only return the whole clipboard.clips array ?
    no_system_inAllClips: true ‖ false
    When using Genie.getClip('all clips'), ¿ avoid clips with names that contain _system_ ?
    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, popNewField(), cutField(), copyField(), pasteField(), and deleteField(), only take two parameters (the fieldNodeGroup 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 (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 popNewField() and pasteField() only. If you pass (Boolean) true when using popNewField(), a new fieldNodeGroup will be popped at the end of the fieldNodeGroupFieldset regardless of whether the last fieldNodeGroup is empty;  but not exceeding maxGroups.  Empty fieldNodeGroups may be removed as usual.  Empty fieldNodeGroups will not be automatically removed if "insert" when using popNewField().  If you pass "insert" or "paste" when using popNewField(), a new field will be popped and inserted before the passed fieldNodeGroup, regardless of whether the last field is empty; but not exceeding maxGroups.
    With popNewField(), “insert” inserts an empty fieldNodeGroup.
    With pasteField(), “insert” inserts the selected clip.
    With popNewField(), “paste” inserts the selected clip.
    addTo: true
    This applies to popNewField() and pasteField() only. If you pass opts.addto=true, then the value that would be passed into popNewField() as fieldNodeGroup will be instead considered the fieldNodeGroupFieldset.  This will allow you to add a new fieldNodeGroup to an empty fieldNodeGroupFieldset 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 fieldNodeGroupFieldset is empty and there is no clone and no paste).  Note that pasteField() with opts.doso='insert' internally calls calls popNewField(), and this option may then take effect.
    clip: numberstring
    Object-member/property-identifier — ( a.k.a. %%your-clip-reference%% )
    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.

    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 tags in your fieldNodeGroupFieldset, 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]+)\[/