Index: glossify.module
===================================================================
--- glossify.module	(revision 6756)
+++ glossify.module	(working copy)
@@ -150,12 +150,41 @@
   }
 }

-
 /**
  * Implementation of hook_nodeapi().
  */
 function glossify_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
   switch ($op) {
+    case 'delete':
+
+      // omerida, 9/30/2010: I think it'd be quicker to
+      // remove from the db directly (DELETE FROM {glossify} where nid=%d
+      // but I figured it'd be safer to stick with the module's api
+      // and use _keyword_table function.
+      $configurations = variable_get('glossify_configurations', array());
+      foreach ($configurations as $config_name => $configuration) {
+        if (isset($configuration['to'][$node->type])) {
+          $enabled_styles = array_filter($configuration['style']);
+          $methods = $configuration['methods'];
+          if (isset($methods['use_title'])) {
+            _keyword_table('delete', $node->nid, 'title', $node->language);
+          }
+
+          if (isset($methods['use_internal'])) {
+            _keyword_table('delete', $node->nid, 'internal', $node->language);
+          }
+
+          if (isset($methods['use_cck'])) {
+            _keyword_table('delete', $node->nid, 'cck', $node->language);
+          }
+
+          if (isset($methods['use_taxonomy'])) {
+            _keyword_table('delete', $node->nid, 'taxonomy', $node->language);
+          }
+        }
+      }
+      break;
+
     case 'load':
       $glossify_keywords = implode(', ', _fetch_keywords($node->nid, 'internal'));
       $glossify_override = db_result(db_query('SELECT alternate FROM {glossify} WHERE nid = %d', $node->nid));
@@ -316,6 +345,8 @@
  * Implementation of hook_filter().
  */
 function glossify_filter($op, $delta = 0, $format = -1, $text = '', $cache_id = 0) {
+  static $configurations;
+
   switch ($op) {
     case 'list':
       return array(0 => t('Glossify filter'));
@@ -327,31 +358,48 @@
       return $text;

     case 'process':
+      // TODO: option should be to only do this on a node page
+      // or have a list of paths where this filter will not be invoked
       if (($node = menu_get_object()) == NULL) {
         $q = db_query("SELECT nid FROM {node_revisions} WHERE '%s'=CONCAT(format, ':', MD5(body))", $cache_id);
         $r = db_fetch_object($q);
+        if ($r->nid) {
         $node = node_load($r->nid);
+        } else {
+          // have to return something so that we don't lose output
+          return $text;
       }
+      }
+
+      if (!isset($configurations)) {
       $configurations = variable_get('glossify_configurations', array());
+        foreach ($configurations as $key => &$config) {
+          if (isset($config['exclude_tags']) && !empty($config['exclude_tags'])) {
+            $html_tags = preg_split("/\r\n|\r|\n|,/", $config['exclude_tags']);
+            $config['exclude_tags'] = array_combine($html_tags, $html_tags);
+          }
+        }
+      }
+
       $html_body = str_get_html($text);
       foreach ($configurations as $config_name => $configuration) {
-        if (in_array($node->type, $configuration['from'])) {
+        if (isset($configuration['from'][$node->type])) {
+          $enabled_styles = array_filter($configuration['style']);
           foreach (_fetch_possible_keywords($configuration, $node->nid) as $term_title => $target_url) {
-            foreach ($configuration['style'] as $style => $enabled) {
-              if ($enabled) {
-                if (isset($old_body) && $old_body !== $html_body->innertext) {
-                  $html_body = str_get_html($html_body->innertext);
-                }
-
+            $replaced = 0;
+            foreach ($enabled_styles as $style => $enabled) {
+              if ($enabled && !empty($term_title) && false !== strpos($html_body->innertext, $term_title)) {
                 $replacement = _fetch_replacement($style, $term_title, $target_url);
-                $old_body = $html_body->innertext;
-                $replaced = 0;
                 _glossify_replace($configuration, $html_body, $term_title, $replacement, $replaced);
+                // update the state of the DOM to reflect new tags added.
+                if (0 < $replaced) {
+                  $html_body = str_get_html($html_body->innertext);
               }
             }
           }
         }
       }
+      }
       return $html_body->innertext;

     default:
@@ -359,24 +407,36 @@
   }
 }

+/**
+ * Helper function that emulates a contributing module's nodeapi load operation
+ * @param stdObject
+ * @param module
+ */
+function _glossify_node_load(&$node, $module) {
+  $fn = $module . '_nodeapi';
+  $result = $fn($node, 'load');
+  foreach ($result as $key => $value) {
+    $node->$key = $value;
+  }
+}

 /**
  * Helper function that fetches and returns the styled term depending on the style and term.
  */
 function _glossify_replace($configuration, &$html, $term_title, $replacement, &$replaced) {
-  if (count($html->childNodes()) > 0) {
-    foreach ($html->childNodes() as $child_node) {
-      _glossify_replace($configuration, $child_node, $term_title, $replacement, $replaced);
+  $text_nodes = $html->find('text');
+  foreach ($text_nodes as $node) {
+    if ('a' == $node->parent->tag) {
+      continue;
     }
-  }
-  else {
-    if (isset($html->tag) && $html->tag !== 'root') {
-      if ($configuration['only_first'] && $replaced == 0) {
+
+    if (!isset($node->parent->tag) || !isset($configuration['exclude_tags'][$node->parent->tag])) {
+      if (0 == $replaced) {
         if ($configuration['break']) {
-          $html->innertext = preg_replace('/\b'.$term_title.'\b/', $replacement, $html->innertext, ($configuration['only_first'] ? 1 : -1), $temp_replaced);
+          $node->innertext = preg_replace('/\b'.$term_title.'\b/', $replacement, $node->innertext, ($configuration['only_first'] ? 1 : -1), $temp_replaced);
         }
         else {
-          $html->innertext = preg_replace('/'.$term_title.'/', $replacement, $html->innertext, ($configuration['only_first'] ? 1 : -1), $temp_replaced);
+          $node->innertext = preg_replace('/'.$term_title.'/', $replacement, $node->innertext, ($configuration['only_first'] ? 1 : -1), $temp_replaced);
         }
         $replaced = $temp_replaced;
       }
@@ -479,9 +539,13 @@
     case 'cck':
       $keyword = $node->$keywordsource;
       $override = $node->$override;
-      if (isset($keyword[0]['value']) && isset($override[0]['value'])) {
+      if (isset($keyword[0]['value'])) {
         $form_keywords = !empty($keyword[0]['value'])? array_map('trim', explode(',', $keyword[0]['value'])) : array();
+      }
+      if (isset($override[0]['value'])) {
         $form_override = $override[0]['value'];
+      } else {
+        $form_override = '';
       }
       break;
     case 'taxonomy':
@@ -588,9 +652,9 @@
  * Helper function that updates the keyword-tables according to the new/old methods.
  */
 function _update_keywords_for_methods($content_types, $methods) {
-  $q = db_query("SELECT nid FROM {node} WHERE type IN (". db_placeholders($content_types, 'varchar') .")", $content_types);
-  while ($r = db_fetch_array($q)) {
-    $node = node_load($r['nid'], NULL, TRUE);
+
+  $q = db_query("SELECT nid, vid, type, title, language FROM {node} WHERE status=1 AND type IN (". db_placeholders($content_types, 'varchar') .")", $content_types);
+  while ($node = db_fetch_object($q)) {
     if (isset($methods['title'])) {
       if ($methods['title']) {
         _keyword_table('insert', $node->nid, 'title', $node->language, $node->title);
@@ -600,6 +664,7 @@
       }
     }
     if (isset($methods['internal'])) {
+      _glossify_node_load($node, 'glossify');
       if ($methods['internal']['use_internal']) {
         _fetch_affected_keywords($node, 'internal');
       }
@@ -608,6 +673,7 @@
       }
     }
     if (isset($methods['cck'])) {
+      _glossify_node_load($node, 'content');
       if ($methods['cck']['use_cck']) {
         _fetch_affected_keywords($node, 'cck', $methods['cck']['keyword_field'], $methods['cck']['override_field']);
       }
@@ -615,7 +681,9 @@
         _keyword_table('delete', $node->nid, 'cck', $node->language);
       }
     }
+
     if (isset($methods['taxonomy'])) {
+      _glossify_node_load($node, 'taxonomy');
       if ($methods['taxonomy']['use_taxonomy']) {
         _fetch_affected_keywords($node, 'taxonomy', $methods['taxonomy']['vocabulary']);
       }
Index: glossify.admin.inc
===================================================================
--- glossify.admin.inc	(revision 6756)
+++ glossify.admin.inc	(working copy)
@@ -93,6 +93,13 @@
     '#default_value' => $configuration['language'],
   );

+  $form['exclude_tags'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Exclude tags'),
+    '#default_value' => $configuration['exclude_tags'],
+    '#description' => t('Enter tags to exclude from linking.  Either one per line, or seperated by commas. ex ("h1,h2,h3,strong")'),
+  );
+
   $form['methods'] = array(
     '#type' => 'fieldset',
     '#title' => t('Methods'),
@@ -271,6 +278,7 @@
       'style' => $form_state['values']['style'],
       'break' => $form_state['values']['break'],
       'language' => $form_state['values']['language'],
+      'exclude_tags' => $form_state['values']['exclude_tags'],
       'methods' => $methods,
     );
     $name = empty($form_state['values']['name']) ? 'global' : $form_state['values']['name'];
