Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / FilteredItemSelectionDialog.js
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.DialogDelegate}
34  * @implements {WebInspector.ViewportControl.Provider}
35  * @param {!WebInspector.SelectionDialogContentProvider} delegate
36  */
37 WebInspector.FilteredItemSelectionDialog = function(delegate)
38 {
39     WebInspector.DialogDelegate.call(this);
40
41     if (!WebInspector.FilteredItemSelectionDialog._stylesLoaded) {
42         WebInspector.View.createStyleElement("filteredItemSelectionDialog.css");
43         WebInspector.FilteredItemSelectionDialog._stylesLoaded = true;
44     }
45
46     this.element = document.createElement("div");
47     this.element.className = "filtered-item-list-dialog";
48     this.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
49
50     this._promptElement = this.element.createChild("input", "monospace");
51     this._promptElement.addEventListener("input", this._onInput.bind(this), false);
52     this._promptElement.type = "text";
53     this._promptElement.setAttribute("spellcheck", "false");
54
55     this._filteredItems = [];
56     this._viewportControl = new WebInspector.ViewportControl(this);
57     this._viewportControl.element.classList.add("fill");
58     this._itemElementsContainer = this._viewportControl.element;
59     this._itemElementsContainer.classList.add("container");
60     this._itemElementsContainer.classList.add("monospace");
61     this._itemElementsContainer.addEventListener("click", this._onClick.bind(this), false);
62     this.element.appendChild(this._itemElementsContainer);
63
64     this._delegate = delegate;
65     this._delegate.setRefreshCallback(this._itemsLoaded.bind(this));
66     this._itemsLoaded();
67 }
68
69 WebInspector.FilteredItemSelectionDialog.prototype = {
70     /**
71      * @param {!Element} element
72      * @param {!Element} relativeToElement
73      */
74     position: function(element, relativeToElement)
75     {
76         const shadow = 10;
77         const shadowPadding = 20; // shadow + padding
78         var container = WebInspector.Dialog.modalHostView().element;
79         var preferredWidth = Math.max(relativeToElement.offsetWidth * 2 / 3, 500);
80         var width = Math.min(preferredWidth, container.offsetWidth - 2 * shadowPadding);
81         var preferredHeight = Math.max(relativeToElement.offsetHeight * 2 / 3, 204);
82         var height = Math.min(preferredHeight, container.offsetHeight - 2 * shadowPadding);
83
84         this.element.style.width = width + "px";
85         var box = relativeToElement.boxInWindow(window).relativeToElement(container);
86         var positionX = box.x + Math.max((box.width - width - 2 * shadowPadding) / 2, shadow);
87         positionX = Math.max(shadow, Math.min(container.offsetWidth - width - 2 * shadowPadding, positionX));
88         var positionY = box.y + Math.max((box.height - height - 2 * shadowPadding) / 2, shadow);
89         positionY = Math.max(shadow, Math.min(container.offsetHeight - height - 2 * shadowPadding, positionY));
90         element.positionAt(positionX, positionY, container);
91         this._dialogHeight = height;
92
93         this._updateShowMatchingItems();
94     },
95
96     focus: function()
97     {
98         WebInspector.setCurrentFocusElement(this._promptElement);
99         if (this._filteredItems.length && this._viewportControl.lastVisibleIndex() === -1)
100             this._viewportControl.refresh();
101     },
102
103     willHide: function()
104     {
105         if (this._isHiding)
106             return;
107         this._isHiding = true;
108         this._delegate.dispose();
109         if (this._filterTimer)
110             clearTimeout(this._filterTimer);
111     },
112
113     renderAsTwoRows: function()
114     {
115         this._renderAsTwoRows = true;
116     },
117
118     onEnter: function()
119     {
120         if (!this._delegate.itemCount())
121             return;
122         var selectedIndex = this._shouldShowMatchingItems() && this._selectedIndexInFiltered < this._filteredItems.length ? this._filteredItems[this._selectedIndexInFiltered] : null;
123         this._delegate.selectItem(selectedIndex, this._promptElement.value.trim());
124     },
125
126     _itemsLoaded: function()
127     {
128
129         if (this._loadTimeout)
130             return;
131         this._loadTimeout = setTimeout(this._updateAfterItemsLoaded.bind(this), 0);
132     },
133
134     _updateAfterItemsLoaded: function()
135     {
136         delete this._loadTimeout;
137         this._filterItems();
138     },
139
140     /**
141      * @param {number} index
142      * @return {!Element}
143      */
144     _createItemElement: function(index)
145     {
146         var itemElement = document.createElement("div");
147         itemElement.className = "filtered-item-list-dialog-item " + (this._renderAsTwoRows ? "two-rows" : "one-row");
148         itemElement._titleElement = itemElement.createChild("div", "filtered-item-list-dialog-title");
149         itemElement._subtitleElement = itemElement.createChild("div", "filtered-item-list-dialog-subtitle");
150         itemElement._subtitleElement.textContent = "\u200B";
151         itemElement._index = index;
152         this._delegate.renderItem(index, this._promptElement.value.trim(), itemElement._titleElement, itemElement._subtitleElement);
153         return itemElement;
154     },
155
156     /**
157      * @param {string} query
158      */
159     setQuery: function(query)
160     {
161         this._promptElement.value = query;
162         this._scheduleFilter();
163     },
164
165     _filterItems: function()
166     {
167         delete this._filterTimer;
168         if (this._scoringTimer) {
169             clearTimeout(this._scoringTimer);
170             delete this._scoringTimer;
171         }
172
173         var query = this._delegate.rewriteQuery(this._promptElement.value.trim());
174         this._query = query;
175         var queryLength = query.length;
176         var filterRegex = query ? WebInspector.FilePathScoreFunction.filterRegex(query) : null;
177
178         var oldSelectedAbsoluteIndex = this._selectedIndexInFiltered ? this._filteredItems[this._selectedIndexInFiltered] : null;
179         var filteredItems = [];
180         this._selectedIndexInFiltered = 0;
181
182         var bestScores = [];
183         var bestItems = [];
184         var bestItemsToCollect = 100;
185         var minBestScore = 0;
186         var overflowItems = [];
187
188         scoreItems.call(this, 0);
189
190         /**
191          * @param {number} a
192          * @param {number} b
193          * @return {number}
194          */
195         function compareIntegers(a, b)
196         {
197             return b - a;
198         }
199
200         /**
201          * @param {number} fromIndex
202          * @this {WebInspector.FilteredItemSelectionDialog}
203          */
204         function scoreItems(fromIndex)
205         {
206             var maxWorkItems = 1000;
207             var workDone = 0;
208             for (var i = fromIndex; i < this._delegate.itemCount() && workDone < maxWorkItems; ++i) {
209                 // Filter out non-matching items quickly.
210                 if (filterRegex && !filterRegex.test(this._delegate.itemKeyAt(i)))
211                     continue;
212
213                 // Score item.
214                 var score = this._delegate.itemScoreAt(i, query);
215                 if (query)
216                     workDone++;
217
218                 // Find its index in the scores array (earlier elements have bigger scores).
219                 if (score > minBestScore || bestScores.length < bestItemsToCollect) {
220                     var index = insertionIndexForObjectInListSortedByFunction(score, bestScores, compareIntegers, true);
221                     bestScores.splice(index, 0, score);
222                     bestItems.splice(index, 0, i);
223                     if (bestScores.length > bestItemsToCollect) {
224                         // Best list is too large -> drop last elements.
225                         overflowItems.push(bestItems.peekLast());
226                         bestScores.length = bestItemsToCollect;
227                         bestItems.length = bestItemsToCollect;
228                     }
229                     minBestScore = bestScores.peekLast();
230                 } else
231                     filteredItems.push(i);
232             }
233
234             // Process everything in chunks.
235             if (i < this._delegate.itemCount()) {
236                 this._scoringTimer = setTimeout(scoreItems.bind(this, i), 0);
237                 return;
238             }
239             delete this._scoringTimer;
240
241             this._filteredItems = bestItems.concat(overflowItems).concat(filteredItems);
242             for (var i = 0; i < this._filteredItems.length; ++i) {
243                 if (this._filteredItems[i] === oldSelectedAbsoluteIndex) {
244                     this._selectedIndexInFiltered = i;
245                     break;
246                 }
247             }
248             this._viewportControl.invalidate();
249             if (!query)
250                 this._selectedIndexInFiltered = 0;
251             this._updateSelection(this._selectedIndexInFiltered, false);
252         }
253     },
254
255     /**
256      * @return {boolean}
257      */
258     _shouldShowMatchingItems: function()
259     {
260         return this._delegate.shouldShowMatchingItems(this._promptElement.value);
261     },
262
263     _onInput: function(event)
264     {
265         this._updateShowMatchingItems();
266         this._scheduleFilter();
267     },
268
269     _updateShowMatchingItems: function()
270     {
271         var shouldShowMatchingItems = this._shouldShowMatchingItems();
272         this._itemElementsContainer.classList.toggle("hidden", !shouldShowMatchingItems);
273         this.element.style.height = shouldShowMatchingItems ? this._dialogHeight + "px" : "auto";
274     },
275
276     /**
277      * @return {number}
278      */
279     _rowsPerViewport: function()
280     {
281         return Math.floor(this._viewportControl.element.clientHeight / this._rowHeight);
282     },
283
284     _onKeyDown: function(event)
285     {
286         var newSelectedIndex = this._selectedIndexInFiltered;
287
288         switch (event.keyCode) {
289         case WebInspector.KeyboardShortcut.Keys.Down.code:
290             if (++newSelectedIndex >= this._filteredItems.length)
291                 newSelectedIndex = this._filteredItems.length - 1;
292             this._updateSelection(newSelectedIndex, true);
293             event.consume(true);
294             break;
295         case WebInspector.KeyboardShortcut.Keys.Up.code:
296             if (--newSelectedIndex < 0)
297                 newSelectedIndex = 0;
298             this._updateSelection(newSelectedIndex, false);
299             event.consume(true);
300             break;
301         case WebInspector.KeyboardShortcut.Keys.PageDown.code:
302             newSelectedIndex = Math.min(newSelectedIndex + this._rowsPerViewport(), this._filteredItems.length - 1);
303             this._updateSelection(newSelectedIndex, true);
304             event.consume(true);
305             break;
306         case WebInspector.KeyboardShortcut.Keys.PageUp.code:
307             newSelectedIndex = Math.max(newSelectedIndex - this._rowsPerViewport(), 0);
308             this._updateSelection(newSelectedIndex, false);
309             event.consume(true);
310             break;
311         default:
312         }
313     },
314
315     _scheduleFilter: function()
316     {
317         if (this._filterTimer)
318             return;
319         this._filterTimer = setTimeout(this._filterItems.bind(this), 0);
320     },
321
322     /**
323      * @param {number} index
324      * @param {boolean} makeLast
325      */
326     _updateSelection: function(index, makeLast)
327     {
328         var element = this._viewportControl.renderedElementAt(this._selectedIndexInFiltered);
329         if (element)
330             element.classList.remove("selected");
331         this._viewportControl.scrollItemIntoView(index, makeLast);
332         this._selectedIndexInFiltered = index;
333         element = this._viewportControl.renderedElementAt(index);
334         if (element)
335             element.classList.add("selected");
336     },
337
338     _onClick: function(event)
339     {
340         var itemElement = event.target.enclosingNodeOrSelfWithClass("filtered-item-list-dialog-item");
341         if (!itemElement)
342             return;
343         this._delegate.selectItem(itemElement._index, this._promptElement.value.trim());
344         WebInspector.Dialog.hide();
345     },
346
347     /**
348      * @return {number}
349      */
350     itemCount: function()
351     {
352         return this._filteredItems.length;
353     },
354
355     /**
356      * @param {number} index
357      * @return {number}
358      */
359     fastHeight: function(index)
360     {
361         if (!this._rowHeight) {
362             var delegateIndex = this._filteredItems[index];
363             var element = this._createItemElement(delegateIndex);
364             this._rowHeight = element.measurePreferredSize(this._viewportControl.contentElement()).height;
365         }
366         return this._rowHeight;
367     },
368
369     /**
370      * @param {number} index
371      * @return {!WebInspector.ViewportElement}
372      */
373     itemElement: function(index)
374     {
375         var delegateIndex = this._filteredItems[index];
376         var element = this._createItemElement(delegateIndex);
377         if (index === this._selectedIndexInFiltered)
378             element.classList.add("selected");
379         return new WebInspector.StaticViewportElement(element);
380     },
381
382     /**
383      * @return {number}
384      */
385     minimumRowHeight: function()
386     {
387         return this.fastHeight(0);
388     },
389
390     __proto__: WebInspector.DialogDelegate.prototype
391 }
392
393 /**
394  * @constructor
395  */
396 WebInspector.SelectionDialogContentProvider = function()
397 {
398 }
399
400 WebInspector.SelectionDialogContentProvider.prototype = {
401     /**
402      * @param {function():void} refreshCallback
403      */
404     setRefreshCallback: function(refreshCallback)
405     {
406         this._refreshCallback = refreshCallback;
407     },
408
409     /**
410      * @param {string} query
411      * @return {boolean}
412      */
413     shouldShowMatchingItems: function(query)
414     {
415         return true;
416     },
417
418     /**
419      * @return {number}
420      */
421     itemCount: function()
422     {
423         return 0;
424     },
425
426     /**
427      * @param {number} itemIndex
428      * @return {string}
429      */
430     itemKeyAt: function(itemIndex)
431     {
432         return "";
433     },
434
435     /**
436      * @param {number} itemIndex
437      * @param {string} query
438      * @return {number}
439      */
440     itemScoreAt: function(itemIndex, query)
441     {
442         return 1;
443     },
444
445     /**
446      * @param {number} itemIndex
447      * @param {string} query
448      * @param {!Element} titleElement
449      * @param {!Element} subtitleElement
450      */
451     renderItem: function(itemIndex, query, titleElement, subtitleElement)
452     {
453     },
454
455     /**
456      * @param {!Element} element
457      * @param {string} query
458      * @return {boolean}
459      */
460     highlightRanges: function(element, query)
461     {
462         if (!query)
463             return false;
464
465         /**
466          * @param {string} text
467          * @param {string} query
468          * @return {?Array.<!WebInspector.SourceRange>}
469          */
470         function rangesForMatch(text, query)
471         {
472             var sm = new difflib.SequenceMatcher(query, text);
473             var opcodes = sm.get_opcodes();
474             var ranges = [];
475
476             for (var i = 0; i < opcodes.length; ++i) {
477                 var opcode = opcodes[i];
478                 if (opcode[0] === "equal")
479                     ranges.push(new WebInspector.SourceRange(opcode[3], opcode[4] - opcode[3]));
480                 else if (opcode[0] !== "insert")
481                     return null;
482             }
483             return ranges;
484         }
485
486         var text = element.textContent;
487         var ranges = rangesForMatch(text, query);
488         if (!ranges)
489             ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase());
490         if (ranges) {
491             WebInspector.highlightRangesWithStyleClass(element, ranges, "highlight");
492             return true;
493         }
494         return false;
495     },
496
497     /**
498      * @param {?number} itemIndex
499      * @param {string} promptValue
500      */
501     selectItem: function(itemIndex, promptValue)
502     {
503     },
504
505     refresh: function()
506     {
507         this._refreshCallback();
508     },
509
510     /**
511      * @param {string} query
512      * @return {string}
513      */
514     rewriteQuery: function(query)
515     {
516         return query;
517     },
518
519     dispose: function()
520     {
521     }
522 }
523
524 /**
525  * @constructor
526  * @extends {WebInspector.SelectionDialogContentProvider}
527  * @param {!WebInspector.UISourceCode} uiSourceCode
528  * @param {function(number, number)} selectItemCallback
529  */
530 WebInspector.JavaScriptOutlineDialog = function(uiSourceCode, selectItemCallback)
531 {
532     WebInspector.SelectionDialogContentProvider.call(this);
533
534     this._functionItems = [];
535     this._selectItemCallback = selectItemCallback;
536     this._outlineWorker = new Worker("script_formatter_worker/ScriptFormatterWorker.js");
537     this._outlineWorker.onmessage = this._didBuildOutlineChunk.bind(this);
538     this._outlineWorker.postMessage({ method: "javaScriptOutline", params: { content: uiSourceCode.workingCopy() } });
539 }
540
541 /**
542  * @param {!WebInspector.View} view
543  * @param {!WebInspector.UISourceCode} uiSourceCode
544  * @param {function(number, number)} selectItemCallback
545  */
546 WebInspector.JavaScriptOutlineDialog.show = function(view, uiSourceCode, selectItemCallback)
547 {
548     if (WebInspector.Dialog.currentInstance())
549         return;
550     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.JavaScriptOutlineDialog(uiSourceCode, selectItemCallback));
551     WebInspector.Dialog.show(view.element, filteredItemSelectionDialog);
552 }
553
554 WebInspector.JavaScriptOutlineDialog.prototype = {
555     /**
556      * @param {!MessageEvent} event
557      */
558     _didBuildOutlineChunk: function(event)
559     {
560         var data = /** @type {!WebInspector.JavaScriptOutlineDialog.MessageEventData} */ (event.data);
561         var chunk = data.chunk;
562         for (var i = 0; i < chunk.length; ++i)
563             this._functionItems.push(chunk[i]);
564
565         if (data.total === data.index + 1)
566             this.dispose();
567
568         this.refresh();
569     },
570
571     /**
572      * @return {number}
573      */
574     itemCount: function()
575     {
576         return this._functionItems.length;
577     },
578
579     /**
580      * @param {number} itemIndex
581      * @return {string}
582      */
583     itemKeyAt: function(itemIndex)
584     {
585         return this._functionItems[itemIndex].name;
586     },
587
588     /**
589      * @param {number} itemIndex
590      * @param {string} query
591      * @return {number}
592      */
593     itemScoreAt: function(itemIndex, query)
594     {
595         var item = this._functionItems[itemIndex];
596         return -item.line;
597     },
598
599     /**
600      * @param {number} itemIndex
601      * @param {string} query
602      * @param {!Element} titleElement
603      * @param {!Element} subtitleElement
604      */
605     renderItem: function(itemIndex, query, titleElement, subtitleElement)
606     {
607         var item = this._functionItems[itemIndex];
608         titleElement.textContent = item.name + (item.arguments ? item.arguments : "");
609         this.highlightRanges(titleElement, query);
610         subtitleElement.textContent = ":" + (item.line + 1);
611     },
612
613     /**
614      * @param {?number} itemIndex
615      * @param {string} promptValue
616      */
617     selectItem: function(itemIndex, promptValue)
618     {
619         if (itemIndex === null)
620             return;
621         var lineNumber = this._functionItems[itemIndex].line;
622         if (!isNaN(lineNumber) && lineNumber >= 0)
623             this._selectItemCallback(lineNumber, this._functionItems[itemIndex].column);
624     },
625
626     dispose: function()
627     {
628         if (this._outlineWorker) {
629             this._outlineWorker.terminate();
630             delete this._outlineWorker;
631         }
632     },
633
634     __proto__: WebInspector.SelectionDialogContentProvider.prototype
635 }
636
637 /**
638  * @constructor
639  * @extends {WebInspector.SelectionDialogContentProvider}
640  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
641  */
642 WebInspector.SelectUISourceCodeDialog = function(defaultScores)
643 {
644     WebInspector.SelectionDialogContentProvider.call(this);
645
646     this._populate();
647     this._defaultScores = defaultScores;
648     this._scorer = new WebInspector.FilePathScoreFunction("");
649     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
650     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
651 }
652
653 WebInspector.SelectUISourceCodeDialog.prototype = {
654     _projectRemoved: function(event)
655     {
656         var project = /** @type {!WebInspector.Project} */ (event.data);
657         this._populate(project);
658         this.refresh();
659     },
660
661     /**
662      * @param {!WebInspector.Project=} skipProject
663      */
664     _populate: function(skipProject)
665     {
666         /** @type {!Array.<!WebInspector.UISourceCode>} */
667         this._uiSourceCodes = [];
668         var projects = WebInspector.workspace.projects().filter(this.filterProject.bind(this));
669         for (var i = 0; i < projects.length; ++i) {
670             if (skipProject && projects[i] === skipProject)
671                 continue;
672             this._uiSourceCodes = this._uiSourceCodes.concat(projects[i].uiSourceCodes());
673         }
674     },
675
676     /**
677      * @param {?WebInspector.UISourceCode} uiSourceCode
678      * @param {number=} lineNumber
679      * @param {number=} columnNumber
680      */
681     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
682     {
683         // Overridden by subclasses
684     },
685
686     /**
687      * @param {!WebInspector.Project} project
688      * @return {boolean}
689      */
690     filterProject: function(project)
691     {
692         return true;
693         // Overridden by subclasses
694     },
695
696     /**
697      * @return {number}
698      */
699     itemCount: function()
700     {
701         return this._uiSourceCodes.length;
702     },
703
704     /**
705      * @param {number} itemIndex
706      * @return {string}
707      */
708     itemKeyAt: function(itemIndex)
709     {
710         return this._uiSourceCodes[itemIndex].fullDisplayName();
711     },
712
713     /**
714      * @param {number} itemIndex
715      * @param {string} query
716      * @return {number}
717      */
718     itemScoreAt: function(itemIndex, query)
719     {
720         var uiSourceCode = this._uiSourceCodes[itemIndex];
721         var score = this._defaultScores ? (this._defaultScores.get(uiSourceCode) || 0) : 0;
722         if (!query || query.length < 2)
723             return score;
724
725         if (this._query !== query) {
726             this._query = query;
727             this._scorer = new WebInspector.FilePathScoreFunction(query);
728         }
729
730         var path = uiSourceCode.fullDisplayName();
731         return score + 10 * this._scorer.score(path, null);
732     },
733
734     /**
735      * @param {number} itemIndex
736      * @param {string} query
737      * @param {!Element} titleElement
738      * @param {!Element} subtitleElement
739      * @return {!Array.<!Element>}
740      */
741     renderItem: function(itemIndex, query, titleElement, subtitleElement)
742     {
743         query = this.rewriteQuery(query);
744         var uiSourceCode = this._uiSourceCodes[itemIndex];
745         titleElement.textContent = uiSourceCode.displayName() + (this._queryLineNumberAndColumnNumber || "");
746         subtitleElement.textContent = uiSourceCode.fullDisplayName().trimEnd(100);
747
748         var indexes = [];
749         var score = new WebInspector.FilePathScoreFunction(query).score(subtitleElement.textContent, indexes);
750         var fileNameIndex = subtitleElement.textContent.lastIndexOf("/");
751         var ranges = [];
752         for (var i = 0; i < indexes.length; ++i)
753             ranges.push({offset: indexes[i], length: 1});
754         if (indexes[0] > fileNameIndex) {
755             for (var i = 0; i < ranges.length; ++i)
756                 ranges[i].offset -= fileNameIndex + 1;
757             return WebInspector.highlightRangesWithStyleClass(titleElement, ranges, "highlight");
758         } else {
759             return WebInspector.highlightRangesWithStyleClass(subtitleElement, ranges, "highlight");
760         }
761     },
762
763     /**
764      * @param {?number} itemIndex
765      * @param {string} promptValue
766      */
767     selectItem: function(itemIndex, promptValue)
768     {
769         var parsedExpression = promptValue.trim().match(/^([^:]*)(:\d+)?(:\d+)?$/);
770         if (!parsedExpression)
771             return;
772
773         var lineNumber;
774         var columnNumber;
775         if (parsedExpression[2])
776             lineNumber = parseInt(parsedExpression[2].substr(1), 10) - 1;
777         if (parsedExpression[3])
778             columnNumber = parseInt(parsedExpression[3].substr(1), 10) - 1;
779         var uiSourceCode = itemIndex !== null ? this._uiSourceCodes[itemIndex] : null;
780         this.uiSourceCodeSelected(uiSourceCode, lineNumber, columnNumber);
781     },
782
783     /**
784      * @param {string} query
785      * @return {string}
786      */
787     rewriteQuery: function(query)
788     {
789         if (!query)
790             return query;
791         query = query.trim();
792         var lineNumberMatch = query.match(/^([^:]+)((?::[^:]*){0,2})$/);
793         this._queryLineNumberAndColumnNumber = lineNumberMatch ? lineNumberMatch[2] : "";
794         return lineNumberMatch ? lineNumberMatch[1] : query;
795     },
796
797     /**
798      * @param {!WebInspector.Event} event
799      */
800     _uiSourceCodeAdded: function(event)
801     {
802         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
803         if (!this.filterProject(uiSourceCode.project()))
804             return;
805         this._uiSourceCodes.push(uiSourceCode)
806         this.refresh();
807     },
808
809     dispose: function()
810     {
811         WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
812         WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
813     },
814
815     __proto__: WebInspector.SelectionDialogContentProvider.prototype
816 }
817
818 /**
819  * @constructor
820  * @extends {WebInspector.SelectUISourceCodeDialog}
821  * @param {!WebInspector.SourcesView} sourcesView
822  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
823  */
824 WebInspector.OpenResourceDialog = function(sourcesView, defaultScores)
825 {
826     WebInspector.SelectUISourceCodeDialog.call(this, defaultScores);
827     this._sourcesView = sourcesView;
828 }
829
830 WebInspector.OpenResourceDialog.prototype = {
831
832     /**
833      * @param {?WebInspector.UISourceCode} uiSourceCode
834      * @param {number=} lineNumber
835      * @param {number=} columnNumber
836      */
837     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
838     {
839         if (!uiSourceCode)
840             uiSourceCode = this._sourcesView.currentUISourceCode();
841         if (!uiSourceCode)
842             return;
843         this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber);
844     },
845
846     /**
847      * @param {string} query
848      * @return {boolean}
849      */
850     shouldShowMatchingItems: function(query)
851     {
852         return !query.startsWith(":");
853     },
854
855     /**
856      * @param {!WebInspector.Project} project
857      * @return {boolean}
858      */
859     filterProject: function(project)
860     {
861         return !project.isServiceProject();
862     },
863
864     __proto__: WebInspector.SelectUISourceCodeDialog.prototype
865 }
866
867 /**
868  * @param {!WebInspector.SourcesView} sourcesView
869  * @param {!Element} relativeToElement
870  * @param {string=} query
871  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
872  */
873 WebInspector.OpenResourceDialog.show = function(sourcesView, relativeToElement, query, defaultScores)
874 {
875     if (WebInspector.Dialog.currentInstance())
876         return;
877
878     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.OpenResourceDialog(sourcesView, defaultScores));
879     filteredItemSelectionDialog.renderAsTwoRows();
880     if (query)
881         filteredItemSelectionDialog.setQuery(query);
882     WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
883 }
884
885 /**
886  * @constructor
887  * @extends {WebInspector.SelectUISourceCodeDialog}
888  * @param {!Array.<string>} types
889  * @param {function(!WebInspector.UISourceCode)} callback
890  */
891 WebInspector.SelectUISourceCodeForProjectTypesDialog = function(types, callback)
892 {
893     this._types = types;
894     WebInspector.SelectUISourceCodeDialog.call(this);
895     this._callback = callback;
896 }
897
898 WebInspector.SelectUISourceCodeForProjectTypesDialog.prototype = {
899     /**
900      * @param {!WebInspector.UISourceCode} uiSourceCode
901      * @param {number=} lineNumber
902      * @param {number=} columnNumber
903      */
904     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
905     {
906         this._callback(uiSourceCode);
907     },
908
909     /**
910      * @param {!WebInspector.Project} project
911      * @return {boolean}
912      */
913     filterProject: function(project)
914     {
915         return this._types.indexOf(project.type()) !== -1;
916     },
917
918     __proto__: WebInspector.SelectUISourceCodeDialog.prototype
919 }
920
921 /**
922  * @param {string} name
923  * @param {!Array.<string>} types
924  * @param {function(!WebInspector.UISourceCode)} callback
925  * @param {!Element} relativeToElement
926  */
927 WebInspector.SelectUISourceCodeForProjectTypesDialog.show = function(name, types, callback, relativeToElement)
928 {
929     if (WebInspector.Dialog.currentInstance())
930         return;
931
932     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.SelectUISourceCodeForProjectTypesDialog(types, callback));
933     filteredItemSelectionDialog.setQuery(name);
934     filteredItemSelectionDialog.renderAsTwoRows();
935     WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
936 }
937
938 /**
939  * @typedef {{index: number, total: number, chunk: !Array.<!{selectorText: string, lineNumber: number, columnNumber: number}>}}
940  */
941 WebInspector.JavaScriptOutlineDialog.MessageEventData;