]> git.gir.st - VimFx.git/blob - extension/packages/find.coffee
Merge branch 'native-find' into develop
[VimFx.git] / extension / packages / find.coffee
1 utils = require 'utils'
2
3 CONTAINER_ID = 'VimFxFindContainer'
4 DIRECTION_FORWARDS = 0
5 DIRECTION_BACKWARDS = 1
6
7 # Create and inserts into DOM find controls and handlers
8 injectFind = (document, cb) ->
9 # Clean up just in case...
10 removeFind document
11
12 # Create container div insert a text input into it
13 [div, input] = createFindContainer(document)
14
15 # Call back in new input
16 input.addEventListener 'input', (event) ->
17 result = cb(input.value)
18 if result
19 utils.removeClass input, 'VimFxNotFound'
20 else
21 utils.addClass input, 'VimFxNotFound'
22
23 # Call back on (Shift)-Enter with proper direction
24 input.addEventListener 'keypress', (event) ->
25 if event.keyCode == event.DOM_VK_RETURN
26 removeFind(document, false)
27
28 document.documentElement.appendChild div
29 input.focus()
30
31 # Removes find controls from DOM
32 removeFind = (document, clear = true) ->
33 if div = document.getElementById CONTAINER_ID
34 document.documentElement.removeChild div
35
36 if clear
37 clearSelection(document.defaultView)
38
39 createFindContainer = (document) ->
40 div = document.createElement 'div'
41 div.className = 'VimFxReset'
42 div.id = CONTAINER_ID
43
44 input = document.createElement 'input'
45 input.type = 'text'
46 input.className = 'VimFxReset'
47 input.id = 'VimFxFindInput'
48
49 div.appendChild input
50
51 return [ div, input ]
52
53 clearSelection = (window, selectionType = Ci.nsISelectionController.SELECTION_FIND) ->
54 for frame in window.frames
55 clearSelection(frame)
56
57 if controller = getController(window)
58 controller.getSelection(selectionType).removeAllRanges()
59
60 findFactory = (selectionType) ->
61 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
62 .createInstance()
63 .QueryInterface(Components.interfaces.nsIFind)
64
65 return (window, findStr, findRng = null, direction = DIRECTION_FORWARDS) ->
66 # `find` will also recursively search in all frames. # `innerFind` does the work:
67 # searches, selects, scrolls, and optionally reaches into frames
68 innerFind = (window) ->
69 if controller = getController(window)
70 finder.findBackwards = (direction == DIRECTION_BACKWARDS)
71 finder.caseSensitive = (findStr != findStr.toLowerCase())
72
73 searchRange = window.document.createRange()
74 searchRange.selectNodeContents window.document.body
75
76 if findRng and findRng.commonAncestorContainer.ownerDocument == window.document
77 if finder.findBackwards
78 searchRange.setEnd(findRng.startContainer, findRng.startOffset)
79 else
80 searchRange.setStart(findRng.endContainer, findRng.endOffset)
81
82 (startPt = searchRange.cloneRange()).collapse(true)
83 (endPt = searchRange.cloneRange()).collapse(false)
84
85 if finder.findBackwards
86 [startPt, endPt] = [endPt, startPt]
87
88 if range = finder.Find(findStr, searchRange, startPt, endPt)
89 controller.getSelection(selectionType).addRange(range)
90 controller.scrollSelectionIntoView(selectionType, range, 0)
91 return range
92
93 clearSelection(window, selectionType)
94
95 if findStr.length > 0
96 # Get all embedded windows/frames including the passed window
97 wnds = getAllWindows window
98 # In backward searching reverse windows mode so that
99 # it starts off the deepest iframe
100 if finder.findBackwards
101 wnds.reverse()
102
103 # First search in the same window to which current `findRng` belongs
104 if rngWindow = findRng?.commonAncestorContainer.ownerDocument.defaultView
105 wnds = cycleToItem wnds, rngWindow
106
107 for w in wnds
108 if range = innerFind w
109 break
110
111 return if (findStr.length == 0) then true else range
112
113 highlightFactory = (selectionType) ->
114 finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
115 .createInstance()
116 .QueryInterface(Components.interfaces.nsIFind)
117
118 return (window, findStr) ->
119 matchesCount = 0
120 finder.findBackwards = false
121 finder.caseSensitive = (findStr != findStr.toLowerCase())
122
123 innerHighlight = (window) ->
124 if controller = getController(window)
125 searchRange = window.document.createRange()
126 searchRange.selectNodeContents window.document.body
127
128 (startPt = searchRange.cloneRange()).collapse(true)
129 (endPt = searchRange.cloneRange()).collapse(false)
130
131 selection = controller.getSelection(selectionType)
132 while (range = finder.Find(findStr, searchRange, startPt, endPt))
133 selection.addRange(range)
134 matchesCount += 1
135 (startPt = range.cloneRange()).collapse(false)
136
137 # Highlight in iframes
138 for frame in window.frames
139 innerHighlight(frame)
140
141 clearSelection(window, selectionType)
142
143 if findStr.length > 0
144 innerHighlight(window)
145
146 return if (findStr.length == 0) then true else matchesCount
147
148 getController = (window) ->
149 if not window.innerWidth or not window.innerHeight
150 return null
151
152 return window.QueryInterface(Ci.nsIInterfaceRequestor)
153 .getInterface(Ci.nsIWebNavigation)
154 .QueryInterface(Ci.nsIInterfaceRequestor)
155 .getInterface(Ci.nsISelectionDisplay)
156 .QueryInterface(Ci.nsISelectionController)
157
158 # Returns flat list of frmaes within provided window
159 getAllWindows = (window) ->
160 result = [window]
161 for frame in window.frames
162 result = result.concat(getAllWindows(frame))
163
164 return result
165
166 cycleToItem = (array, item) ->
167 if item and array.indexOf(item) != -1
168 while array[0] != item
169 array.push array.shift()
170
171 return array
172
173 exports.injectFind = injectFind
174 exports.removeFind = removeFind
175 exports.find = findFactory(Ci.nsISelectionController.SELECTION_FIND)
176 exports.highlight = highlightFactory(Ci.nsISelectionController.SELECTION_FIND)
177 exports.DIRECTION_FORWARDS = DIRECTION_FORWARDS
178 exports.DIRECTION_BACKWARDS = DIRECTION_BACKWARDS
Imprint / Impressum