Upstream version 9.38.198.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._itemElementsContainer = this._viewportControl.element;
58     this._itemElementsContainer.classList.add("container");
59     this._itemElementsContainer.classList.add("monospace");
60     this._itemElementsContainer.addEventListener("click", this._onClick.bind(this), false);
61     this.element.appendChild(this._itemElementsContainer);
62
63     this._delegate = delegate;
64     this._delegate.setRefreshCallback(this._itemsLoaded.bind(this));
65     this._itemsLoaded();
66 }
67
68 WebInspector.FilteredItemSelectionDialog.prototype = {
69     /**
70      * @param {!Element} element
71      * @param {!Element} relativeToElement
72      */
73     position: function(element, relativeToElement)
74     {
75         const shadow = 10;
76         const shadowPadding = 20; // shadow + padding
77         var container = WebInspector.Dialog.modalHostView().element;
78         var preferredWidth = Math.max(relativeToElement.offsetWidth * 2 / 3, 500);
79         var width = Math.min(preferredWidth, container.offsetWidth - 2 * shadowPadding);
80         var preferredHeight = Math.max(relativeToElement.offsetHeight * 2 / 3, 204);
81         var height = Math.min(preferredHeight, container.offsetHeight - 2 * shadowPadding);
82
83         this.element.style.width = width + "px";
84         var box = relativeToElement.boxInWindow(window).relativeToElement(container);
85         var positionX = box.x + Math.max((box.width - width - 2 * shadowPadding) / 2, shadow);
86         positionX = Math.max(shadow, Math.min(container.offsetWidth - width - 2 * shadowPadding, positionX));
87         var positionY = box.y + Math.max((box.height - height - 2 * shadowPadding) / 2, shadow);
88         positionY = Math.max(shadow, Math.min(container.offsetHeight - height - 2 * shadowPadding, positionY));
89         element.positionAt(positionX, positionY, container);
90         this._dialogHeight = height;
91
92         this._updateShowMatchingItems();
93         this._viewportControl.refresh();
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         if (!this._filteredItems.length)
329             return;
330         var element = this._viewportControl.renderedElementAt(this._selectedIndexInFiltered);
331         if (element)
332             element.classList.remove("selected");
333         this._viewportControl.scrollItemIntoView(index, makeLast);
334         this._selectedIndexInFiltered = index;
335         element = this._viewportControl.renderedElementAt(index);
336         if (element)
337             element.classList.add("selected");
338     },
339
340     _onClick: function(event)
341     {
342         var itemElement = event.target.enclosingNodeOrSelfWithClass("filtered-item-list-dialog-item");
343         if (!itemElement)
344             return;
345         this._delegate.selectItem(itemElement._index, this._promptElement.value.trim());
346         WebInspector.Dialog.hide();
347     },
348
349     /**
350      * @return {number}
351      */
352     itemCount: function()
353     {
354         return this._filteredItems.length;
355     },
356
357     /**
358      * @param {number} index
359      * @return {number}
360      */
361     fastHeight: function(index)
362     {
363         if (!this._rowHeight) {
364             var delegateIndex = this._filteredItems[index];
365             var element = this._createItemElement(delegateIndex);
366             this._rowHeight = element.measurePreferredSize(this._viewportControl.contentElement()).height;
367         }
368         return this._rowHeight;
369     },
370
371     /**
372      * @param {number} index
373      * @return {!WebInspector.ViewportElement}
374      */
375     itemElement: function(index)
376     {
377         var delegateIndex = this._filteredItems[index];
378         var element = this._createItemElement(delegateIndex);
379         if (index === this._selectedIndexInFiltered)
380             element.classList.add("selected");
381         return new WebInspector.StaticViewportElement(element);
382     },
383
384     /**
385      * @return {number}
386      */
387     minimumRowHeight: function()
388     {
389         return this.fastHeight(0);
390     },
391
392     __proto__: WebInspector.DialogDelegate.prototype
393 }
394
395 /**
396  * @constructor
397  */
398 WebInspector.SelectionDialogContentProvider = function()
399 {
400 }
401
402 WebInspector.SelectionDialogContentProvider.prototype = {
403     /**
404      * @param {function():void} refreshCallback
405      */
406     setRefreshCallback: function(refreshCallback)
407     {
408         this._refreshCallback = refreshCallback;
409     },
410
411     /**
412      * @param {string} query
413      * @return {boolean}
414      */
415     shouldShowMatchingItems: function(query)
416     {
417         return true;
418     },
419
420     /**
421      * @return {number}
422      */
423     itemCount: function()
424     {
425         return 0;
426     },
427
428     /**
429      * @param {number} itemIndex
430      * @return {string}
431      */
432     itemKeyAt: function(itemIndex)
433     {
434         return "";
435     },
436
437     /**
438      * @param {number} itemIndex
439      * @param {string} query
440      * @return {number}
441      */
442     itemScoreAt: function(itemIndex, query)
443     {
444         return 1;
445     },
446
447     /**
448      * @param {number} itemIndex
449      * @param {string} query
450      * @param {!Element} titleElement
451      * @param {!Element} subtitleElement
452      */
453     renderItem: function(itemIndex, query, titleElement, subtitleElement)
454     {
455     },
456
457     /**
458      * @param {!Element} element
459      * @param {string} query
460      * @return {boolean}
461      */
462     highlightRanges: function(element, query)
463     {
464         if (!query)
465             return false;
466
467         /**
468          * @param {string} text
469          * @param {string} query
470          * @return {?Array.<!WebInspector.SourceRange>}
471          */
472         function rangesForMatch(text, query)
473         {
474             var sm = new difflib.SequenceMatcher(query, text);
475             var opcodes = sm.get_opcodes();
476             var ranges = [];
477
478             for (var i = 0; i < opcodes.length; ++i) {
479                 var opcode = opcodes[i];
480                 if (opcode[0] === "equal")
481                     ranges.push(new WebInspector.SourceRange(opcode[3], opcode[4] - opcode[3]));
482                 else if (opcode[0] !== "insert")
483                     return null;
484             }
485             return ranges;
486         }
487
488         var text = element.textContent;
489         var ranges = rangesForMatch(text, query);
490         if (!ranges)
491             ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase());
492         if (ranges) {
493             WebInspector.highlightRangesWithStyleClass(element, ranges, "highlight");
494             return true;
495         }
496         return false;
497     },
498
499     /**
500      * @param {?number} itemIndex
501      * @param {string} promptValue
502      */
503     selectItem: function(itemIndex, promptValue)
504     {
505     },
506
507     refresh: function()
508     {
509         this._refreshCallback();
510     },
511
512     /**
513      * @param {string} query
514      * @return {string}
515      */
516     rewriteQuery: function(query)
517     {
518         return query;
519     },
520
521     dispose: function()
522     {
523     }
524 }
525
526 /**
527  * @constructor
528  * @extends {WebInspector.SelectionDialogContentProvider}
529  * @param {!WebInspector.UISourceCode} uiSourceCode
530  * @param {function(number, number)} selectItemCallback
531  */
532 WebInspector.JavaScriptOutlineDialog = function(uiSourceCode, selectItemCallback)
533 {
534     WebInspector.SelectionDialogContentProvider.call(this);
535
536     this._functionItems = [];
537     this._selectItemCallback = selectItemCallback;
538     this._outlineWorker = Runtime.startWorker("script_formatter_worker");
539     this._outlineWorker.onmessage = this._didBuildOutlineChunk.bind(this);
540     this._outlineWorker.postMessage({ method: "javaScriptOutline", params: { content: uiSourceCode.workingCopy() } });
541 }
542
543 /**
544  * @param {!WebInspector.View} view
545  * @param {!WebInspector.UISourceCode} uiSourceCode
546  * @param {function(number, number)} selectItemCallback
547  */
548 WebInspector.JavaScriptOutlineDialog.show = function(view, uiSourceCode, selectItemCallback)
549 {
550     if (WebInspector.Dialog.currentInstance())
551         return;
552     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.JavaScriptOutlineDialog(uiSourceCode, selectItemCallback));
553     WebInspector.Dialog.show(view.element, filteredItemSelectionDialog);
554 }
555
556 WebInspector.JavaScriptOutlineDialog.prototype = {
557     /**
558      * @param {!MessageEvent} event
559      */
560     _didBuildOutlineChunk: function(event)
561     {
562         var data = /** @type {!WebInspector.JavaScriptOutlineDialog.MessageEventData} */ (event.data);
563         var chunk = data.chunk;
564         for (var i = 0; i < chunk.length; ++i)
565             this._functionItems.push(chunk[i]);
566
567         if (data.total === data.index + 1)
568             this.dispose();
569
570         this.refresh();
571     },
572
573     /**
574      * @return {number}
575      */
576     itemCount: function()
577     {
578         return this._functionItems.length;
579     },
580
581     /**
582      * @param {number} itemIndex
583      * @return {string}
584      */
585     itemKeyAt: function(itemIndex)
586     {
587         return this._functionItems[itemIndex].name;
588     },
589
590     /**
591      * @param {number} itemIndex
592      * @param {string} query
593      * @return {number}
594      */
595     itemScoreAt: function(itemIndex, query)
596     {
597         var item = this._functionItems[itemIndex];
598         return -item.line;
599     },
600
601     /**
602      * @param {number} itemIndex
603      * @param {string} query
604      * @param {!Element} titleElement
605      * @param {!Element} subtitleElement
606      */
607     renderItem: function(itemIndex, query, titleElement, subtitleElement)
608     {
609         var item = this._functionItems[itemIndex];
610         titleElement.textContent = item.name + (item.arguments ? item.arguments : "");
611         this.highlightRanges(titleElement, query);
612         subtitleElement.textContent = ":" + (item.line + 1);
613     },
614
615     /**
616      * @param {?number} itemIndex
617      * @param {string} promptValue
618      */
619     selectItem: function(itemIndex, promptValue)
620     {
621         if (itemIndex === null)
622             return;
623         var lineNumber = this._functionItems[itemIndex].line;
624         if (!isNaN(lineNumber) && lineNumber >= 0)
625             this._selectItemCallback(lineNumber, this._functionItems[itemIndex].column);
626     },
627
628     dispose: function()
629     {
630         if (this._outlineWorker) {
631             this._outlineWorker.terminate();
632             delete this._outlineWorker;
633         }
634     },
635
636     __proto__: WebInspector.SelectionDialogContentProvider.prototype
637 }
638
639 /**
640  * @constructor
641  * @extends {WebInspector.SelectionDialogContentProvider}
642  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
643  */
644 WebInspector.SelectUISourceCodeDialog = function(defaultScores)
645 {
646     WebInspector.SelectionDialogContentProvider.call(this);
647
648     this._populate();
649     this._defaultScores = defaultScores;
650     this._scorer = new WebInspector.FilePathScoreFunction("");
651     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
652     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
653 }
654
655 WebInspector.SelectUISourceCodeDialog.prototype = {
656     _projectRemoved: function(event)
657     {
658         var project = /** @type {!WebInspector.Project} */ (event.data);
659         this._populate(project);
660         this.refresh();
661     },
662
663     /**
664      * @param {!WebInspector.Project=} skipProject
665      */
666     _populate: function(skipProject)
667     {
668         /** @type {!Array.<!WebInspector.UISourceCode>} */
669         this._uiSourceCodes = [];
670         var projects = WebInspector.workspace.projects().filter(this.filterProject.bind(this));
671         for (var i = 0; i < projects.length; ++i) {
672             if (skipProject && projects[i] === skipProject)
673                 continue;
674             this._uiSourceCodes = this._uiSourceCodes.concat(projects[i].uiSourceCodes());
675         }
676     },
677
678     /**
679      * @param {?WebInspector.UISourceCode} uiSourceCode
680      * @param {number=} lineNumber
681      * @param {number=} columnNumber
682      */
683     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
684     {
685         // Overridden by subclasses
686     },
687
688     /**
689      * @param {!WebInspector.Project} project
690      * @return {boolean}
691      */
692     filterProject: function(project)
693     {
694         return true;
695         // Overridden by subclasses
696     },
697
698     /**
699      * @return {number}
700      */
701     itemCount: function()
702     {
703         return this._uiSourceCodes.length;
704     },
705
706     /**
707      * @param {number} itemIndex
708      * @return {string}
709      */
710     itemKeyAt: function(itemIndex)
711     {
712         return this._uiSourceCodes[itemIndex].fullDisplayName();
713     },
714
715     /**
716      * @param {number} itemIndex
717      * @param {string} query
718      * @return {number}
719      */
720     itemScoreAt: function(itemIndex, query)
721     {
722         var uiSourceCode = this._uiSourceCodes[itemIndex];
723         var score = this._defaultScores ? (this._defaultScores.get(uiSourceCode) || 0) : 0;
724         if (!query || query.length < 2)
725             return score;
726
727         if (this._query !== query) {
728             this._query = query;
729             this._scorer = new WebInspector.FilePathScoreFunction(query);
730         }
731
732         var path = uiSourceCode.fullDisplayName();
733         return score + 10 * this._scorer.score(path, null);
734     },
735
736     /**
737      * @param {number} itemIndex
738      * @param {string} query
739      * @param {!Element} titleElement
740      * @param {!Element} subtitleElement
741      * @return {!Array.<!Element>}
742      */
743     renderItem: function(itemIndex, query, titleElement, subtitleElement)
744     {
745         query = this.rewriteQuery(query);
746         var uiSourceCode = this._uiSourceCodes[itemIndex];
747         titleElement.textContent = uiSourceCode.displayName() + (this._queryLineNumberAndColumnNumber || "");
748         subtitleElement.textContent = uiSourceCode.fullDisplayName().trimEnd(100);
749
750         var indexes = [];
751         var score = new WebInspector.FilePathScoreFunction(query).score(subtitleElement.textContent, indexes);
752         var fileNameIndex = subtitleElement.textContent.lastIndexOf("/");
753         var ranges = [];
754         for (var i = 0; i < indexes.length; ++i)
755             ranges.push({offset: indexes[i], length: 1});
756         if (indexes[0] > fileNameIndex) {
757             for (var i = 0; i < ranges.length; ++i)
758                 ranges[i].offset -= fileNameIndex + 1;
759             return WebInspector.highlightRangesWithStyleClass(titleElement, ranges, "highlight");
760         } else {
761             return WebInspector.highlightRangesWithStyleClass(subtitleElement, ranges, "highlight");
762         }
763     },
764
765     /**
766      * @param {?number} itemIndex
767      * @param {string} promptValue
768      */
769     selectItem: function(itemIndex, promptValue)
770     {
771         var parsedExpression = promptValue.trim().match(/^([^:]*)(:\d+)?(:\d+)?$/);
772         if (!parsedExpression)
773             return;
774
775         var lineNumber;
776         var columnNumber;
777         if (parsedExpression[2])
778             lineNumber = parseInt(parsedExpression[2].substr(1), 10) - 1;
779         if (parsedExpression[3])
780             columnNumber = parseInt(parsedExpression[3].substr(1), 10) - 1;
781         var uiSourceCode = itemIndex !== null ? this._uiSourceCodes[itemIndex] : null;
782         this.uiSourceCodeSelected(uiSourceCode, lineNumber, columnNumber);
783     },
784
785     /**
786      * @param {string} query
787      * @return {string}
788      */
789     rewriteQuery: function(query)
790     {
791         if (!query)
792             return query;
793         query = query.trim();
794         var lineNumberMatch = query.match(/^([^:]+)((?::[^:]*){0,2})$/);
795         this._queryLineNumberAndColumnNumber = lineNumberMatch ? lineNumberMatch[2] : "";
796         return lineNumberMatch ? lineNumberMatch[1] : query;
797     },
798
799     /**
800      * @param {!WebInspector.Event} event
801      */
802     _uiSourceCodeAdded: function(event)
803     {
804         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
805         if (!this.filterProject(uiSourceCode.project()))
806             return;
807         this._uiSourceCodes.push(uiSourceCode)
808         this.refresh();
809     },
810
811     dispose: function()
812     {
813         WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
814         WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
815     },
816
817     __proto__: WebInspector.SelectionDialogContentProvider.prototype
818 }
819
820 /**
821  * @constructor
822  * @extends {WebInspector.SelectUISourceCodeDialog}
823  * @param {!WebInspector.SourcesView} sourcesView
824  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
825  */
826 WebInspector.OpenResourceDialog = function(sourcesView, defaultScores)
827 {
828     WebInspector.SelectUISourceCodeDialog.call(this, defaultScores);
829     this._sourcesView = sourcesView;
830 }
831
832 WebInspector.OpenResourceDialog.prototype = {
833
834     /**
835      * @param {?WebInspector.UISourceCode} uiSourceCode
836      * @param {number=} lineNumber
837      * @param {number=} columnNumber
838      */
839     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
840     {
841         if (!uiSourceCode)
842             uiSourceCode = this._sourcesView.currentUISourceCode();
843         if (!uiSourceCode)
844             return;
845         this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber);
846     },
847
848     /**
849      * @param {string} query
850      * @return {boolean}
851      */
852     shouldShowMatchingItems: function(query)
853     {
854         return !query.startsWith(":");
855     },
856
857     /**
858      * @param {!WebInspector.Project} project
859      * @return {boolean}
860      */
861     filterProject: function(project)
862     {
863         return !project.isServiceProject();
864     },
865
866     __proto__: WebInspector.SelectUISourceCodeDialog.prototype
867 }
868
869 /**
870  * @param {!WebInspector.SourcesView} sourcesView
871  * @param {!Element} relativeToElement
872  * @param {string=} query
873  * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores
874  */
875 WebInspector.OpenResourceDialog.show = function(sourcesView, relativeToElement, query, defaultScores)
876 {
877     if (WebInspector.Dialog.currentInstance())
878         return;
879
880     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.OpenResourceDialog(sourcesView, defaultScores));
881     filteredItemSelectionDialog.renderAsTwoRows();
882     WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
883     if (query)
884         filteredItemSelectionDialog.setQuery(query);
885 }
886
887 /**
888  * @constructor
889  * @extends {WebInspector.SelectUISourceCodeDialog}
890  * @param {!Array.<string>} types
891  * @param {function(!WebInspector.UISourceCode)} callback
892  */
893 WebInspector.SelectUISourceCodeForProjectTypesDialog = function(types, callback)
894 {
895     this._types = types;
896     WebInspector.SelectUISourceCodeDialog.call(this);
897     this._callback = callback;
898 }
899
900 WebInspector.SelectUISourceCodeForProjectTypesDialog.prototype = {
901     /**
902      * @param {!WebInspector.UISourceCode} uiSourceCode
903      * @param {number=} lineNumber
904      * @param {number=} columnNumber
905      */
906     uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber)
907     {
908         this._callback(uiSourceCode);
909     },
910
911     /**
912      * @param {!WebInspector.Project} project
913      * @return {boolean}
914      */
915     filterProject: function(project)
916     {
917         return this._types.indexOf(project.type()) !== -1;
918     },
919
920     __proto__: WebInspector.SelectUISourceCodeDialog.prototype
921 }
922
923 /**
924  * @param {string} name
925  * @param {!Array.<string>} types
926  * @param {function(!WebInspector.UISourceCode)} callback
927  * @param {!Element} relativeToElement
928  */
929 WebInspector.SelectUISourceCodeForProjectTypesDialog.show = function(name, types, callback, relativeToElement)
930 {
931     if (WebInspector.Dialog.currentInstance())
932         return;
933
934     var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.SelectUISourceCodeForProjectTypesDialog(types, callback));
935     filteredItemSelectionDialog.setQuery(name);
936     filteredItemSelectionDialog.renderAsTwoRows();
937     WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
938 }
939
940 /**
941  * @typedef {{index: number, total: number, chunk: !Array.<!{selectorText: string, lineNumber: number, columnNumber: number}>}}
942  */
943 WebInspector.JavaScriptOutlineDialog.MessageEventData;