search.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. // Copyright 2007-2012 The MathWorks, Inc.
  2. function createHighlightSpanStart(num)
  3. {
  4. return "<span class=\"highlighted\" name=\"highlight" + num + "\">";
  5. }
  6. var str2pos; // This is a map between a tag stripped string and the original string.
  7. function getTagStrippedStringAndMap(aString)
  8. {
  9. var tagStrippedString = new String();
  10. str2pos = new Array();
  11. var inTag = false;
  12. var inScript = false;
  13. for (var strPos = 0; strPos < aString.length; strPos++) {
  14. if (inTag && aString.charAt(strPos) == '>') {
  15. inTag = false;
  16. if (inScript && (strPos > 8) && (aString.slice(strPos, strPos - 8) == '/script>')) {
  17. inScript = false;
  18. }
  19. continue;
  20. } else if (!inTag && aString.charAt(strPos) == '<') {
  21. inTag = true;
  22. if (!inScript && aString.slice(strPos, strPos + 7) == '<script') {
  23. inScript = true;
  24. strPos += 7;
  25. }
  26. continue;
  27. }
  28. if (inTag == false && inScript == false) {
  29. tagStrippedString += aString.charAt(strPos);
  30. str2pos.push(strPos);
  31. }
  32. }
  33. return tagStrippedString;
  34. }
  35. function escapeSpecialChars(string)
  36. {
  37. // let the browser handle the escaping rather than doing a String.replace
  38. // call
  39. var div = document.createElement("div");
  40. var text = document.createTextNode(string);
  41. div.appendChild(text);
  42. var escapedString = div.innerHTML;
  43. delete div;
  44. return escapedString;
  45. }
  46. // insert highlight tags into the body. Split it up into multiple sections if necessary
  47. // (i.e. if there is a tag in the middle).
  48. function insertHighlighting(bodyText, previ, i, length, highlightStartTag, highlightEndTag)
  49. {
  50. var newText = "";
  51. newText = bodyText.slice(previ, str2pos[i]);
  52. // insert start
  53. newText += highlightStartTag;
  54. var str2posprev = str2pos[i];
  55. // deal with intermediate tags
  56. for(var cnt = i; cnt < i+length; cnt++)
  57. {
  58. if (str2pos[cnt] > str2posprev+1) // we have jumped some text, so there must be a tag
  59. {
  60. // insert end tag
  61. newText += highlightEndTag;
  62. // insert intermediate body text tags
  63. newText += bodyText.slice(str2posprev+1, str2pos[cnt]);
  64. // insert start tag
  65. newText += highlightStartTag;
  66. }
  67. newText += bodyText.charAt(str2pos[cnt]);
  68. str2posprev=str2pos[cnt];
  69. }
  70. // insert end
  71. newText += highlightEndTag;
  72. return newText;
  73. }
  74. // check to see if the sequence at position 'i' in taglessString is actually in
  75. // the middle of an escape sequence. We assume escape sequences follow the pattern
  76. // &<sequenceText>;. We check for &nbsp;, &lt;, &gt; and &amp;
  77. function isInEscapedSequence(i, taglessString, searchTerm)
  78. {
  79. var escapeSeq = /&nbsp;|&lt;|&gt;|&amp;/gi;
  80. var maxEscapeSeqLength = 6;
  81. var startPos = 0;
  82. var endPos = 0;
  83. // exit if the search term has an escape sequence inside it
  84. if (escapeSeq.test(searchTerm)) {
  85. return false;
  86. }
  87. // reset the escape sequence
  88. escapeSeq = /&nbsp;|&lt;|&gt;|&amp;/gi;
  89. // go back in the string until we find an ampersand or we hit maxEscapeSeqLength characters
  90. tempI = i;
  91. var bFound = false;
  92. while (tempI >= 0 && tempI > (i-maxEscapeSeqLength)) {
  93. if (taglessString.charAt(tempI) == "&") {
  94. startPos = tempI;
  95. bFound = true;
  96. break;
  97. }
  98. tempI = tempI-1;
  99. // if we hit a ';' in any position other than the first while searching
  100. // for an ampersand, then we cannot be inside an escape sequence
  101. if (tempI >= 0 && taglessString.charAt(tempI) == ";") {
  102. return false;
  103. }
  104. }
  105. if (!bFound) {
  106. return false;
  107. }
  108. // reset the escape sequence
  109. escapeSeq = /&nbsp;|&lt;|&gt;|&amp;/gi;
  110. var subString = taglessString.substring(startPos, startPos + maxEscapeSeqLength);
  111. return escapeSeq.test(subString);
  112. }
  113. // Adds highlighting to bodyText around searchTerm. Case sensitivity is optional.
  114. // hitCount is used to a) count the number of search matches and b) Generate unique
  115. // name strings for each highlighting SPAN element.
  116. function addHighlight(bodyText, searchTerm, caseSensitive, hitCount)
  117. {
  118. var highlightStartTag = "";
  119. var highlightEndTag = "</span>";
  120. var newText = "";
  121. var i = 0;
  122. var previ = 0;
  123. var bodyTextUC = bodyText.toUpperCase();
  124. if (caseSensitive) {
  125. var taglessString = getTagStrippedStringAndMap(bodyText);
  126. } else {
  127. var taglessString = getTagStrippedStringAndMap(bodyTextUC);
  128. }
  129. // escape the search term in case the user input '<' or '>' etc
  130. searchTerm = escapeSpecialChars(searchTerm);
  131. if (!caseSensitive) {
  132. var searchTermUC = searchTerm.toUpperCase();
  133. }
  134. // search for subsequent matches
  135. while (true) {
  136. if (caseSensitive) {
  137. i = taglessString.indexOf(searchTerm, i);
  138. } else {
  139. i = taglessString.indexOf(searchTermUC, i);
  140. }
  141. if (i < 0) break;
  142. // we have a match!
  143. // make sure that the match is not inside an escaped sequence of text
  144. // such as &nbsp;
  145. if (isInEscapedSequence(i, taglessString, searchTerm)) {
  146. i=i+1;
  147. continue;
  148. }
  149. // insert highlight tags that cross tag boundaries
  150. highlightStartTag = createHighlightSpanStart(hitCount);
  151. hitCount = hitCount+1;
  152. newText += insertHighlighting(bodyText, previ, i, searchTerm.length, highlightStartTag, highlightEndTag);
  153. previ = str2pos[i+searchTerm.length-1]+1;
  154. i = i + searchTerm.length;
  155. }
  156. newText += bodyText.slice(previ, bodyText.length);
  157. return [newText, hitCount];
  158. }
  159. function removeHighlight(bodyText)
  160. {
  161. // We use regular expressions here rather than a straight text search because
  162. // some browsers actually insert double quotes and capitalize. Also, each highlight
  163. // name is uniquely numbered in order of discovery
  164. var highlightStartTag = /<span class=[\"]*highlighted(Current)*[\"]* name=[\"]*highlight[0-9]*[\"]*>/i;
  165. var highlightEndTag = /<\/span>/i;
  166. var newText = "";
  167. var startPatternFirstIndex = -1;
  168. var startPatternLastIndex = -1;
  169. var endPatternFirstIndex = -1;
  170. var endPatternLastIndex = -1;
  171. while (highlightStartTag.test(bodyText) === true) {
  172. startPatternFirstIndex = bodyText.search(highlightStartTag);
  173. newText += bodyText.substring(0, startPatternFirstIndex);
  174. startPatternLastIndex = bodyText.indexOf('>', startPatternFirstIndex+1);
  175. bodyText = bodyText.substr(startPatternLastIndex+1);
  176. endPatternFirstIndex = bodyText.search(highlightEndTag);
  177. newText += bodyText.substring(0, endPatternFirstIndex);
  178. endPatternLastIndex = endPatternFirstIndex+7;
  179. bodyText = bodyText.substr(endPatternLastIndex);
  180. }
  181. if (startPatternFirstIndex < 0) {
  182. return bodyText;
  183. } else {
  184. return newText+bodyText;
  185. }
  186. }
  187. function removeHighlightInAllDocs()
  188. {
  189. if (top) {
  190. for (var i = 0; i < top.frames.length; i++) {
  191. if (top.frames[i].name === "rtwreport_contents_frame" || top.frames[i].name === "rtwreport_document_frame") {
  192. var currentDoc = top.frames[i].document;
  193. if (typeof currentDoc.body !== "undefined" && currentDoc.body !== null)
  194. currentDoc.body.innerHTML=removeHighlight(currentDoc.body.innerHTML);
  195. }
  196. }
  197. }
  198. }
  199. function findInDoc(searchStringFromSubmitBox, doc, caseSensitive, hitCount)
  200. {
  201. var searchBody = doc.body.innerHTML;
  202. // if the document is empty, or the documents is invalid, return
  203. if (!doc.body || typeof(searchBody) === "undefined") {
  204. return [false, hitCount];
  205. }
  206. // inject highlighting code into html
  207. var result = addHighlight(searchBody, searchStringFromSubmitBox, caseSensitive, hitCount);
  208. doc.body.innerHTML = result[0];
  209. return [true, result[1]];
  210. }
  211. var currentlyHighlightedHit;
  212. function getSpansByName(name)
  213. {
  214. var allSpans = [];
  215. for (i = 0; i < top.frames.length; i++) {
  216. if (top.frames[i].name === "rtwreport_contents_frame" || top.frames[i].name === "rtwreport_document_frame") {
  217. var currentDoc = top.frames[i].document;
  218. var highlightedSpans = currentDoc.getElementsByName(name);
  219. if (highlightedSpans && highlightedSpans.length && highlightedSpans.length > 0) {
  220. for (j = 0; j < highlightedSpans.length; j++) {
  221. allSpans = allSpans.concat(highlightedSpans[j]);
  222. }
  223. }
  224. }
  225. }
  226. return allSpans;
  227. }
  228. function isVisibleElement(elementID)
  229. {
  230. if (elementID)
  231. return elementID.offsetWidth > 0 || elementID.offsetHeight > 0;
  232. else
  233. return false;
  234. }
  235. function areAllSpansVisible(spans)
  236. {
  237. isVisible = true;
  238. for (i = 0; i < highlightedSpans.length; i++) {
  239. isVisible = isVisible && isVisibleElement(highlightedSpans[i]);
  240. }
  241. return isVisible;
  242. }
  243. function getNextVisible()
  244. {
  245. var isVisible = false;
  246. var foundVisible = false;
  247. while (!isVisible) {
  248. currentlyHighlightedHit = currentlyHighlightedHit + 1;
  249. highlightedSpans = setCurrentSearchMatchIfVisible(currentlyHighlightedHit);
  250. if (highlightedSpans && highlightedSpans.length > 0) {
  251. isVisible = true;
  252. } else if (currentlyHighlightedHit < totalHits) {
  253. continue;
  254. } else {
  255. // we have reached the end
  256. isVisible = false;
  257. currentlyHighlightedHit = 0;
  258. highlightedSpans = null;
  259. break;
  260. }
  261. }
  262. return highlightedSpans;
  263. }
  264. function clearCurrentSearchMatch()
  265. {
  266. // clear prior highlighting
  267. spanName = "highlight" + currentlyHighlightedHit;
  268. highlightedSpans = getSpansByName(spanName);
  269. if (highlightedSpans && highlightedSpans.length) {
  270. for (i = 0; i < highlightedSpans.length; i++) {
  271. if (highlightedSpans[i]) {
  272. highlightedSpans[i].setAttribute("class", "highlighted");
  273. }
  274. }
  275. }
  276. }
  277. function setCurrentSearchMatchIfVisible(hitNum){
  278. currentlyHighlightedHit = hitNum;
  279. var spanName = "highlight" + currentlyHighlightedHit;
  280. var highlightedSpans = getSpansByName(spanName);
  281. if (highlightedSpans && highlightedSpans.length) {
  282. for (i = 0; i < highlightedSpans.length; i++) {
  283. if (!isVisibleElement(highlightedSpans[i])) {
  284. highlightedSpans = null;
  285. break;
  286. }
  287. }
  288. }
  289. return highlightedSpans;
  290. }
  291. // this takes in an option integer 'hitNum'. If not specified, it scrolls
  292. // to the next hit
  293. function scrollToNextHit(hitNum)
  294. {
  295. var i = 0;
  296. var found = false;
  297. var spanName = "";
  298. var highlightedSpans;
  299. var searchBox = findElement('searchTxtBox');
  300. clearCurrentSearchMatch();
  301. if (hitNum) {
  302. // if a number is provided, use it
  303. highlightedSpans = setCurrentSearchMatchIfVisible(hitNum);
  304. } else {
  305. // start working on next element to highlight
  306. highlightedSpans = getNextVisible();
  307. }
  308. // we found the current
  309. if (highlightedSpans && highlightedSpans.length > 0) {
  310. highlightedSpans[0].scrollIntoView();
  311. for (i = 0; i < highlightedSpans.length; i++) {
  312. highlightedSpans[i].setAttribute("class", "highlightedCurrent");
  313. }
  314. searchBox.setAttribute("class", "search");
  315. // if highlightedSpans is invalid, then we did not find any valid, visible subsequent matches
  316. // wrap to beginning or indicate no matches
  317. } else {
  318. // Element not found. Scroll to first visible element
  319. currentlyHighlightedHit = 0;
  320. var highlightedSpans = getNextVisible(currentlyHighlightedHit);
  321. if (highlightedSpans && highlightedSpans.length > 0) {
  322. highlightedSpans[0].scrollIntoView();
  323. for (i = 0; i < highlightedSpans.length; i++) {
  324. highlightedSpans[i].setAttribute("class", "highlightedCurrent");
  325. }
  326. searchBox.setAttribute("class", "search");
  327. } else {
  328. // there aren't any matches
  329. searchBox.setAttribute("class", "failedSearch");
  330. }
  331. }
  332. }
  333. // find search box
  334. function findElement(element)
  335. {
  336. var i = 0;
  337. for (i = 0; i < top.frames.length; i++) {
  338. if (top.frames[i].name === "rtwreport_contents_frame" || top.frames[i].name === "rtwreport_document_frame") {
  339. var elem = top.frames[i].document.getElementById(element);
  340. if (elem) {
  341. return elem;
  342. }
  343. }
  344. }
  345. }
  346. // Restore search term once form is submitted
  347. function initSearchBox(strInitValue)
  348. {
  349. var txtBox = findElement('searchTxtBox');
  350. if (txtBox) {
  351. txtBox.value = strInitValue;
  352. }
  353. }
  354. // Sets focus back on to the text box
  355. function setFocusOnTxtBox()
  356. {
  357. var txtBox = findElement('searchTxtBox');
  358. if (txtBox) {
  359. txtBox.focus();
  360. txtBox.select();
  361. }
  362. return txtBox;
  363. }
  364. var previousSearchString;
  365. var totalHits;
  366. function findInAllDocs(searchStringFromSubmitBox, caseSensitive)
  367. {
  368. if (previousSearchString != searchStringFromSubmitBox) {
  369. // If the search string has changed or a new page has been loaded, do a new search
  370. var hitCount = 1;
  371. var i = 0;
  372. var success = false;
  373. previousSearchString = searchStringFromSubmitBox;
  374. // start by removing traceinfo highlighting
  375. rtwRemoveHighlighting();
  376. // remove all previous search highlighting
  377. removeHighlightInAllDocs();
  378. // 1. Iterate through all frames in window and search
  379. for (i = 0; i < top.frames.length; i++) {
  380. var currentDoc = top.frames[i].document;
  381. // if we have no search term, restore
  382. if (searchStringFromSubmitBox !== "") {
  383. // search and highlight in all frames
  384. var srchResult = findInDoc(searchStringFromSubmitBox, currentDoc, caseSensitive, hitCount);
  385. hitCount = srchResult[1];
  386. totalHits = srchResult[1];
  387. }
  388. }
  389. // 2. Restore search term once form is submitted and text highlighted
  390. if (searchStringFromSubmitBox != "") {
  391. strInitValue = searchStringFromSubmitBox;
  392. }
  393. initSearchBox(strInitValue);
  394. // 3. Scroll to the first hit encountered
  395. scrollToNextHit(1);
  396. // 4. Set focus back to text box and select text
  397. var txtBox = setFocusOnTxtBox();
  398. if (txtBox) {
  399. txtBox.select();
  400. }
  401. } else {
  402. // If the search string is the same, then scroll to the next
  403. // hit if the hit is valid. Else wrap back.
  404. scrollToNextHit();
  405. }
  406. return false;
  407. }
  408. // if the search box is empty, clear highlighting
  409. function clearIfEmpty()
  410. {
  411. txtBox = findElement('searchTxtBox');
  412. if (txtBox.value == "") {
  413. txtBox.setAttribute("class", "search");
  414. removeHighlightInAllDocs();
  415. previousSearchString="";
  416. setFocusOnTxtBox();
  417. }
  418. }
  419. function keyPressSwitchyard(keyPressEvent)
  420. {
  421. var kc;
  422. keyPressEvent = (keyPressEvent == null ? window.keyPressEvent : keyPressEvent);
  423. // typically IE does not support this
  424. if (!keyPressEvent || (typeof keyPressEvent == "undefined")) {
  425. return;
  426. }
  427. if (keyPressEvent.keyCode) {
  428. kc=keyPressEvent.keyCode;
  429. } else if (keyPressEvent.which) {
  430. kc=keyPressEvent.which;
  431. } else {
  432. return;
  433. }
  434. // we do not care about the browser find appearing. If it does appear, then
  435. // we are running an external browser and that is okay.
  436. // handle Ctrl-Key combinations
  437. if (keyPressEvent.ctrlKey) {
  438. switch (kc) {
  439. case 70: // Ctrl-F
  440. {
  441. setFocusOnTxtBox();
  442. break;
  443. }
  444. default: break;
  445. }
  446. }
  447. }
  448. function installDocumentKeyPressHandler()
  449. {
  450. var i = 0;
  451. for (i = 0; i < top.frames.length; i++) {
  452. var currentDoc = top.frames[i].document;
  453. currentDoc.onkeydown = keyPressSwitchyard;
  454. }
  455. top.document.onkeydown = keyPressSwitchyard;
  456. // This also clears search related highlighting
  457. removeHighlightInAllDocs();
  458. currentlyHighlightedHit = 0;
  459. if (previousSearchString) initSearchBox(previousSearchString);
  460. previousSearchString = "";
  461. }
  462. // This function is a onresize callback for the rtwreport_contents_frame
  463. // It is used to dynamically resize the searchbox based on the size of the frame.
  464. function setWidthDynamic(frameID, elementID, extraSpace, minSize)
  465. {
  466. var frame = document.getElementById(frameID);
  467. // sanity check input args
  468. if (frame && extraSpace > 0 && minSize > 0) {
  469. var frameWidth = frame.scrollWidth;
  470. var newSize = extraSpace + minSize + 40; // 40 is the extra whitespace
  471. var element = findElement(elementID);
  472. if (element)
  473. {
  474. if (frameWidth < newSize) {
  475. element.style.width = minSize;
  476. } else {
  477. element.style.width = frameWidth - extraSpace - 40;
  478. }
  479. }
  480. }
  481. }