Index: maxlength/maxlength.inc
===================================================================
--- maxlength/maxlength.inc	(revision 737)
+++ maxlength/maxlength.inc	(revision 749)
@@ -10,3 +10,7 @@
 
 // Permissions
 define('ADMINISTER_MAXLENGTH', 'administer maxlength');
+
+// Types of limits
+define('LIMIT_CHAR', 'char');
+define('LIMIT_WORD', 'word');
Index: maxlength/maxlength.js
===================================================================
--- maxlength/maxlength.js	(revision 737)
+++ maxlength/maxlength.js	(revision 749)
@@ -2,14 +2,29 @@
 
 Drupal.maxLength_limit = function (field) {
   var limit = field.attr("limit");
+  var limitType = field.attr("limit_type");
 
   // calculate the remaining count of chars
-  var remainingCnt = limit - field.val().length;
+  var remainingCnt = 0;
+  if (limitType == 'char') {
+    remainingCnt = limit - field.val().length;
+  }
+  else if (limitType == 'word') {
+    var trimmed = field.val().replace(/^\s+|\s+$/g, '');
+    if (trimmed.length == 0) {  // required as .split() on empty string returns 1-element array
+      remainingCnt = limit;
+    }
+    else {
+      remainingCnt = limit - trimmed.split(/\s+/).length;
+    }
+  }
 
   // if there is not remaining char, we clear additional content
   if (remainingCnt < 0) {
-    field.val(field.val().substr(0, limit));
-    remainingCnt = 0;
+    if (limitType == 'char') {
+      field.val(field.val().substr(0, limit));
+      remainingCnt = 0;
+    }
   }
 
   // update the remaing chars text
@@ -17,17 +32,18 @@
 }
 
 Drupal.maxLength_change = function () {
-  var element = $(this);
-  Drupal.maxLength_limit($(this), $(this).attr("limit"));
+  Drupal.maxLength_limit($(this));
 }
 
 if (Drupal.jsEnabled) {
   $(document).ready(function(){
-    // get all the settings, and save the limits in the fields
+    // get all the settings, and save the limits and limit types in the fields
     for (var id in Drupal.settings.maxlength) {
       var limit = Drupal.settings.maxlength[id];
+      var limitType = Drupal.settings.limit_type[id];
       var element = $("#"+ id);
       element.attr("limit", limit);
+      element.attr("limit_type", limitType);
       // update the count at the page load
       Drupal.maxLength_limit(element);
 
Index: maxlength/maxlength.module
===================================================================
--- maxlength/maxlength.module	(revision 737)
+++ maxlength/maxlength.module	(revision 749)
@@ -35,6 +35,7 @@
   if ($form['#id'] == 'node-form') {
     _maxlength_content_form_alter($form, $form_state, $form_id);
   }
+
   // Editing the content type
   elseif ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
     _maxlength_content_type_form_alter($form, $form_state, $form_id);
@@ -48,6 +49,7 @@
   // Deleting a CCK text field.
   elseif ($form_id == '_content_admin_field_remove') {
     variable_del(MAXLENGTH_NODE_TYPE . $form['field_name']['#value']);
+    variable_del(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_type');
     variable_del(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_js');
     variable_del(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text');
   }
@@ -56,12 +58,11 @@
 function _maxlength_content_form_alter(&$form, &$form_state, $form_id) {
   $type = $form['type']['#value'];
   // update the title as needed
-  _maxlength_format_element($form['title'], $form['title']['#default_value'], 'title', 'title', $type);
+  _maxlength_format_element($form['title'], 'title', 'title', $type);
 
   // Update the body as needed
-  _maxlength_format_element($form['body_field']['body'], $form['body_field']['body']['#default_value'], 'body', 'body', $type);
+  _maxlength_format_element($form['body_field']['body'], 'body', 'body', $type);
 
-
   if (module_exists('content')) {
     // Get a list of all the CCK fields for this content type 
     $list = array_keys(content_fields(NULL, $type));
@@ -70,7 +71,7 @@
     foreach ($list as $field) {
       if (is_array($form[$field])) {
         foreach (element_children($form[$field]) as $key) {
-          _maxlength_format_element($form[$field][$key], $form[$field][$key]['value'], $field, str_replace('_', '-', $field) .'-'. $key .'-value', $type);
+          _maxlength_format_element($form[$field][$key], $field, str_replace('_', '-', $field) .'-'. $key .'-value', $type);
         }
       }
     }
@@ -97,23 +98,32 @@
     $form['submission'][MAXLENGTH_NODE_TYPE . $label][MAXLENGTH_NODE_TYPE . $label] = array(
       '#type' => 'textfield',
       '#title' => t('!label max length', array('!label' => ucwords($label))),
-      '#field_suffix' => t('characters'),
       '#return_value' => 1,
       '#size' => 4,
       '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $label .'_'. $type, ''),
-      '#description' => t('Maximum number of characters allowed for the !type field of this content type. Leave blank for an unlimited size.', array('!type' => $label)) .'<br/>'.
-      '<b>'. t('Please remember, it counts all characters, including HTML, so may not work as expected with rich text editors e.g. FCKeditor / tinyMCE.') .'</b>',
-        );
+      '#description' => t('Maximum number of characters or words allowed for the !type field of this content type. Leave blank for an unlimited size.', array('!type' => $label)) .'<br/>'.
+      '<b>'. t('Please remember, it counts all characters/words, including HTML, so may not work as expected with rich text editors e.g. FCKeditor / tinyMCE.') .'</b>',
+    );
+
+    $form['submission'][MAXLENGTH_NODE_TYPE . $label][MAXLENGTH_NODE_TYPE . $label .'_type'] = array(
+      '#type' => 'select',
+      '#title' => t('!label Limit Type', array('!label' => ucwords($label))),
+      '#options' => array(LIMIT_CHAR => t('Characters'), LIMIT_WORD => t('Words')),
+      '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $label .'_type_'. $type, ''),
+      '#description' => t('Choose whether to limit the title field size by characters or words.'),
+    );
+
     $form['submission'][MAXLENGTH_NODE_TYPE . $label][MAXLENGTH_NODE_TYPE . $label .'_js'] = array(
       '#type' => 'checkbox',
-      '#title' => t('Enable remaining characters countdown for the !label', array('!label' => ucwords($label))),
+      '#title' => t('Enable remaining characters/words countdown for the !label', array('!label' => ucwords($label))),
       '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $label .'_js_'. $type, '0'),
       '#description' => t('This will enable a Javascript based count down, as well as the client side validation for the !type field of this content type. If no limit set this is ignored.', array('!type' => $label)),
     );
+
     $form['submission'][MAXLENGTH_NODE_TYPE . $label][MAXLENGTH_NODE_TYPE . $label .'_text'] = array(
       '#type' => 'textarea',
       '#title' => t('!label count down message', array('!label' => ucwords($label))),
-      '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $label .'_text_'. $type, 'Content limited to !limit characters, remaining: <strong>!remaining</strong>'),
+      '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $label .'_text_'. $type, 'Content limited to !limit characters/words, remaining: <strong>!remaining</strong>'),
       '#description' => t('The text used in the Javascript message under the !type input, where "!limit" and "!remaining" are replaced by the appropriate numbers.', array('!type' => $label)),
     );
   }
@@ -126,9 +136,17 @@
     foreach ($form['field'] as $key => $field) {
       $new_fields[$key] = $field;
       if ($key == 'max_length') {
+        $new_fields[MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_type'] = array(
+          '#type' => 'select',
+          '#title' => t('!label Limit Type', array('!label' => ucwords($form['field_name']['#value']))),
+          '#options' => array('char' => t('Characters'), 'word' => t('Words')),
+          '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_type', ''),
+          '#description' => t('Choose whether to limit the title field size by characters or words.'),
+        );
+
         $new_fields[MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_js'] = array(
           '#type' => 'checkbox',
-          '#title' => t('Enable remaining characters countdown for this field'),
+          '#title' => t('Enable remaining characters/words countdown for this field'),
           '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_js', '0'),
           '#description' => t('This will enable a Javascript based count down, as well as the client side validation for this field. If no limit set this is ignored.'),
         );
@@ -136,7 +154,7 @@
         $new_fields[MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text'] = array(
           '#type' => 'textarea',
           '#title' => t('Count down message'),
-          '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text', 'Content limited to !limit characters, remaining: <strong>!remaining</strong>'),
+          '#default_value' => variable_get(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text', 'Content limited to !limit characters/words, remaining: <strong>!remaining</strong>'),
           '#description' => t('The text used in the Javascript message under the input, where "!limit" and "!remaining" are replaced by the appropriate numbers.'),
         );
       }
@@ -149,6 +167,7 @@
 function _maxlength_cck_form_submit($form, &$form_state) {
   //note, max lenght for the CCK field is stored in this way as for textareas, its not in $element var passed to theme functions.
   variable_set(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'], $form['#post']['max_length']);
+  variable_set(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_type', $form['#post'][MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_type']);
   variable_set(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_js', $form['#post'][MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_js']);
   variable_set(MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text', $form['#post'][MAXLENGTH_NODE_TYPE . $form['field_name']['#value'] .'_text']);
 }
@@ -172,37 +191,29 @@
  *   Could this all be done without drupal_add_js and just theming of the form element.
  *   Can we use a theme function to let alter the placement of the maxlength text.
  */
-function _maxlength_format_element(&$element, $value = '', $field, $id, $type = '') {
+function _maxlength_format_element(&$element, $field, $id, $type = '') {
   $values = _maxlength_get_values($field, $type);
 
   if ($values !== FALSE AND isset($values['limit']) AND $values['limit'] AND $values['use_js']) {
     $path = drupal_get_path('module', 'maxlength');
     drupal_add_js($path .'/maxlength.js');
 
-    $remaining = $values['limit'] - drupal_strlen($value);
-
-    if ($remaining < 0) {
-      drupal_set_message(
-        t('%body_field_label truncated to %limit characters!',
-        array(
-          '%body_field_label' => $element['#title'],
-          '%limit' => $values['limit'])
-        ),
-        'error'
-      );
-
-      $element['#default_value'] = drupal_substr($element['#default_value'], 0, $values['limit']);
-      $remaining = 0;
-    }
-
     $js_settings = array(
       'maxlength' => array(
         'edit-'. $id => $values['limit'],
       ),
+      'limit_type' => array(
+        'edit-'. $id => $values['type'],
+      ),
     );
     drupal_add_js($js_settings, 'setting');
-    $element['#suffix'] = '<div id="maxlength-'. $id .'"
-      class="maxlength-counter">'. t($values['text'], array('!limit' => $values['limit'], '!remaining' => '<span class="maxlength-counter-remaining">'. $remaining .'</span>')) .'</div>';
+
+    $element['#suffix'] = '<div id="maxlength-'. $id .'" class="maxlength-counter">'.
+      t($values['text'],
+        array('!limit' => $values['limit'],
+          '!remaining' => '<span class="maxlength-counter-remaining"></span>',
+        )
+      ) .'</div>';
   }
 }
 
@@ -213,11 +224,13 @@
   //CCK
   if (strpos($field, 'field_') === 0) {
     $values['limit'] = variable_get(MAXLENGTH_NODE_TYPE . $field, FALSE);
+    $values['type'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_type', FALSE);
     $values['use_js'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_js', FALSE);
     $values['text'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_text', FALSE);
   } //body and title
   elseif ($type != '') {
     $values['limit'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_'. $type, FALSE);
+    $values['type'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_type_'. $type, FALSE);
     $values['use_js'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_js_'. $type, FALSE);
     $values['text'] = variable_get(MAXLENGTH_NODE_TYPE . $field .'_text_'. $type, FALSE);
   }
@@ -242,14 +255,18 @@
   foreach ($fields as $field) {
     $limit = intval(variable_get(MAXLENGTH_NODE_TYPE . $field .'_'. $node->type, ''));
     if ($limit > 0) {
+      $type = variable_get(MAXLENGTH_NODE_TYPE . $field .'_type_'. $node->type, '');
       switch ($op) {
         case 'validate':
           $form = $a3;
-          if (drupal_strlen($node->$field) > $limit) {
+          if ($type == LIMIT_CHAR && drupal_strlen($node->$field) > $limit) {
             form_set_error($field, t('The !field field has exceeded its maximum number of characters (!limit).', array('!limit' => $limit, '!field' => $field)));
           }
+          elseif ($type == LIMIT_WORD && _maxlength_count_words($node->$field) > $limit) {
+            form_set_error($field, t('The !field field has exceeded its maximum number of words (!limit).', array('!limit' => $limit, '!field' => $field)));
+          }
           break;
-        }
+      }
     }
   }
 }
@@ -259,8 +276,8 @@
  */
 function maxlength_node_type($op, $info) {
   $labels = array(
-    'title', 'js_title', 'text_title',
-    'body', 'js_body', 'text_body');
+    'title', 'type_title', 'js_title', 'text_title',
+    'body', 'type_body', 'js_body', 'text_body');
 
   switch ($op) {
     case 'delete':
@@ -277,8 +294,14 @@
           variable_del(MAXLENGTH_NODE_TYPE . $label . $info->old_type);
         }
       }
-
       break;
   }
 }
 
+function _maxlength_count_words($value) {
+  if (drupal_strlen(trim($value)) == 0) {  // required as preg_split() on empty string returns 1-element array
+    return 0;
+  }
+  
+  return count(preg_split('/\s+/', trim($value)));
+}
Index: maxlength/maxlength.install
===================================================================
--- maxlength/maxlength.install	(revision 737)
+++ maxlength/maxlength.install	(revision 749)
@@ -57,8 +57,8 @@
  */
 function maxlength_uninstall() {
   foreach (node_get_types() as $type => $name) {
-    $labels = array('title', 'js_title', 'text_title',
-                    'body', 'js_body', 'text_body');
+    $labels = array('title', 'type_title','js_title', 'text_title',
+                    'body', 'type_body','js_body', 'text_body');
     
     foreach ($labels as $label) {
       variable_del(MAXLENGTH_NODE_TYPE . $label . $type);    
