diff options
Diffstat (limited to 'content_scripts')
| -rw-r--r-- | content_scripts/hud.coffee | 40 | ||||
| -rw-r--r-- | content_scripts/mode_find.coffee | 156 | ||||
| -rw-r--r-- | content_scripts/mode_visual_edit.coffee | 29 | ||||
| -rw-r--r-- | content_scripts/vimium.css | 16 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 288 |
5 files changed, 241 insertions, 288 deletions
diff --git a/content_scripts/hud.coffee b/content_scripts/hud.coffee index 84b8abeb..bfad71b7 100644 --- a/content_scripts/hud.coffee +++ b/content_scripts/hud.coffee @@ -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 @@ -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 @@ -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 diff --git a/content_scripts/mode_find.coffee b/content_scripts/mode_find.coffee index ed08fbd5..d7c628be 100644 --- a/content_scripts/mode_find.coffee +++ b/content_scripts/mode_find.coffee @@ -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 diff --git a/content_scripts/mode_visual_edit.coffee b/content_scripts/mode_visual_edit.coffee index 58e4f815..ce3caafe 100644 --- a/content_scripts/mode_visual_edit.coffee +++ b/content_scripts/mode_visual_edit.coffee @@ -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. diff --git a/content_scripts/vimium.css b/content_scripts/vimium.css index 38a968fc..e02df7c2 100644 --- a/content_scripts/vimium.css +++ b/content_scripts/vimium.css @@ -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; } diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index bffbd457..8f4c7e82 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -5,10 +5,6 @@ # "domReady". # -findModeQuery = { rawQuery: "", matchCount: 0 } -findModeQueryHasResults = false -findModeAnchorNode = null -findModeInitialRange = null isShowingHelpDialog = false keyPort = null isEnabledForUrl = true @@ -622,108 +618,7 @@ window.refreshCompletionKeys = (response) -> isValidFirstKey = (keyChar) -> validFirstKeys[keyChar] || /^[1-9]/.test(keyChar) -# This implements find-mode query history (using the "findModeRawQueryList" setting) as a list of raw queries, -# most recent first. -FindModeHistory = - storage: chrome.storage.local - key: "findModeRawQueryList" - max: 50 - rawQueryList: null - - init: -> - unless @rawQueryList - @rawQueryList = [] # Prevent repeated initialization. - @key = "findModeRawQueryListIncognito" if isIncognitoMode - @storage.get @key, (items) => - unless chrome.runtime.lastError - @rawQueryList = items[@key] if items[@key] - if isIncognitoMode and not items[@key] - # This is the first incognito tab, so we need to initialize the incognito-mode query history. - @storage.get "findModeRawQueryList", (items) => - unless chrome.runtime.lastError - @rawQueryList = items.findModeRawQueryList - @storage.set findModeRawQueryListIncognito: @rawQueryList - - chrome.storage.onChanged.addListener (changes, area) => - @rawQueryList = changes[@key].newValue if changes[@key] - - getQuery: (index = 0) -> - @rawQueryList[index] or "" - - saveQuery: (query) -> - if 0 < query.length - @rawQueryList = @refreshRawQueryList query, @rawQueryList - newSetting = {}; newSetting[@key] = @rawQueryList - @storage.set newSetting - # If there are any active incognito-mode tabs, then propagte this query to those tabs too. - unless isIncognitoMode - @storage.get "findModeRawQueryListIncognito", (items) => - if not chrome.runtime.lastError and items.findModeRawQueryListIncognito - @storage.set - findModeRawQueryListIncognito: @refreshRawQueryList query, items.findModeRawQueryListIncognito - - refreshRawQueryList: (query, rawQueryList) -> - ([ query ].concat rawQueryList.filter (q) => q != query)[0..@max] - -# should be called whenever rawQuery is modified. -updateFindModeQuery = -> - # 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. - findModeQuery.isRegex = Settings.get 'regexFindMode' - hasNoIgnoreCaseFlag = false - findModeQuery.parsedQuery = findModeQuery.rawQuery.replace /(\\{1,2})([rRI]?)/g, (match, slashes, flag) -> - return match if flag == "" or slashes.length != 1 - switch (flag) - when "r" - findModeQuery.isRegex = true - when "R" - findModeQuery.isRegex = false - when "I" - hasNoIgnoreCaseFlag = true - "" - - # default to 'smartcase' mode, unless noIgnoreCase is explicitly specified - findModeQuery.ignoreCase = !hasNoIgnoreCaseFlag && !Utils.hasUpperCase(findModeQuery.parsedQuery) - - # Don't count matches in the HUD. - HUD.hide(true) - - # 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 findModeQuery.isRegex - try - pattern = new RegExp(findModeQuery.parsedQuery, "g" + (if findModeQuery.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 - findModeQuery.regexMatches = text.match(pattern) - findModeQuery.activeRegexIndex = 0 - findModeQuery.matchCount = findModeQuery.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 = findModeQuery.parsedQuery.replace(escapeRegExp, (char) -> "\\" + char) - pattern = new RegExp(parsedNonRegexQuery, "g" + (if findModeQuery.ignoreCase then "i" else "")) - text = document.body.innerText - findModeQuery.matchCount = text.match(pattern)?.length - -updateQueryForFindMode = (rawQuery) -> - findModeQuery.rawQuery = rawQuery - updateFindModeQuery() - performFindInPlace() - showFindModeHUDForQuery() - -handleKeyCharForFindMode = (keyChar) -> - updateQueryForFindMode findModeQuery.rawQuery + keyChar - -handleEscapeForFindMode = -> +window.handleEscapeForFindMode = -> document.body.classList.remove("vimiumFindMode") # removing the class does not re-color existing selections. we recreate the current selection so it reverts # back to the default color. @@ -734,158 +629,38 @@ handleEscapeForFindMode = -> window.getSelection().addRange(range) focusFoundLink() || selectFoundInputElement() -# Return true if character deleted, false otherwise. -handleDeleteForFindMode = -> - if findModeQuery.rawQuery.length == 0 - HUD.hide() - false - else - updateQueryForFindMode findModeQuery.rawQuery.substring(0, findModeQuery.rawQuery.length - 1) - true - # <esc> sends us into insert mode if possible, but <cr> does not. # <esc> corresponds approximately to 'nevermind, I have found it already' while <cr> means 'I want to save # this query and do more searches with it' -handleEnterForFindMode = -> +window.handleEnterForFindMode = -> focusFoundLink() document.body.classList.add("vimiumFindMode") - FindModeHistory.saveQuery findModeQuery.rawQuery - -class FindMode extends Mode - constructor: (options = {}) -> - @historyIndex = -1 - @partialQuery = "" - if options.returnToViewport - @scrollX = window.scrollX - @scrollY = window.scrollY - super - name: "find" - indicator: false - exitOnEscape: true - exitOnClick: true - - keydown: (event) => - window.scrollTo @scrollX, @scrollY if options.returnToViewport - if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey - @exit() unless handleDeleteForFindMode() - @suppressEvent - else if event.keyCode == keyCodes.enter - handleEnterForFindMode() - @exit() - @suppressEvent - else if event.keyCode == keyCodes.upArrow - if rawQuery = FindModeHistory.getQuery @historyIndex + 1 - @historyIndex += 1 - @partialQuery = findModeQuery.rawQuery if @historyIndex == 0 - updateQueryForFindMode rawQuery - @suppressEvent - else if event.keyCode == keyCodes.downArrow - @historyIndex = Math.max -1, @historyIndex - 1 - rawQuery = if 0 <= @historyIndex then FindModeHistory.getQuery @historyIndex else @partialQuery - updateQueryForFindMode rawQuery - @suppressEvent - else - DomUtils.suppressPropagation(event) - handlerStack.stopBubblingAndFalse - - keypress: (event) => - handlerStack.neverContinueBubbling => - if event.keyCode > 31 - keyChar = String.fromCharCode event.charCode - handleKeyCharForFindMode keyChar if keyChar - - keyup: (event) => @suppressEvent - - exit: (event) -> - super() - handleEscapeForFindMode() if event?.type == "keydown" and KeyboardUtils.isEscape event - handleEscapeForFindMode() if event?.type == "click" - if findModeQueryHasResults and event?.type != "click" - new PostFindMode - -performFindInPlace = -> - # 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. - findModeRestoreSelection() - query = if findModeQuery.isRegex then getNextQueryFromRegexMatches(0) else findModeQuery.parsedQuery - findModeQueryHasResults = executeFind(query, { caseSensitive: !findModeQuery.ignoreCase }) - -# :options is an optional dict. valid parameters are 'caseSensitive' and 'backwards'. -executeFind = (query, options) -> - result = null - options = options || {} - - 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) - 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 - - # we need to save the anchor node here because <esc> seems to nullify it, regardless of whether we do - # preventDefault() - findModeAnchorNode = document.getSelection().anchorNode - result - -restoreDefaultSelectionHighlight = -> document.body.classList.remove("vimiumFindMode") + FindMode.saveQuery() focusFoundLink = -> - if (findModeQueryHasResults) + if (FindMode.query.hasResults) link = getLinkFromSelection() link.focus() if link selectFoundInputElement = -> - # if the found text is in an input element, getSelection().anchorNode will be null, so we use activeElement - # instead. however, since the last focused element might not be the one currently pointed to by find (e.g. - # the current one might be disabled and therefore unable to receive focus), we use the approximate - # heuristic of checking that the last anchor node is an ancestor of our element. - if (findModeQueryHasResults && document.activeElement && + # Since the last focused element might not be the one currently pointed to by find (e.g. the current one + # might be disabled and therefore unable to receive focus), we use the approximate heuristic of checking + # that the last anchor node is an ancestor of our element. + findModeAnchorNode = document.getSelection().anchorNode + if (FindMode.query.hasResults && document.activeElement && DomUtils.isSelectable(document.activeElement) && DomUtils.isDOMDescendant(findModeAnchorNode, document.activeElement)) DomUtils.simulateSelect(document.activeElement) -getNextQueryFromRegexMatches = (stepSize) -> - # find()ing an empty query always returns false - return "" unless findModeQuery.regexMatches - - totalMatches = findModeQuery.regexMatches.length - findModeQuery.activeRegexIndex += stepSize + totalMatches - findModeQuery.activeRegexIndex %= totalMatches - - findModeQuery.regexMatches[findModeQuery.activeRegexIndex] - -window.getFindModeQuery = (backwards) -> - # check if the query has been changed by a script in another frame - mostRecentQuery = FindModeHistory.getQuery() - if (mostRecentQuery != findModeQuery.rawQuery) - findModeQuery.rawQuery = mostRecentQuery - updateFindModeQuery() - - if findModeQuery.isRegex - getNextQueryFromRegexMatches(if backwards then -1 else 1) - else - findModeQuery.parsedQuery - findAndFocus = (backwards) -> Marks.setPreviousPosition() - query = getFindModeQuery backwards - - findModeQueryHasResults = - executeFind(query, { backwards: backwards, caseSensitive: !findModeQuery.ignoreCase }) + FindMode.query.hasResults = FindMode.execute null, {backwards} - if findModeQueryHasResults + if FindMode.query.hasResults focusFoundLink() - new PostFindMode() if findModeQueryHasResults + new PostFindMode() else - HUD.showForDuration("No matches for '" + findModeQuery.rawQuery + "'", 1000) + HUD.showForDuration("No matches for '#{FindMode.query.rawQuery}'", 1000) window.performFind = -> findAndFocus() @@ -992,43 +767,10 @@ window.goNext = -> nextStrings = nextPatterns.split(",").filter( (s) -> s.trim().length ) findAndFollowRel("next") || findAndFollowLink(nextStrings) -showFindModeHUDForQuery = -> - if findModeQuery.rawQuery and (findModeQueryHasResults || findModeQuery.parsedQuery.length == 0) - plural = if findModeQuery.matchCount == 1 then "" else "es" - HUD.show("/" + findModeQuery.rawQuery + " (" + findModeQuery.matchCount + " Match#{plural})") - else if findModeQuery.rawQuery - HUD.show("/" + findModeQuery.rawQuery + " (No Matches)") - else - HUD.show("/") - -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 - -findModeSaveSelection = -> - findModeInitialRange = getCurrentRange() - -findModeRestoreSelection = (range = findModeInitialRange) -> - selection = getSelection() - selection.removeAllRanges() - selection.addRange range - # Enters find mode. Returns the new find-mode instance. -window.enterFindMode = (options = {}) -> +window.enterFindMode = -> Marks.setPreviousPosition() - # Save the selection, so performFindInPlace can restore it. - findModeSaveSelection() - findModeQuery = rawQuery: "" - findMode = new FindMode options - HUD.show "/" - findMode + new FindMode() window.showHelpDialog = (html, fid) -> return if (isShowingHelpDialog || !document.body || fid != frameId) |
