• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
37WebInspector.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
69WebInspector.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 */
396WebInspector.SelectionDialogContentProvider = function()
397{
398}
399
400WebInspector.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 */
530WebInspector.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 */
546WebInspector.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
554WebInspector.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 */
642WebInspector.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
653WebInspector.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 */
824WebInspector.OpenResourceDialog = function(sourcesView, defaultScores)
825{
826    WebInspector.SelectUISourceCodeDialog.call(this, defaultScores);
827    this._sourcesView = sourcesView;
828}
829
830WebInspector.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 */
873WebInspector.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 */
891WebInspector.SelectUISourceCodeForProjectTypesDialog = function(types, callback)
892{
893    this._types = types;
894    WebInspector.SelectUISourceCodeDialog.call(this);
895    this._callback = callback;
896}
897
898WebInspector.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 */
927WebInspector.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 */
941WebInspector.JavaScriptOutlineDialog.MessageEventData;
942