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