Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / ui / SearchableView.js
blob15f46d94a46621513e22180f5836f252139b3287
1 /*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
4 * Copyright (C) 2009 Joseph Pecoraro
5 * Copyright (C) 2011 Google Inc. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 /**
33 * @constructor
34 * @extends {WebInspector.VBox}
35 * @param {!WebInspector.Searchable} searchable
36 * @param {string=} settingName
38 WebInspector.SearchableView = function(searchable, settingName)
40 WebInspector.VBox.call(this, true);
41 this.registerRequiredCSS("ui/searchableView.css");
43 this._searchProvider = searchable;
44 this._setting = settingName ? WebInspector.settings.createSetting(settingName, {}) : null;
46 this.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
48 this.contentElement.createChild("content");
49 this._footerElementContainer = this.contentElement.createChild("div", "search-bar hidden");
50 this._footerElementContainer.style.order = 100;
52 var toolbar = new WebInspector.Toolbar(this._footerElementContainer);
54 if (this._searchProvider.supportsCaseSensitiveSearch()) {
55 this._caseSensitiveButton = new WebInspector.ToolbarTextButton(WebInspector.UIString("Case sensitive"), "case-sensitive-search-toolbar-item", "Aa", 2);
56 this._caseSensitiveButton.addEventListener("click", this._toggleCaseSensitiveSearch, this);
57 toolbar.appendToolbarItem(this._caseSensitiveButton);
60 if (this._searchProvider.supportsRegexSearch()) {
61 this._regexButton = new WebInspector.ToolbarTextButton(WebInspector.UIString("Regex"), "regex-search-toolbar-item", ".*", 2);
62 this._regexButton.addEventListener("click", this._toggleRegexSearch, this);
63 toolbar.appendToolbarItem(this._regexButton);
66 this._footerElement = this._footerElementContainer.createChild("table", "toolbar-search");
67 this._footerElement.cellSpacing = 0;
69 this._firstRowElement = this._footerElement.createChild("tr");
70 this._secondRowElement = this._footerElement.createChild("tr", "hidden");
72 // Column 1
73 var searchControlElementColumn = this._firstRowElement.createChild("td");
74 this._searchControlElement = searchControlElementColumn.createChild("span", "toolbar-search-control");
76 this._searchInputElement = WebInspector.HistoryInput.create();
77 this._searchInputElement.classList.add("search-replace");
78 this._searchControlElement.appendChild(this._searchInputElement);
80 this._searchInputElement.id = "search-input-field";
81 this._searchInputElement.placeholder = WebInspector.UIString("Find");
83 this._matchesElement = this._searchControlElement.createChild("label", "search-results-matches");
84 this._matchesElement.setAttribute("for", "search-input-field");
86 this._searchNavigationElement = this._searchControlElement.createChild("div", "toolbar-search-navigation-controls");
88 this._searchNavigationPrevElement = this._searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-prev");
89 this._searchNavigationPrevElement.addEventListener("click", this._onPrevButtonSearch.bind(this), false);
90 this._searchNavigationPrevElement.title = WebInspector.UIString("Search Previous");
92 this._searchNavigationNextElement = this._searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-next");
93 this._searchNavigationNextElement.addEventListener("click", this._onNextButtonSearch.bind(this), false);
94 this._searchNavigationNextElement.title = WebInspector.UIString("Search Next");
96 this._searchInputElement.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected
97 this._searchInputElement.addEventListener("keydown", this._onSearchKeyDown.bind(this), true);
98 this._searchInputElement.addEventListener("input", this._onInput.bind(this), false);
100 this._replaceInputElement = this._secondRowElement.createChild("td").createChild("input", "search-replace toolbar-replace-control");
101 this._replaceInputElement.addEventListener("keydown", this._onReplaceKeyDown.bind(this), true);
102 this._replaceInputElement.placeholder = WebInspector.UIString("Replace");
104 // Column 2
105 this._findButtonElement = this._firstRowElement.createChild("td").createChild("button", "search-action-button hidden");
106 this._findButtonElement.textContent = WebInspector.UIString("Find");
107 this._findButtonElement.tabIndex = -1;
108 this._findButtonElement.addEventListener("click", this._onFindClick.bind(this), false);
110 this._replaceButtonElement = this._secondRowElement.createChild("td").createChild("button", "search-action-button");
111 this._replaceButtonElement.textContent = WebInspector.UIString("Replace");
112 this._replaceButtonElement.disabled = true;
113 this._replaceButtonElement.tabIndex = -1;
114 this._replaceButtonElement.addEventListener("click", this._replace.bind(this), false);
116 // Column 3
117 this._prevButtonElement = this._firstRowElement.createChild("td").createChild("button", "search-action-button hidden");
118 this._prevButtonElement.textContent = WebInspector.UIString("Previous");
119 this._prevButtonElement.tabIndex = -1;
120 this._prevButtonElement.addEventListener("click", this._onPreviousClick.bind(this), false);
122 this._replaceAllButtonElement = this._secondRowElement.createChild("td").createChild("button", "search-action-button");
123 this._replaceAllButtonElement.textContent = WebInspector.UIString("Replace All");
124 this._replaceAllButtonElement.addEventListener("click", this._replaceAll.bind(this), false);
126 // Column 4
127 this._replaceElement = this._firstRowElement.createChild("td").createChild("span");
129 this._replaceLabelElement = createCheckboxLabel(WebInspector.UIString("Replace"));
130 this._replaceCheckboxElement = this._replaceLabelElement.checkboxElement;
131 this._uniqueId = ++WebInspector.SearchableView._lastUniqueId;
132 var replaceCheckboxId = "search-replace-trigger" + this._uniqueId;
133 this._replaceCheckboxElement.id = replaceCheckboxId;
134 this._replaceCheckboxElement.addEventListener("change", this._updateSecondRowVisibility.bind(this), false);
136 this._replaceElement.appendChild(this._replaceLabelElement);
138 // Column 5
139 var cancelButtonElement = this._firstRowElement.createChild("td").createChild("button", "search-action-button");
140 cancelButtonElement.textContent = WebInspector.UIString("Cancel");
141 cancelButtonElement.tabIndex = -1;
142 cancelButtonElement.addEventListener("click", this.closeSearch.bind(this), false);
143 this._minimalSearchQuerySize = 3;
145 this._registerShortcuts();
146 this._loadSetting();
149 WebInspector.SearchableView._lastUniqueId = 0;
152 * @return {!Array.<!WebInspector.KeyboardShortcut.Descriptor>}
154 WebInspector.SearchableView.findShortcuts = function()
156 if (WebInspector.SearchableView._findShortcuts)
157 return WebInspector.SearchableView._findShortcuts;
158 WebInspector.SearchableView._findShortcuts = [WebInspector.KeyboardShortcut.makeDescriptor("f", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)];
159 if (!WebInspector.isMac())
160 WebInspector.SearchableView._findShortcuts.push(WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F3));
161 return WebInspector.SearchableView._findShortcuts;
165 * @return {!Array.<!WebInspector.KeyboardShortcut.Descriptor>}
167 WebInspector.SearchableView.cancelSearchShortcuts = function()
169 if (WebInspector.SearchableView._cancelSearchShortcuts)
170 return WebInspector.SearchableView._cancelSearchShortcuts;
171 WebInspector.SearchableView._cancelSearchShortcuts = [WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Esc)];
172 return WebInspector.SearchableView._cancelSearchShortcuts;
176 * @return {!Array.<!WebInspector.KeyboardShortcut.Descriptor>}
178 WebInspector.SearchableView.findNextShortcut = function()
180 if (WebInspector.SearchableView._findNextShortcut)
181 return WebInspector.SearchableView._findNextShortcut;
182 WebInspector.SearchableView._findNextShortcut = [];
183 if (WebInspector.isMac())
184 WebInspector.SearchableView._findNextShortcut.push(WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Meta));
185 return WebInspector.SearchableView._findNextShortcut;
189 * @return {!Array.<!WebInspector.KeyboardShortcut.Descriptor>}
191 WebInspector.SearchableView.findPreviousShortcuts = function()
193 if (WebInspector.SearchableView._findPreviousShortcuts)
194 return WebInspector.SearchableView._findPreviousShortcuts;
195 WebInspector.SearchableView._findPreviousShortcuts = [];
196 if (WebInspector.isMac())
197 WebInspector.SearchableView._findPreviousShortcuts.push(WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Meta | WebInspector.KeyboardShortcut.Modifiers.Shift));
198 return WebInspector.SearchableView._findPreviousShortcuts;
201 WebInspector.SearchableView.prototype = {
202 _toggleCaseSensitiveSearch: function()
204 this._caseSensitiveButton.setToggled(!this._caseSensitiveButton.toggled());
205 this._saveSetting();
206 this._performSearch(false, true);
209 _toggleRegexSearch: function()
211 this._regexButton.setToggled(!this._regexButton.toggled());
212 this._saveSetting();
213 this._performSearch(false, true);
216 _saveSetting: function()
218 if (!this._setting)
219 return;
220 var settingValue = this._setting.get() || {};
221 settingValue.caseSensitive = this._caseSensitiveButton.toggled();
222 settingValue.isRegex = this._regexButton.toggled();
223 this._setting.set(settingValue);
226 _loadSetting: function()
228 var settingValue = this._setting ? (this._setting.get() || {}) : {};
229 if (this._searchProvider.supportsCaseSensitiveSearch())
230 this._caseSensitiveButton.setToggled(!!settingValue.caseSensitive);
231 if (this._searchProvider.supportsRegexSearch())
232 this._regexButton.setToggled(!!settingValue.isRegex);
236 * @override
237 * @return {!Element}
239 defaultFocusedElement: function()
241 var children = this.children();
242 for (var i = 0; i < children.length; ++i) {
243 var element = children[i].defaultFocusedElement();
244 if (element)
245 return element;
247 return WebInspector.Widget.prototype.defaultFocusedElement.call(this);
251 * @param {!Event} event
253 _onKeyDown: function(event)
255 var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(/**@type {!KeyboardEvent}*/(event));
256 var handler = this._shortcuts[shortcutKey];
257 if (handler && handler(event))
258 event.consume(true);
261 _registerShortcuts: function()
263 this._shortcuts = {};
266 * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
267 * @param {function()} handler
268 * @this {WebInspector.SearchableView}
270 function register(shortcuts, handler)
272 for (var i = 0; i < shortcuts.length; ++i)
273 this._shortcuts[shortcuts[i].key] = handler;
276 register.call(this, WebInspector.SearchableView.findShortcuts(), this.handleFindShortcut.bind(this));
277 register.call(this, WebInspector.SearchableView.cancelSearchShortcuts(), this.handleCancelSearchShortcut.bind(this));
278 register.call(this, WebInspector.SearchableView.findNextShortcut(), this.handleFindNextShortcut.bind(this));
279 register.call(this, WebInspector.SearchableView.findPreviousShortcuts(), this.handleFindPreviousShortcut.bind(this));
283 * @param {number} minimalSearchQuerySize
285 setMinimalSearchQuerySize: function(minimalSearchQuerySize)
287 this._minimalSearchQuerySize = minimalSearchQuerySize;
291 * @param {string} placeholder
293 setPlaceholder: function(placeholder)
295 this._searchInputElement.placeholder = placeholder;
299 * @param {boolean} replaceable
301 setReplaceable: function(replaceable)
303 this._replaceable = replaceable;
307 * @param {number} matches
309 updateSearchMatchesCount: function(matches)
311 this._searchProvider.currentSearchMatches = matches;
312 this._updateSearchMatchesCountAndCurrentMatchIndex(this._searchProvider.currentQuery ? matches : 0, -1);
316 * @param {number} currentMatchIndex
318 updateCurrentMatchIndex: function(currentMatchIndex)
320 this._updateSearchMatchesCountAndCurrentMatchIndex(this._searchProvider.currentSearchMatches, currentMatchIndex);
324 * @return {boolean}
326 isSearchVisible: function()
328 return this._searchIsVisible;
331 closeSearch: function()
333 this.cancelSearch();
334 if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().isDescendant(this._footerElementContainer))
335 this.focus();
338 _toggleSearchBar: function(toggled)
340 this._footerElementContainer.classList.toggle("hidden", !toggled);
341 this.doResize();
344 cancelSearch: function()
346 if (!this._searchIsVisible)
347 return;
348 this.resetSearch();
349 delete this._searchIsVisible;
350 this._toggleSearchBar(false);
353 resetSearch: function()
355 this._clearSearch();
356 this._updateReplaceVisibility();
357 this._matchesElement.textContent = "";
360 refreshSearch: function()
362 if (!this._searchIsVisible)
363 return;
364 this.resetSearch();
365 this._performSearch(false, false);
369 * @return {boolean}
371 handleFindNextShortcut: function()
373 if (!this._searchIsVisible)
374 return false;
375 this._searchProvider.jumpToNextSearchResult();
376 return true;
380 * @return {boolean}
382 handleFindPreviousShortcut: function()
384 if (!this._searchIsVisible)
385 return false;
386 this._searchProvider.jumpToPreviousSearchResult();
387 return true;
391 * @return {boolean}
393 handleFindShortcut: function()
395 this.showSearchField();
396 return true;
400 * @return {boolean}
402 handleCancelSearchShortcut: function()
404 if (!this._searchIsVisible)
405 return false;
406 this.closeSearch();
407 return true;
411 * @param {boolean} enabled
413 _updateSearchNavigationButtonState: function(enabled)
415 this._replaceButtonElement.disabled = !enabled;
416 if (enabled) {
417 this._searchNavigationPrevElement.classList.add("enabled");
418 this._searchNavigationNextElement.classList.add("enabled");
419 } else {
420 this._searchNavigationPrevElement.classList.remove("enabled");
421 this._searchNavigationNextElement.classList.remove("enabled");
426 * @param {number} matches
427 * @param {number} currentMatchIndex
429 _updateSearchMatchesCountAndCurrentMatchIndex: function(matches, currentMatchIndex)
431 if (!this._currentQuery)
432 this._matchesElement.textContent = "";
433 else if (matches === 0 || currentMatchIndex >= 0)
434 this._matchesElement.textContent = WebInspector.UIString("%d of %d", currentMatchIndex + 1, matches);
435 else if (matches === 1)
436 this._matchesElement.textContent = WebInspector.UIString("1 match");
437 else
438 this._matchesElement.textContent = WebInspector.UIString("%d matches", matches);
439 this._updateSearchNavigationButtonState(matches > 0);
442 showSearchField: function()
444 if (this._searchIsVisible)
445 this.cancelSearch();
447 var queryCandidate;
448 if (WebInspector.currentFocusElement() !== this._searchInputElement) {
449 var selection = this._searchInputElement.getComponentSelection();
450 if (selection.rangeCount)
451 queryCandidate = selection.toString().replace(/\r?\n.*/, "");
454 this._toggleSearchBar(true);
455 this._updateReplaceVisibility();
456 if (queryCandidate)
457 this._searchInputElement.value = queryCandidate;
458 this._performSearch(false, false);
459 this._searchInputElement.focus();
460 this._searchInputElement.select();
461 this._searchIsVisible = true;
464 _updateReplaceVisibility: function()
466 this._replaceElement.classList.toggle("hidden", !this._replaceable);
467 if (!this._replaceable) {
468 this._replaceCheckboxElement.checked = false;
469 this._updateSecondRowVisibility();
474 * @param {!Event} event
476 _onSearchFieldManualFocus: function(event)
478 WebInspector.setCurrentFocusElement(/** @type {?Node} */ (event.target));
482 * @param {!Event} event
484 _onSearchKeyDown: function(event)
486 if (!isEnterKey(event))
487 return;
489 if (!this._currentQuery)
490 this._performSearch(true, true, event.shiftKey);
491 else
492 this._jumpToNextSearchResult(event.shiftKey);
496 * @param {!Event} event
498 _onReplaceKeyDown: function(event)
500 if (isEnterKey(event))
501 this._replace();
505 * @param {boolean=} isBackwardSearch
507 _jumpToNextSearchResult: function(isBackwardSearch)
509 if (!this._currentQuery || !this._searchNavigationPrevElement.classList.contains("enabled"))
510 return;
512 if (isBackwardSearch)
513 this._searchProvider.jumpToPreviousSearchResult();
514 else
515 this._searchProvider.jumpToNextSearchResult();
518 _onNextButtonSearch: function(event)
520 if (!this._searchNavigationNextElement.classList.contains("enabled"))
521 return;
522 this._jumpToNextSearchResult();
523 this._searchInputElement.focus();
526 _onPrevButtonSearch: function(event)
528 if (!this._searchNavigationPrevElement.classList.contains("enabled"))
529 return;
530 this._jumpToNextSearchResult(true);
531 this._searchInputElement.focus();
534 _onFindClick: function(event)
536 if (!this._currentQuery)
537 this._performSearch(true, true);
538 else
539 this._jumpToNextSearchResult();
540 this._searchInputElement.focus();
543 _onPreviousClick: function(event)
545 if (!this._currentQuery)
546 this._performSearch(true, true, true);
547 else
548 this._jumpToNextSearchResult(true);
549 this._searchInputElement.focus();
552 _clearSearch: function()
554 delete this._currentQuery;
555 if (!!this._searchProvider.currentQuery) {
556 delete this._searchProvider.currentQuery;
557 this._searchProvider.searchCanceled();
559 this._updateSearchMatchesCountAndCurrentMatchIndex(0, -1);
563 * @param {boolean} forceSearch
564 * @param {boolean} shouldJump
565 * @param {boolean=} jumpBackwards
567 _performSearch: function(forceSearch, shouldJump, jumpBackwards)
569 var query = this._searchInputElement.value;
570 if (!query || (!forceSearch && query.length < this._minimalSearchQuerySize && !this._currentQuery)) {
571 this._clearSearch();
572 return;
575 this._currentQuery = query;
576 this._searchProvider.currentQuery = query;
578 var searchConfig = this._currentSearchConfig();
579 this._searchProvider.performSearch(searchConfig, shouldJump, jumpBackwards);
583 * @return {!WebInspector.SearchableView.SearchConfig}
585 _currentSearchConfig: function()
587 var query = this._searchInputElement.value;
588 var caseSensitive = this._caseSensitiveButton ? this._caseSensitiveButton.toggled() : false;
589 var isRegex = this._regexButton ? this._regexButton.toggled() : false;
590 return new WebInspector.SearchableView.SearchConfig(query, caseSensitive, isRegex);
593 _updateSecondRowVisibility: function()
595 var secondRowVisible = this._replaceCheckboxElement.checked;
596 this._footerElementContainer.classList.toggle("replaceable", secondRowVisible);
597 this._footerElement.classList.toggle("toolbar-search-replace", secondRowVisible);
598 this._secondRowElement.classList.toggle("hidden", !secondRowVisible);
599 this._prevButtonElement.classList.toggle("hidden", !secondRowVisible);
600 this._findButtonElement.classList.toggle("hidden", !secondRowVisible);
601 this._replaceCheckboxElement.tabIndex = secondRowVisible ? -1 : 0;
603 if (secondRowVisible)
604 this._replaceInputElement.focus();
605 else
606 this._searchInputElement.focus();
607 this.doResize();
610 _replace: function()
612 var searchConfig = this._currentSearchConfig();
613 /** @type {!WebInspector.Replaceable} */ (this._searchProvider).replaceSelectionWith(searchConfig, this._replaceInputElement.value);
614 delete this._currentQuery;
615 this._performSearch(true, true);
618 _replaceAll: function()
620 var searchConfig = this._currentSearchConfig();
621 /** @type {!WebInspector.Replaceable} */ (this._searchProvider).replaceAllWith(searchConfig, this._replaceInputElement.value);
625 * @param {!Event} event
627 _onInput: function(event)
629 if (this._valueChangedTimeoutId)
630 clearTimeout(this._valueChangedTimeoutId);
631 var timeout = this._searchInputElement.value.length < 3 ? 200 : 0;
632 this._valueChangedTimeoutId = setTimeout(this._onValueChanged.bind(this), timeout);
635 _onValueChanged: function()
637 delete this._valueChangedTimeoutId;
638 this._performSearch(false, true);
641 __proto__: WebInspector.VBox.prototype
645 * @interface
647 WebInspector.Searchable = function()
651 WebInspector.Searchable.prototype = {
652 searchCanceled: function() { },
655 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
656 * @param {boolean} shouldJump
657 * @param {boolean=} jumpBackwards
659 performSearch: function(searchConfig, shouldJump, jumpBackwards) { },
661 jumpToNextSearchResult: function() { },
663 jumpToPreviousSearchResult: function() { },
666 * @return {boolean}
668 supportsCaseSensitiveSearch: function() { },
671 * @return {boolean}
673 supportsRegexSearch: function() { }
677 * @interface
679 WebInspector.Replaceable = function()
683 WebInspector.Replaceable.prototype = {
685 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
686 * @param {string} replacement
688 replaceSelectionWith: function(searchConfig, replacement) { },
691 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
692 * @param {string} replacement
694 replaceAllWith: function(searchConfig, replacement) { }
698 * @constructor
699 * @param {string} query
700 * @param {boolean} caseSensitive
701 * @param {boolean} isRegex
703 WebInspector.SearchableView.SearchConfig = function(query, caseSensitive, isRegex)
705 this.query = query;
706 this.caseSensitive = caseSensitive;
707 this.isRegex = isRegex;
710 WebInspector.SearchableView.SearchConfig.prototype = {
712 * @param {boolean=} global
713 * @return {!RegExp}
715 toSearchRegex: function(global)
717 var modifiers = this.caseSensitive ? "" : "i";
718 if (global)
719 modifiers += "g";
720 var query = this.isRegex ? "/" + this.query + "/" : this.query;
722 var regex;
724 // First try creating regex if user knows the / / hint.
725 try {
726 if (/^\/.+\/$/.test(query)) {
727 regex = new RegExp(query.substring(1, query.length - 1), modifiers);
728 regex.__fromRegExpQuery = true;
730 } catch (e) {
731 // Silent catch.
734 // Otherwise just do a plain text search.
735 if (!regex)
736 regex = createPlainTextSearchRegex(query, modifiers);
738 return regex;