diff --git a/sites/all/modules/options_element/LICENSE.txt b/sites/all/modules/options_element/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2c095c8d3f42488e8168f9710a4ffbfc4125a159
--- /dev/null
+++ b/sites/all/modules/options_element/LICENSE.txt
@@ -0,0 +1,274 @@
+GNU GENERAL PUBLIC LICENSE
+
+              Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+                  Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public License
+applies to most of the Free Software Foundation's software and to any other
+program whose authors commit to using it. (Some other Free Software
+Foundation software is covered by the GNU Library General Public License
+instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this service if
+you wish), that you receive source code or can get it if you want it, that you
+can change the software or use pieces of it in new free programs; and that
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to
+deny you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients
+to know that what they have is not the original, so that any problems
+introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+           GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+               MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms
+of this General Public License. The "Program", below, refers to any such
+program or work, and a "work based on the Program" means either the
+Program or any derivative work under copyright law: that is to say, a work
+containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language. (Hereinafter, translation
+is included without limitation in the term "modification".) Each licensee is
+addressed as "you".
+
+Activities other than copying, distribution and modification are not covered
+by this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made
+by running the Program). Whether that is true depends on what the Program
+does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you
+may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you
+also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be
+licensed as a whole at no charge to all third parties under the terms of this
+License.
+
+c) If the modified program normally reads commands interactively when run,
+you must cause it, when started running for such interactive use in the most
+ordinary way, to print or display an announcement including an appropriate
+copyright notice and a notice that there is no warranty (or else, saying that
+you provide a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this License.
+(Exception: if the Program itself is interactive but does not normally print such
+an announcement, your work based on the Program is not required to print
+an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be
+reasonably considered independent and separate works in themselves, then
+this License, and its terms, do not apply to those sections when you distribute
+them as separate works. But when you distribute the same sections as part
+of a whole which is a work based on the Program, the distribution of the
+whole must be on the terms of this License, whose permissions for other
+licensees extend to the entire whole, and thus to each and every part
+regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to
+control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the scope
+of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1
+and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above
+on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give
+any third party, for a charge no more than your cost of physically performing
+source distribution, a complete machine-readable copy of the corresponding
+source code, to be distributed under the terms of Sections 1 and 2 above on
+a medium customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for
+noncommercial distribution and only if you received the program in object
+code or executable form with such an offer, in accord with Subsection b
+above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation and
+installation of the executable. However, as a special exception, the source
+code distributed need not include anything that is normally distributed (in
+either source or binary form) with the major components (compiler, kernel,
+and so on) of the operating system on which the executable runs, unless that
+component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy the
+source code from the same place counts as distribution of the source code,
+even though third parties are not compelled to copy the source along with the
+object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy,
+modify, sublicense or distribute the Program is void, and will automatically
+terminate your rights under this License. However, parties who have received
+copies, or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you
+do not accept this License. Therefore, by modifying or distributing the
+Program (or any work based on the Program), you indicate your acceptance
+of this License to do so, and all its terms and conditions for copying,
+distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose
+that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will be
+similar in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that
+version or of any later version published by the Free Software Foundation. If
+the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software
+Foundation, write to the Free Software Foundation; we sometimes make
+exceptions for this. Our decision will be guided by the two goals of
+preserving the free status of all derivatives of our free software and of
+promoting the sharing and reuse of software generally.
+
+               NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
+PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
+AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
+ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
+SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
+OR DATA BEING RENDERED INACCURATE OR LOSSES
+SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
+IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.
+
+          END OF TERMS AND CONDITIONS
diff --git a/sites/all/modules/options_element/add.png b/sites/all/modules/options_element/add.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb88b1fe9be44aa048e1cc2bdd6e155e86d72dea
Binary files /dev/null and b/sites/all/modules/options_element/add.png differ
diff --git a/sites/all/modules/options_element/delete.png b/sites/all/modules/options_element/delete.png
new file mode 100644
index 0000000000000000000000000000000000000000..0de805b6cb3bc1ba72b99ef642e04e1e1c9d06c6
Binary files /dev/null and b/sites/all/modules/options_element/delete.png differ
diff --git a/sites/all/modules/options_element/options_element.css b/sites/all/modules/options_element/options_element.css
new file mode 100644
index 0000000000000000000000000000000000000000..d3654f97c5edce1223fe299e6439ab76e2b97b73
--- /dev/null
+++ b/sites/all/modules/options_element/options_element.css
@@ -0,0 +1,96 @@
+/* $Id: options_element.css,v 1.6 2010/03/24 23:53:14 quicksketch Exp $ */
+
+div.options-widget .draggable a.tabledrag-handle {
+  padding-right: .3em /* RTL */
+}
+
+div.form-options fieldset.options {
+  margin-bottom: 0;
+}
+
+div.options-widget a.add span,
+div.options-widget a.remove span {
+  display: none;
+}
+
+div.options-widget a.add,
+div.options-widget a.remove,
+div.form-option-add a {
+  display: block;
+  width: 16px;
+  height: 16px;
+  margin: .2em .1em;
+  background-repeat: no-repeat;
+  background-position: 0 0;
+  float: left; /* RTL */
+}
+
+div.options-widget a.add:hover,
+div.options-widget a.remove:hover,
+div.form-option-add a:hover {
+  background-position: 0 -16px;
+}
+
+div.options-widget a.add,
+div.form-option-add a {
+  background-image: url(add.png);
+}
+
+div.options-widget a.remove {
+  background-image: url(delete.png);
+}
+
+div.form-options-manual,
+div.form-option-add {
+  text-align: right; /* RTL */
+}
+div.form-options-manual a,
+div.form-option-add a {
+  float: none;
+  display: inline;
+  padding-left: 20px; /* RTL */
+}
+
+div.options-widget table {
+  width: 100%;
+  margin: 0;
+}
+
+div.options-widget table,
+div.options-widget tbody {
+  border: none;
+}
+
+div.options-widget table tr {
+  border-style: none;
+  background: none;
+}
+
+div.options-widget table td {
+  padding: 4px 6px;
+}
+
+div.options-widget table td.option-default-cell {
+  width: 6em;
+  padding: 4px 0;
+}
+
+div.options-widget table td.option-order-cell {
+  width: 2em;
+}
+
+div.options-widget table tr.indented td.option-order-cell {
+  width: 4em;
+}
+
+div.options-widget table td.option-key-cell {
+  width: 20%;
+}
+
+div.options-widget table td.option-actions-cell {
+  width: 40px;
+}
+
+div.options-widget input.form-text {
+  width: 100%;
+}
diff --git a/sites/all/modules/options_element/options_element.inc b/sites/all/modules/options_element/options_element.inc
new file mode 100644
index 0000000000000000000000000000000000000000..eda288ad29c989a26b077f8f095d63012a4d3f38
--- /dev/null
+++ b/sites/all/modules/options_element/options_element.inc
@@ -0,0 +1,423 @@
+<?php
+// $Id: options_element.inc,v 1.12 2011/01/12 07:25:13 quicksketch Exp $
+
+/**
+ * @file
+ * All logic for options_element form elements.
+ */
+
+
+/**
+ * Theme an options element.
+ */
+function theme_options($variables) {
+  $element = $variables['element'];
+
+  $classes = array();
+  if (isset($element['#attributes']['class'])) {
+    $classes = $element['#attributes']['class'];
+  }
+
+  $classes[] = 'form-options';
+  $classes[] = 'options-key-type-'. $element['#key_type'];
+
+  if ($element['#key_type_toggled']) {
+    $classes[] = 'options-key-custom';
+  }
+
+  if (isset($element['#optgroups']) && $element['#optgroups']) {
+    $classes[] = 'options-optgroups';
+  }
+
+  if (isset($element['#multiple']) && $element['#multiple']) {
+    $classes[] = 'options-multiple';
+  }
+
+  $options = '';
+  $options .= drupal_render($element['options_field']);
+  if (isset($element['default_value_field'])) {
+    $options .= drupal_render($element['default_value_field']);
+  }
+
+  $settings = '';
+  if (isset($element['custom_keys'])) {
+    $settings .= drupal_render($element['custom_keys']);
+  }
+  if (isset($element['multiple'])) {
+    $settings .= drupal_render($element['multiple']);
+  }
+  if (isset($element['option_settings'])) {
+    $settings .= drupal_render($element['option_settings']);
+  }
+
+  $output = '';
+  $output .= '<div class="' . implode(' ', $classes) .'">';
+  $output .= theme('fieldset', array('element' => array(
+    '#title' => t('Options'),
+    '#collapsible' => FALSE,
+    '#children' => $options,
+    '#attributes' => array('class' => array('options')),
+  )));
+
+  if (!empty($settings)) {
+    $output .= theme('fieldset', array('element' => array(
+      '#title' => t('Option settings'),
+      '#collapsible' => FALSE,
+      '#children' => $settings,
+      '#attributes' => array('class' => array('option-settings')),
+    )));
+  }
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * Logic function for form_options_expand(). Do not call directly.
+ *
+ * @see form_options_expand()
+ */
+function _form_options_expand($element) {
+  $element['#options'] = isset($element['#options']) ? $element['#options'] : array();
+  $element['#multiple'] = isset($element['#multiple']) ? $element['#multiple'] : FALSE;
+
+  $element['#tree'] = TRUE;
+  $element['#theme'] = 'options';
+
+  $path = drupal_get_path('module', 'options_element');
+  $element['#attached']['js'] = array(
+    'misc/tabledrag.js' => array('group' => JS_LIBRARY, 'weight' => 5),
+    'misc/jquery.cookie.js' => array('group' => JS_LIBRARY),
+    $path . '/options_element.js',
+  );
+  $element['#attached']['css'] = array(
+    $path . '/options_element.css',
+  );
+
+  // Add the key type toggle checkbox.
+  if (!isset($element['custom_keys']) && $element['#key_type'] != 'custom' && !empty($element['#key_type_toggle'])) {
+    $element['custom_keys'] = array(
+      '#title' => is_string($element['#key_type_toggle']) ? $element['#key_type_toggle'] : t('Customize keys'),
+      '#type' => 'checkbox',
+      '#default_value' => $element['#key_type_toggled'],
+      '#attributes' => array('class' => array('key-type-toggle')),
+      '#description' => t('Customizing the keys will allow you to save one value internally while showing a different option to the user.'),
+    );
+  }
+
+  // Add the multiple value toggle checkbox.
+  if (!isset($element['multiple']) && !empty($element['#multiple_toggle'])) {
+    $element['multiple'] = array(
+      '#title' => is_string($element['#multiple_toggle']) ? $element['#multiple_toggle'] : t('Allow multiple values'),
+      '#type' => 'checkbox',
+      '#default_value' => !empty($element['#multiple']),
+      '#attributes' => array('class' => array('multiple-toggle')),
+      '#description' => t('Multiple values will let users select multiple items in this list.'),
+    );
+  }
+  // If the element had a custom interface for toggling whether or not multiple
+  // values are accepted, make sure that form_type_options_value() knows to use
+  // it.
+  if (isset($element['multiple']) && empty($element['#multiple_toggle'])) {
+    $element['#multiple_toggle'] = TRUE;
+  }
+
+  // Add the main textarea for adding options.
+  if (!isset($element['options'])) {
+    $element['options_field'] = array(
+      '#type' => 'textarea',
+      '#resizable' => TRUE,
+      '#cols' => 60,
+      '#rows' => 5,
+      '#required' => isset($element['#required']) ? $element['#required'] : FALSE,
+      '#description' => t('List options one option per line.'),
+      '#attributes' => $element['#disabled'] ? array('readonly' => 'readonly') : array(),
+      '#wysiwyg' => FALSE, // Prevent CKeditor from trying to hijack.
+    );
+
+    // If validation fails, reload the user's text even if it's not valid.
+    if (isset($element['#value']['text'])) {
+      $element['options_field']['#value'] = $element['#value']['text'];
+    }
+    // Most of the time, we'll be converting the options array into the text.
+    else {
+      $element['options_field']['#value'] = isset($element['#options']) ? form_options_to_text($element['#options'], $element['#key_type']) : '';
+    }
+
+
+    if ($element['#key_type'] == 'mixed' || $element['#key_type'] == 'numeric' || $element['#key_type'] == 'custom') {
+      $element['options_field']['#description'] .= ' ' . t('Key-value pairs may be specified by separating each option with pipes, such as <em>key|value</em>.');
+    }
+    elseif ($element['#key_type_toggle']) {
+      $element['options_field']['#description'] .= ' ' . t('If the %toggle field is checked, key-value pairs may be specified by separating each option with pipes, such as <em>key|value</em>.', array('%toggle' => $element['custom_keys']['#title']));
+    }
+    if ($element['#key_type'] == 'numeric') {
+      $element['options_field']['#description'] .= ' ' . t('This field requires all specified keys to be integers.');
+    }
+  }
+
+  // Add the field for storing default values.
+  if ($element['#default_value'] !== FALSE && !isset($element['default_value_field'])) {
+    $element['default_value_field'] = array(
+      '#title' => t('Default value'),
+      '#type' => 'textfield',
+      '#size' => 60,
+      '#value' => isset($element['#default_value']) ? ($element['#multiple'] ? implode(', ', (array) $element['#default_value']) : $element['#default_value']) : '',
+      '#description' => t('Specify the keys that should be selected by default.'),
+    );
+    if ($element['#multiple']) {
+      $element['default_value_field']['#description'] .= ' ' . t('Multiple default values may be specified by separating keys with commas.');
+    }
+  }
+
+  // Remove properties that will confuse the FAPI.
+  unset($element['#options']);
+  $element['#required'] = FALSE;
+
+  return $element;
+}
+
+/**
+ * Logic function for form_options_validate(). Do not call directly.
+ *
+ * @see form_options_validate()
+ */
+function _form_options_validate($element, &$form_state) {
+  // Convert text to an array of options.
+  $duplicates = array();
+  $options = form_options_from_text($element['#value']['options_text'], $element['#key_type'], empty($element['#optgroups']), $duplicates);
+
+  // Check if a key is used multiple times.
+  if (count($duplicates) == 1) {
+    form_error($element, t('The key %key has been used multiple times. Each key must be unique to display properly.', array('%key' => reset($duplicates))));
+  }
+  elseif (!empty($duplicates)) {
+    array_walk($duplicates, 'check_plain');
+    $duplicate_list = theme('item_list', array('items' => $duplicates));
+    form_error($element, t('The following keys have been used multiple times. Each key must be unique to display properly.') . $duplicate_list);
+  }
+
+  // Add the list of duplicates to the page so that we can highlight the fields.
+  if (!empty($duplicates)) {
+    drupal_add_js(array('optionsElement' => array('errors' => drupal_map_assoc($duplicates))), 'setting');
+  }
+
+  // Check if no options are specified.
+  if (empty($options) && $element['#required']) {
+    form_error($element, t('At least one option must be specified.'));
+  }
+
+  // Check for numeric keys if needed.
+  if ($element['#key_type'] == 'numeric') {
+    foreach ($options as $key => $value) {
+      if (!is_int($key)) {
+        form_error($element, t('The keys for the %title field must be integers.', array('%title' => $element['#title'])));
+        break;
+      }
+    }
+  }
+
+  // Check that the limit of options has not been exceeded.
+  if (!empty($element['#limit'])) {
+    $count = 0;
+    foreach ($options as $value) {
+      if (is_array($value)) {
+        $count += count($value);
+      }
+      else {
+        $count++;
+      }
+    }
+    if ($count > $element['#limit']) {
+      form_error($element, t('The %title field supports a maximum of @count options. Please reduce the number of options.', array('%title' => $element['#title'], '@count' => $element['#limit'])));
+    }
+  }
+}
+
+/**
+ * Logic function for form_type_options_value(). Do not call directly.
+ *
+ * @see form_type_options_value()
+ */
+function _form_type_options_value(&$element, $edit = FALSE) {
+  if ($edit === FALSE) {
+     return array(
+       'options' => isset($element['#options']) ? $element['#options'] : array(),
+       'default_value' => isset($element['#default_value']) ? $element['#default_value'] : '',
+     );
+  }
+  else {
+    // Convert text to an array of options.
+    $duplicates = array();
+    $options = form_options_from_text($edit['options_field'], $element['#key_type'], empty($element['#optgroups']), $duplicates);
+
+    // Convert default value.
+    if (isset($edit['default_value_field'])) {
+      // If the element supports toggling whether or not it will accept
+      // multiple values, use the value that was passed in via $edit (keeping
+      // in mind that this value may not be set, if a checkbox was used to
+      // configure it). Otherwise, use the current setting stored with the
+      // element itself.
+      $multiple = !empty($element['#multiple_toggle']) ? !empty($edit['multiple']) : !empty($element['#multiple']);
+      if ($multiple) {
+        $default_value = array();
+        $default_items = explode(',', $edit['default_value_field']);
+        foreach ($default_items as $key) {
+          $key = trim($key);
+          if ($value = _form_options_search($key, $options)) {
+            $default_value[] = $value;
+          }
+        }
+      }
+      else {
+        $default_value = _form_options_search(trim($edit['default_value_field']), $options);
+      }
+    }
+
+    return array(
+      'options' => $options,
+      'default_value' => $default_value,
+      'options_text' => $edit['options_field'],
+      'default_value_text' => $edit['default_value_field'],
+    );
+  }
+}
+
+/**
+ * Logic function for form_options_to_text(). Do not call directly.
+ *
+ * @see form_options_to_text()
+ */
+function _form_options_to_text($options, $key_type) {
+  $output = '';
+  $previous_key = false;
+
+  foreach ($options as $key => $value) {
+    // Convert groups.
+    if (is_array($value)) {
+      $output .= '<' . $key . '>' . "\n";
+      foreach ($value as $subkey => $subvalue) {
+        $output .= (($key_type == 'mixed' || $key_type == 'numeric' || $key_type == 'custom') ? $subkey . '|' : '') . $subvalue . "\n";
+      }
+      $previous_key = $key;
+    }
+    // Typical key|value pairs.
+    else {
+      // Exit out of any groups.
+      if (isset($options[$previous_key]) && is_array($options[$previous_key])) {
+        $output .= "<>\n";
+      }
+      // Skip empty rows.
+      if ($options[$key] !== '') {
+        if ($key_type == 'mixed' || $key_type == 'numeric' || $key_type == 'custom') {
+          $output .= $key . '|' . $value . "\n";
+        }
+        else {
+          $output .= $value . "\n";
+        }
+      }
+      $previous_key = $key;
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Logic function for form_options_from_text(). Do not call directly.
+ *
+ * @see form_options_from_text()
+ */
+function _form_options_from_text($text, $key_type, $flat = FALSE, &$duplicates = array()) {
+  $keys = array();
+  $items = array();
+  $rows = array_filter(explode("\n", trim($text)));
+  $group = FALSE;
+  foreach ($rows as $row) {
+    $row = trim($row);
+    $matches = array();
+
+    // Check for a simple empty row.
+    if (empty($row)) {
+      continue;
+    }
+    // Check if this row is a group.
+    elseif (!$flat && preg_match('/^\<((([^>|]*)\|)?([^>]*))\>$/', $row, $matches)) {
+      if ($matches[1] === '') {
+        $group = FALSE;
+      }
+      else {
+        $group = $matches[4] ? $matches[4] : '';
+        $keys[] = $group;
+      }
+    }
+    // Check if this row is a key|value pair.
+    elseif (($key_type == 'mixed' || $key_type == 'custom' || $key_type == 'numeric') && preg_match('/^([^|]+)\|(.*)$/', $row, $matches)) {
+      $key = $matches[1];
+      $value = $matches[2];
+      $keys[] = $key;
+      $items[] = array(
+        'key' => $key,
+        'value' => $value,
+        'group' => $group,
+      );
+    }
+    // Set this this row as a straight value.
+    else {
+      $items[] = array(
+        'key' => NULL,
+        'value' => $row,
+        'group' => $group,
+      );
+    }
+  }
+
+  // Expand the list into a nested array, assign keys and check duplicates.
+  $options = array();
+  $new_key = 0;
+  foreach ($items as $item) {
+    // Assign a key if needed.
+    if ($key_type == 'none') {
+      $item['key'] = $new_key++;
+    }
+    elseif (!isset($item['key'])) {
+      while (in_array($new_key, $keys)) {
+        $new_key++;
+      }
+      $keys[] = $new_key;
+      $item['key'] = $new_key;
+    }
+
+    if ($item['group']) {
+      if (isset($options[$item['group']][$item['key']])) {
+        $duplicates[] = $item['key'];
+      }
+      $options[$item['group']][$item['key']] = $item['value'];
+    }
+    else {
+      if (isset($options[$item['key']])) {
+        $duplicates[] = $item['key'];
+      }
+      $options[$item['key']] = $item['value'];
+    }
+  }
+
+  return $options;
+}
+
+/**
+ * Recursive function for finding default value keys. Matches on keys or values.
+ */
+function _form_options_search($needle, $haystack) {
+  if (isset($haystack[$needle])) {
+    return $needle;
+  }
+  foreach ($haystack as $key => $value) {
+    if (is_array($value) && ($return = _form_options_search($needle, $value))) {
+      return $return;
+    }
+    if ($value == $needle) {
+      return $key;
+    }
+  }
+}
diff --git a/sites/all/modules/options_element/options_element.info b/sites/all/modules/options_element/options_element.info
new file mode 100644
index 0000000000000000000000000000000000000000..1b03698a5be3f3a0bfbccf391e476e28dc98f742
--- /dev/null
+++ b/sites/all/modules/options_element/options_element.info
@@ -0,0 +1,11 @@
+; $Id: options_element.info,v 1.3 2011/01/05 02:25:47 quicksketch Exp $
+name = Options element
+description = A custom form element for entering the options in select lists, radios, or checkboxes.
+core = 7.x
+
+; Information added by drupal.org packaging script on 2011-01-12
+version = "7.x-1.4"
+core = "7.x"
+project = "options_element"
+datestamp = "1294818406"
+
diff --git a/sites/all/modules/options_element/options_element.js b/sites/all/modules/options_element/options_element.js
new file mode 100644
index 0000000000000000000000000000000000000000..db771d387ae0b8b4abc0210d318df126332d7864
--- /dev/null
+++ b/sites/all/modules/options_element/options_element.js
@@ -0,0 +1,778 @@
+// $Id: options_element.js,v 1.13 2011/01/12 07:22:01 quicksketch Exp $
+
+/**
+ * @file
+ * Add JavaScript behaviors for the "options" form element type.
+ */
+
+(function($) {
+
+Drupal.optionElements = Drupal.optionElements || {};
+Drupal.behaviors.optionsElement = Drupal.behaviors.optionsElement || {};
+
+Drupal.behaviors.optionsElement.attach = function(context) {
+  $('div.form-options:not(.options-element-processed)', context).each(function() {
+    $(this).addClass('options-element-processed');
+    var optionsElement = new Drupal.optionsElement(this);
+    Drupal.optionElements[optionsElement.identifier] = optionsElement;
+  });
+};
+
+/**
+ * Constructor for an options element.
+ */
+Drupal.optionsElement = function(element) {
+  var self = this;
+
+  // Find the original "manual" fields.
+  this.element = element;
+  this.manualElement = $(element).find('fieldset.options, div.fieldset.options').get(0);
+  this.manualOptionsElement = $(element).find('textarea').get(0);
+  this.manualDefaultValueElement = $(element).find('input.form-text').get(0);
+  this.keyTypeToggle = $(element).find('input.key-type-toggle').get(0);
+  this.multipleToggle = $(element).find('input.multiple-toggle').get(0);
+
+  // Setup variables containing the current status of the widget.
+  this.optgroups = $(element).is('.options-optgroups');
+  this.multiple = $(element).is('.options-multiple');
+  this.keyType = element.className.replace(/^.*?options-key-type-([a-z]+).*?$/, '$1');
+  this.customKeys = Boolean(element.className.match(/options-key-custom/));
+  this.identifier = this.manualOptionsElement.id + '-widget';
+  this.enabled = $(this.manualOptionsElement).attr('readonly') == '';
+
+  // Warning messages.
+  this.keyChangeWarning = Drupal.t('Custom keys have been specified in this list. Removing these custom keys may change way data is stored. Are you sure you wish to remove these custom keys?');
+
+  // Setup new DOM elements containing the actual options widget.
+  this.optionsElement = $('<div></div>').get(0); // Temporary DOM object.
+  this.optionsToggleElement = $(Drupal.theme('optionsElementToggle')).get(0);
+  this.optionAddElement = $(Drupal.theme('optionsElementAdd')).get(0);
+
+  // Add the options widget and toggle elements to the page.
+  $(this.manualElement).css('display', 'none').before(this.optionsElement).after(this.optionsToggleElement).after(this.optionAddElement);
+
+  // Enable add item link.
+  $(this.optionAddElement).find('a').click(function() {
+    self.addOption($('table tr:last', self.optionsElement).get(0));
+    return false;
+  });
+
+  // Enable the toggle action for manual entry of options.
+  $(this.optionsToggleElement).find('a').click(function() {
+    self.toggleMode();
+    return false;
+  });
+
+  // Add a handler for key type changes.
+  if (this.keyTypeToggle) {
+    $(this.keyTypeToggle).click(function() {
+      var checked = $(this).attr('checked');
+      // Before switching the key type, ensure we're not destroying user keys.
+      if (!checked) {
+        var options = self.optionsFromText();
+        var confirm = false;
+        if (self.keyType == 'associative') {
+          for (var n = 0; n < options.length; n++) {
+            if (options[n].key != options[n].value) {
+              confirm = true;
+              break;
+            }
+          }
+        }
+        if (confirm) {
+          if (window.confirm(self.keyChangeWarning)) {
+            self.setCustomKeys(false);
+          }
+        }
+        else {
+          self.setCustomKeys(false);
+        }
+      }
+      else {
+        self.setCustomKeys(true);
+      }
+    });
+  }
+
+  // Add a handler for multiple value changes.
+  if (this.multipleToggle) {
+    $(this.multipleToggle).click(function(){
+      self.setMultiple($(this).attr('checked'));
+    });
+  }
+
+  // Be sure to show the custom keys if we have any errors.
+  if (Drupal.settings.optionsElement && Drupal.settings.optionsElement.errors) {
+    this.customKeys = true;
+  }
+
+  // Update the options widget with the current state of the textarea.
+  this.updateWidgetElements();
+
+  // Highlight errors that may have occurred during Drupal validation.
+  if (Drupal.settings.optionsElement && Drupal.settings.optionsElement.errors) {
+    this.checkKeys(Drupal.settings.optionsElement.errors, 'error');
+  }
+}
+
+/**
+ * Update the widget element based on the current values of the manual elements.
+ */
+Drupal.optionsElement.prototype.updateWidgetElements = function() {
+  var self = this;
+
+  // Create a new options element and replace the existing one.
+  var newElement = $(Drupal.theme('optionsElement', this)).get(0);
+  if ($(this.optionsElement).css('display') == 'none') {
+    $(newElement).css('display', 'none');
+  }
+  $(this.optionsElement).replaceWith(newElement);
+  this.optionsElement = newElement;
+
+  // Manually set up table drag for the created table.
+  Drupal.settings.tableDrag = Drupal.settings.tableDrag || {};
+  Drupal.settings.tableDrag[this.identifier] = {
+    'option-depth': {
+      0: {
+        action: 'depth',
+        hidden: false,
+        limit: 0,
+        relationship: 'self',
+        source: 'option-depth',
+        target: 'option-depth'
+      }
+    }
+  };
+
+  // Allow indentation of elements if optgroups are supported.
+  if (this.optgroups) {
+    Drupal.settings.tableDrag[this.identifier]['option-parent'] = {
+      0: {
+        action: 'match',
+        hidden: false,
+        limit: 1,
+        relationship: 'parent',
+        source: 'option-value',
+        target: 'option-parent'
+      }
+    };
+  }
+
+  // Enable button for adding options.
+  $('a.add', this.optionsElement).click(function() {
+    var newOption = self.addOption($(this).parents('tr:first').get(0));
+    $(newOption).find('a.add').focus();
+    return false;
+  });
+
+  // Enable button for removing options.
+  $('a.remove', this.optionsElement).click(function() {
+    self.removeOption($(this).parents('tr:first').get(0));
+    return false;
+  });
+
+  // Add the same update action to all textfields and radios.
+  $('input', this.optionsElement).change(function() {
+    self.updateOptionElements();
+    self.updateManualElements();
+  });
+
+  // Add a delayed update to textfields.
+  $('input.option-value', this.optionsElement).keyup(function(e) {
+    self.pendingUpdate(e);
+  });
+
+  // Attach behaviors as normal to the new widget.
+  Drupal.attachBehaviors(this.optionsElement);
+
+  // Remove the "Show row weights" link
+  $(".tabledrag-toggle-weight-wrapper").remove();
+
+  // Add an onDrop action to the table drag instance.
+  Drupal.tableDrag[this.identifier].onDrop = function() {
+    // Update the checkbox/radio buttons for selecting default values.
+    if (self.optgroups) {
+      self.updateOptionElements();
+    }
+    // Update the options within the hidden text area.
+    self.updateManualElements();
+  };
+
+  // Add an onIndent action to the table drag row instances.
+  Drupal.tableDrag[this.identifier].row.prototype.onIndent = function() {
+    if (this.indents) {
+      $(this.element).addClass('indented');
+    }
+    else {
+      $(this.element).removeClass('indented');
+    }
+  };
+
+  // Update the default value and optgroups.
+  this.updateOptionElements();
+}
+
+/**
+ * Update the original form element based on the current state of the widget.
+ */
+Drupal.optionsElement.prototype.updateManualElements = function() {
+  var options = {};
+
+  // Build a list of current options.
+  var previousOption = false;
+  $(this.optionsElement).find('input.option-value').each(function() {
+    var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first');
+    var depth = $row.find('input.option-depth').val();
+    if (depth == 1 && previousOption) {
+      if (typeof(options[previousOption]) != 'object') {
+        options[previousOption] = {};
+      }
+      options[previousOption][this.value] = this.value;
+    }
+    else {
+      options[this.value] = this.value;
+      previousOption = this.value;
+    }
+  });
+  this.options = options;
+
+  // Update the default value.
+  var defaultValue = this.multiple ? [] : '';
+  var multiple = this.multiple;
+  $(this.optionsElement).find('input.option-default').each(function() {
+    if (this.checked && this.value) {
+      if (multiple) {
+        defaultValue.push(this.value);
+      }
+      else {
+        defaultValue = this.value;
+      }
+    }
+  });
+  this.defaultValue = defaultValue;
+
+  // Update with the new text and trigger the change action on the field.
+  this.optionsToText();
+  if (this.manualDefaultValueElement) {
+    this.manualDefaultValueElement.value = multiple ? defaultValue.join(', ') : defaultValue;
+  }
+
+  $(this.manualOptionsElement).change();
+}
+
+/**
+ * Several maintenance routines to update all rows of the options element.
+ *
+ * - Disable options for optgroups if indented.
+ * - Disable add and delete links if indented.
+ * - Match the default value radio button value to the key of the text element.
+ */
+Drupal.optionsElement.prototype.updateOptionElements = function() {
+  var self = this;
+  var previousRow = false;
+  var previousElement = false;
+  var $rows = $(this.optionsElement).find('tbody tr');
+
+  $rows.each(function(index) {
+    var optionValue = $(this).find('input.option-value').val();
+    var optionKey = $(this).find('input.option-key').val();
+
+    // Update the elements key if matching the key and value.
+    if (self.keyType == 'associative') {
+      $(this).find('input.option-key').val(optionValue);
+    }
+
+    // Match the default value checkbox/radio button to the option's key.
+    $(this).find('input.option-default').val(optionKey ? optionKey : optionValue);
+
+    // Hide the add/remove links the row if indented.
+    var depth = $(this).find('input.option-depth').val();
+    var defaultInput = $(this).find('input.option-default').get(0);
+
+    if (depth == 1) {
+      // Affect the parent row, adjusting properties for optgroup items.
+      $(previousElement).attr('disabled', true).attr('checked', false);
+      $(previousRow).addClass('optgroup').find('a.add, a.remove').css('display', 'none');
+      $(this).find('a.add, a.remove').css('display', '');
+      $(defaultInput).attr('disabled', false);
+
+      // Hide the key column for the optgroup. It would be nice if hiding
+      // columns worked in IE7, but for now this only works in IE8 and other
+      // standards-compliant browsers.
+      if (self.customKeys && (!$.browser.msie || $.browser.version >= 8)) {
+        $(previousRow).find('td.option-key-cell').css('display', 'none');
+        $(previousRow).find('td.option-value-cell').attr('colspan', 2);
+      }
+    }
+    else {
+      // Set properties for normal options that are not optgroups.
+      $(defaultInput).attr('disabled', false);
+      $(this).removeClass('optgroup').find('a.add, a.remove').css('display', '');
+
+      // Hide the key column. See note above for compatibility concerns.
+      if (self.customKeys && (!$.browser.msie || $.browser.version >= 8)) {
+        $(this).find('td.option-key-cell').css('display', '');
+        $(this).find('td.option-value-cell').attr('colspan', '');
+      }
+      previousElement = defaultInput;
+      previousRow = this;
+    }
+  });
+
+  // Do not allow the last item to be removed.
+  if ($rows.size() == 1) {
+    $rows.find('a.remove').css('display', 'none')
+  }
+
+  // Disable items if needed.
+  if (this.enabled == false) {
+    this.disable();
+  }
+}
+
+/**
+ * Add a new option below the current row.
+ */
+Drupal.optionsElement.prototype.addOption = function(currentOption) {
+  var self = this;
+  var windowHieght = $(document).height();
+  var newOption = $(currentOption).clone()
+    .find('input.option-key').val(self.keyType == 'numeric' ? self.nextNumericKey() : '').end()
+    .find('input.option-value').val('').end()
+    .find('input.option-default').attr('checked', false).end()
+    .find('a.tabledrag-handle').remove().end()
+    .removeClass('drag-previous')
+    .insertAfter(currentOption)
+    .get(0);
+
+  // Scroll down to accomidate the new option.
+  $(window).scrollTop($(window).scrollTop() + $(document).height() - windowHieght);
+
+  // Make the new option draggable.
+  Drupal.tableDrag[this.identifier].makeDraggable(newOption);
+
+  // Enable button for adding options.
+  $('a.add', newOption).click(function() {
+    var newOption = self.addOption($(this).parents('tr:first').get(0));
+    $(newOption).find('a.add').focus();
+    return false;
+  });
+
+  // Enable buttons for removing options.
+  $('a.remove', newOption).click(function() {
+    self.removeOption(newOption);
+    return false;
+  });
+
+  // Add the update action to all textfields and radios.
+  $('input', newOption).change(function() {
+    self.updateOptionElements();
+    self.updateManualElements();
+  });
+
+  // Add a delayed update to textfields.
+  $('input.option-value', newOption).keyup(function(e) {
+    self.pendingUpdate(e);
+  });
+
+  this.updateOptionElements();
+  this.updateManualElements();
+
+  return newOption;
+}
+
+/**
+ * Remove the current row.
+ */
+Drupal.optionsElement.prototype.removeOption = function(currentOption) {
+  $(currentOption).remove();
+
+  this.updateOptionElements();
+  this.updateManualElements();
+}
+
+/**
+ * Toggle link for switching between the JavaScript and manual entry.
+ */
+Drupal.optionsElement.prototype.toggleMode = function() {
+  if ($(this.optionsElement).is(':visible')) {
+    var height = $(this.optionsElement).height();
+    $(this.optionsElement).css('display', 'none');
+    $(this.optionAddElement).css('display', 'none');
+    $(this.manualElement).css('display', '').find('textarea').height(height);
+    $(this.optionsToggleElement).find('a').text(Drupal.t('Normal entry'));
+  }
+  else {
+    this.updateWidgetElements();
+    $(this.optionsElement).css('display', '');
+    $(this.optionAddElement).css('display', '');
+    $(this.manualElement).css('display', 'none');
+    $(this.optionsToggleElement).find('a').text(Drupal.t('Manual entry'));
+  }
+}
+
+/**
+ * Enable the changing of options.
+ */
+Drupal.optionsElement.prototype.enable = function() {
+  this.enabled = true;
+  $(this.manualOptionsElement).attr('readonly', '');
+  $(this.element).removeClass('options-disabled');
+
+  $('a.add, a.remove, a.tabledrag-handle, div.form-option-add a', this.element).css('display', '');
+  $('input.form-text', this.optionsElement).attr('disabled', '');
+};
+
+/**
+ * Disable the changing of options.
+ */
+Drupal.optionsElement.prototype.disable = function() {
+  this.enabled = false;
+  $(this.manualOptionsElement).attr('readonly', true);
+  $(this.element).addClass('options-disabled');
+
+  $('a.add, a.remove, a.tabledrag-handle, div.form-option-add a', this.element).css('display', 'none');
+  $('input.form-text', this.optionsElement).attr('disabled', 'disabled');
+};
+
+/**
+ * Enable entering of custom key values.
+ */
+Drupal.optionsElement.prototype.setCustomKeys = function(enabled) {
+  if (enabled) {
+    $(this.element).addClass('options-key-custom');
+  }
+  else {
+    $(this.element).removeClass('options-key-custom');
+  }
+
+  this.customKeys = enabled;
+  // Rebuild the options widget.
+  this.updateManualElements();
+  this.updateWidgetElements();
+}
+
+/**
+ * Change the current key type (associative, custom, numeric, none).
+ */
+Drupal.optionsElement.prototype.setKeyType = function(type) {
+  $(this.element)
+    .removeClass('options-key-type-' + this.keyType)
+    .addClass('options-key-type-' + type);
+  this.keyType = type;
+  // Rebuild the options widget.
+  this.updateManualElements();
+  this.updateWidgetElements();
+}
+
+/**
+ * Set the element's #multiple property. Boolean TRUE or FALSE.
+ */
+Drupal.optionsElement.prototype.setMultiple = function(multiple) {
+  if (multiple) {
+    $(this.element).addClass('options-multiple');
+  }
+  else {
+    // Unselect all default options except the first.
+    $(this.optionsElement).find('input.option-default:checked:not(:first)').attr('checked', false);
+    this.updateManualElements();
+    $(this.element).removeClass('options-multiple');
+  }
+  this.multiple = multiple;
+  // Rebuild the options widget.
+  this.updateWidgetElements();
+};
+
+/**
+ * Highlight duplicate keys.
+ */
+Drupal.optionsElement.prototype.checkKeys = function(duplicateKeys, cssClass){
+  $(this.optionsElement).find('input.option-key').each(function() {
+    if (duplicateKeys[this.value]) {
+      $(this).addClass(cssClass);
+    }
+  });
+};
+
+/**
+ * Update a field after a delay.
+ *
+ * Similar to immediately changing a field, this field as pending changes that
+ * will be updated after a delay. This includes textareas and textfields in
+ * which updating continuously would be a strain the server and actually slow
+ * down responsiveness.
+ */
+Drupal.optionsElement.prototype.pendingUpdate = function(e) {
+  var self = this;
+
+  // Only operate on "normal" keys, excluding special function keys.
+  // http://protocolsofmatrix.blogspot.com/2007/09/javascript-keycode-reference-table-for.html
+  if (!(
+    e.keyCode >= 48 && e.keyCode <= 90 || // 0-9, A-Z.
+    e.keyCode >= 93 && e.keyCode <= 111 || // Number pad.
+    e.keyCode >= 186 && e.keyCode <= 222 || // Symbols.
+    e.keyCode == 8) // Backspace.
+    ) {
+    return;
+  }
+
+  if (this.updateDelay) {
+    clearTimeout(this.updateDelay);
+  }
+
+  this.updateDelay = setTimeout(function(){
+    self.updateOptionElements();
+    self.updateManualElements();
+  }, 500);
+};
+
+/**
+ * Given an object of options, convert it to a text string.
+ */
+Drupal.optionsElement.prototype.optionsToText = function() {
+  var $rows = $('tbody tr', this.optionsElement);
+  var output = '';
+  var inGroup = false;
+  var rowCount = $rows.size();
+  var defaultValues = [];
+
+  for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+    var isOptgroup = $rows.eq(rowIndex).is('.optgroup');
+    var isChild = $rows.eq(rowIndex).is('.indented');
+    var key = $rows.eq(rowIndex).find('input.option-key').val();
+    var value = $rows.eq(rowIndex).find('input.option-value').val();
+
+    // Handle groups.
+    if (this.optgroups && value !== '' && isOptgroup) {
+      output += '<' + ((key !== '') ? (key + '|') : '') + value + '>' + "\n";
+      inGroup = true;
+    }
+    // Typical key|value pairs.
+    else {
+      // Exit out of any groups.
+      if (this.optgroups && inGroup && !isChild) {
+        output += "<>\n";
+        inGroup = false;
+      }
+
+      // Add the row for the option.
+      if (this.keyType == 'none' || this.keyType == 'associative') {
+        output += value + "\n";
+      }
+      else if (value == '') {
+        output += "\n";
+      }
+      else {
+        output += ((key !== '') ? (key + '|') : '') + value + "\n";
+      }
+    }
+  }
+
+  this.manualOptionsElement.value = output;
+};
+
+/**
+ * Given a text string, convert it to an object.
+ */
+Drupal.optionsElement.prototype.optionsFromText = function() {
+  // Use jQuery val() instead of value because it fixes Windows line breaks.
+  var rows = $(this.manualOptionsElement).val().match(/^.*$/mg);
+  var parent = '';
+  var options = [];
+  var defaultValues = {};
+
+  // Drop the last row if empty.
+  if (rows.length && rows[rows.length - 1] == '') {
+    rows.pop();
+  }
+
+  if (this.manualDefaultValueElement) {
+    if (this.multiple) {
+      var defaults = this.manualDefaultValueElement.value.split(',');
+      for (var n = 0; n < defaults.length; n++) {
+        var defaultValue = defaults[n].replace(/^[ ]*(.*?)[ ]*$/, '$1'); // trim().
+        defaultValues[defaultValue] = defaultValue;
+      }
+    }
+    else {
+      var defaultValue = this.manualDefaultValueElement.value.replace(/^[ ]*(.*?)[ ]*$/, '$1'); // trim().
+      defaultValues[defaultValue] = defaultValue;
+    }
+  }
+
+  for (var n = 0; n < rows.length; n++) {
+    var row = rows[n].replace(/^[ \r\n]*(.*?)[ \r\n]*$/, '$1'); // trim().
+    var key = '';
+    var value = '';
+    var checked = false;
+    var hasChildren = false;
+    var groupClear = false;
+
+    var matches = {};
+    // Row is a group.
+    if (this.optgroups && (matches = row.match(/^\<((([^>|]*)\|)?([^>]*))\>$/))) {
+      if (matches[0] == '<>') {
+        parent = '';
+        groupClear = true;
+      }
+      else {
+        key = matches[3] ? matches[3] : '';
+        parent = value = matches[4];
+        hasChildren = true;
+      }
+    }
+    // Check if this row is a key|value pair.
+    else if ((this.keyType == 'mixed' || this.keyType == 'numeric' || this.keyType == 'custom') && (matches = row.match(/^([^|]+)\|(.*)$/))) {
+      key = matches[1];
+      value = matches[2];
+      checked = defaultValues[key];
+    }
+    // Row is a straight value.
+    else {
+      key = (this.keyType == 'mixed' || this.keyType == 'numeric') ? '' : row;
+      value = row;
+      if (!key && this.keyType == 'mixed') {
+        checked = defaultValues[value];
+      }
+      else {
+        checked = defaultValues[key];
+      }
+    }
+
+    if (!groupClear) {
+      options.push({
+        key: key,
+        value: value,
+        parent: (value !== parent ? parent : ''),
+        hasChildren: hasChildren,
+        checked: (checked ? 'checked' : false)
+      });
+    }
+  }
+
+  // Convert options to numeric if no key is specified.
+  if (this.keyType == 'numeric') {
+    var nextKey = this.nextNumericKey();
+    for (var n = 0; n < options.length; n++) {
+      if (options[n].key == '') {
+        options[n].key = nextKey;
+        nextKey++;
+      }
+    }
+  }
+
+  return options;
+};
+
+/**
+ * Utility method to get the next numeric option in a list of options.
+ */
+Drupal.optionsElement.prototype.nextNumericKey = function(options) {
+  this.keyType = 'custom';
+  options = this.optionsFromText();
+  this.keyType = 'numeric';
+
+  var maxKey = -1;
+  for (var n = 0; n < options.length; n++) {
+    if (options[n].key.match(/^[0-9]+$/)) {
+      maxKey = Math.max(maxKey, options[n].key);
+    }
+  }
+  return maxKey + 1;
+};
+
+/**
+ * Theme function for creating a new options element.
+ *
+ * @param optionsElement
+ *   An options element object.
+ */
+Drupal.theme.prototype.optionsElement = function(optionsElement) {
+  var output = '';
+  var options = optionsElement.optionsFromText();
+  var hasDefault = optionsElement.manualDefaultValueElement;
+  var defaultType = optionsElement.multiple ? 'checkbox' : 'radio';
+  var keyType = optionsElement.customKeys ? 'textfield' : 'hidden';
+
+  // Helper function to print out a single draggable option row.
+  function tableDragRow(key, value, parent, indent, status) {
+    var output = '';
+    output += '<tr class="draggable' + (indent > 0 ? ' indented' : '') + '">'
+    output += '<td class="' + (hasDefault ? 'option-default-cell' : 'option-order-cell') + '">';
+    for (var n = 0; n < indent; n++) {
+      output += Drupal.theme('tableDragIndentation');
+    }
+    output += '<input type="hidden" class="option-parent" value="' + parent + '" />';
+    output += '<input type="hidden" class="option-depth" value="' + indent + '" />';
+    if (hasDefault) {
+      output += '<input type="' + defaultType + '" name="' + optionsElement.identifier + '-default" class="form-radio option-default" value="' + key + '"' + (status == 'checked' ? ' checked="checked"' : '') + (status == 'disabled' ? ' disabled="disabled"' : '') + ' />';
+    }
+    output += '</td><td class="' + (keyType == 'textfield' ? 'option-key-cell' : 'option-value-cell') +'">';
+    output += '<input type="' + keyType + '" class="' + (keyType == 'textfield' ? 'form-text ' : '') + 'option-key" value="' + key + '" />';
+    output += keyType == 'textfield' ? '</td><td class="option-value-cell">' : '';
+    output += '<input class="form-text option-value" type="text" value="' + value + '" />';
+    output += '</td><td class="option-actions-cell">'
+    output += '<a class="add" title="' + Drupal.t('Add new option') + '" href="#"' + (status == 'disabled' ? ' style="display: none"' : '') + '><span class="add">' + Drupal.t('Add') + '</span></a>';
+    output += '<a class="remove" title="' + Drupal.t('Remove option') + '" href="#"' + (status == 'disabled' ? ' style="display: none"' : '') + '><span class="remove">' + Drupal.t('Remove') + '</span></a>';
+    output += '</td>';
+    output += '</tr>';
+    return output;
+  }
+
+  output += '<div class="options-widget">';
+  output += '<table id="' + optionsElement.identifier + '">';
+
+  output += '<thead><tr>';
+  output += '<th>' + (hasDefault ? Drupal.t('Default') : '&nbsp;') + '</th>';
+  output += keyType == 'textfield' ? '<th>' + Drupal.t('Key') + '</th>' : '';
+  output += '<th>' + Drupal.t('Value') + '</th>';
+  output += '<th>&nbsp;</th>';
+  output += '</tr></thead>';
+
+  output += '<tbody>';
+
+  // Make sure that at least a few options exist if empty.
+  if (!options.length) {
+    var newOption = {
+      key: '',
+      value: '',
+      parent: '',
+      hasChildren: false,
+      checked: false
+    }
+    options.push(newOption);
+    options.push(newOption);
+    options.push(newOption);
+  }
+
+  for (var n = 0; n < options.length; n++) {
+    var option = options[n];
+    var depth = option.parent === '' ? 0 : 1;
+    var checked = !option.hasChildren && option.checked;
+    output += tableDragRow(option.key, option.value, option.parent, depth, checked);
+  }
+
+  output += '</tbody>';
+  output += '</table>';
+  output += '<div>';
+
+  return output;
+};
+
+Drupal.theme.prototype.optionsElementAdd = function() {
+  return '<div class="form-option-add"><a href="#">' + Drupal.t('Add item') + '</a></div>';
+};
+
+Drupal.theme.prototype.optionsElementToggle = function() {
+  return '<div class="form-options-manual"><a href="#">' + Drupal.t('Manual entry') + '</a></div>';
+};
+
+Drupal.theme.tableDragChangedMarker = function () {
+  return ' ';
+};
+
+Drupal.theme.tableDragChangedWarning = function() {
+  return '<span></span>';
+};
+
+})(jQuery);
diff --git a/sites/all/modules/options_element/options_element.module b/sites/all/modules/options_element/options_element.module
new file mode 100644
index 0000000000000000000000000000000000000000..cee8a84f1aa911128d39c0933634bcc8aac793c3
--- /dev/null
+++ b/sites/all/modules/options_element/options_element.module
@@ -0,0 +1,168 @@
+<?php
+// $Id: options_element.module,v 1.7 2010/09/05 02:17:22 quicksketch Exp $
+
+/**
+ * @file
+ * Defines an "options" form element type for entering select list options.
+ */
+
+/**
+ * Implements hook_element_info().
+ *
+ * Defines the #type = 'options' form element type.
+ *
+ * The 'options' form element type is useful when collecting a series of
+ * values in a list. The values within the list may optionally have unique
+ * keys, such as that in a array structure. In addition, a default choice
+ * (or several default choices) may be selected by the user.
+ *
+ * @code
+ * $element['options'] = array(
+ *   '#type' => 'options',
+ *   '#limit' => 20,
+ *   '#optgroups' => FALSE,
+ *   '#multiple' => FALSE,
+ *   '#options' => array(
+ *     'foo' => 'foo',
+ *     'bar' => 'bar',
+ *     'baz' => 'baz',
+ *   ),
+ *   '#default_value' => 'foo'
+ *   '#key_type' => 'associative',
+ * );
+ * @endcode
+ *
+ * Properties for the 'options' element include:
+ * - limit: The maximum number of options that can be added to a list. Defaults
+ *   to 100.
+ * - optgroups: If nesting of options is supported, up to one level. This is
+ *   used when building a select HTML element that uses optgroups. Defaults to
+ *   FALSE.
+ * - multiple: Affects the number of default values that may be selected.
+ * - default_value: The key(s) for the options that are currently selected. If
+ *   #multiple is TRUE then, the default value is an array, otherwise it is a
+ *   string.
+ * - options: An array of options currently within the list.
+ * - key_type: The method by which keys are determined for each value in the
+ *   option list. Available options include:
+ *   - mixed: Each value is not given any ID automatically, but any manually
+ *     specified keys will be retained. This most emulates the existing
+ *     conventions within Drupal, where keys are optional but allowed.
+ *   - numeric: Each value is automatically given a unique numeric ID. This can
+ *     be useful when wanting duplicate values in a list and not have to bother
+ *     the end-user for keys.
+ *   - associative: Keys are automatically mapped from the user-entered values.
+ *     This is equivalent to making key|value pairs, but both the key and value
+ *     are the same. Each key must be unique.
+ *   - custom: Keys are manually entered by the end user. A second set of
+ *     textfields are presented to let the user provide keys as well as values.
+ *   - none: No keys are specified at all. This effectively creates numeric keys
+ *     but unlike numeric keys, the keys are renumbered if the options in the
+ *     list are rearranged.
+ * - key_type_toggle: If specified, a checkbox will be added that allows the
+ *   user to toggle between the current key type and the "custom" key type,
+ *   letting them customize the keys as desired. This option has no effect with
+ *   the "none" key type.
+ * - key_type_toggled: Determine if the toggle checkbox is set or not by
+ *   default.
+ *   @code
+ *   $element['options'] = array(
+ *     '#type' => 'options',
+ *     '#key_type' => 'associative',
+ *     '#key_type_toggle' => t('Custom keys'),
+ *     '#key_type_toggled' => TRUE,
+ *   );
+ *   @endcode
+ */
+function options_element_element_info() {
+  $type = array();
+
+  $type['options'] = array(
+    '#input' => TRUE,
+    '#process' => array('form_options_expand'),
+    '#limit' => 100,
+    '#optgroups' => TRUE,
+    '#multiple' => FALSE,
+    '#options' => array(),
+    '#key_type' => 'mixed',
+    '#key_type_toggle' => NULL,
+    '#key_type_toggled' => FALSE,
+    '#element_validate' => array('form_options_validate'),
+    '#disabled' => FALSE,
+  );
+
+  return $type;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function options_element_theme() {
+  return array(
+    'options' => array(
+      'render element' => 'element',
+      'file' => 'options_element.inc',
+    ),
+  );
+}
+
+/**
+ * Expand the "options" form element type.
+ *
+ * The "options" type is simply an enhanced textarea that makes it easier to
+ * create key|value pairs and put items into optgroups.
+ */
+function form_options_expand($element) {
+  module_load_include('inc', 'options_element');
+  return _form_options_expand($element);
+}
+
+/**
+ * Validate the "options" form element type.
+ */
+function form_options_validate($element, &$form_state) {
+  module_load_include('inc', 'options_element');
+  _form_options_validate($element, $form_state);
+}
+
+/**
+ * This function adjusts the value of the element from a text value to an array.
+ */
+function form_type_options_value(&$element, $edit = FALSE) {
+  module_load_include('inc', 'options_element');
+  return _form_type_options_value($element, $edit);
+}
+
+/**
+ * Create a textual representation of options from an array.
+ *
+ * @param $options
+ *   An array of options used in a select list.
+ * @param $key_type
+ *   How key/value pairs should be interpreted. Available options:
+ *   - mixed
+ *   - numeric
+ *   - associative
+ *   - custom
+ *   - none
+ */
+function form_options_to_text($options, $key_type) {
+  module_load_include('inc', 'options_element');
+  return _form_options_to_text($options, $key_type);
+}
+
+/**
+ * Create an array representation of text option values.
+ *
+ * If the Key of the option is within < >, treat as an optgroup
+ * 
+ * <Group 1>
+ *   creates an optgroup with the label "Group 1"
+ * 
+ * <>
+ *   Exits the current group, allowing items to be inserted at the root element.
+ */
+function form_options_from_text($text, $key_type, $flat = FALSE, &$duplicates = array()) {
+  module_load_include('inc', 'options_element');
+  return _form_options_from_text($text, $key_type, $flat, $duplicates);
+}
diff --git a/sites/all/modules/options_element/translations/ru.po b/sites/all/modules/options_element/translations/ru.po
new file mode 100644
index 0000000000000000000000000000000000000000..c3116ea6cb8b7c9cc880c7d956fe985d1deebeb0
--- /dev/null
+++ b/sites/all/modules/options_element/translations/ru.po
@@ -0,0 +1,146 @@
+# $Id$
+#
+# LANGUAGE translation of Drupal (general)
+# Copyright YEAR NAME <EMAIL@ADDRESS>
+# Generated from files:
+#  options_element.inc,v 1.6 2010/03/29 03:22:11 quicksketch
+#  options_element.info,v 1.2 2010/03/24 23:53:14 quicksketch
+#  options_element.js,v 1.8 2010/03/29 03:22:11 quicksketch
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: rus-drupal.ru\n"
+"POT-Creation-Date: 2010-03-31 03:04+0400\n"
+"PO-Revision-Date: 2010-03-31 03:32+0300\n"
+"Last-Translator: Andrey Mikheychik <andrey@armaturich.ru>\n"
+"Language-Team: Rus-Drupal <mail@rus-drupal.ru>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Russian\n"
+"X-Poedit-Country: Russia\n"
+
+#: options_element.inc:56
+msgid "Options"
+msgstr "Варианты выбора"
+
+#: options_element.inc:64
+msgid "Option settings"
+msgstr "Настройки варианта выбора"
+
+#: options_element.inc:99
+msgid "Customize keys"
+msgstr "Модифицировать ключи"
+
+#: options_element.inc:103
+msgid "Customizing the keys will allow you to save one value internally while showing a different option to the user."
+msgstr "Модификация ключей позволит сохранить одно значение внутренним, показывая другое значение пользователю."
+
+#: options_element.inc:110
+msgid "Allow multiple values"
+msgstr "Позволить различные значения"
+
+#: options_element.inc:114
+msgid "Multiple values will let users select multiple items in this list."
+msgstr "Различные значения позволят пользователям выбирать несколько пунктов из списка."
+
+#: options_element.inc:126
+msgid "List options one option per line."
+msgstr "Перечислите варианты выбора по одному варианту в строке."
+
+#: options_element.inc:141
+msgid "Key-value pairs may be specified by separating each option with pipes, such as <em>key|value</em>."
+msgstr "Пары ключ-значение могут быть определены разделением каждого пункта вертикальной чертой, например, <em>key|value</em>."
+
+#: options_element.inc:144
+msgid "If the %toggle field is checked, key-value pairs may be specified by separating each option with pipes, such as <em>key|value</em>."
+msgstr "Если поле %toggle отмечено, то пары ключ-значение могут быть определены разделением каждого пункта с помощью вертикальной черты, например <em>key|value</em>."
+
+#: options_element.inc:147
+msgid "This field requires all specified keys to be integers."
+msgstr "Это поле требует, чтобы все ключи были целочисленными."
+
+#: options_element.inc:154
+msgid "Default value"
+msgstr "Значение по умолчанию"
+
+#: options_element.inc:158
+msgid "Specify the keys that should be selected by default."
+msgstr "Определяет ключ, который должен быть выбран по умолчанию."
+
+#: options_element.inc:161
+msgid "Multiple default values may be specified by separating keys with commas."
+msgstr "Различные значения могут быть определены, указав ключи через запятую."
+
+#: options_element.inc:184
+msgid "The key %key has been used multiple times. Each key must be unique to display properly."
+msgstr "Ключ %key использован несколько раз. Каждый ключ должен быть уникальным, чтобы отображаться правильно."
+
+#: options_element.inc:189
+msgid "The following keys have been used multiple times. Each key must be unique to display properly."
+msgstr "Эти ключи использованы несколько раз. Каждый ключ должен быть уникальным для корректного отображения."
+
+#: options_element.inc:199
+msgid "At least one option must be specified."
+msgstr "Как минимум один вариант выбора должен быть определён."
+
+#: options_element.inc:206
+msgid "The keys for the %title field must be integers."
+msgstr "Ключи для поля %title должны быть целочисленными."
+
+#: options_element.inc:224
+msgid "The %title field supports a maximum of @count options. Please reduce the number of options."
+msgstr "Поле %title поддерживает максимум @count вариантов выбора. Пожалуйста, уменьшите количество вариантов."
+
+#: options_element.info:0
+msgid "Options element"
+msgstr "Элемент вариантов выбора"
+
+#: options_element.info:0
+msgid "A custom form element for entering the options in select lists, radios, or checkboxes."
+msgstr "Специальный элемент формы для ввода вариантов выбор в выпадающих списках, кнопках-переключателях или флаговой кнопки."
+
+#: options_element.js:0
+msgid "Custom keys have been specified in this list. Removing these custom keys may change way data is stored. Are you sure you wish to remove these custom keys?"
+msgstr "Отдельные ключи определяются в этом списке. Удаление этих отдельных ключей может изменить то, как хранятся данные. Вы уверены, что хотите удалить эти ключи?"
+
+#: options_element.js:0
+msgid "Normal entry"
+msgstr "Обычный ввод"
+
+#: options_element.js:0;0
+msgid "Manual entry"
+msgstr "Ввод вручную"
+
+#: options_element.js:0
+msgid "Add new option"
+msgstr "Добавить новый вариант выбора"
+
+#: options_element.js:0
+msgid "Add"
+msgstr "Добавить"
+
+#: options_element.js:0
+msgid "Remove option"
+msgstr "Удалить вариант выбора"
+
+#: options_element.js:0
+msgid "Remove"
+msgstr "Удалить"
+
+#: options_element.js:0
+msgid "Default"
+msgstr "По умолчанию"
+
+#: options_element.js:0
+msgid "Key"
+msgstr "Ключ"
+
+#: options_element.js:0
+msgid "Value"
+msgstr "Значение"
+
+#: options_element.js:0
+msgid "Add item"
+msgstr "Добавить пункт"
+