Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.902
diff -u -p -r1.902 node.module
--- modules/node/node.module	14 Nov 2007 12:43:13 -0000	1.902
+++ modules/node/node.module	16 Nov 2007 19:58:39 -0000
@@ -283,36 +283,53 @@ function node_teaser($body, $format = NU
     return $body;
   }
 
+  // If the delimiter has not been specified, try to split at paragraph,
+  // sentence, or word boundaries.
+
   // The teaser may not be longer than maximum length specified. Initial slice.
   $teaser = truncate_utf8($body, $size);
-  $position = 0;
-  // Cache the reverse of the teaser.
+
+  // Set $max_length to the actual length of the UTF8 string -- which might not
+  // be the same as $size.
+  $max_length = strlen($teaser);
+
+  // How much to cut off the end of the teaser so that it doesn't end in the
+  // middle of a paragraph, sentence, or word.
+  // Initialize it to the largest possible value.
+  $min_length = $max_length;
+
+  // Store the reverse of the teaser.  We use strpos on the reversed needle and
+  // haystack for speed and convenience.
   $reversed = strrev($teaser);
 
-  // In some cases, no delimiter has been specified. In this case, we try to
-  // split at paragraph boundaries.
-  $breakpoints = array('</p>' => 0, '<br />' => 6, '<br>' => 4, "\n" => 1);
-  // We use strpos on the reversed needle and haystack for speed.
-  foreach ($breakpoints as $point => $offset) {
-    $length = strpos($reversed, strrev($point));
-    if ($length !== FALSE) {
-      $position = - $length - $offset;
-      return ($position == 0) ? $teaser : substr($teaser, 0, $position);
+  // Build an array of arrays of break points grouped by preference.
+  $break_points = array();
+
+  // A paragraph or line break near the end of sliced teaser is most preferable.
+  $break_points[] = array('</p>' => 0, '<br />' => 6, '<br>' => 4, "\n" => 1);
+
+  // If the first paragraph is too long, split at the end of a sentence.
+  $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
+
+  // Iterate over the groups of break points until a break point is found.
+  foreach ($break_points as $points) {
+    // Look for each break point, starting at the end of the teaser.
+    foreach ($points as $point => $offset) {
+      // The teaser is already reversed, but the break point isn't.
+      $length = strpos($reversed, strrev($point));
+      if ($length !== FALSE) {
+        $min_length = min($length + $offset, $min_length);
+      }
     }
-  }
 
-  // When even the first paragraph is too long, we try to split at the end of
-  // the last full sentence.
-  $breakpoints = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
-  $min_length = strlen($reversed);
-  foreach ($breakpoints as $point => $offset) {
-    $length = strpos($reversed, strrev($point));
-    if ($length !== FALSE) {
-      $min_length = min($length, $min_length);
-      $position = 0 - $length - $offset;
+    // If a break point was found in this group, slice and return the teaser.
+    if ($min_length !== $max_length) {
+      return substr($teaser, 0, 0 - $min_length);
     }
   }
-  return ($position == 0) ? $teaser : substr($teaser, 0, $position);
+
+  // If a break point was not found, still return a teaser.
+  return $teaser;
 }
 
 /**
