Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Ok, forgive me for how large this is. I thought this was a very interesting question but while playing with it, I quickly realized that innerHTML and its ilk are quite unreliable wrt maintaining whitespace, comments, etc. With that in mind, I fell back to actually pulling down a full copy of the source so that I could be absolutely sure I got the full source. I then used jquery and a few (relatively small) regexes to find the location of each node. It seems to work well although I'm sure I've missed some edge cases. And, yeah, yeah, regexes and two problems, blah blah blah.</p> <p><strong>Edit:</strong> As an exercise in building jquery plugins, I've modified my code to function reasonably well as a standalone <a href="http://rvandam.com/line_numbers/jquery.line_numbers.js" rel="noreferrer">plugin</a> with an <a href="http://rvandam.com/line_numbers/source.html" rel="noreferrer">example</a> similar to the html found below (which I will leave here for posterity). I've tried to make the code slightly more robust (such as now handling tags inside quoted strings, such as onclick), but the biggest remaining bug is that it can't account for any modifications to the page, such as appending elements. I would need probably need to use an iframe instead of an ajax call to handle that case.</p> <pre><code>&lt;html&gt; &lt;head id="node0"&gt; &lt;!-- first comment --&gt; &lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&gt;&lt;/script&gt; &lt;style id="node1"&gt; /* div { border: 1px solid black; } */ pre { border: 1px solid black; } &lt;/style&gt; &lt;!-- second comment --&gt; &lt;script&gt; $(function() { // fetch and display source var source; $.ajax({ url: location.href, type: 'get', dataType: 'text', success: function(data) { source = data; var lines = data.split(/\r?\n/); var html = $.map(lines, function(line, i) { return ['&lt;span id="line_number_', i, '"&gt;&lt;strong&gt;', i, ':&lt;/strong&gt; ', line.replace(/&lt;/g, '&amp;lt;').replace(/&gt;/g, '&amp;gt;'), '&lt;/span&gt;'].join(''); }).join('\n'); // now sanitize the raw html so you don't get false hits in code or comments var inside = false; var tag = ''; var closing = { xmp: '&lt;\\/\\s*xmp\\s*&gt;', script: '&lt;\\/\\s*script\\s*&gt;', '!--': '--&gt;' }; var clean_source = $.map(lines, function(line) { if (inside &amp;&amp; line.match(closing[tag])) { var re = new RegExp('.*(' + closing[tag] + ')', 'i'); line = line.replace(re, "$1"); inside = false; } else if (inside) { line = ''; } if (line.match(/&lt;(script|!--)/)) { tag = RegExp.$1; line = line.replace(/&lt;(script|xmp|!--)[^&gt;]*.*(&lt;(\/(script|xmp)|--)?&gt;)/i, "&lt;$1&gt;$2"); var re = new RegExp(closing[tag], 'i'); inside = ! (re).test(line); } return line; }); // nodes we're looking for var nodes = $.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(num) { return $('#node' + num) }); // now find each desired node in both the DOM and the source var line_numbers = $.map(nodes, function(node) { var tag = node.attr('tagName'); var tags = $(tag); var index = tags.index(node) + 1; var count = 0; for (var i = 0; i &lt; clean_source.length; i++) { var re = new RegExp('&lt;' + tag, 'gi'); var matches = clean_source[i].match(re); if (matches &amp;&amp; matches.length) { count += matches.length; if (count &gt;= index) { console.debug(node, tag, index, count, i); return i; } } } return count; }); // saved till end to avoid affecting source html $('#source_pretty').html(html); $('#source_raw').text(source); $('#source_clean').text(clean_source.join('\n')); $.each(line_numbers, function() { $('#line_number_' + this).css('background-color', 'orange'); }); }, }); var false_matches = [ "&lt;div&gt;", "&lt;div&gt;", "&lt;/div&gt;", "&lt;/div&gt;" ].join(''); }); &lt;/script&gt; &lt;/head&gt; &lt;!-- third comment --&gt; &lt;body id="node2"&gt; &lt;div&gt; &lt;pre id="source_pretty"&gt; &lt;/pre&gt; &lt;pre id="source_raw"&gt; &lt;/pre&gt; &lt;pre id="source_clean"&gt; &lt;/pre&gt; &lt;/div&gt; &lt;div id="node3"&gt; &lt;xmp&gt; &lt;code&gt; // &lt;xmp&gt; is deprecated, you should put it in &lt;code&gt; instead &lt;/code&gt; &lt;/xmp&gt; &lt;/div&gt; &lt;!-- fourth comment --&gt; &lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;div id="node4"&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;em&gt; &lt;i&gt;&lt;strong&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/strong&gt;&lt;/i&gt;&lt;div&gt;&lt;div id="node5"&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/em&gt; &lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span id="node6"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt; &lt;div&gt; &lt;div id="node7"&gt; &lt;div&gt; &lt;div&gt; &lt;div id="node8"&gt; &lt;span&gt; &lt;!-- fifth comment --&gt; &lt;div&gt; &lt;span&gt; &lt;span&gt; &lt;b&gt; &lt;em id="node9"&gt; &lt;i&gt; &lt;strong&gt; &lt;pre&gt; &lt;/pre&gt; &lt;/strong&gt; &lt;/i&gt; &lt;div&gt; &lt;div&gt; &lt;div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/em&gt; &lt;/b&gt; &lt;/span&gt; &lt;span&gt; &lt;span id="node10"&gt; &lt;/span&gt; &lt;/span&gt; &lt;/span&gt; &lt;/div&gt; &lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload