Dom annotation (question)

by yan dong

HTML

<h2>
Story:
</h2>
Mr. Sima Suyuan is a big fan of A Song of Ice and Fire as well as an exceptional frontend engineer. To help himself understand the content of the books more visually, he decides to annotate different part of the text with different styles. For example, he wants to annotate each major character names with the main color of his/her sigil and underline importance sentence.

<p>
For example, he wants to annotate the following text (under a div with id="root" ) with a bunch of queries embeded in the unfinished Javascript file
</p>

<div id='root'>During the War of the Five Kings, Robb Stark, also known as the Young Wolf, fought Lord Tywin Lannister in various battles in the Riverlands. In the mean time, Mace Tyrell declared for King Renly Baratheon even though his elder brother Stannis Baratheon had a better claim to the throne. Across the narrow sea, Daenerys Targaryen hatched three dragons and crowned herself Queen of Meereen.
</div>

<p>
The queries are pairs of source range in original text and a CSS class that Mr Sima wants to annotate. e.g. following quries means Mr Sima wants to annotate "Robb Stark" with "start" class which uses his family color, and also wants to annotate "Robb" wiht class "first" so first name of characters could be emphasized.
</p>

<code>
  [{start: 34, end: 44}, 'stark'],
  
  [{start: 34, end: 38}, 'first'],
</code>

<p>
All other style are defined in the CSS and all other queries are in the unifished Javascript file which Mr Sima just started.
</p>

<p>
<span class=important>In addition, Mr Sima want to the algorithm to be able to handle queries dynamically</span>, so that, whenever an new queries comes in, we don't need to update the whole DOM tree.
</p>
<p>
(hints: that also means you can't sort the queries, instead, you need to handle them one by one)
</p>

<p>
Since Mr Sima is very busy recently, he wants you to help him finish the code that actually doing the annotation, if the code works correctly, the original...

CSS

.lannister    {color: red;}
.baratheon     {color: orange;}
.stark    {color: silver;}
.tyrell   {color: green;}
.targaryen {color: white; background-color: black;}
.first {font-weight: bold;}
.important {text-decoration: underline;}
.place {color: purple; font-size: 20px;}

JavaScript

var queries = [
  [{start: 34, end: 44}, 'stark'],
  [{start: 88, end: 103}, 'lannister'],
  [{start: 160, end: 171}, 'tyrell'],
  [{start: 190, end: 205}, 'baratheon'],  
  [{start: 236, end: 253}, 'baratheon'],
  [{start: 311, end: 329}, 'targaryen'],
  [{start: 34, end: 38}, 'first'],
  [{start: 88, end: 93}, 'first'],
  [{start: 160, end: 164}, 'first'],
  [{start: 190, end: 195}, 'first'],  
  [{start: 236, end: 243}, 'first'],
  [{start: 311, end: 319}, 'first'],
  [{start: 160, end: 286}, 'important'],
];

var root = document.getElementById("root");

// Hi dear friend I'm super busy, could you help me finish the rest of the code?

annotate(root, queries);

setTimeout(function () {
  annotate(root, [[{start: 130,end: 140}, 'place']]);
},2000);

//ele: DOM Element; queries: Array
function annotate(ele, queries){
  var text = ele.innerHTML;
  for(var i = 0; i < queries.length; i++){
    var start = queries[i][0]['start'],
        end = queries[i][0]['end'],
        style = queries[i][1];
    var substr = ele.innerText.substring(start, end);
    
    //Match the character, ignore all span tags.
    if(!text.match(substr)){
      var substr = new RegExp("(<[^/]*>)?" + substr.replace(/\ /g, ".*?") + "(</.*>)?", 'ig');
    }
    
    //Wrap matched content
    text = text.replace(substr, '<span class="'+ style +'">'+ text.match(substr) +'</span>');
  }
  ele.innerHTML = text;

}