diff --git a/linkchecker.module b/linkchecker.module
index bc1abf9..6467519 100644
--- a/linkchecker.module
+++ b/linkchecker.module
@@ -203,6 +203,7 @@ function _linkchecker_link_access($link) {
  *   user may be allowed to view.
  */
 function _linkchecker_link_node_ids($link, $node_author_account = NULL) {
+  static $all_node_links = array();
   static $fields_with_node_links = array();
 
   // Exit if all node types are disabled or if the user cannot access content,
@@ -252,12 +253,16 @@ function _linkchecker_link_node_ids($link, $node_author_account = NULL) {
     // do not want to return it (and it is not safe to, since we cannot know if
     // it contained access restrictions for the current user at the point which
     // it was originally extracted by the Link checker module).
-    if (!isset($fields_with_node_links[$node->nid])) {
-      $fields_with_node_links[$node->nid] = _linkchecker_extract_node_links($node, TRUE);
+    if (!isset($all_node_links[$node->nid])) {
+      $all_node_links[$node->nid] = _linkchecker_extract_node_links($node);
     }
-    if (empty($fields_with_node_links[$node->nid][$link->url])) {
+    if (empty($all_node_links[$node->nid][$link->url])) {
       continue;
     }
+    if (!isset($fields_with_node_links[$node->nid])) {
+      $fields_with_node_links[$node->nid] = _linkchecker_extract_node_links($node, TRUE);
+    }
+
     // If the link appears in fields and a field access module is being used,
     // we must check that the current user has access to view at least one field
     // that contains the link; if they don't, we should not return the node.
@@ -609,6 +614,14 @@ function _linkchecker_status_handling(&$response, $link) {
             $node_original = clone $node;
             $node = _linkchecker_replace_fields('node', $node->type, $node, $link->url, $response->redirect_url);
 
+            // Allow modules to replace link in custom attributes.
+            // Modules implementing this should call _linkchecker_link_replace().
+            $context = array(
+              'old' => $link->url,
+              'new' => $response->redirect_url,
+            );
+            drupal_alter('linkchecker_replace_link', $node, $context);
+
             if ($node_original != $node) {
               // Always use the default revision setting. For more information,
               // see node_object_prepare().
@@ -1053,6 +1066,10 @@ function _linkchecker_extract_node_links($node, $return_field_names = FALSE) {
   $text_items_by_field['title'][] = _filter_url($node->title, $filter);
   $text_items = _linkchecker_array_values_recursive($text_items_by_field);
 
+  // Allow modules to provide additional text items.
+  $module_text_items = module_invoke_all('linkchecker_extract_node_links', $node);
+  $text_items = array_merge($text_items, $module_text_items);
+
   // Get the absolute node path for extraction of relative links.
   $languages = language_list();
   // Note: An "undefined language" (value: 'und') isn't listed in the available
