// // FindTextHighlightJS.swift // flutter_inappwebview // // Created by Lorenzo Pichilli on 16/02/21. // import Foundation let FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT" let FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._searchResultCount" let FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._currentHighlight" let FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._isDoneCounting" let FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT = PluginScript( groupName: FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT_GROUP_NAME, source: FIND_TEXT_HIGHLIGHT_JS_SOURCE, injectionTime: .atDocumentStart, forMainFrameOnly: true, requiredInAllContentWorlds: false, messageHandlerNames: ["onFindResultReceived"]) let FIND_TEXT_HIGHLIGHT_JS_SOURCE = """ \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) = 0; \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = 0; \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = false; window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement = function(element, keyword) { if (element) { if (element.nodeType == 3) { // Text node var elementTmp = element; while (true) { var value = elementTmp.nodeValue; // Search for keyword in text node var idx = value.toLowerCase().indexOf(keyword); if (idx < 0) break; var span = document.createElement("span"); var text = document.createTextNode(value.substr(idx, keyword.length)); span.appendChild(text); span.setAttribute( "id", "\(JAVASCRIPT_BRIDGE_NAME)_SEARCH_WORD_" + \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) ); span.setAttribute("class", "\(JAVASCRIPT_BRIDGE_NAME)_Highlight"); var backgroundColor = \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) == 0 ? "#FF9732" : "#FFFF00"; span.setAttribute("style", "color: #000 !important; background: " + backgroundColor + " !important; padding: 0px !important; margin: 0px !important; border: 0px !important;"); text = document.createTextNode(value.substr(idx + keyword.length)); element.deleteData(idx, value.length - idx); var next = element.nextSibling; element.parentNode.insertBefore(span, next); element.parentNode.insertBefore(text, next); element = text; \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)++; elementTmp = document.createTextNode( value.substr(idx + keyword.length) ); var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE); window.webkit.messageHandlers["onFindResultReceived"].postMessage( { 'findResult': { 'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE), 'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE), 'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) }, '_windowId': _windowId } ); } } else if (element.nodeType == 1) { // Element node if ( element.style.display != "none" && element.nodeName.toLowerCase() != "select" ) { for (var i = element.childNodes.length - 1; i >= 0; i--) { window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement( element.childNodes[element.childNodes.length - 1 - i], keyword ); } } } } } // the main entry point to start the search window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsync = function(keyword) { window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches(); window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement(document.body, keyword.toLowerCase()); \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = true; var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE); window.webkit.messageHandlers["onFindResultReceived"].postMessage( { 'findResult': { 'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE), 'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE), 'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) }, '_windowId': _windowId } ); } // helper function, recursively removes the highlights in elements and their children window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement = function(element) { if (element) { if (element.nodeType == 1) { if (element.getAttribute("class") == "\(JAVASCRIPT_BRIDGE_NAME)_Highlight") { var text = element.removeChild(element.firstChild); element.parentNode.insertBefore(text, element); element.parentNode.removeChild(element); return true; } else { var normalize = false; for (var i = element.childNodes.length - 1; i >= 0; i--) { if (window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement(element.childNodes[i])) { normalize = true; } } if (normalize) { element.normalize(); } } } } return false; } // the main entry point to remove the highlights window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches = function() { \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) = 0; \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = 0; \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = false; window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement(document.body); } window.\(JAVASCRIPT_BRIDGE_NAME)._findNext = function(forward) { if (\(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) <= 0) return; var idx = \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) + (forward ? +1 : -1); idx = idx < 0 ? \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) - 1 : idx >= \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) ? 0 : idx; \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = idx; var scrollTo = document.getElementById("\(JAVASCRIPT_BRIDGE_NAME)_SEARCH_WORD_" + idx); if (scrollTo) { var highlights = document.getElementsByClassName("\(JAVASCRIPT_BRIDGE_NAME)_Highlight"); for (var i = 0; i < highlights.length; i++) { var span = highlights[i]; span.style.backgroundColor = "#FFFF00"; } scrollTo.style.backgroundColor = "#FF9732"; scrollTo.scrollIntoView({ behavior: "auto", block: "center" }); var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE); window.webkit.messageHandlers["onFindResultReceived"].postMessage( { 'findResult': { 'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE), 'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE), 'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) }, '_windowId': _windowId } ); } } """