Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.181
diff -u -F^f -r1.181 form.inc
--- includes/form.inc	7 Mar 2007 17:31:40 -0000	1.181
+++ includes/form.inc	15 Mar 2007 07:33:41 -0000
@@ -1461,6 +1461,17 @@ function theme_form($element) {
  */
 function theme_textarea($element) {
   $class = array('form-textarea');
+
+  // Add teaser behaviour (must come before resizable)
+  if (!empty($element['#teaser'])) {
+    drupal_add_js('misc/teaser.js');
+    // Note: arrays are merged in drupal_get_js().
+    drupal_add_js(array('teaserButton' => array(t('Join teaser'), t('Split teaser at cursor'))), 'setting');
+    drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting');
+    $class[] = 'teaser';
+  }
+
+  // Add resizable behaviour
   if ($element['#resizable'] !== FALSE) {
     drupal_add_js('misc/textarea.js');
     $class[] = 'resizable';
Index: misc/drupal.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
retrieving revision 1.29
diff -u -F^f -r1.29 drupal.js
--- misc/drupal.js	14 Oct 2006 02:39:48 -0000	1.29
+++ misc/drupal.js	15 Mar 2007 07:33:41 -0000
@@ -200,6 +200,26 @@
   return uri.indexOf('?q=') ? item : item.replace('%26', '%2526').replace('%23', '%2523');
 };
 
+/**
+ * Get the text selection in a textarea.
+ */
+Drupal.getSelection = function (element) {
+  if (document.selection) {
+    // The current selection
+    var range1 = document.selection.createRange();
+    var range2 = range1.duplicate();
+    // Select all text.
+    range2.moveToElementText(element);
+    // Now move 'dummy' end point to end point of original range.
+    range2.setEndPoint('EndToEnd', range1);
+    // Now we can calculate start and end points.
+    var start = range2.text.length - range1.text.length;
+    var end = start + range1.text.length;
+    return { 'start': start, 'end': end };
+  }
+  return { 'start': element.selectionStart, 'end': element.selectionEnd };
+}
+
 // Global Killswitch on the <html> element
 if (Drupal.jsEnabled) {
   document.documentElement.className = 'js';
Index: misc/textarea.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/textarea.js,v
retrieving revision 1.12
diff -u -F^f -r1.12 textarea.js
--- misc/textarea.js	15 Feb 2007 07:10:11 -0000	1.12
+++ misc/textarea.js	15 Mar 2007 07:33:41 -0000
@@ -7,6 +7,12 @@
     $(this).wrap('<div class="resizable-textarea"></div>')
       .parent().append($('<div class="grippie"></div>').mousedown(startDrag));
 
+    // Inherit visibility
+    if ($(this).is(':hidden')) {
+      $(this).parent().hide();
+      $(this).show();
+    }
+
     var grippie = $('div.grippie', $(this).parent())[0];
     grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) +'px';
 
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.251
diff -u -F^f -r1.251 block.module
--- modules/block/block.module	31 Jan 2007 15:49:22 -0000	1.251
+++ modules/block/block.module	15 Mar 2007 07:33:41 -0000
@@ -576,8 +576,8 @@ function block_box_form($edit = array())
     '#required' => TRUE,
     '#weight' => -19,
   );
-  $form['body_filter']['#weight'] = -17;
-  $form['body_filter']['body'] = array(
+  $form['body_field']['#weight'] = -17;
+  $form['body_field']['body'] = array(
     '#type' => 'textarea',
     '#title' => t('Block body'),
     '#default_value' => $edit['body'],
@@ -588,7 +588,7 @@ function block_box_form($edit = array())
   if (!isset($edit['format'])) {
     $edit['format'] = FILTER_FORMAT_DEFAULT;
   }
-  $form['body_filter']['format'] = filter_form($edit['format'], -16);
+  $form['body_field']['format'] = filter_form($edit['format'], -16);
 
   return $form;
 }
Index: modules/blog/blog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v
retrieving revision 1.276
diff -u -F^f -r1.276 blog.module
--- modules/blog/blog.module	7 Mar 2007 12:41:02 -0000	1.276
+++ modules/blog/blog.module	15 Mar 2007 07:33:42 -0000
@@ -211,8 +211,7 @@ function blog_form(&$node) {
   }
 
   $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => !empty($node->title) ? $node->title : NULL, '#weight' => -5);
-  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => !empty($node->body) ? $node->title : NULL, '#rows' => 20, '#required' => TRUE);
-  $form['body_filter']['filter'] = filter_form($node->format);
+  $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
   return $form;
 }
 
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.411
diff -u -F^f -r1.411 book.module
--- modules/book/book.module	11 Feb 2007 09:30:51 -0000	1.411
+++ modules/book/book.module	15 Mar 2007 07:33:42 -0000
@@ -215,13 +215,8 @@ function book_form(&$node) {
     '#default_value' => $node->title,
     '#weight' => -5,
   );
-  $form['body_filter']['body'] = array('#type' => 'textarea',
-    '#title' => check_plain($type->body_label),
-    '#default_value' => $node->body,
-    '#rows' => 20,
-    '#required' => TRUE,
-  );
-  $form['body_filter']['format'] = filter_form($node->format);
+
+  $form['body_field'] = node_body_field($node, $type->body_label, 1);
 
   if (user_access('administer nodes')) {
     $form['weight'] = array('#type' => 'weight',
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.386
diff -u -F^f -r1.386 forum.module
--- modules/forum/forum.module	28 Feb 2007 20:42:50 -0000	1.386
+++ modules/forum/forum.module	15 Mar 2007 07:33:42 -0000
@@ -402,8 +402,7 @@ function forum_form(&$node) {
     $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
   }
 
-  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => !empty($node->body) ? $node->body : '', '#rows' => 20, '#required' => TRUE);
-  $form['body_filter']['format'] = filter_form($node->format);
+  $form['body_field'] = node_body_field($node, $type->body_label, 1);
 
   return $form;
 }
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.790
diff -u -F^f -r1.790 node.module
--- modules/node/node.module	14 Mar 2007 21:41:06 -0000	1.790
+++ modules/node/node.module	15 Mar 2007 07:33:43 -0000
@@ -139,6 +139,28 @@ function node_mark($nid, $timestamp) {
 }
 
 /**
+ * See if the user used JS to submit a teaser.
+ */
+function node_teaser_js(&$form, $form_values) {
+  // Glue the teaser to the body.
+  if (isset($form['#post']['teaser_js'])) {
+    if (trim($form_values['teaser_js'])) {
+      // Space the teaser from the body
+      $body = trim($form_values['teaser_js']) ."\r\n<!--break-->\r\n". trim($form_values['body']);
+    }
+    else {
+      // Empty teaser, no spaces.
+      $body = '<!--break-->'. $form_values['body'];
+    }
+    // Pass value onto preview/submit
+    form_set_value($form['body'], $body);
+    // Pass value back onto form
+    $form['body']['#value'] = $body;
+  }
+  return $form;
+}
+
+/**
  * Automatically generate a teaser for a node body in a given format.
  */
 function node_teaser($body, $format = NULL) {
@@ -1838,7 +1860,16 @@ function node_submit($node) {
   // Auto-generate the teaser, but only if it hasn't been set (e.g. by a
   // module-provided 'teaser' form item).
   if (!isset($node->teaser)) {
-    $node->teaser = isset($node->body) ? node_teaser($node->body, isset($node->format) ? $node->format : NULL) : '';
+    if (isset($node->body)) {
+      $node->teaser = node_teaser($node->body, isset($node->format) ? $node->format : NULL);
+      // Chop off the teaser from the body if needed.
+      if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
+        $node->body = substr($node->body, strlen($node->teaser));
+      }
+    }
+    else {
+      $node->teaser = '';
+    }
   }
 
   if (user_access('administer nodes')) {
@@ -2172,6 +2203,10 @@ function node_preview($node) {
     // 'teaser' form item).
     if (!isset($node->teaser)) {
       $node->teaser = node_teaser($node->body, $node->format);
+      // Chop off the teaser from the body if needed.
+      if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
+        $node->body = substr($node->body, strlen($node->teaser));
+      }
     }
 
     // Display a preview of the node:
@@ -2197,7 +2232,7 @@ function node_preview($node) {
 function theme_node_preview($node) {
   $output = '<div class="preview">';
   if ($node->teaser && $node->teaser != $node->body) {
-    drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication. You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.'));
+    drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
     $output .= '<h3>'. t('Preview trimmed version') .'</h3>';
     $output .= node_view(drupal_clone($node), 1, FALSE, 0);
     $output .= '<h3>'. t('Preview full version') .'</h3>';
@@ -2915,6 +2950,42 @@ function node_content_access($op, $node)
 }
 
 /**
+ * Return a node body field, with format and teaser.
+ */
+function node_body_field(&$node, $label, $word_count) {
+
+  // Check if we need to restore the teaser at the beginning of the body.
+  $include = !isset($node->teaser) || ($node->teaser == substr($node->body, 0, strlen($node->teaser)));
+
+  $form = array(
+    '#after_build' => array('node_teaser_js'));
+
+  $form['teaser_js'] = array(
+    '#type' => 'textarea',
+    '#rows' => 10,
+    '#teaser' => 'edit-body',
+    '#disabled' => TRUE);
+
+  $form['teaser_include'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include teaser in body'),
+    '#default_value' => $include,
+    '#prefix' => '<div class="teaser-checkbox">',
+    '#suffix' => '</div>',
+  );
+
+  $form['body'] = array(
+    '#type' => 'textarea',
+    '#title' => check_plain($label),
+    '#default_value' => $include ? $node->body : ($node->teaser . $node->body),
+    '#rows' => 20,
+    '#required' => ($word_count > 0));
+
+  $form['format'] = filter_form($node->format);
+
+  return $form;
+}
+/**
  * Implementation of hook_form().
  */
 function node_content_form($node) {
@@ -2932,13 +3003,7 @@ function node_content_form($node) {
   }
 
   if ($type->has_body) {
-    $form['body_filter']['body'] = array(
-      '#type' => 'textarea',
-      '#title' => check_plain($type->body_label),
-      '#default_value' => $node->body,
-      '#rows' => 20,
-      '#required' => ($type->min_word_count > 0));
-    $form['body_filter']['format'] = filter_form($node->format);
+    $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
   }
 
   return $form;
Index: modules/system/system.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.css,v
retrieving revision 1.23
diff -u -F^f -r1.23 system.css
--- modules/system/system.css	2 Mar 2007 09:40:13 -0000	1.23
+++ modules/system/system.css	15 Mar 2007 07:33:43 -0000
@@ -358,6 +358,31 @@
 }
 
 /*
+** Teaser splitter
+*/
+.joined + .grippie {
+  height: 5px;
+  background-position: center 1px;
+  margin-bottom: -2px;
+}
+input.teaser-button {
+  float: right;
+  margin-right: 5%;
+  margin-top: 0;
+}
+.teaser-checkbox div.form-item {
+  float: right;
+  margin: 0 5% 0 0;
+  padding: 0;
+}
+textarea.teaser {
+  display: none;
+}
+html.js .no-js {
+  display: none;
+}
+
+/*
 ** Progressbar styles
 */
 .progress {
