OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008 Apple 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 | |
6 * are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
14 * its contributors may be used to endorse or promote products derived | |
15 * from this software without specific prior written permission. | |
16 * | |
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 */ | |
28 | |
29 WebInspector.SourceView = function(resource) | |
30 { | |
31 // Set the sourceFrame first since WebInspector.ResourceView will set header
sVisible | |
32 // and our override of headersVisible needs the sourceFrame. | |
33 this.sourceFrame = new WebInspector.SourceFrame(null, this._addBreakpoint.bi
nd(this)); | |
34 | |
35 WebInspector.ResourceView.call(this, resource); | |
36 | |
37 resource.addEventListener("finished", this._resourceLoadingFinished, this); | |
38 | |
39 this.element.addStyleClass("source"); | |
40 | |
41 this._frameNeedsSetup = true; | |
42 | |
43 this.contentElement.appendChild(this.sourceFrame.element); | |
44 | |
45 var gutterElement = document.createElement("div"); | |
46 gutterElement.className = "webkit-line-gutter-backdrop"; | |
47 this.element.appendChild(gutterElement); | |
48 } | |
49 | |
50 WebInspector.SourceView.prototype = { | |
51 set headersVisible(x) | |
52 { | |
53 if (x === this._headersVisible) | |
54 return; | |
55 | |
56 var superSetter = WebInspector.ResourceView.prototype.__lookupSetter__("
headersVisible"); | |
57 if (superSetter) | |
58 superSetter.call(this, x); | |
59 | |
60 this.sourceFrame.autoSizesToFitContentHeight = x; | |
61 }, | |
62 | |
63 show: function(parentElement) | |
64 { | |
65 WebInspector.ResourceView.prototype.show.call(this, parentElement); | |
66 this.setupSourceFrameIfNeeded(); | |
67 }, | |
68 | |
69 hide: function() | |
70 { | |
71 WebInspector.View.prototype.hide.call(this); | |
72 this._currentSearchResultIndex = -1; | |
73 }, | |
74 | |
75 resize: function() | |
76 { | |
77 if (this.sourceFrame.autoSizesToFitContentHeight) | |
78 this.sourceFrame.sizeToFitContentHeight(); | |
79 }, | |
80 | |
81 detach: function() | |
82 { | |
83 WebInspector.ResourceView.prototype.detach.call(this); | |
84 | |
85 // FIXME: We need to mark the frame for setup on detach because the fram
e DOM is cleared | |
86 // when it is removed from the document. Is this a bug? | |
87 this._frameNeedsSetup = true; | |
88 this._sourceFrameSetup = false; | |
89 }, | |
90 | |
91 setupSourceFrameIfNeeded: function() | |
92 { | |
93 if (!this._frameNeedsSetup) | |
94 return; | |
95 | |
96 this.attach(); | |
97 | |
98 if (!InspectorController.addResourceSourceToFrame(this.resource.identifi
er, this.sourceFrame.element)) | |
99 return; | |
100 | |
101 delete this._frameNeedsSetup; | |
102 | |
103 if (this.resource.type === WebInspector.Resource.Type.Script) { | |
104 this.sourceFrame.addEventListener("syntax highlighting complete", th
is._syntaxHighlightingComplete, this); | |
105 this.sourceFrame.syntaxHighlightJavascript(); | |
106 } else | |
107 this._sourceFrameSetupFinished(); | |
108 }, | |
109 | |
110 _resourceLoadingFinished: function(event) | |
111 { | |
112 this._frameNeedsSetup = true; | |
113 this._sourceFrameSetup = false; | |
114 if (this.visible) | |
115 this.setupSourceFrameIfNeeded(); | |
116 this.resource.removeEventListener("finished", this._resourceLoadingFinis
hed, this); | |
117 }, | |
118 | |
119 _addBreakpoint: function(line) | |
120 { | |
121 var sourceID = null; | |
122 var closestStartingLine = 0; | |
123 var scripts = this.resource.scripts; | |
124 for (var i = 0; i < scripts.length; ++i) { | |
125 var script = scripts[i]; | |
126 if (script.startingLine <= line && script.startingLine >= closestSta
rtingLine) { | |
127 closestStartingLine = script.startingLine; | |
128 sourceID = script.sourceID; | |
129 } | |
130 } | |
131 | |
132 if (WebInspector.panels.scripts) { | |
133 var breakpoint = new WebInspector.Breakpoint(this.resource.url, line
, sourceID); | |
134 WebInspector.panels.scripts.addBreakpoint(breakpoint); | |
135 } | |
136 }, | |
137 | |
138 // The rest of the methods in this prototype need to be generic enough to wo
rk with a ScriptView. | |
139 // The ScriptView prototype pulls these methods into it's prototype to avoid
duplicate code. | |
140 | |
141 searchCanceled: function() | |
142 { | |
143 this._currentSearchResultIndex = -1; | |
144 this._searchResults = []; | |
145 delete this._delayedFindSearchMatches; | |
146 }, | |
147 | |
148 performSearch: function(query, finishedCallback) | |
149 { | |
150 // Call searchCanceled since it will reset everything we need before doi
ng a new search. | |
151 this.searchCanceled(); | |
152 | |
153 var lineQueryRegex = /(^|\s)(?:#|line:\s*)(\d+)(\s|$)/i; | |
154 var lineQueryMatch = query.match(lineQueryRegex); | |
155 if (lineQueryMatch) { | |
156 var lineToSearch = parseInt(lineQueryMatch[2]); | |
157 | |
158 // If there was a space before and after the line query part, replac
e with a space. | |
159 // Otherwise replace with an empty string to eat the prefix or postf
ix space. | |
160 var lineQueryReplacement = (lineQueryMatch[1] && lineQueryMatch[3] ?
" " : ""); | |
161 var filterlessQuery = query.replace(lineQueryRegex, lineQueryReplace
ment); | |
162 } | |
163 | |
164 this._searchFinishedCallback = finishedCallback; | |
165 | |
166 function findSearchMatches(query, finishedCallback) | |
167 { | |
168 if (isNaN(lineToSearch)) { | |
169 // Search the whole document since there was no line to search. | |
170 this._searchResults = (InspectorController.search(this.sourceFra
me.element.contentDocument, query) || []); | |
171 } else { | |
172 var sourceRow = this.sourceFrame.sourceRow(lineToSearch); | |
173 if (sourceRow) { | |
174 if (filterlessQuery) { | |
175 // There is still a query string, so search for that str
ing in the line. | |
176 this._searchResults = (InspectorController.search(source
Row, filterlessQuery) || []); | |
177 } else { | |
178 // Match the whole line, since there was no remaining qu
ery string to match. | |
179 var rowRange = this.sourceFrame.element.contentDocument.
createRange(); | |
180 rowRange.selectNodeContents(sourceRow); | |
181 this._searchResults = [rowRange]; | |
182 } | |
183 } | |
184 | |
185 // Attempt to search for the whole query, just incase it matches
a color like "#333". | |
186 var wholeQueryMatches = InspectorController.search(this.sourceFr
ame.element.contentDocument, query); | |
187 if (wholeQueryMatches) | |
188 this._searchResults = this._searchResults.concat(wholeQueryM
atches); | |
189 } | |
190 | |
191 if (this._searchResults) | |
192 finishedCallback(this, this._searchResults.length); | |
193 } | |
194 | |
195 if (!this._sourceFrameSetup) { | |
196 // The search is performed in _sourceFrameSetupFinished by calling _
delayedFindSearchMatches. | |
197 this._delayedFindSearchMatches = findSearchMatches.bind(this, query,
finishedCallback); | |
198 this.setupSourceFrameIfNeeded(); | |
199 return; | |
200 } | |
201 | |
202 findSearchMatches.call(this, query, finishedCallback); | |
203 }, | |
204 | |
205 jumpToFirstSearchResult: function() | |
206 { | |
207 if (!this._searchResults || !this._searchResults.length) | |
208 return; | |
209 this._currentSearchResultIndex = 0; | |
210 this._jumpToSearchResult(this._currentSearchResultIndex); | |
211 }, | |
212 | |
213 jumpToLastSearchResult: function() | |
214 { | |
215 if (!this._searchResults || !this._searchResults.length) | |
216 return; | |
217 this._currentSearchResultIndex = (this._searchResults.length - 1); | |
218 this._jumpToSearchResult(this._currentSearchResultIndex); | |
219 }, | |
220 | |
221 jumpToNextSearchResult: function() | |
222 { | |
223 if (!this._searchResults || !this._searchResults.length) | |
224 return; | |
225 if (++this._currentSearchResultIndex >= this._searchResults.length) | |
226 this._currentSearchResultIndex = 0; | |
227 this._jumpToSearchResult(this._currentSearchResultIndex); | |
228 }, | |
229 | |
230 jumpToPreviousSearchResult: function() | |
231 { | |
232 if (!this._searchResults || !this._searchResults.length) | |
233 return; | |
234 if (--this._currentSearchResultIndex < 0) | |
235 this._currentSearchResultIndex = (this._searchResults.length - 1); | |
236 this._jumpToSearchResult(this._currentSearchResultIndex); | |
237 }, | |
238 | |
239 showingFirstSearchResult: function() | |
240 { | |
241 return (this._currentSearchResultIndex === 0); | |
242 }, | |
243 | |
244 showingLastSearchResult: function() | |
245 { | |
246 return (this._searchResults && this._currentSearchResultIndex === (this.
_searchResults.length - 1)); | |
247 }, | |
248 | |
249 revealLine: function(lineNumber) | |
250 { | |
251 this.setupSourceFrameIfNeeded(); | |
252 this.sourceFrame.revealLine(lineNumber); | |
253 }, | |
254 | |
255 highlightLine: function(lineNumber) | |
256 { | |
257 this.setupSourceFrameIfNeeded(); | |
258 this.sourceFrame.highlightLine(lineNumber); | |
259 }, | |
260 | |
261 addMessage: function(msg) | |
262 { | |
263 this.sourceFrame.addMessage(msg); | |
264 }, | |
265 | |
266 clearMessages: function() | |
267 { | |
268 this.sourceFrame.clearMessages(); | |
269 }, | |
270 | |
271 _jumpToSearchResult: function(index) | |
272 { | |
273 var foundRange = this._searchResults[index]; | |
274 if (!foundRange) | |
275 return; | |
276 | |
277 var selection = window.getSelection(); | |
278 selection.removeAllRanges(); | |
279 selection.addRange(foundRange); | |
280 | |
281 if (foundRange.startContainer.scrollIntoViewIfNeeded) | |
282 foundRange.startContainer.scrollIntoViewIfNeeded(true); | |
283 else if (foundRange.startContainer.parentNode) | |
284 foundRange.startContainer.parentNode.scrollIntoViewIfNeeded(true); | |
285 }, | |
286 | |
287 _sourceFrameSetupFinished: function() | |
288 { | |
289 this._sourceFrameSetup = true; | |
290 if (this._delayedFindSearchMatches) { | |
291 this._delayedFindSearchMatches(); | |
292 delete this._delayedFindSearchMatches; | |
293 } | |
294 }, | |
295 | |
296 _syntaxHighlightingComplete: function(event) | |
297 { | |
298 this._sourceFrameSetupFinished(); | |
299 this.sourceFrame.removeEventListener("syntax highlighting complete", nul
l, this); | |
300 } | |
301 } | |
302 | |
303 WebInspector.SourceView.prototype.__proto__ = WebInspector.ResourceView.prototyp
e; | |
OLD | NEW |