• 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 * @interface
33 */
34WebInspector.LinkifierFormatter = function()
35{
36}
37
38WebInspector.LinkifierFormatter.prototype = {
39    /**
40     * @param {!Element} anchor
41     * @param {!WebInspector.UILocation} uiLocation
42     */
43    formatLiveAnchor: function(anchor, uiLocation) { }
44}
45
46/**
47 * @constructor
48 * @param {!WebInspector.LinkifierFormatter=} formatter
49 */
50WebInspector.Linkifier = function(formatter)
51{
52    this._formatter = formatter || new WebInspector.Linkifier.DefaultFormatter(WebInspector.Linkifier.MaxLengthForDisplayedURLs);
53    this._liveLocations = [];
54}
55
56/**
57 * @param {!WebInspector.Linkifier.LinkHandler} handler
58 */
59WebInspector.Linkifier.setLinkHandler = function(handler)
60{
61    WebInspector.Linkifier._linkHandler = handler;
62}
63
64/**
65 * @param {string} url
66 * @param {number=} lineNumber
67 * @return {boolean}
68 */
69WebInspector.Linkifier.handleLink = function(url, lineNumber)
70{
71    if (!WebInspector.Linkifier._linkHandler)
72        return false;
73    return WebInspector.Linkifier._linkHandler.handleLink(url, lineNumber)
74}
75
76/**
77 * @param {!Object} revealable
78 * @param {string} text
79 * @param {string=} fallbackHref
80 * @param {number=} fallbackLineNumber
81 * @param {string=} title
82 * @param {string=} classes
83 * @return {!Element}
84 */
85WebInspector.Linkifier.linkifyUsingRevealer = function(revealable, text, fallbackHref, fallbackLineNumber, title, classes)
86{
87    var a = document.createElement("a");
88    a.className = (classes || "") + " webkit-html-resource-link";
89    a.textContent = text.trimMiddle(WebInspector.Linkifier.MaxLengthForDisplayedURLs);
90    a.title = title || text;
91    if (fallbackHref) {
92        a.href = fallbackHref;
93        a.lineNumber = fallbackLineNumber;
94    }
95    /**
96     * @param {?Event} event
97     * @this {Object}
98     */
99    function clickHandler(event)
100    {
101        event.stopImmediatePropagation();
102        event.preventDefault();
103        if (fallbackHref && WebInspector.Linkifier.handleLink(fallbackHref, fallbackLineNumber))
104            return;
105
106        WebInspector.Revealer.reveal(this);
107    }
108    a.addEventListener("click", clickHandler.bind(revealable), false);
109    return a;
110}
111
112WebInspector.Linkifier.prototype = {
113    /**
114     * @param {!WebInspector.Target} target
115     * @param {string} sourceURL
116     * @param {number} lineNumber
117     * @param {number=} columnNumber
118     * @param {string=} classes
119     * @return {?Element}
120     */
121    linkifyLocation: function(target, sourceURL, lineNumber, columnNumber, classes)
122    {
123        var rawLocation = target.debuggerModel.createRawLocationByURL(sourceURL, lineNumber, columnNumber || 0);
124        if (!rawLocation)
125            return WebInspector.linkifyResourceAsNode(sourceURL, lineNumber, classes);
126        return this.linkifyRawLocation(rawLocation, classes);
127    },
128
129    /**
130     * @param {!WebInspector.DebuggerModel.Location} rawLocation
131     * @param {string=} classes
132     * @return {?Element}
133     */
134    linkifyRawLocation: function(rawLocation, classes)
135    {
136        // FIXME: this check should not be here.
137        var script = rawLocation.target().debuggerModel.scriptForId(rawLocation.scriptId);
138        if (!script)
139            return null;
140        var anchor = this._createAnchor(classes);
141        var liveLocation = rawLocation.createLiveLocation(this._updateAnchor.bind(this, anchor));
142        this._liveLocations.push(liveLocation);
143        return anchor;
144    },
145
146    /**
147     * @param {?CSSAgent.StyleSheetId} styleSheetId
148     * @param {!WebInspector.CSSLocation} rawLocation
149     * @param {string=} classes
150     * @return {?Element}
151     */
152    linkifyCSSLocation: function(styleSheetId, rawLocation, classes)
153    {
154        var anchor = this._createAnchor(classes);
155        var liveLocation = rawLocation.createLiveLocation(styleSheetId, this._updateAnchor.bind(this, anchor));
156        if (!liveLocation)
157            return null;
158        this._liveLocations.push(liveLocation);
159        return anchor;
160    },
161
162    /**
163     * @param {string=} classes
164     * @return {!Element}
165     */
166    _createAnchor: function(classes)
167    {
168        var anchor = document.createElement("a");
169        anchor.className = (classes || "") + " webkit-html-resource-link";
170
171        /**
172         * @param {?Event} event
173         */
174        function clickHandler(event)
175        {
176            event.stopImmediatePropagation();
177            event.preventDefault();
178            if (!anchor.__uiLocation)
179                return;
180            if (WebInspector.Linkifier.handleLink(anchor.__uiLocation.uiSourceCode.url, anchor.__uiLocation.lineNumber))
181                return;
182            WebInspector.Revealer.reveal(anchor.__uiLocation);
183        }
184        anchor.addEventListener("click", clickHandler, false);
185        return anchor;
186    },
187
188    reset: function()
189    {
190        for (var i = 0; i < this._liveLocations.length; ++i)
191            this._liveLocations[i].dispose();
192        this._liveLocations = [];
193    },
194
195    /**
196     * @param {!Element} anchor
197     * @param {!WebInspector.UILocation} uiLocation
198     */
199    _updateAnchor: function(anchor, uiLocation)
200    {
201        anchor.__uiLocation = uiLocation;
202        this._formatter.formatLiveAnchor(anchor, uiLocation);
203    }
204}
205
206/**
207 * @constructor
208 * @implements {WebInspector.LinkifierFormatter}
209 * @param {number=} maxLength
210 */
211WebInspector.Linkifier.DefaultFormatter = function(maxLength)
212{
213    this._maxLength = maxLength;
214}
215
216WebInspector.Linkifier.DefaultFormatter.prototype = {
217    /**
218     * @param {!Element} anchor
219     * @param {!WebInspector.UILocation} uiLocation
220     */
221    formatLiveAnchor: function(anchor, uiLocation)
222    {
223        var text = uiLocation.linkText();
224        if (this._maxLength)
225            text = text.trimMiddle(this._maxLength);
226        anchor.textContent = text;
227
228        var titleText = uiLocation.uiSourceCode.originURL();
229        if (typeof uiLocation.lineNumber === "number")
230            titleText += ":" + (uiLocation.lineNumber + 1);
231        anchor.title = titleText;
232    }
233}
234
235/**
236 * @constructor
237 * @extends {WebInspector.Linkifier.DefaultFormatter}
238 */
239WebInspector.Linkifier.DefaultCSSFormatter = function()
240{
241    WebInspector.Linkifier.DefaultFormatter.call(this, WebInspector.Linkifier.DefaultCSSFormatter.MaxLengthForDisplayedURLs);
242}
243
244WebInspector.Linkifier.DefaultCSSFormatter.MaxLengthForDisplayedURLs = 30;
245
246WebInspector.Linkifier.DefaultCSSFormatter.prototype = {
247    /**
248     * @param {!Element} anchor
249     * @param {!WebInspector.UILocation} uiLocation
250     */
251    formatLiveAnchor: function(anchor, uiLocation)
252    {
253        WebInspector.Linkifier.DefaultFormatter.prototype.formatLiveAnchor.call(this, anchor, uiLocation);
254        anchor.classList.add("webkit-html-resource-link");
255        anchor.setAttribute("data-uncopyable", anchor.textContent);
256        anchor.textContent = "";
257    },
258    __proto__: WebInspector.Linkifier.DefaultFormatter.prototype
259}
260
261/**
262 * The maximum number of characters to display in a URL.
263 * @const
264 * @type {number}
265 */
266WebInspector.Linkifier.MaxLengthForDisplayedURLs = 150;
267
268/**
269 * @interface
270 */
271WebInspector.Linkifier.LinkHandler = function()
272{
273}
274
275WebInspector.Linkifier.LinkHandler.prototype = {
276    /**
277     * @param {string} url
278     * @param {number=} lineNumber
279     * @return {boolean}
280     */
281    handleLink: function(url, lineNumber) {}
282}
283
284/**
285 * @param {!WebInspector.Target} target
286 * @param {string} scriptId
287 * @param {number} lineNumber
288 * @param {number=} columnNumber
289 * @return {string}
290 */
291WebInspector.Linkifier.liveLocationText = function(target, scriptId, lineNumber, columnNumber)
292{
293    var script = target.debuggerModel.scriptForId(scriptId);
294    if (!script)
295        return "";
296    var uiLocation = script.rawLocationToUILocation(lineNumber, columnNumber);
297    return uiLocation.linkText();
298}
299