Skip to content

Commit

Permalink
Merge branch 'hud-iframe-input-with-store-all-settings'
Browse files Browse the repository at this point in the history
  • Loading branch information
smblott-github committed Jun 25, 2015
2 parents c404f67 + e5faeff commit cb42acf
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 330 deletions.
40 changes: 40 additions & 0 deletions content_scripts/hud.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ HUD =
tween: null
hudUI: null
_displayElement: null
findMode: null

# This HUD is styled to precisely mimick the chrome HUD on Mac. Use the "has_popup_and_link_hud.html"
# test harness to tweak these styles to match Chrome's. One limitation of our HUD display is that
Expand All @@ -26,6 +27,19 @@ HUD =
@hudUI.show {name: "show", text}
@tween.fade 1.0, 150

showFindMode: (@findMode = null) ->
return unless @enabled()
@hudUI.show {name: "showFindMode", text: ""}
@tween.fade 1.0, 150

search: (data) ->
@findMode.findInPlace data.query

# Show the number of matches in the HUD UI.
matchCount = if FindMode.query.parsedQuery.length > 0 then FindMode.query.matchCount else 0
showMatchText = FindMode.query.rawQuery.length > 0
@hudUI.postMessage {name: "updateMatchesCount", matchCount, showMatchText}

# Hide the HUD.
# If :immediate is falsy, then the HUD is faded out smoothly (otherwise it is hidden immediately).
# If :updateIndicator is truthy, then we also refresh the mode indicator. The only time we don't update the
Expand All @@ -42,6 +56,32 @@ HUD =
else
@tween.fade 0, 150, => @hide true, updateIndicator

hideFindMode: (data) ->
@findMode.checkReturnToViewPort()

# An element element won't receive a focus event if the search landed on it while we were in the HUD
# iframe. To end up with the correct modes active, we create a focus/blur event manually after refocusing
# this window.
window.focus()

focusNode = DomUtils.getSelectionFocusElement()
document.activeElement?.blur()
focusNode?.focus()

{event} = data

if event.keyCode == keyCodes.enter
handleEnterForFindMode()
if FindMode.query.hasResults
postExit = -> new PostFindMode
else if KeyboardUtils.isEscape event
# We don't want FindMode to handle the click events that handleEscapeForFindMode can generate, so we
# wait until the mode is closed before running it.
postExit = handleEscapeForFindMode

@findMode.exit()
postExit?()

isReady: do ->
ready = false
DomUtils.documentReady -> ready = true
Expand Down
156 changes: 156 additions & 0 deletions content_scripts/mode_find.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,161 @@ class PostFindMode extends SuppressPrintable
handlerStack.remove()
@continueBubbling

class FindMode extends Mode
@query:
rawQuery: ""
matchCount: 0
hasResults: false

constructor: (options = {}) ->
# Save the selection, so findInPlace can restore it.
@initialRange = getCurrentRange()
FindMode.query = rawQuery: ""
if options.returnToViewport
@scrollX = window.scrollX
@scrollY = window.scrollY
super extend options,
name: "find"
indicator: false
exitOnClick: true

HUD.showFindMode this

exit: (event) ->
super()
handleEscapeForFindMode() if event

restoreSelection: ->
range = @initialRange
selection = getSelection()
selection.removeAllRanges()
selection.addRange range

findInPlace: (query) ->
# If requested, restore the scroll position (so that failed searches leave the scroll position unchanged).
@checkReturnToViewPort()
FindMode.updateQuery query
# Restore the selection. That way, we're always searching forward from the same place, so we find the right
# match as the user adds matching characters, or removes previously-matched characters. See #1434.
@restoreSelection()
query = if FindMode.query.isRegex then FindMode.getNextQueryFromRegexMatches(0) else FindMode.query.parsedQuery
FindMode.query.hasResults = FindMode.execute query

@updateQuery: (query) ->
@query.rawQuery = query
# the query can be treated differently (e.g. as a plain string versus regex depending on the presence of
# escape sequences. '\' is the escape character and needs to be escaped itself to be used as a normal
# character. here we grep for the relevant escape sequences.
@query.isRegex = Settings.get 'regexFindMode'
hasNoIgnoreCaseFlag = false
@query.parsedQuery = @query.rawQuery.replace /(\\{1,2})([rRI]?)/g, (match, slashes, flag) ->
return match if flag == "" or slashes.length != 1
switch (flag)
when "r"
@query.isRegex = true
when "R"
@query.isRegex = false
when "I"
hasNoIgnoreCaseFlag = true
""

# default to 'smartcase' mode, unless noIgnoreCase is explicitly specified
@query.ignoreCase = !hasNoIgnoreCaseFlag && !Utils.hasUpperCase(@query.parsedQuery)

# if we are dealing with a regex, grep for all matches in the text, and then call window.find() on them
# sequentially so the browser handles the scrolling / text selection.
if @query.isRegex
try
pattern = new RegExp(@query.parsedQuery, "g" + (if @query.ignoreCase then "i" else ""))
catch error
# if we catch a SyntaxError, assume the user is not done typing yet and return quietly
return
# innerText will not return the text of hidden elements, and strip out tags while preserving newlines
text = document.body.innerText
@query.regexMatches = text.match(pattern)
@query.activeRegexIndex = 0
@query.matchCount = @query.regexMatches?.length
# if we are doing a basic plain string match, we still want to grep for matches of the string, so we can
# show a the number of results. We can grep on document.body.innerText, as it should be indistinguishable
# from the internal representation used by window.find.
else
# escape all special characters, so RegExp just parses the string 'as is'.
# Taken from http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
escapeRegExp = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g
parsedNonRegexQuery = @query.parsedQuery.replace(escapeRegExp, (char) -> "\\" + char)
pattern = new RegExp(parsedNonRegexQuery, "g" + (if @query.ignoreCase then "i" else ""))
text = document.body.innerText
@query.matchCount = text.match(pattern)?.length

@getNextQueryFromRegexMatches: (stepSize) ->
# find()ing an empty query always returns false
return "" unless @query.regexMatches

totalMatches = @query.regexMatches.length
@query.activeRegexIndex += stepSize + totalMatches
@query.activeRegexIndex %= totalMatches

@query.regexMatches[@query.activeRegexIndex]

@getQuery: (backwards) ->
# check if the query has been changed by a script in another frame
mostRecentQuery = FindModeHistory.getQuery()
if (mostRecentQuery != @query.rawQuery)
@updateQuery mostRecentQuery

if @query.isRegex
@getNextQueryFromRegexMatches(if backwards then -1 else 1)
else
@query.parsedQuery

@saveQuery: -> FindModeHistory.saveQuery @query.rawQuery

# :options is an optional dict. valid parameters are 'caseSensitive' and 'backwards'.
@execute: (query, options) ->
result = null
options = extend {
backwards: false
caseSensitive: !@query.ignoreCase
colorSelection: true
}, options
query ?= FindMode.getQuery options.backwards

if options.colorSelection
document.body.classList.add("vimiumFindMode")
# ignore the selectionchange event generated by find()
document.removeEventListener("selectionchange", @restoreDefaultSelectionHighlight, true)

result = window.find(query, options.caseSensitive, options.backwards, true, false, true, false)

if options.colorSelection
setTimeout(
-> document.addEventListener("selectionchange", @restoreDefaultSelectionHighlight, true)
, 0)

# We are either in normal mode ("n"), or find mode ("/"). We are not in insert mode. Nevertheless, if a
# previous find landed in an editable element, then that element may still be activated. In this case, we
# don't want to leave it behind (see #1412).
if document.activeElement and DomUtils.isEditable document.activeElement
document.activeElement.blur() unless DomUtils.isSelected document.activeElement

result

@restoreDefaultSelectionHighlight: -> document.body.classList.remove("vimiumFindMode")

checkReturnToViewPort: ->
window.scrollTo @scrollX, @scrollY if @options.returnToViewport

getCurrentRange = ->
selection = getSelection()
if selection.type == "None"
range = document.createRange()
range.setStart document.body, 0
range.setEnd document.body, 0
range
else
selection.collapseToStart() if selection.type == "Range"
selection.getRangeAt 0

root = exports ? window
root.PostFindMode = PostFindMode
root.FindMode = FindMode
29 changes: 14 additions & 15 deletions content_scripts/mode_visual_edit.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -351,22 +351,21 @@ class Movement extends CountPrefix
# element), or if this instance has been created to execute only a single movement.
unless @options.parentMode or options.oneMovementOnly
do =>
executeFind = (count, findBackwards) =>
if query = getFindModeQuery findBackwards
initialRange = @selection.getRangeAt(0).cloneRange()
for [0...count]
unless window.find query, Utils.hasUpperCase(query), findBackwards, true, false, true, false
@setSelectionRange initialRange
HUD.showForDuration("No matches for '" + query + "'", 1000)
return
# The find was successfull. If we're in caret mode, then we should now have a selection, so we can
# drop back into visual mode.
@changeMode VisualMode if @name == "caret" and 0 < @selection.toString().length

@movements.n = (count) -> executeFind count, false
@movements.N = (count) -> executeFind count, true
doFind = (count, backwards) =>
initialRange = @selection.getRangeAt(0).cloneRange()
for [0...count] by 1
unless FindMode.execute null, {colorSelection: false, backwards}
@setSelectionRange initialRange
HUD.showForDuration("No matches for '#{FindMode.query.rawQuery}'", 1000)
return
# The find was successfull. If we're in caret mode, then we should now have a selection, so we can
# drop back into visual mode.
@changeMode VisualMode if @name == "caret" and 0 < @selection.toString().length

@movements.n = (count) -> doFind count, false
@movements.N = (count) -> doFind count, true
@movements["/"] = ->
@findMode = window.enterFindMode returnToViewport: true
@findMode = new FindMode returnToViewport: true
@findMode.onExit => @changeMode VisualMode
#
# End of Movement constructor.
Expand Down
16 changes: 16 additions & 0 deletions content_scripts/vimium.css
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,22 @@ iframe.vimiumHUDFrame {
opacity: 0;
}

div.vimiumHUD span#hud-find-input, div.vimiumHUD span#hud-match-count {
display: inline;
outline: none;
white-space: nowrap;
overflow-y: hidden;
}

div.vimiumHUD span#hud-find-input br {
display: none;
}

div.vimiumHUD span#hud-find-input * {
display: inline;
white-space: nowrap;
}

body.vimiumFindMode ::selection {
background: #ff9632;
}
Expand Down
Loading

0 comments on commit cb42acf

Please sign in to comment.