]> git.gir.st - VimFx.git/blob - extension/packages/find.coffee
Merge pull request #133 from lydell/develop
[VimFx.git] / extension / packages / find.coffee
1 CONTAINER_ID = 'VimFxFindContainer'
2 DIRECTION_FORWARDS = 0
3 DIRECTION_BACKWARDS = 1
4
5 # Create and inserts into DOM find controls and handlers
6 injectFind = (document, cb) ->
7 # Clean up just in case...
8 removeFind document
9
10 # Create container div insert a text input into it
11 [div, input] = createFindContainer(document)
12
13 # Call back in new input
14 input.addEventListener 'input', (event) ->
15 result = cb(input.value)
16 if result
17 input.classList.remove 'VimFxNotFound'
18 else
19 input.classList.add 'VimFxNotFound'
20
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)
26
27 document.documentElement.appendChild div
28 input.focus()
29
30 # Removes find controls from DOM
31 removeFind = (document, clear = true) ->
32 if div = document.getElementById CONTAINER_ID
33 document.documentElement.removeChild div
34
35 if clear
36 clearSelection(document.defaultView)
37
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
43 if element.focus
44 element.focus()
45
46 createFindContainer = (document) ->
47 div = document.createElement 'div'
48 div.className = 'VimFxReset'
49 div.id = CONTAINER_ID
50
51 input = document.createElement 'input'
52 input.type = 'text'
53 input.className = 'VimFxReset'
54 input.id = 'VimFxFindInput'
55
56 div.appendChild input
57
58 return [ div, input ]
59
60 clearSelection = (window, selectionType = Ci.nsISelectionController.SELECTION_FIND) ->
61 for frame in window.frames
62 clearSelection(frame)
63
64 if controller = getController(window)
65 controller.getSelection(selectionType).removeAllRanges()
66
67 findFactory = (selectionType) ->
68 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
69 .createInstance()
70 .QueryInterface(Components.interfaces.nsIFind)
71
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())
79
80 searchRange = window.document.createRange()
81 searchRange.selectNodeContents window.document.body
82
83 if findRng and findRng.commonAncestorContainer.ownerDocument == window.document
84 if finder.findBackwards
85 searchRange.setEnd(findRng.startContainer, findRng.startOffset)
86 else
87 searchRange.setStart(findRng.endContainer, findRng.endOffset)
88
89 (startPt = searchRange.cloneRange()).collapse(true)
90 (endPt = searchRange.cloneRange()).collapse(false)
91
92 if finder.findBackwards
93 [startPt, endPt] = [endPt, startPt]
94
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)
98 if focus
99 focusSelection(window.document, selectionType)
100
101 return range
102
103 clearSelection(window, selectionType)
104
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
111 wnds.reverse()
112
113 # First search in the same window to which current `findRng` belongs
114 if rngWindow = findRng?.commonAncestorContainer.ownerDocument.defaultView
115 wnds = cycleToItem wnds, rngWindow
116
117 for w in wnds
118 if range = innerFind w
119 break
120
121 return if (findStr.length == 0) then true else range
122
123 highlightFactory = (selectionType) ->
124 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
125 .createInstance()
126 .QueryInterface(Components.interfaces.nsIFind)
127
128 return (window, findStr) ->
129 matchesCount = 0
130 finder.findBackwards = false
131 finder.caseSensitive = (findStr != findStr.toLowerCase())
132
133 innerHighlight = (window) ->
134 if controller = getController(window)
135 searchRange = window.document.createRange()
136 searchRange.selectNodeContents window.document.body
137
138 (startPt = searchRange.cloneRange()).collapse(true)
139 (endPt = searchRange.cloneRange()).collapse(false)
140
141 selection = controller.getSelection(selectionType)
142 while (range = finder.Find(findStr, searchRange, startPt, endPt))
143 selection.addRange(range)
144 matchesCount += 1
145 (startPt = range.cloneRange()).collapse(false)
146
147 # Highlight in iframes
148 for frame in window.frames
149 innerHighlight(frame)
150
151 clearSelection(window, selectionType)
152
153 if findStr.length > 0
154 innerHighlight(window)
155
156 return if (findStr.length == 0) then true else matchesCount
157
158 getController = (window) ->
159 if not window.innerWidth or not window.innerHeight
160 return null
161
162 return window.QueryInterface(Ci.nsIInterfaceRequestor)
163 .getInterface(Ci.nsIWebNavigation)
164 .QueryInterface(Ci.nsIInterfaceRequestor)
165 .getInterface(Ci.nsISelectionDisplay)
166 .QueryInterface(Ci.nsISelectionController)
167
168 # Returns flat list of frmaes within provided window
169 getAllWindows = (window) ->
170 result = [window]
171 for frame in window.frames
172 result = result.concat(getAllWindows(frame))
173
174 return result
175
176 cycleToItem = (array, item) ->
177 if item and array.indexOf(item) != -1
178 while array[0] != item
179 array.push array.shift()
180
181 return array
182
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
Imprint / Impressum