Index: devel_themer.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/devel/devel_themer.js,v
retrieving revision 1.17.2.6
diff -u -r1.17.2.6 devel_themer.js
--- devel_themer.js	2 Apr 2009 18:05:20 -0000	1.17.2.6
+++ devel_themer.js	6 Apr 2009 22:23:47 -0000
@@ -1,24 +1,15 @@
 // $Id: devel_themer.js,v 1.17.2.6 2009/04/02 18:05:20 jjeff Exp $
 
+var themeCalls;
+
 if (Drupal.jsEnabled) {
   $(document).ready(function () {
+    themeCalls = findThemeCalls();
+    setupOutline();
+
     lastObj = false;
     thmrSpanified = false;
     strs = Drupal.settings.thmrStrings;
-    $('body').addClass("thmr_call").attr("id", "thmr_" + Drupal.settings.page_id);
-    $('body.thmr_call,span.thmr_call')
-    .hover(
-      function () {
-        if (themerEnabled && this.parentNode.nodeName != 'BODY' && $(this).attr('thmr_curr') != 1) {
-          $(this).css('outline', 'red solid 1px');
-        }
-      },
-      function () {
-        if (themerEnabled && $(this).attr('thmr_curr') != 1) {
-          $(this).css('outline', 'none');
-        }
-      }
-    );
 
     var themerEnabled = 0;
     var themerToggle = function () {
@@ -28,17 +19,12 @@
       if (themerEnabled) {
         document.onclick = themerEvent;
         if (lastObj != false) {
-          $(lastObj).css('outline', '3px solid #999');
-        }
-        if (!thmrSpanified) {
-          spanify();
+          displayOutline(lastObj);
         }
       }
       else {
         document.onclick = null;
-        if (lastObj != false) {
-          $(lastObj).css('outline', 'none');
-        }
+        hideOutline();
       }
     };
     $(Drupal.settings.thmr_popup)
@@ -63,48 +49,19 @@
   });
 }
 
-/**
- * Known issue: IE does NOT support outline css property.
- * Solution: use another browser
- */
-function themerHilight(obj) {
-  // hilight the current object (and un-highlight the last)
-  if (lastObj != false) {
-    $(lastObj).css('outline', 'none').attr('thmr_curr', 0);
-  }
-  $(obj).css('outline', '#999 solid 3px').attr('thmr_curr', 1);
-  lastObj = obj;
-}
-
 function themerDoIt(obj) {
   if (thmrInPop(obj)) {
     return true;
   }
   // start throbber
   //$('#themer-popup img.throbber').show();
-  var objs = thmrFindParents(obj);
-  if (objs.length) {
-    themerHilight(objs[0]);
-    thmrRebuildPopup(objs);
-  }
-  return false;
-}
 
-function spanify() {
-  $('span.thmr_call')
-    .each(function () {
-      // make spans around block elements into block elements themselves
-      var kids = $(this).children();
-      for(i=0;i<kids.length;i++) {
-        //console.log(kids[i].style.display);
-        if ($(kids[i]).css('display') != 'inline' && $(kids[i]).is('DIV, P, ADDRESS, BLOCKQUOTE, CENTER, DIR, DL, FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HR, ISINDEX, MENU, NOFRAMES, NOSCRIPT, OL, PRE, TABLE, UL,  DD, DT, FRAMESET, LI, TBODY, TD, TFOOT, TH, THEAD, TR')) {
-          $(this).css('display', 'block');
-        }
-      }
-    });
-  thmrSpanified = true;
-  // turn off the throbber
-  //$('#themer-toggle img.throbber').hide();
+  var id = obj.themeCallId;
+  if(!id) return;
+  lastObj = themeCalls[id];
+  displayOutline(lastObj);
+  thmrRebuildPopup([lastObj]);
+  return false;
 }
 
 function thmrInPop(obj) {
@@ -127,47 +84,6 @@
   return themerDoIt(tg);
 }
 
-/**
- * Find all parents with class="thmr_call"
- */
-function thmrFindParents(obj) {
-  var parents = new Array();
-  if ($(obj).hasClass('thmr_call')) {
-    parents[parents.length] = obj;
-  }
-  if (obj && obj.parentNode) {
-    while (obj = obj.parentNode) {
-      if ($(obj).hasClass('thmr_call')) {
-        parents[parents.length] = obj;
-      }
-    }
-  }
-  return parents;
-}
-
-/**
- * Check to see if object is a block element
- */
-function thmrIsBlock(obj) {
-  if (obj.style.display == 'block') {
-    return true;
-  }
-  else if (obj.style.display == 'inline' || obj.style.display == 'none') {
-    return false;
-  }
-  if (obj.tagName != undefined) {
-    var i = blocks.length;
-    if (i > 0) {
-      do {
-        if (blocks[i] === obj.tagName) {
-          return true;
-        }
-      } while (i--);
-    }
-  }
-  return false;
-}
-
 function thmrRefreshCollapse() {
   $('#themer-popup .devel-obj-output dt').each(function() {
       $(this).toggle(function() {
@@ -268,4 +184,120 @@
   }
   // stop throbber
   //$('#themer-popup img.throbber').hide();
-}
\ No newline at end of file
+}
+
+// This walks the document tree, finding the comments which deliminate
+// theme calls.
+
+function findThemeCalls() {
+  var themeCalls = {};
+  var idStack = [];
+  var contentsStack = [];
+  var contents;
+  
+  recurse(document.body);
+  if(idStack.length) throw "Missing end node.";
+  return themeCalls;
+
+  function recurse(parent) {
+    for (var i = 0; i < parent.childNodes.length; ++i) {
+      var node = parent.childNodes[i];
+
+      switch (node.nodeType) {
+        case Node.COMMENT_NODE:
+          var m = node.nodeValue.match(/thmr_call_(begin|end):(thmr_\d+)/)
+          if(m) {
+            switch (m[1]) {
+              case 'begin':
+                idStack.push(m[2]);
+                contentsStack.push(contents);
+                contents = [];
+                break;
+              case 'end':
+                if(!idStack.length || idStack[idStack.length - 1] != m[2])
+                  throw "Mismatched end comment.";
+      
+                if(contents.length) {
+                  var dimensions = contents[0];
+      
+                  for(var j = 1; j < contents.length; ++j) {
+                    dimensions.left = Math.min(dimensions.left, contents[j].left);
+                    dimensions.right = Math.max(dimensions.right, contents[j].right);
+                    dimensions.top = Math.min(dimensions.top, contents[j].top);
+                    dimensions.bottom = Math.max(dimensions.bottom, contents[j].bottom);
+                  }
+      
+                  dimensions.id = m[2]
+                  themeCalls[m[2]] = dimensions;
+                }
+      
+                contents = contentsStack.pop();
+                if(contents && dimensions) contents.push(dimensions);
+                idStack.pop();
+      
+                break;
+            }
+          }
+          break;
+        case Node.ELEMENT_NODE:
+          if(contents) addNode(node);
+          recurse(node);
+          break;
+        case Node.TEXT_NODE:
+          // Put a 'span' around the text node to find its dimensions.
+          // Most of the time this isn't really necessary.
+          if(contents && node.parentNode.childNodes.length > 1 && node.nodeValue.match(/[^\s]/)) {
+            var span = document.createElement('span');
+            span.appendChild(node.cloneNode(false));
+            node.parentNode.replaceChild(span, node);
+            addNode(span);
+          }
+          break;
+      }
+    }
+  }
+
+  function addNode(node) {
+    node.themeCallId = idStack[idStack.length - 1];
+    var offset = $(node).offset();
+    contents.push({
+      left: offset.left, right:offset.left + node.offsetWidth,
+      top: offset.top, bottom:offset.top + node.offsetHeight,
+    })
+  }
+}
+
+// Used to display the outline around a themer call.
+
+var outlineNodes;
+
+function setupOutline() {
+  outlineNodes = [];
+
+  for(var i = 0; i < 4; ++i) {
+    outlineNodes[i] = document.createElement('div');
+    outlineNodes[i].style.background = "#999";
+    outlineNodes[i].style.position = "absolute";
+    document.body.appendChild(outlineNodes[i]);
+  }
+  outlineNodes = $(outlineNodes);
+}
+
+function hideOutline() {
+  outlineNodes.css("display", "none");
+}
+
+function displayOutline(dimensions) {
+  outlineNodes.css("display", "block");
+  position(outlineNodes[0], dimensions.left - 3, dimensions.top - 3, 3, dimensions.bottom - dimensions.top + 6);
+  position(outlineNodes[1], dimensions.left - 3, dimensions.top - 3, dimensions.right - dimensions.left + 6, 3);
+  position(outlineNodes[2], dimensions.right, dimensions.top - 3, 3, dimensions.bottom - dimensions.top + 6);
+  position(outlineNodes[3], dimensions.left - 3, dimensions.bottom, dimensions.right - dimensions.left + 6, 3);
+
+  function position(element, left, top, width, height) {
+    element.style.left = left + "px";
+    element.style.top = top + "px";
+    element.style.width = width + "px";
+    element.style.height = height + "px";
+  }
+}
Index: devel_themer.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/devel/devel_themer.module,v
retrieving revision 1.32.2.22
diff -u -r1.32.2.22 devel_themer.module
--- devel_themer.module	24 Dec 2008 02:28:16 -0000	1.32.2.22
+++ devel_themer.module	6 Apr 2009 22:23:48 -0000
@@ -493,7 +493,7 @@
 
 function devel_theme_call_marker($name, $counter, $type) {
   $id = "thmr_". $counter;
-  return array("<span id=\"$id\" class=\"thmr_call\">", "</span>\n");
+  return array("<!-- thmr_call_begin:$id -->", "<!-- thmr_call_end:$id -->\n");
 }
 
 // just hand out next counter, or return current value
