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