OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
13 * distribution. | 13 * distribution. |
14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
17 * | 17 * |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | |
33 * @extends {WebInspector.VBox} | |
34 * @implements {WebInspector.Searchable} | 31 * @implements {WebInspector.Searchable} |
35 * @param {!WebInspector.ParsedJSON} parsedJSON | 32 * @unrestricted |
36 */ | 33 */ |
37 WebInspector.JSONView = function(parsedJSON) | 34 WebInspector.JSONView = class extends WebInspector.VBox { |
38 { | 35 /** |
39 WebInspector.VBox.call(this); | 36 * @param {!WebInspector.ParsedJSON} parsedJSON |
| 37 */ |
| 38 constructor(parsedJSON) { |
| 39 super(); |
40 this._parsedJSON = parsedJSON; | 40 this._parsedJSON = parsedJSON; |
41 this.element.classList.add("json-view"); | 41 this.element.classList.add('json-view'); |
42 | 42 |
43 /** @type {?WebInspector.SearchableView} */ | 43 /** @type {?WebInspector.SearchableView} */ |
44 this._searchableView; | 44 this._searchableView; |
45 /** @type {!WebInspector.ObjectPropertiesSection} */ | 45 /** @type {!WebInspector.ObjectPropertiesSection} */ |
46 this._treeOutline; | 46 this._treeOutline; |
47 /** @type {number} */ | 47 /** @type {number} */ |
48 this._currentSearchFocusIndex = 0; | 48 this._currentSearchFocusIndex = 0; |
49 /** @type {!Array.<!TreeElement>} */ | 49 /** @type {!Array.<!TreeElement>} */ |
50 this._currentSearchTreeElements = []; | 50 this._currentSearchTreeElements = []; |
51 /** @type {?RegExp} */ | 51 /** @type {?RegExp} */ |
52 this._searchRegex = null; | 52 this._searchRegex = null; |
53 }; | 53 } |
54 | 54 |
55 /** | 55 /** |
56 * @param {!WebInspector.ParsedJSON} parsedJSON | 56 * @param {!WebInspector.ParsedJSON} parsedJSON |
57 * @return {!WebInspector.SearchableView} | 57 * @return {!WebInspector.SearchableView} |
58 */ | 58 */ |
59 WebInspector.JSONView.createSearchableView = function(parsedJSON) | 59 static createSearchableView(parsedJSON) { |
60 { | |
61 var jsonView = new WebInspector.JSONView(parsedJSON); | 60 var jsonView = new WebInspector.JSONView(parsedJSON); |
62 var searchableView = new WebInspector.SearchableView(jsonView); | 61 var searchableView = new WebInspector.SearchableView(jsonView); |
63 searchableView.setPlaceholder(WebInspector.UIString("Find")); | 62 searchableView.setPlaceholder(WebInspector.UIString('Find')); |
64 jsonView._searchableView = searchableView; | 63 jsonView._searchableView = searchableView; |
65 jsonView.show(searchableView.element); | 64 jsonView.show(searchableView.element); |
66 jsonView.element.setAttribute("tabIndex", 0); | 65 jsonView.element.setAttribute('tabIndex', 0); |
67 return searchableView; | 66 return searchableView; |
68 }; | 67 } |
69 | 68 |
70 /** | 69 /** |
71 * @param {?string} text | 70 * @param {?string} text |
72 * @return {!Promise<?WebInspector.ParsedJSON>} | 71 * @return {!Promise<?WebInspector.ParsedJSON>} |
73 */ | 72 */ |
74 WebInspector.JSONView.parseJSON = function(text) | 73 static parseJSON(text) { |
75 { | |
76 var returnObj = null; | 74 var returnObj = null; |
77 if (text) | 75 if (text) |
78 returnObj = WebInspector.JSONView._extractJSON(/** @type {string} */ (te
xt)); | 76 returnObj = WebInspector.JSONView._extractJSON(/** @type {string} */ (text
)); |
79 if (!returnObj) | 77 if (!returnObj) |
80 return Promise.resolve(/** @type {?WebInspector.ParsedJSON} */ (null)); | 78 return Promise.resolve(/** @type {?WebInspector.ParsedJSON} */ (null)); |
81 return WebInspector.formatterWorkerPool.runTask("relaxedJSONParser", {conten
t: returnObj.data}) | 79 return WebInspector.formatterWorkerPool.runTask('relaxedJSONParser', {conten
t: returnObj.data}) |
82 .then(handleReturnedJSON); | 80 .then(handleReturnedJSON); |
83 | 81 |
84 /** | 82 /** |
85 * @param {?MessageEvent} event | 83 * @param {?MessageEvent} event |
86 * @return {?WebInspector.ParsedJSON} | 84 * @return {?WebInspector.ParsedJSON} |
87 */ | 85 */ |
88 function handleReturnedJSON(event) | 86 function handleReturnedJSON(event) { |
89 { | 87 if (!event || !event.data) |
90 if (!event || !event.data) | 88 return null; |
91 return null; | 89 returnObj.data = event.data; |
92 returnObj.data = event.data; | 90 return returnObj; |
93 return returnObj; | 91 } |
94 } | 92 } |
95 }; | 93 |
96 | 94 /** |
97 /** | 95 * @param {string} text |
98 * @param {string} text | 96 * @return {?WebInspector.ParsedJSON} |
99 * @return {?WebInspector.ParsedJSON} | 97 */ |
100 */ | 98 static _extractJSON(text) { |
101 WebInspector.JSONView._extractJSON = function(text) | |
102 { | |
103 // Do not treat HTML as JSON. | 99 // Do not treat HTML as JSON. |
104 if (text.startsWith("<")) | 100 if (text.startsWith('<')) |
105 return null; | 101 return null; |
106 var inner = WebInspector.JSONView._findBrackets(text, "{", "}"); | 102 var inner = WebInspector.JSONView._findBrackets(text, '{', '}'); |
107 var inner2 = WebInspector.JSONView._findBrackets(text, "[", "]"); | 103 var inner2 = WebInspector.JSONView._findBrackets(text, '[', ']'); |
108 inner = inner2.length > inner.length ? inner2 : inner; | 104 inner = inner2.length > inner.length ? inner2 : inner; |
109 | 105 |
110 // Return on blank payloads or on payloads significantly smaller than origin
al text. | 106 // Return on blank payloads or on payloads significantly smaller than origin
al text. |
111 if (inner.length === -1 || text.length - inner.length > 80) | 107 if (inner.length === -1 || text.length - inner.length > 80) |
112 return null; | 108 return null; |
113 | 109 |
114 var prefix = text.substring(0, inner.start); | 110 var prefix = text.substring(0, inner.start); |
115 var suffix = text.substring(inner.end + 1); | 111 var suffix = text.substring(inner.end + 1); |
116 text = text.substring(inner.start, inner.end + 1); | 112 text = text.substring(inner.start, inner.end + 1); |
117 | 113 |
118 // Only process valid JSONP. | 114 // Only process valid JSONP. |
119 if (suffix.trim().length && !(suffix.trim().startsWith(")") && prefix.trim()
.endsWith("("))) | 115 if (suffix.trim().length && !(suffix.trim().startsWith(')') && prefix.trim()
.endsWith('('))) |
120 return null; | 116 return null; |
121 | 117 |
122 return new WebInspector.ParsedJSON(text, prefix, suffix); | 118 return new WebInspector.ParsedJSON(text, prefix, suffix); |
123 }; | 119 } |
124 | 120 |
125 /** | 121 /** |
126 * @param {string} text | 122 * @param {string} text |
127 * @param {string} open | 123 * @param {string} open |
128 * @param {string} close | 124 * @param {string} close |
129 * @return {{start: number, end: number, length: number}} | 125 * @return {{start: number, end: number, length: number}} |
130 */ | 126 */ |
131 WebInspector.JSONView._findBrackets = function(text, open, close) | 127 static _findBrackets(text, open, close) { |
132 { | |
133 var start = text.indexOf(open); | 128 var start = text.indexOf(open); |
134 var end = text.lastIndexOf(close); | 129 var end = text.lastIndexOf(close); |
135 var length = end - start - 1; | 130 var length = end - start - 1; |
136 if (start === -1 || end === -1 || end < start) | 131 if (start === -1 || end === -1 || end < start) |
137 length = -1; | 132 length = -1; |
138 return {start: start, end: end, length: length}; | 133 return {start: start, end: end, length: length}; |
| 134 } |
| 135 |
| 136 /** |
| 137 * @override |
| 138 */ |
| 139 wasShown() { |
| 140 this._initialize(); |
| 141 } |
| 142 |
| 143 _initialize() { |
| 144 if (this._initialized) |
| 145 return; |
| 146 this._initialized = true; |
| 147 |
| 148 var obj = WebInspector.RemoteObject.fromLocalObject(this._parsedJSON.data); |
| 149 var title = this._parsedJSON.prefix + obj.description + this._parsedJSON.suf
fix; |
| 150 this._treeOutline = new WebInspector.ObjectPropertiesSection(obj, title); |
| 151 this._treeOutline.setEditable(false); |
| 152 this._treeOutline.expand(); |
| 153 this.element.appendChild(this._treeOutline.element); |
| 154 } |
| 155 |
| 156 /** |
| 157 * @param {number} index |
| 158 */ |
| 159 _jumpToMatch(index) { |
| 160 if (!this._searchRegex) |
| 161 return; |
| 162 var previousFocusElement = this._currentSearchTreeElements[this._currentSear
chFocusIndex]; |
| 163 if (previousFocusElement) |
| 164 previousFocusElement.setSearchRegex(this._searchRegex); |
| 165 |
| 166 var newFocusElement = this._currentSearchTreeElements[index]; |
| 167 if (newFocusElement) { |
| 168 this._updateSearchIndex(index); |
| 169 newFocusElement.setSearchRegex(this._searchRegex, WebInspector.highlighted
CurrentSearchResultClassName); |
| 170 newFocusElement.reveal(); |
| 171 } else { |
| 172 this._updateSearchIndex(0); |
| 173 } |
| 174 } |
| 175 |
| 176 /** |
| 177 * @param {number} count |
| 178 */ |
| 179 _updateSearchCount(count) { |
| 180 if (!this._searchableView) |
| 181 return; |
| 182 this._searchableView.updateSearchMatchesCount(count); |
| 183 } |
| 184 |
| 185 /** |
| 186 * @param {number} index |
| 187 */ |
| 188 _updateSearchIndex(index) { |
| 189 this._currentSearchFocusIndex = index; |
| 190 if (!this._searchableView) |
| 191 return; |
| 192 this._searchableView.updateCurrentMatchIndex(index); |
| 193 } |
| 194 |
| 195 /** |
| 196 * @override |
| 197 */ |
| 198 searchCanceled() { |
| 199 this._searchRegex = null; |
| 200 this._currentSearchTreeElements = []; |
| 201 |
| 202 for (var element = this._treeOutline.rootElement(); element; element = eleme
nt.traverseNextTreeElement(false)) { |
| 203 if (!(element instanceof WebInspector.ObjectPropertyTreeElement)) |
| 204 continue; |
| 205 element.revertHighlightChanges(); |
| 206 } |
| 207 this._updateSearchCount(0); |
| 208 this._updateSearchIndex(0); |
| 209 } |
| 210 |
| 211 /** |
| 212 * @override |
| 213 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig |
| 214 * @param {boolean} shouldJump |
| 215 * @param {boolean=} jumpBackwards |
| 216 */ |
| 217 performSearch(searchConfig, shouldJump, jumpBackwards) { |
| 218 var newIndex = this._currentSearchFocusIndex; |
| 219 var previousSearchFocusElement = this._currentSearchTreeElements[newIndex]; |
| 220 this.searchCanceled(); |
| 221 this._searchRegex = searchConfig.toSearchRegex(true); |
| 222 |
| 223 for (var element = this._treeOutline.rootElement(); element; element = eleme
nt.traverseNextTreeElement(false)) { |
| 224 if (!(element instanceof WebInspector.ObjectPropertyTreeElement)) |
| 225 continue; |
| 226 var hasMatch = element.setSearchRegex(this._searchRegex); |
| 227 if (hasMatch) |
| 228 this._currentSearchTreeElements.push(element); |
| 229 if (previousSearchFocusElement === element) { |
| 230 var currentIndex = this._currentSearchTreeElements.length - 1; |
| 231 if (hasMatch || jumpBackwards) |
| 232 newIndex = currentIndex; |
| 233 else |
| 234 newIndex = currentIndex + 1; |
| 235 } |
| 236 } |
| 237 this._updateSearchCount(this._currentSearchTreeElements.length); |
| 238 |
| 239 if (!this._currentSearchTreeElements.length) { |
| 240 this._updateSearchIndex(0); |
| 241 return; |
| 242 } |
| 243 newIndex = mod(newIndex, this._currentSearchTreeElements.length); |
| 244 |
| 245 this._jumpToMatch(newIndex); |
| 246 } |
| 247 |
| 248 /** |
| 249 * @override |
| 250 */ |
| 251 jumpToNextSearchResult() { |
| 252 if (!this._currentSearchTreeElements.length) |
| 253 return; |
| 254 var newIndex = mod(this._currentSearchFocusIndex + 1, this._currentSearchTre
eElements.length); |
| 255 this._jumpToMatch(newIndex); |
| 256 } |
| 257 |
| 258 /** |
| 259 * @override |
| 260 */ |
| 261 jumpToPreviousSearchResult() { |
| 262 if (!this._currentSearchTreeElements.length) |
| 263 return; |
| 264 var newIndex = mod(this._currentSearchFocusIndex - 1, this._currentSearchTre
eElements.length); |
| 265 this._jumpToMatch(newIndex); |
| 266 } |
| 267 |
| 268 /** |
| 269 * @override |
| 270 * @return {boolean} |
| 271 */ |
| 272 supportsCaseSensitiveSearch() { |
| 273 return true; |
| 274 } |
| 275 |
| 276 /** |
| 277 * @override |
| 278 * @return {boolean} |
| 279 */ |
| 280 supportsRegexSearch() { |
| 281 return true; |
| 282 } |
139 }; | 283 }; |
140 | 284 |
141 WebInspector.JSONView.prototype = { | |
142 wasShown: function() | |
143 { | |
144 this._initialize(); | |
145 }, | |
146 | |
147 _initialize: function() | |
148 { | |
149 if (this._initialized) | |
150 return; | |
151 this._initialized = true; | |
152 | |
153 var obj = WebInspector.RemoteObject.fromLocalObject(this._parsedJSON.dat
a); | |
154 var title = this._parsedJSON.prefix + obj.description + this._parsedJSON
.suffix; | |
155 this._treeOutline = new WebInspector.ObjectPropertiesSection(obj, title)
; | |
156 this._treeOutline.setEditable(false); | |
157 this._treeOutline.expand(); | |
158 this.element.appendChild(this._treeOutline.element); | |
159 }, | |
160 | |
161 /** | |
162 * @param {number} index | |
163 */ | |
164 _jumpToMatch: function(index) | |
165 { | |
166 if (!this._searchRegex) | |
167 return; | |
168 var previousFocusElement = this._currentSearchTreeElements[this._current
SearchFocusIndex]; | |
169 if (previousFocusElement) | |
170 previousFocusElement.setSearchRegex(this._searchRegex); | |
171 | |
172 var newFocusElement = this._currentSearchTreeElements[index]; | |
173 if (newFocusElement) { | |
174 this._updateSearchIndex(index); | |
175 newFocusElement.setSearchRegex(this._searchRegex, WebInspector.highl
ightedCurrentSearchResultClassName); | |
176 newFocusElement.reveal(); | |
177 } else { | |
178 this._updateSearchIndex(0); | |
179 } | |
180 }, | |
181 | |
182 /** | |
183 * @param {number} count | |
184 */ | |
185 _updateSearchCount: function(count) | |
186 { | |
187 if (!this._searchableView) | |
188 return; | |
189 this._searchableView.updateSearchMatchesCount(count); | |
190 }, | |
191 | |
192 /** | |
193 * @param {number} index | |
194 */ | |
195 _updateSearchIndex: function(index) | |
196 { | |
197 this._currentSearchFocusIndex = index; | |
198 if (!this._searchableView) | |
199 return; | |
200 this._searchableView.updateCurrentMatchIndex(index); | |
201 }, | |
202 | |
203 /** | |
204 * @override | |
205 */ | |
206 searchCanceled: function() | |
207 { | |
208 this._searchRegex = null; | |
209 this._currentSearchTreeElements = []; | |
210 | |
211 for (var element = this._treeOutline.rootElement(); element; element = e
lement.traverseNextTreeElement(false)) { | |
212 if (!(element instanceof WebInspector.ObjectPropertyTreeElement)) | |
213 continue; | |
214 element.revertHighlightChanges(); | |
215 } | |
216 this._updateSearchCount(0); | |
217 this._updateSearchIndex(0); | |
218 }, | |
219 | |
220 /** | |
221 * @override | |
222 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig | |
223 * @param {boolean} shouldJump | |
224 * @param {boolean=} jumpBackwards | |
225 */ | |
226 performSearch: function(searchConfig, shouldJump, jumpBackwards) | |
227 { | |
228 var newIndex = this._currentSearchFocusIndex; | |
229 var previousSearchFocusElement = this._currentSearchTreeElements[newInde
x]; | |
230 this.searchCanceled(); | |
231 this._searchRegex = searchConfig.toSearchRegex(true); | |
232 | |
233 for (var element = this._treeOutline.rootElement(); element; element = e
lement.traverseNextTreeElement(false)) { | |
234 if (!(element instanceof WebInspector.ObjectPropertyTreeElement)) | |
235 continue; | |
236 var hasMatch = element.setSearchRegex(this._searchRegex); | |
237 if (hasMatch) | |
238 this._currentSearchTreeElements.push(element); | |
239 if (previousSearchFocusElement === element) { | |
240 var currentIndex = this._currentSearchTreeElements.length - 1; | |
241 if (hasMatch || jumpBackwards) | |
242 newIndex = currentIndex; | |
243 else | |
244 newIndex = currentIndex + 1; | |
245 } | |
246 } | |
247 this._updateSearchCount(this._currentSearchTreeElements.length); | |
248 | |
249 if (!this._currentSearchTreeElements.length) { | |
250 this._updateSearchIndex(0); | |
251 return; | |
252 } | |
253 newIndex = mod(newIndex, this._currentSearchTreeElements.length); | |
254 | |
255 this._jumpToMatch(newIndex); | |
256 }, | |
257 | |
258 /** | |
259 * @override | |
260 */ | |
261 jumpToNextSearchResult: function() | |
262 { | |
263 if (!this._currentSearchTreeElements.length) | |
264 return; | |
265 var newIndex = mod(this._currentSearchFocusIndex + 1, this._currentSearc
hTreeElements.length); | |
266 this._jumpToMatch(newIndex); | |
267 }, | |
268 | |
269 /** | |
270 * @override | |
271 */ | |
272 jumpToPreviousSearchResult: function() | |
273 { | |
274 if (!this._currentSearchTreeElements.length) | |
275 return; | |
276 var newIndex = mod(this._currentSearchFocusIndex - 1, this._currentSearc
hTreeElements.length); | |
277 this._jumpToMatch(newIndex); | |
278 }, | |
279 | |
280 /** | |
281 * @override | |
282 * @return {boolean} | |
283 */ | |
284 supportsCaseSensitiveSearch: function() | |
285 { | |
286 return true; | |
287 }, | |
288 | |
289 /** | |
290 * @override | |
291 * @return {boolean} | |
292 */ | |
293 supportsRegexSearch: function() | |
294 { | |
295 return true; | |
296 }, | |
297 | |
298 __proto__: WebInspector.VBox.prototype | |
299 }; | |
300 | 285 |
301 /** | 286 /** |
302 * @constructor | 287 * @unrestricted |
303 * @param {*} data | |
304 * @param {string} prefix | |
305 * @param {string} suffix | |
306 */ | 288 */ |
307 WebInspector.ParsedJSON = function(data, prefix, suffix) | 289 WebInspector.ParsedJSON = class { |
308 { | 290 /** |
| 291 * @param {*} data |
| 292 * @param {string} prefix |
| 293 * @param {string} suffix |
| 294 */ |
| 295 constructor(data, prefix, suffix) { |
309 this.data = data; | 296 this.data = data; |
310 this.prefix = prefix; | 297 this.prefix = prefix; |
311 this.suffix = suffix; | 298 this.suffix = suffix; |
| 299 } |
312 }; | 300 }; |
OLD | NEW |