1 CONTAINER_ID = 'VimFxFindContainer'
3 DIRECTION_BACKWARDS = 1
5 # Create and inserts into DOM find controls and handlers
6 injectFind = (document, cb) ->
7 # Clean up just in case...
10 # Create container div insert a text input into it
11 [div, input] = createFindContainer(document)
13 # Call back in new input
14 input.addEventListener 'input', (event) ->
15 result = cb(input.value)
17 input.classList.remove 'VimFxNotFound'
19 input.classList.add 'VimFxNotFound'
21 # Call back on (Shift)-Enter with proper direction
22 input.addEventListener 'keypress', (event) ->
23 if event.keyCode == event.DOM_VK_RETURN
24 focusSelection(document, Ci.nsISelectionController.SELECTION_FIND)
25 removeFind(document, false)
27 document.documentElement.appendChild div
30 # Removes find controls from DOM
31 removeFind = (document, clear = true) ->
32 if div = document.getElementById CONTAINER_ID
33 document.documentElement.removeChild div
36 clearSelection(document.defaultView)
38 focusSelection = (document, selectionType) ->
39 if controller = getController(document.defaultView)
40 if selection = controller.getSelection(selectionType)
41 if selection.rangeCount > 0
42 element = selection.getRangeAt(0).commonAncestorContainer?.parentNode
46 createFindContainer = (document) ->
47 div = document.createElement 'div'
48 div.className = 'VimFxReset'
51 input = document.createElement 'input'
53 input.className = 'VimFxReset'
54 input.id = 'VimFxFindInput'
60 clearSelection = (window, selectionType = Ci.nsISelectionController.SELECTION_FIND) ->
61 for frame in window.frames
64 if controller = getController(window)
65 controller.getSelection(selectionType).removeAllRanges()
67 findFactory = (selectionType) ->
68 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
70 .QueryInterface(Components.interfaces.nsIFind)
72 return (window, findStr, findRng = null, direction = DIRECTION_FORWARDS, focus = false) ->
73 # `find` will also recursively search in all frames. # `innerFind` does the work:
74 # searches, selects, scrolls, and optionally reaches into frames
75 innerFind = (window) ->
76 if controller = getController(window)
77 finder.findBackwards = (direction == DIRECTION_BACKWARDS)
78 finder.caseSensitive = (findStr != findStr.toLowerCase())
80 searchRange = window.document.createRange()
81 searchRange.selectNodeContents window.document.body
83 if findRng and findRng.commonAncestorContainer.ownerDocument == window.document
84 if finder.findBackwards
85 searchRange.setEnd(findRng.startContainer, findRng.startOffset)
87 searchRange.setStart(findRng.endContainer, findRng.endOffset)
89 (startPt = searchRange.cloneRange()).collapse(true)
90 (endPt = searchRange.cloneRange()).collapse(false)
92 if finder.findBackwards
93 [startPt, endPt] = [endPt, startPt]
95 if range = finder.Find(findStr, searchRange, startPt, endPt)
96 controller.getSelection(selectionType).addRange(range)
97 controller.scrollSelectionIntoView(selectionType, range, Ci.nsISelectionController.SCROLL_CENTER_VERTICALLY)
99 focusSelection(window.document, selectionType)
103 clearSelection(window, selectionType)
105 if findStr.length > 0
106 # Get all embedded windows/frames including the passed window
107 wnds = getAllWindows window
108 # In backward searching reverse windows mode so that
109 # it starts off the deepest iframe
110 if finder.findBackwards
113 # First search in the same window to which current `findRng` belongs
114 if rngWindow = findRng?.commonAncestorContainer.ownerDocument.defaultView
115 wnds = cycleToItem wnds, rngWindow
118 if range = innerFind w
121 return if (findStr.length == 0) then true else range
123 highlightFactory = (selectionType) ->
124 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
126 .QueryInterface(Components.interfaces.nsIFind)
128 return (window, findStr) ->
130 finder.findBackwards = false
131 finder.caseSensitive = (findStr != findStr.toLowerCase())
133 innerHighlight = (window) ->
134 if controller = getController(window)
135 searchRange = window.document.createRange()
136 searchRange.selectNodeContents window.document.body
138 (startPt = searchRange.cloneRange()).collapse(true)
139 (endPt = searchRange.cloneRange()).collapse(false)
141 selection = controller.getSelection(selectionType)
142 while (range = finder.Find(findStr, searchRange, startPt, endPt))
143 selection.addRange(range)
145 (startPt = range.cloneRange()).collapse(false)
147 # Highlight in iframes
148 for frame in window.frames
149 innerHighlight(frame)
151 clearSelection(window, selectionType)
153 if findStr.length > 0
154 innerHighlight(window)
156 return if (findStr.length == 0) then true else matchesCount
158 getController = (window) ->
159 if not window.innerWidth or not window.innerHeight
162 return window.QueryInterface(Ci.nsIInterfaceRequestor)
163 .getInterface(Ci.nsIWebNavigation)
164 .QueryInterface(Ci.nsIInterfaceRequestor)
165 .getInterface(Ci.nsISelectionDisplay)
166 .QueryInterface(Ci.nsISelectionController)
168 # Returns flat list of frmaes within provided window
169 getAllWindows = (window) ->
171 for frame in window.frames
172 result = result.concat(getAllWindows(frame))
176 cycleToItem = (array, item) ->
177 if item and array.indexOf(item) != -1
178 while array[0] != item
179 array.push array.shift()
183 exports.injectFind = injectFind
184 exports.removeFind = removeFind
185 exports.find = findFactory(Ci.nsISelectionController.SELECTION_FIND)
186 exports.highlight = highlightFactory(Ci.nsISelectionController.SELECTION_FIND)
187 exports.DIRECTION_FORWARDS = DIRECTION_FORWARDS
188 exports.DIRECTION_BACKWARDS = DIRECTION_BACKWARDS