| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 (function() { |
| 6 var WRAPPER_CSS_CLASS = 'search-highlight-wrapper'; |
| 7 var HIT_CSS_CLASS = 'search-highlight-hit'; |
| 8 |
| 9 function findChildren_(element) { |
| 10 var children = []; |
| 11 var walker = document.createTreeWalker( |
| 12 element.shadowRoot, NodeFilter.SHOW_ELEMENTS, null, false); |
| 13 var node = walker.nextNode(); |
| 14 while (node) { |
| 15 if (!!node.shadowRoot) |
| 16 children.push(node); |
| 17 |
| 18 node = walker.nextNode(); |
| 19 } |
| 20 return children; |
| 21 } |
| 22 |
| 23 /** |
| 24 * Highlights all matches in the given element. Ignores children that have |
| 25 * their own Shadow DOW. |
| 26 * @param {!RegExp} regexp |
| 27 * @param {!Element} element |
| 28 * @return {boolean} Whether any matches were found. |
| 29 */ |
| 30 function highlightSelfMatches_(regExp, element) { |
| 31 var found = false; |
| 32 var walker = document.createTreeWalker( |
| 33 element.shadowRoot, NodeFilter.SHOW_TEXT, null, false); |
| 34 |
| 35 var node = walker.nextNode(); |
| 36 while (node) { |
| 37 var textContent = node.nodeValue; |
| 38 var tokens = textContent.split(regExp); |
| 39 |
| 40 if (node.parentNode.tagName == 'STYLE' || tokens.length == 1) { |
| 41 node = walker.nextNode(); |
| 42 continue; |
| 43 } |
| 44 |
| 45 var nextNode = walker.nextNode(); |
| 46 var parentNode = node.parentNode; |
| 47 // Use existing node as placeholder to determine where to insert the |
| 48 // replacement content. |
| 49 |
| 50 var wrapper = document.createElement('span'); |
| 51 wrapper.classList.add(WRAPPER_CSS_CLASS); |
| 52 parentNode.insertBefore(wrapper, node); |
| 53 |
| 54 for (var i = 0; i < tokens.length; ++i) { |
| 55 if (i % 2 == 0) { |
| 56 wrapper.appendChild(document.createTextNode(tokens[i])); |
| 57 } else { |
| 58 fount = true; |
| 59 var span = document.createElement('span'); |
| 60 span.classList.add(HIT_CSS_CLASS); |
| 61 span.style['background-color'] = 'yellow'; |
| 62 span.textContent = tokens[i]; |
| 63 wrapper.appendChild(span); |
| 64 } |
| 65 } |
| 66 |
| 67 // Remove old node. |
| 68 node.remove(); |
| 69 node = nextNode; |
| 70 } |
| 71 |
| 72 return found; |
| 73 } |
| 74 |
| 75 /** |
| 76 * Un-highlights all previous matches in the given element. It ignores |
| 77 * children that have their own Shadow DOM. |
| 78 * @param {!Element} element |
| 79 */ |
| 80 function unhighlightSelfMatches_(element) { |
| 81 var wrappers = element.shadowRoot.querySelectorAll('.' + WRAPPER_CSS_CLASS); |
| 82 wrappers.forEach(function(wrapper) { |
| 83 wrapperParent = wrapper.parentElement; |
| 84 var elements = wrapper.querySelectorAll('.' + HIT_CSS_CLASS); |
| 85 // For each element, remove the highlighting. |
| 86 for (var i = 0; i < elements.length; i++) { |
| 87 var node = elements[i]; |
| 88 wrapper.replaceChild(node.firstChild, node); |
| 89 } |
| 90 |
| 91 // Normalize so that adjacent text nodes will be combined. |
| 92 wrapper.normalize(); |
| 93 // Restore the DOM structure as it was before the search occurred. |
| 94 wrapperParent.appendChild(wrapper.firstChild); |
| 95 wrapper.remove(); |
| 96 }); |
| 97 } |
| 98 |
| 99 /** |
| 100 * Highlights all matches (both within self and children's Shadow DOM). |
| 101 * @param {!RegExp} regexp |
| 102 * @param {!Element} element |
| 103 */ |
| 104 function highlightMatches_(regexp, element) { |
| 105 highlightSelfMatches_(regexp, element); |
| 106 findChildren_(element).forEach(function(child) { |
| 107 highlightMatches_(regexp, child); |
| 108 }); |
| 109 } |
| 110 |
| 111 /** |
| 112 * Un-highlights all previous matches (both within self and children's Shadow |
| 113 * DOM). |
| 114 * @param {!Element} element |
| 115 */ |
| 116 function unhighlightMatches_(element) { |
| 117 unhighlightSelfMatches_(element); |
| 118 findChildren_(element).forEach(unhighlightMatches_); |
| 119 } |
| 120 |
| 121 /** |
| 122 * Performs hierarchical search, starting at the given element. |
| 123 * @param {string} text |
| 124 * @param {!Element} element |
| 125 */ |
| 126 function search(text, element) { |
| 127 unhighlightMatches_(element); |
| 128 |
| 129 var searchText = |
| 130 text.trim().toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); |
| 131 if (searchText.length == 0) { |
| 132 return; |
| 133 } |
| 134 |
| 135 highlightMatches_(new RegExp('(' + searchText + ')', 'ig'), element); |
| 136 } |
| 137 |
| 138 window.search = search; |
| 139 })(); |
| OLD | NEW |