--- module_builder.api.inc.orig	2008-01-02 03:49:33.000000000 +0100
+++ module_builder.api.inc	2008-05-06 03:37:59.000000000 +0200
@@ -11,29 +11,31 @@
  *
  * Components are things like 'node', 'menu', 'comment'.  Basically,
  * a component is a hook (however, like the node component, it is not limnited
- * to one   Components are supplied with hook_module_builder.  It returns an array 
- * of arrays, which give information about the components.  The components each 
- * have a title, a machine(-readable name) a callback (which returns an FAPI array), 
+ * to one   Components are supplied with hook_module_builder.  It returns an array
+ * of arrays, which give information about the components.  The components each
+ * have a title, a machine(-readable name) a callback (which returns an FAPI array),
  * and a submit function (which is set to a reasonable default).
  *
  * @return
  *   An array of all the components
  *
- * @see module_builder_module_builder for the example components array
- * @see module_builder_edit_form for an example form
+ * @see module_builder_module_builder(), module_builder_general_form()
  */
 function module_builder_get_components() {
   include_once drupal_get_path('module', 'module_builder') .'/module_builder.components.inc';
   $return = array();
   foreach (module_implements('module_builder') as $module) {
-    $function = $module .'_module_builder';
-    $result = $function();
-    foreach ($result as $item) {
-      $return[$item['machine']] = $item + array(
+    $queryfunction = $module .'_module_builder';
+    $components = $queryfunction();
+    foreach ($components as $component) {
+      $return[$component['machine']] = $component + array(
         'title' => '',
         'machine' => '',
-        'callback' => '',
+        'formconstructor' => '',
         'default' => FALSE,
+        'multiple' => FALSE,
+        'multicreatenewtext' => '',
+        'multidescription' => '',
         'submit' => 'module_builder_default_submit',
         'submit_button' => TRUE,
         'export' => FALSE,
@@ -44,81 +46,210 @@
 }
 
 /**
- * Checks if a module id is valid
- * 
+ * Checks if a module id is valid (wildcard loader called for module_builder/%mid urls)
+ *
  * @param $mid
  *   The module ID to be loaded
  * @return
  *   Either the module ID if found or FALSE if it wasn't found
  */
-function module_builder_load($mid) {
-  $result = db_fetch_object(db_query("SELECT * FROM {module_builder_basic} WHERE mid = %d", $mid));
+function mid_load($mid) {
+  $result = db_fetch_object(db_query("SELECT data FROM {module_builder_data} WHERE mid = %d AND type = 'general'", $mid));
   if ($result) {
-    return $result;
+    $module = unserialize($result->data);
+    $module->mid = $mid;
+    return $module;
   }
-  else {
+  else
     return FALSE;
-  }
 }
 
 /**
- * Builder form
- * 
+ * Builder form (this renders all the forms)
+ *
  * @param $form_state
- *   The form state (from form.inc).  Passed into the callback.
- * @param $callback
- *   The callback function
- * @param $mid
- *   The module id
+ *   The form state (from form.inc).  Passed into the constructor.
+ * @param $module
+ *   The module entry (loaded by mid_load)
  * @param $type
- *   The name of the component (menu, node, comment)
+ *   The type of component form to show (menu, node, comment)
+ *
  * @return
  *   An FAPI form array
  */
 function module_builder_build(&$form_state, $module, $type) {
-  $result = db_result(db_query("SELECT name FROM {module_builder_basic} WHERE mid = %d", $module->mid));
-  drupal_set_title($result);
+  drupal_set_title(t('Project "!name"', array('!name' => $module->name)));
   $components = module_builder_get_components();
   $component = $components[$type];
-  $result = unserialize(db_result(db_query("SELECT data FROM {module_builder_data} WHERE mid = %d AND type = '%s'", $module->mid, $type)));
-  $values = ($result !== FALSE? $result : new stdClass);
-  $form = $component['callback']($form_state, $values, $module);
+
+  // receive values from form state if already submitted or else from database
+  if (!empty($form_state['values']))
+    $values = (object) $form_state['values'];
+  else {
+    $result = unserialize(db_result(db_query("SELECT data FROM {module_builder_data} WHERE mid = %d AND type = '%s'", $module->mid, $type)));
+    $values = ($result !== FALSE) ? $result : new stdClass();
+  }
+
+  // get form from component's constructor callback
+  $form = $component['formconstructor']($form_state, $values, $module);
+  $form['mid'] = array(
+    '#type' => 'value',
+    '#value' => $module->mid);
+  $form['type'] = array(
+    '#type' => 'value',
+    '#value' => $type);
+
+  // form can have arbritrary number of items of same kind
+  if ($component['multiple'] == TRUE) {
+    $prototype = $form['item_prototype']['#value'];
+    $form['items'] = array(
+      '#type' => 'fieldset',
+      '#description' => $component['multidescription'],
+      '#prefix' => '<div id="items-wrapper">',
+      '#suffix' => '</div>',
+    );
+
+    // if add item button has been clicked, append entry with default values to item list
+    if ($form_state['values']['itemadded'])
+      $values->items[] = $form_state['values']['item_default'];
+    unset($form_state['values']['itemadded']);
+
+    $count = 0;
+    foreach ($values->items as $key => $item) {
+      // remove item marked for deletion from list
+      if (isset($form_state['post']['deleted']) && ((int) $key === (int) $form_state['post']['deleted'])) {
+        unset($values->items[$key]);
+        continue;
+      }
+      // set form item and populate with existing values
+      if (is_array($item)) {
+        $form['items'][$count] = $prototype;
+        foreach ($item as $field => $value) {
+          if ($field === "name" || $field === "title")
+            $form['items'][$count]['#title'] = $value;
+          $form['items'][$count][$field]['#default_value'] = $value;
+        }
+        // add a delete button for each item
+        $form['items'][$count]["delete_$count"] = array(
+          '#type' => 'submit',
+          '#name' => "delete_$count",
+          '#value' => t('Remove'),
+          // call store callback, then confirmation form
+          '#submit' => array($component['submit'], 'module_builder_itemdelete_confirm'),
+        );
+        $count++;
+      }
+    }
+    $form['new'] = array(
+      '#type' => 'submit',
+      '#value' => $component['multicreatenewtext'],
+      '#submit' => array('module_builder_additem_submit'),
+      '#prefix' => '<br />',
+      '#ahah' => array(
+        'wrapper' => 'items-wrapper',
+        'method' => 'append',
+        'path' => 'module_builder/js',
+      ),
+    );
+
+    // set submit handler
+    $form['#submit'][] = 'module_builder_multiple_submit';
+  }
+  $form['#submit'][] = $component['submit'];
+
   if ($component['submit_button']) {
     $form['submit'] = array(
-      '#type' => 'submit', 
-      '#value' => t('Submit'),
+      '#type' => 'submit',
+      '#value' => t('Save changes'),
     );
   }
-  $form['#submit'][] = $component['submit'];
-  $form['mid'] = array('#type' => 'value', '#value' => $module->mid);
-  $form['type'] = array('#type' => 'value', '#value' => $type);
-  
+
   return $form;
 }
 
 /**
- * Module builder default submit
+ * Module builder submit handler for forms with multiple items (filters empty/default items)
+ */
+function module_builder_multiple_submit($form, &$form_state) {
+  $unset_items = array();
+  foreach ($form_state['values']['items'] as $key => $item) {
+    $contains_default_values = TRUE;
+    foreach ($item as $field => $value) {
+      if ($field === "delete_$key")
+        continue;
+      if ($value !== $form_state['values']['item_default'][$field])
+        $contains_default_values = FALSE;
+    }
+    if ($contains_default_values)
+      $unset_items[] = $key;
+  }
+  foreach ($unset_items as $key)
+    unset($form_state['values']['items'][$key]);
+  unset($form_state['values']['new']);
+}
+
+/**
+ * Module builder default submit (saves entered form data)
  */
 function module_builder_default_submit($form, &$form_state) {
+  // create a clean copy of the values and store it in the db
   $values = (object) $form_state['values'];
-  unset($values->form_id, $values->submit, $values->op, $values->form_build_id, $values->form_token);
-  $final = new stdClass;
-  $final->mid = $values->mid;
-  $final->type = $values->type;
+  unset($values->form_id, $values->submit, $values->op, $values->form_build_id, $values->form_token, $values->items['new'], $values->item_default, $values->item_prototype);
+  foreach ($values->items as $key => $item)
+    foreach ($item as $field => $value)
+      if ($field === "delete_$key")
+        unset($values->items[$key][$field]);
+  $storecomponent = new stdClass();
+  $storecomponent->mid = $values->mid;
+  $storecomponent->type = $values->type;
   unset($values->mid, $values->type);
-  $final->data = $values;
-  if ((bool) db_result(db_query("SELECT mid FROM {module_builder_data} WHERE mid = %d AND type = '%s'", $final->mid, $final->type))) {
-    drupal_write_record('module_builder_data', $final, array('mid', 'type'));
+  $storecomponent->data = $values;
+drupal_set_message('<pre>$storecomponent'. print_r($storecomponent, TRUE) .'</pre>');
+
+  if ((bool) db_result(db_query("SELECT mid FROM {module_builder_data} WHERE mid = %d AND type = '%s'", $storecomponent->mid, $storecomponent->type))) {
+    $ret = drupal_write_record('module_builder_data', $storecomponent, array('mid', 'type'));
   }
   else {
-    drupal_write_record('module_builder_data', $final);
+    $ret = drupal_write_record('module_builder_data', $storecomponent);
   }
+// drupal_set_message('<pre>$ret= '.print_r($ret, TRUE).'</pre>');
+//   $form_state['rebuild'] = TRUE;
+}
+
+function module_builder_additem_submit($form, &$form_state) {
+// drupal_set_message('module_builder_additem_submit');
+  $form_state['values']['itemadded'] = TRUE;
   $form_state['rebuild'] = TRUE;
 }
 
+
+function module_builder_itemdelete_confirm($form, &$form_state) {
+  $key = $form_state['clicked_button']['#parents'][1];
+  $type = $form_state['values']['type'];
+  $mid = $form_state['values']['mid'];
+  $name = (!empty($form_state['values']['items'][$key]['title'])) ?
+    $form_state['values']['items'][$key]['title'] : ((!empty($form_state['values']['items'][$key]['name'])) ?
+      $form_state['values']['items'][$key]['name'] : t('(unnamed)'));
+  $output = drupal_get_form('module_builder_itemdelete_confirm_form', $type, $mid, $key, $name);
+  print theme('page', $output);
+  exit;
+}
+
+function module_builder_itemdelete_confirm_form(&$form_state, $type, $mid, $key, $name) {
+  $item_deleted = array(
+    'deleted' => array(
+      '#type' => 'hidden',
+      '#value' => $key,
+    ),
+  );
+  $form = confirm_form($item_deleted, t('Do you really want to delete the !type "!name"?',
+    array('!type' => $type, '!name' => $name)), "admin/build/module_builder/edit/$mid/$type");
+  return $form;
+}
+
 /**
  * The JavaScript callback.
- * 
+ *
  * Uses magic too complex to be explained here.
  */
 function module_builder_js($param = '') {
@@ -126,38 +257,48 @@
   $form_state = array('storage' => NULL, 'submitted' => FALSE);
   $form_build_id = $_POST['form_build_id'];
   $form = form_get_cache($form_build_id, &$form_state);
-  // #parameters has $form_id, $form_state and then whatever was passed to
-  // drupal_get_form.
+
+  // #parameters has $form_id, $form_state and then whatever
+  // was passed to drupal_get_form.
   $args = $form['#parameters'];
   $form_id = array_shift($args);
   $form['#post'] = $_POST;
   $form['#redirect'] = FALSE;
+
   // This will set up $form_state['clicked_button'] and
   // $form_state['storage']['mlid'].
   drupal_process_form($form_id, $form, $form_state);
+
   // Recreate and re-cache the form.
   $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
-  // Pick up the parents of the pressed button.
-  $array_parents = $form_state['clicked_button']['#array_parents'];
-  // The last parent is the button itself, we need the wrapper instead.
-  array_pop($array_parents);
-  while ($array_parents) {
-    $parent = array_shift($array_parents);
-    $form = $form[$parent];
-  }
-  // Remove the button.
-  unset($form['parent_submit']);
-  // Render messages and selects.
-  $output = theme('status_messages') . drupal_render($form);
+
+  // get the added form item and add the js pre_render callback
+  $item = $form['items'][end(element_children($form['items']))];
+  $item['#pre_render'] = (!empty($form['#pre_render'])) ? $form['#pre_render'] : '';
+
+  // render the item and its javascript and send it to the browser via JSON
+  $output = drupal_render($item) . drupal_get_js('appended');
   drupal_json(array('status' => TRUE, 'data' => $output));
 }
 
-function module_builder_add_js($name, $a2, $a3, $a4) {
+function module_builder_add_js() {
+//   drupal_set_message('module_builder_add_js');
   drupal_add_js(drupal_get_path('module', 'module_builder') .'/module_builder.js');
-  switch ($name) {
-    case 'copy':
-      drupal_add_js(array('moduleBuilderCopy' => array(array('type' => $a2, 'from' => $a3, 'to' => $a4))), 'setting');
-  }
+}
+
+function module_builder_add_item_js($name, $type, $from, $to, $scope = 'header') {
+//   drupal_set_message('module_builder_add_item_js');
+  $js_data = array(
+    $name => array(
+      array(
+        'type' => $type,
+        'from' => $from,
+        'to' => $to,
+      ),
+    ),
+  );
+// drupal_set_message('<pre>'.print_r($js_data, TRUE).'</pre>');
+  drupal_add_js($js_data, 'setting', $scope);
 }
 
 function module_builder_add_hook($hook, $module) {
@@ -170,35 +311,52 @@
 }
 
 function module_builder_export_callback($module, $file) {
-  $edit = unserialize(db_result(db_query("SELECT data FROM {module_builder_data} WHERE type = '%s' AND mid = %d", 'edit', $module->mid)));
-  $module = (object) ((array) $module + (array) $edit + array('description' => '', 'machine' => ''));
-  if ($file == 'info') {
-    header('Content-disposition: attachment');
-    print _module_builder_export_info($module);
-    exit();
-  }
-  elseif ($file == 'module') {
-    header('Content-disposition: attachment');
-    print _module_builder_export_module($module);
-    exit();
-  }
-  elseif ($file == 'tar') {
-    
-  }
-  else {
-    drupal_not_found();
-  }
+  switch ($file) {
+    case 'info':
+      drupal_set_header('Content-type: text/plain');
+      drupal_set_header('Content-disposition: attachment; filename="'. $module->machine .'.info"');
+      print _module_builder_export_info($module);
+      exit();
+    case 'module':
+      drupal_set_header('Content-type: text/plain');
+      drupal_set_header('Content-disposition: attachment; filename="'. $module->machine .'.module"');
+      print _module_builder_export_module($module);
+      exit();
+    case 'tar':
+      include_once(drupal_get_path('module', 'module_builder') .'/archive_tar/archive_tar.php');
+      $tarname = file_directory_path() .'/'. $module->machine .'.tar.gz';
+      $tarchive = new Archive_Tar($tarname, 'gz');
+      if ($tarchive->addString($module->machine .'/'. $module->machine .'.info', _module_builder_export_info($module)) &&
+          $tarchive->addString($module->machine .'/'. $module->machine .'.module', _module_builder_export_module($module))) {
+        drupal_set_header('Content-type: application/x-gzip');
+        drupal_set_header('Content-disposition: attachment; filename="'. $module->machine .'.tar.gz"');
+        print file_get_contents($tarname);
+        unlink($tarname);
+        exit();
+      }
+      else {
+        drupal_set_message('Creating GZ compressed TAR archive failed.', 'error');
+        drupal_not_found();
+      }
+    default:
+      drupal_not_found();
+    }
 }
 
 function _module_builder_export_info($module) {
-  $output = '';
+  $output = '; $Id$'."\n";
   $info = array();
   $info['name'] = '"'. $module->name .'"';
   $info['description'] = '"'. $module->description .'"';
+  if (!empty($module->package)) $info['package'] = $module->package;
   $info['core'] = '6.x';
   foreach ($info as $key => $value) {
     $output .= $key .' = '. $value ."\n";
   }
+  $dependencies = (!empty($module->dependencies)) ? explode(' ', $module->dependencies) : '';
+  while (!empty($dependencies)) {
+    $output .= "dependencies[] = ". array_pop($dependencies) ."\n";
+  }
   return $output;
 }
 
--- module_builder.components.inc.orig	2008-01-02 03:49:33.000000000 +0100
+++ module_builder.components.inc	2008-05-06 02:50:19.000000000 +0200
@@ -4,194 +4,209 @@
 function module_builder_module_builder() {
   return array(
     array(
-      'title' => t('Edit'),
-      'machine' => 'edit',
-      'callback' => 'module_builder_edit_form',
+      'title' => t('General'),
+      'machine' => 'general',
+      'formconstructor' => 'module_builder_general_form',
       'default' => TRUE,
-      'submit' => 'module_builder_edit_form_submit',
+      'submit' => 'module_builder_general_form_submit',
     ),
     array(
-      'title' => t('Menu'),
+      'title' => t('Menus'),
       'machine' => 'menu',
-      'callback' => 'module_builder_menu_form',
-    //  'export' => 'module_builder_menu_export',
+      'formconstructor' => 'module_builder_menu_form',
+      'multiple' => TRUE,
+      'multicreatenewtext' => t('Add menu entry'),
+      'multidescription' => t('Add here any menu items the module should define.'),
     ),
     array(
-      'title' => t('Node'),
+      'title' => t('Node types'),
       'machine' => 'node',
-      'callback' => 'module_builder_node_form',
+      'formconstructor' => 'module_builder_node_form',
+      'multiple' => TRUE,
+      'multicreatenewtext' => t('Add content type'),
+      'multidescription' => t('Node content types the module provides can be defined here.'),
       'export' => 'module_builder_node_export',
     ),
     array(
-      'title' => t('Delete'),
-      'machine' => 'delete',
-      'callback' => 'module_builder_delete_form',
-      'submit' => 'module_builder_delete_form_submit',
-      'submit_button' => FALSE,
+      'title' => t('DB schema'),
+      'machine' => 'schema',
+      'formconstructor' => 'module_builder_schema_form',
+    ),
+    array(
+      'title' => t('Functions'),
+      'machine' => 'function',
+      'formconstructor' => 'module_builder_function_form',
+      'multiple' => TRUE,
+      'multicreatenewtext' => t('Add function definition'),
+      'multidescription' => t('The functions which provide the module\'s ..well ... functionality!'),
     ),
     array(
-      'title' => t('Export'),
+      'title' => t('Export module'),
       'machine' => 'export',
-      'callback' => 'module_builder_export_form',
+      'formconstructor' => 'module_builder_export_form',
+      'submit_button' => FALSE,
+    ),
+    array(
+      'title' => t('Delete project'),
+      'machine' => 'delete',
+      'formconstructor' => 'module_builder_delete_form',
+      'submit' => 'module_builder_delete_form_submit',
       'submit_button' => FALSE,
     ),
   );
 }
 
 /**
- * The basic edit form
+ * The general settings form
  */
-function module_builder_edit_form(&$form_state, $values, $mid) {
-  $form = array();
-  $form['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#description' => t('The external name of the module'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#default_value' => isset($values->name)? $values->name : '',
-  );
-  $form['machine'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Machine-readable name'),
-    '#description' => t('Alpha-numeric characters and underscores only'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#default_value' => isset($values->machine)? $values->machine : '',
-  );
-  $form['description'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Description'),
-    '#description' => t('The description of the module'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#default_value' => isset($values->description)? $values->description : '',
+function module_builder_general_form(&$form_state, $values, $module) {
+  $form = array(
+    'name' => array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#description' => t('The human-readable name of the module'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#default_value' => isset($values->name)? $values->name : '',
+    ),
+    'machine' => array(
+      '#type' => 'textfield',
+      '#title' => t('Machine-readable name'),
+      '#description' => t('Alpha-numeric characters and underscores only'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#default_value' => isset($values->machine)? $values->machine : '',
+    ),
+    'description' => array(
+      '#type' => 'textfield',
+      '#title' => t('Description'),
+      '#description' => t('The description of the module'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#default_value' => isset($values->description)? $values->description : '',
+    ),
+    'package' => array(
+      '#type' => 'textfield',
+      '#title' => t('Package'),
+      '#description' => t('Optional category name (f.e. CCK, Development..)'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#default_value' => isset($values->package)? $values->package : '',
+    ),
+    'dependencies' => array(
+      '#type' => 'textfield',
+      '#title' => t('Dependencies'),
+      '#description' => t('Machine-readable names of needed modules (space seperated)'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#default_value' => isset($values->dependencies)? $values->dependencies : '',
+    ),
+    '#pre_render' => array('module_builder_general_js_magic'),
   );
-  module_builder_add_js('copy', 'makeUnderscore', '#edit-name', '#edit-machine');
   return $form;
 }
+
+function module_builder_general_js_magic($form, $foo) {
+// drupal_set_message('module_builder_general_js_magic');
+  module_builder_add_js();
+  module_builder_add_item_js('moduleBuilderCopy', 'makeUnderscore', '#edit-name', '#edit-machine');
+  return $form;
+}
+
 /**
- * Submit callback for the edit form
+ * Submit callback for general settings form
+ * (updates the project table after saving form values)
  */
-function module_builder_edit_form_submit($form, &$form_state) {
+function module_builder_general_form_submit($form, &$form_state) {
   module_builder_default_submit($form, $form_state);
-  db_query("UPDATE {module_builder_basic} SET name = '%s' WHERE mid = %d", $form_state['values']['name'], $form_state['values']['mid']);
+  db_query("UPDATE {module_builder_projects} SET name = '%s' WHERE mid = '%d'", $form_state['values']['name'], $form_state['values']['mid']);
 }
 
 function module_builder_node_form(&$form_state, $values) {
-  if (isset($form_state['storage']['node_type_count']) && $form_state['storage']['node_type_count'] > 1 && isset($form_state['storage']['types']) && !empty($form_state['storage']['types'])) {
-    $node_type_count = $form_state['storage']['node_type_count'];
-    $types = $form_state['storage']['types'];
-  }
-  elseif (isset($values->types)) {
-    $node_type_count = count($values->types);
-    $types = $values->types;
-  }
-  else {
-    $node_type_count = 1;
-  }
-  $default_type = array(
+// drupal_set_message('module_builder_node_form');
+  $item_default = array(
     'name' => '',
     'machine' => '',
     'description' => '',
-    'locked' => FALSE,
+    'locked' => 0,
   );
-  
-  $form = array(
-    '#cache' => TRUE,
-    '#tree' => TRUE,
-  );
-  $form['integration'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Integrate with the node module'),
-    '#description' => t('If your module needs to interact with  node types other than the ones it defines, check this'),
-    '#default_value' => isset($values->integration)? (bool) $values->integration : FALSE,
-  );
-  $form['types'] = array(
+  $prototype = array(
     '#type' => 'fieldset',
-    '#title' => t('Node types'),
-    '#description' => t('If the module defines any node types, fill out these values.'),
-    '#collapsible' => FALSE,
-    '#prefix' => '<div id="node-types-wrapper">',
-    '#suffix' => '</div>',
-  );
-  for ($delta = 0; $delta < $node_type_count; $delta++) {
-    if (!isset($types[$delta])) {
-      $type = $default_type;
-    }
-    else {
-      $type = $types[$delta] + $default_type;
-    }
-    $form['types'][$delta] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Node type'), // As an added bonus, change it to (!empty($type['name'])? $type['name'] : t('Node type'))
-      '#collapsible' => TRUE,
-    );
-    $form['types'][$delta]['name'] = array(
+    '#collapsible' => TRUE,
+    'name' => array(
       '#type' => 'textfield',
       '#title' => t('Name'),
-      '#description' => t('The external (not machine-readable) name of the content type.'),
-      '#default_value' => $type['name'],
-    );
-    $form['types'][$delta]['machine'] = array(
+      '#description' => t('The human-readable name of the content type.'),
+    ),
+    'machine' => array(
       '#type' => 'textfield',
       '#title' => t('Machine-readable name'),
       '#description' => t('The machine-readable name of the content type.'),
-      '#default_value' => $type['machine'],
-    );
-    $form['types'][$delta]['description'] = array(
+    ),
+    'description' => array(
       '#type' => 'textfield',
       '#title' => t('Description'),
       '#description' => t('The description of the content type'),
-      '#default_value' => $type['description'],
-    );
-    $form['types'][$delta]['locked'] = array(
+    ),
+    'locked' => array(
       '#type' => 'checkbox',
       '#title' => t('Locked'),
-      '#description' => t('If the content type is locked, the user can\'t modify any properties from the interface'),
-      '#default_value' => $type['locked'],
-    );
-  }
-  $form['types']['new'] = array(
-    '#type' => 'submit', 
-    '#value' => t('New node type'),
-    '#submit' => array('_module_builder_node_submit'),
-    '#ahah' => array(
-      'wrapper' => 'node-types-wrapper',
-      'path' => 'module_builder/js',
+      '#description' => t('If the content type is locked, the user can not modify any of its properties.'),
     ),
   );
-  
-  $form['#pre_render'][] = 'module_builder_node_after_build';
+  $form = array(
+    '#cache' => FALSE,
+    '#tree' => TRUE,
+    'item_default' => array(
+      '#type' => 'value',
+      '#value' => $item_default,
+    ),
+    'item_prototype' => array(
+      '#type' => 'value',
+      '#value' => $prototype,
+    ),
+    'nodeapiintegration' => array(
+      '#type' => 'checkbox',
+      '#title' => t('Integrate with the node module (adds a nodeapi hook)'),
+      '#description' => t('Check this if your module needs to interact with node types other than its own.'),
+      '#default_value' => (isset($values->nodeapiintegration)) ? $values->nodeapiintegration : 0,
+      '#suffix' => '<hr />',
+    ),
+    '#pre_render' => array('module_builder_node_js_magic'),
+  );
   return $form;
 }
 
-function module_builder_node_after_build($form) {
-  foreach (element_children($form['types']) as $key) {
-    if (is_numeric($key)) {
-      module_builder_add_js('copy', 'makeUnderscore', '#'. $form['types'][$key]['name']['#id'], '#'. $form['types'][$key]['machine']['#id']);      
+// can be passed the whole form or just the items part
+function module_builder_node_js_magic($form, $foo) {
+  if (isset($form['items'])) {
+    $items = $form['items'];
+    foreach (element_children($items) as $key) {
+      if (is_numeric($key)) {
+//         drupal_set_message('id='.$items[$key]['name']['#id']);
+        module_builder_add_js();
+        module_builder_add_item_js('moduleBuilderCopy', 'makeUnderscore', '#'. $items[$key]['name']['#id'], '#'. $items[$key]['machine']['#id']);
+      }
     }
   }
+    else {
+    // handle single item that gets appended into page
+    $item = $form;
+    module_builder_add_item_js('moduleBuilderCopy', 'makeUnderscore', '#'. $item['name']['#id'], '#'. $item['machine']['#id'], 'appended');
+  }
   return $form;
 }
 
-function _module_builder_node_submit($form, &$form_state) {
-  $form_state['storage']['node_type_count'] = count(element_children($form['types']));
-  $types = $form_state['values']['types'];
-  unset($types['new']);
-  $form_state['storage']['types'] = $types;
-}
-
 function module_builder_node_export($values, $module) {
   $output = "";
-  if (isset($values->integration) && $values->integration == 1) {
+  if (isset($values->nodeapiintegration) && $values->nodeapiintegration == 1) {
     $output .= module_builder_add_hook('nodeapi', $module);
   }
   if (isset($values->types) && count($values->types)) {
     $output .= "/**\n * Implementation of hook_node_info().\n */\nfunction ". $module->machine ."_node_info() {\n  return array(\n";
     foreach ($values->types as $type) {
       if (!empty($type['machine']) && $type['machine'] != "N") {
-        $output .= "    '". $type['machine'] ."' => array(,\n";
+        $output .= "    '". $type['machine'] ."' => array(\n";
         $output .= "      'name'        => t('". $type['name'] ."'),\n";
         $output .= "      'module'      => '". $module->machine ."',\n";
         $output .= "      'description' => t('". $type['description'] ."'),\n";
@@ -206,64 +221,74 @@
   return $output;
 }
 
+function module_builder_function_form(&$form_state, $values) {
+  $item_default = array(
+    'name' => '',
+    'header' => '',
+    'body' => '',
+  );
+  $prototype = array(
+    '#type' => 'fieldset',
+    '#collapsible' => TRUE,
+    'name' => array(
+      '#type' => 'textfield',
+      '#title' => t('Function name'),
+      '#description' => t('(Functions called externally should be prefixed with the module\'s name.)'),
+    ),
+    'header' => array(
+      '#type' => 'textarea',
+      '#title' => t('Header comment'),
+      '#description' => t('Can be used to describe the purpose of the function and it\'s parameters and return value.'),
+      '#rows' => 2,
+    ),
+    'body' => array(
+      '#type' => 'textarea',
+      '#title' => t('Function Body'),
+      '#description' => t('Code goes here.'),
+      '#rows' => 10,
+    ),
+  );
+  $form = array(
+    '#cache' => FALSE,
+    '#tree' => TRUE,
+    'item_default' => array(
+      '#type' => 'value',
+      '#value' => $item_default,
+    ),
+    'item_prototype' => array(
+      '#type' => 'value',
+      '#value' => $prototype,
+    ),
+  );
+  return $form;
+}
 
+function module_builder_schema_form(&$form_state, $values) {
+  $form = array();
+  return $form;
+}
 
 function module_builder_menu_form(&$form_state, $values) {
-  if (isset($form_state['storage']['menu_item_count']) && $form_state['storage']['menu_item_count'] > 1 && isset($form_state['storage']['items']) && !empty($form_state['storage']['items'])) {
-    $menu_item_count = $form_state['storage']['menu_item_count'];
-    $items = $form_state['storage']['items'];
-  }
-  elseif (isset($values->items)) {
-    $menu_item_count = count($values->items);
-    $items = $values->items;
-  }
-  else {
-    $menu_item_count = 1;
-  }
-  $default_item = array(
+  $item_default = array(
     'title' => '',
     'path' => '',
     'type' => 'Normal',
     'callback' => '',
   );
-  
-  $form = array(
-    '#cache' => FALSE,
-    '#tree' => TRUE,
-  ); 
-  $form['items'] = array(
+  $prototype = array(
     '#type' => 'fieldset',
-    '#title' => t('Menu items'),
-    '#description' => t('If the module defines any menu items, fill out these values.'),
-    '#collapsible' => FALSE,
-    '#prefix' => '<div id="menu-items-wrapper">',
-    '#suffix' => '</div>',
-  );
-  for ($delta = 0; $delta < $menu_item_count; $delta++) {
-    if (!isset($items[$delta])) {
-      $item = $default_item;
-    }
-    else {
-      $item = $items[$delta] + $default_item;
-    }
-    $form['items'][$delta] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Menu item'), // As an added bonus, change it to (!empty($item['title'])? $item['title'] : t('Menu item'))
-      '#collapsible' => TRUE,
-    );
-    $form['items'][$delta]['title'] = array(
+    '#collapsible' => TRUE,
+    'title' => array(
       '#type' => 'textfield',
       '#title' => t('Title'),
       '#description' => t('The title of the menu item'),
-      '#default_value' => $item['title'],
-    );
-    $form['items'][$delta]['path'] = array(
+    ),
+    'path' => array(
       '#type' => 'textfield',
       '#title' => t('Path'),
       '#description' => t('The path of the menu item, such as admin/build/modules'),
-      '#default_value' => $item['path'],
-    );
-    $form['items'][$delta]['type'] = array(
+    ),
+    'type' => array(
       '#type' => 'select',
       '#title' => t('Type'),
       '#multiple' => FALSE,
@@ -273,59 +298,74 @@
         'Default tab',
         'Callback',
         'Suggested item',
-        '#default_value' => $item['type'],
       )),
-    );
-    $form['items'][$delta]['callback'] = array(
+    ),
+    'callback' => array(
       '#type' => 'textfield',
       '#title' => t('Callback'),
       '#description' => t('The title you enter here appears on the page.'),
       '#size' => 40,
       '#maxlength' => 255,
-      '#default_value' => $item['callback'],
-    );
-    
-  }
-  $form['items']['new'] = array(
-    '#type' => 'submit', 
-    '#value' => t('New menu item'),
-    '#submit' => array('_module_builder_menu_submit'),
-    '#ahah' => array(
-      'wrapper' => 'menu-items-wrapper',
-      'path' => 'module_builder/js',
+    )
+  );
+  $form = array(
+    '#cache' => FALSE,
+    '#tree' => TRUE,
+    'item_default' => array(
+      '#type' => 'value',
+      '#value' => $item_default,
+    ),
+    'item_prototype' => array(
+      '#type' => 'value',
+      '#value' => $prototype,
     ),
   );
   return $form;
 }
 
-function _module_builder_menu_submit($form, &$form_state) {
-  $form_state['storage']['menu_item_count'] = count(element_children($form['items']));
-  $items = $form_state['values']['items'];
-  unset($items['new']);
-  $form_state['storage']['items'] = $items;
-}
-
-function module_builder_export_form() {
-  $form = array();
-  if (!module_builder_archive_tar_enabled()) {
-    $form['note'] = array(
+function module_builder_export_form(&$form_state, $values) {
+  $form = array(
+    'note' => array(
       '#type' => 'item',
-      '#value' => t('Several file downloads have started.  Please put them all in a single directory, %dir.', array('%dir' => unserialize(db_result(db_query("SELECT data FROM {module_builder_data} WHERE mid = '%d' AND type = '%s'", menu_get_object('module_builder', 4)->mid, 'edit')))->machine)),
-    );
-    $form['info_file'] = array(
-      '#value' => '<iframe src="'. url('module_builder/export/'. arg(4) .'/info') .'" width="1"></iframe>',
-      '#prefix' => '<div style="display: none">',
-    );
-    $form['module_file'] = array(
-      '#value' => '<iframe src="'. url('module_builder/export/'. arg(4) .'/module') .'" width="1"></iframe>',
-      '#suffix' => '</div>',
-    );
+      '#value' => t('The module skeleton has been generated. Please download the files below and put them in a single directory %dir.', array('%dir' => menu_get_object('mid', 4)->machine)),
+    ),
+    'info_file' => array(
+      '#value' => '<a href="'. url('module_builder/export/'. arg(4) .'/info') .'">.info file</a>',
+      '#suffix' => '<br />',
+    ),
+    'module_file' => array(
+      '#value' => '<a href="'. url('module_builder/export/'. arg(4) .'/module') .'">.module file</a>',
+    ),
+  );
+  if (!module_builder_archive_tar_enabled()) {
     $form['enhanced'] = array(
       '#type' => 'item',
       '#value' => t('<strong>Note:</strong> For enhanced export capabilities, please download the <a href="!archive_tar">archive tar package</a> and put it in the module_builder directory.', array('!archive_tar' => 'http://dmitrizone.com/files/archive_tar.tgz')),
     );
   }
   else {
+    $form['tar_note'] = array(
+      '#type' => 'item',
+      '#value' => t('Click below to download a GZipped TAR archive of the module directory.'),
+    );
+    $form['tar_file'] = array(
+      '#value' => '<a href="'. url('module_builder/export/'. arg(4) .'/tar') .'">.tar.gz file</a>',
+    );
   }
   return $form;
 }
+
+function module_builder_delete_form(&$form_state, $values, $module) {
+  return confirm_form(
+    array('#modulename' => $module->name),
+    t('Are you sure you want to delete the module "!module"?', array('!module' => $module->name)),
+      'admin/build/module_builder/edit/'. $module->mid
+  );
+}
+
+function module_builder_delete_form_submit($form, &$form_state) {
+  if (db_query('DELETE FROM {module_builder_data} WHERE mid = %d', $form_state['values']['mid']) &&
+      db_query('DELETE FROM {module_builder_projects} WHERE mid = %d', $form_state['values']['mid']))
+    drupal_set_message(t('The project "!name" has been deleted.', array('!name' => $form['#modulename'])));
+  $form_state['redirect'] = 'admin/build/module_builder';
+}
--- module_builder.info.orig	2008-01-03 13:05:02.000000000 +0100
+++ module_builder.info	2008-05-01 17:22:42.000000000 +0200
@@ -1,7 +1,9 @@
 ; $Id: module_builder.info,v 1.2.4.2 2008/01/02 02:49:33 dmitrig01 Exp $
 name = Module Builder
 description = Assists in the process of building modules
+package = Development
 core = 6.x
+
 ; Information added by drupal.org packaging script on 2008-01-03
 version = "6.x-1.x-dev"
 core = "6.x"
--- module_builder.install.orig	2008-01-02 03:49:33.000000000 +0100
+++ module_builder.install	2008-05-06 03:50:52.000000000 +0200
@@ -2,60 +2,45 @@
 // $Id: module_builder.install,v 1.1.2.2 2008/01/02 02:49:33 dmitrig01 Exp $
 
 /**
- * Implementation of hook_install().
+ * Implementation of hook_schema().
  */
-function module_builder_install() {
-  // Create tables.
-  drupal_install_schema('module_builder');
-
-  drupal_set_message('Module builder installed');
-}
-
-/**
- * Implementation of hook_uninstall().
- */
-function module_builder_uninstall() {
-  // Remove tables.
-  drupal_uninstall_schema('module_builder');
-}
-
 function module_builder_schema() {
-  $schema['module_builder_basic'] = array(
-    'description' => t('Stores data having to do with modules.'),
+  $schema['module_builder_projects'] = array(
+    'description' => t('Stores ids and names of modules.'),
     'fields' => array(
       'mid' => array(
         'type' => 'serial',
         'description' => t('The id of the module.'),
       ),
       'name' => array(
-        'type' => 'varchar', 
-        'length' => '255', 
+        'type' => 'varchar',
+        'length' => '255',
         'default' => '',
-        'not null' => TRUE, 
+        'not null' => TRUE,
         'description' => t('The name of the module.'),
       ),
     ),
     'primary key' => array('mid'),
   );
   $schema['module_builder_data'] = array(
-    'description' => t('Stores data having to do with modules built with the module builder.'),
+    'description' => t('Stores each component of modules built with module_builder as data blob.'),
     'fields' => array(
       'mid' => array(
-        'type' => 'int', 
-        'unsigned' => TRUE, 
-        'not null' => TRUE, 
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
         'default' => 0,
         'description' => t('The module this data is attached to.'),
       ),
       'type' => array(
-        'type' => 'varchar', 
-        'length' => '64', 
+        'type' => 'varchar',
+        'length' => '64',
         'default' => '',
-        'not null' => TRUE, 
+        'not null' => TRUE,
         'description' => t('The type, such as "node" or "comment" or "menu".'),
       ),
       'data' => array(
-        'type' => 'blob', 
+        'type' => 'blob',
         'description' => t('The data'),
         'serialize' => TRUE
       ),
@@ -63,4 +48,30 @@
     'indexes' => array('data' => array('mid', 'type')),
   );
   return $schema;
-}
\ No newline at end of file
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function module_builder_install() {
+  // Create tables.
+  drupal_install_schema('module_builder');
+
+  drupal_set_message('Module builder installed');
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function module_builder_uninstall() {
+  // Remove tables.
+  drupal_uninstall_schema('module_builder');
+}
+
+function module_builder_update_1() {
+  $ret = array();
+  $ret[] = update_sql("RENAME TABLE {module_builder_basic} TO {module_builder_projects}");
+  $ret[] = update_sql("UPDATE {module_builder_data} SET type = 'general' WHERE type = 'edit'");
+  $ret[] = update_sql("UPDATE {module_builder_data} SET type = 'menu' WHERE type = 'menu'");
+  return $ret;
+}
--- module_builder.module.orig	2008-01-02 03:49:33.000000000 +0100
+++ module_builder.module	2008-05-06 00:08:41.000000000 +0200
@@ -14,35 +14,36 @@
     'page callback' => 'module_builder_home',
     'access arguments' => array('build modules'),
     'file' => 'module_builder.pages.inc',
-    'description' => 'Make and customize new modules without writing code',
+    'description' => 'Create Drupal modules through a supportive interface.',
   );
-  $modules = module_builder_get_components();
+  $components = module_builder_get_components();
   $default = array();
   $count = 0;
-  foreach ($modules as $builder) {
-    $items['admin/build/module_builder/edit/%module_builder/'. $builder['machine']] = array(
-      'title' => $builder['title'],
+  foreach ($components as $component) {
+    $items['admin/build/module_builder/edit/%mid/'. $component['machine']] = array(
+      'title' => $component['title'],
       'page callback' => 'drupal_get_form',
       'page arguments' => array('module_builder_build', 4, 5),
-      'type' => $builder['default'] == TRUE ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+      'access arguments' => array('build modules'),
+      'type' => $component['default'] == TRUE ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
       'file' => 'module_builder.pages.inc',
-      'description' => 'Make and customize new modules',
+      'description' => 'Create Drupal modules',
       'weight' => $count,
     );
     $count++;
-    if ($builder['default'] == TRUE) {
-      $default = $builder;
+    if ($component['default'] == TRUE) {
+      $default = $component;
     }
   }
-  $items['admin/build/module_builder/edit/%module_builder'] = array(
-    'title' => $default['title'],
+  $items['admin/build/module_builder/edit/%mid'] = array(
     'page callback' => 'drupal_get_form',
     'page arguments' => array('module_builder_build', 4, $default['machine']),
+    'access arguments' => array('build modules'),
     'type' => MENU_CALLBACK,
     'file' => 'module_builder.pages.inc',
   );
   $items['admin/build/module_builder/new'] = array(
-    'title' => 'Module Builder',
+    'title' => t('Creating a new module..'),
     'page callback' => 'drupal_get_form',
     'page arguments' => array('module_builder_build_new'),
     'access arguments' => array('build modules'),
@@ -50,14 +51,12 @@
     'file' => 'module_builder.pages.inc',
   );
   $items['module_builder/js'] = array(
-    'title' => 'Module Builder',
     'page callback' => 'module_builder_js',
     'access arguments' => array('build modules'),
     'type' => MENU_CALLBACK,
     'file' => 'module_builder.api.inc',
   );
-  $items['module_builder/export/%module_builder/%'] = array(
-    'title' => 'Module Builder',
+  $items['module_builder/export/%mid/%'] = array(
     'page callback' => 'module_builder_export_callback',
     'page arguments' => array(2, 3),
     'access arguments' => array('build modules'),
--- module_builder.pages.inc.orig	2008-01-02 03:49:33.000000000 +0100
+++ module_builder.pages.inc	2008-05-06 02:39:23.000000000 +0200
@@ -4,22 +4,16 @@
 include_once drupal_get_path('module', 'module_builder') .'/module_builder.components.inc';
 
 /**
- * Menu callback; home page
+ * Menu callback; entry page
  */
 function module_builder_home() {
   $items = array();
-  $new = l('New module!', 'admin/build/module_builder/new', array('html' => TRUE));
-  $query = db_query("SELECT name, mid FROM {module_builder_basic}");
+  $query = db_query("SELECT name, mid FROM {module_builder_projects}");
   while ($result = db_fetch_array($query)) {
     $items[] = l($result['name'], 'admin/build/module_builder/edit/'. $result['mid']);
   }
-  if (!count($items)) {
-    $output = '<br /><h3>'. $new .'</h3>';
-  }
-  else {
-    $items[] = '<strong>'. $new .'</strong>';
-    $output = theme('item_list', $items);
-  }
+  $output = (!empty($items)) ? theme('item_list', $items, 'Existing module development projects:') : 'No existing projects found.';
+  $output .= '<br />'. l(t('Create a new module!'), 'admin/build/module_builder/new');
   return $output;
 }
 
@@ -28,15 +22,15 @@
  */
 function module_builder_build_new() {
   $form = array();
-  $form['title'] = array(
+  $form['name'] = array(
     '#type' => 'textfield',
-    '#title' => t('Title'),
-    '#description' => t('The title of the module (not the machine-readable name).'),
+    '#title' => t('Project name'),
+    '#description' => t('The title of the module to be created (not the machine-readable name).'),
     '#size' => 40,
     '#maxlength' => 255,
   );
   $form['submit'] = array(
-    '#type' => 'submit', 
+    '#type' => 'submit',
     '#value' => t('Submit'),
   );
   
@@ -44,23 +38,16 @@
 }
 
 function module_builder_build_new_submit($form, &$form_state) {
-  $save = (object) array('name' => $form_state['values']['title']);
-  drupal_write_record('module_builder_basic', $save);
-  $prepared_form_values = array('values' => array('mid' => $save->mid, 'type' => 'edit', 'name' => $save->name));
-  module_builder_default_submit(array(), $prepared_form_values);
-  $form_state['redirect'] = 'admin/build/module_builder/edit/'. $save->mid;
-}
+  $newmodule = new stdClass();
+  $newmodule->name = $form_state['values']['name'];
+  drupal_write_record('module_builder_projects', $newmodule);
 
-function module_builder_delete_form(&$form_state, $values, $module) {
-  return confirm_form(
-    array(),
-    t('Are you sure you want to delete the module %module?', array('%module' => $module->name)),
-    'admin/build/module_builder/edit/'. $module->mid
-  );
-}
-
-function module_builder_delete_form_submit($form, &$form_state) {
-  db_query('DELETE FROM {module_builder_data} WHERE mid = %d', $form_state['values']['mid']);
-  db_query('DELETE FROM {module_builder_basic} WHERE mid = %d', $form_state['values']['mid']);
-  $form_state['redirect'] = 'admin/build/module_builder';
+  // make the entered name appear on the general form then redirect to it
+  $prepared_form_values = array(
+    'values' => array(
+      'mid' => $newmodule->mid,
+      'type' => 'general',
+      'name' => $newmodule->name));
+  module_builder_default_submit(array(), $prepared_form_values);
+  $form_state['redirect'] = 'admin/build/module_builder/edit/'. $newmodule->mid;
 }
