Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 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 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 { | 73 { |
| 74 this.lineNumber = lineNumber; | 74 this.lineNumber = lineNumber; |
| 75 this.columnNumber = columnNumber; | 75 this.columnNumber = columnNumber; |
| 76 this.sourceURL = sourceURL; | 76 this.sourceURL = sourceURL; |
| 77 this.sourceLineNumber = sourceLineNumber; | 77 this.sourceLineNumber = sourceLineNumber; |
| 78 this.sourceColumnNumber = sourceColumnNumber; | 78 this.sourceColumnNumber = sourceColumnNumber; |
| 79 this.name = name; | 79 this.name = name; |
| 80 } | 80 } |
| 81 | 81 |
| 82 /** | 82 /** |
| 83 * @constructor | |
| 84 * @param {?string} content | |
| 85 * @param {?Array<!WebInspector.SourceMapEntry>} reverseMappings | |
| 86 */ | |
| 87 WebInspector.SourceURLInfo = function(content, reverseMappings) { | |
|
lushnikov
2016/09/29 16:29:52
WebInspector.SourceMap.SourceInfo?
eostroukhov
2016/09/30 19:14:41
Done, made it TextSourceMap implementation detail.
| |
| 88 this.content = content; | |
| 89 this.reverseMappings = reverseMappings; | |
| 90 } | |
| 91 | |
| 92 /** | |
| 83 * @interface | 93 * @interface |
| 84 */ | 94 */ |
| 85 WebInspector.SourceMap = function() { } | 95 WebInspector.SourceMap = function() { } |
| 86 | 96 |
| 87 WebInspector.SourceMap.prototype = { | 97 WebInspector.SourceMap.prototype = { |
| 88 /** | 98 /** |
| 89 * @return {string} | 99 * @return {string} |
| 90 */ | 100 */ |
| 91 compiledURL: function() { }, | 101 compiledURL: function() { }, |
| 92 | 102 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 */ | 175 */ |
| 166 WebInspector.TextSourceMap = function(compiledURL, sourceMappingURL, payload) | 176 WebInspector.TextSourceMap = function(compiledURL, sourceMappingURL, payload) |
| 167 { | 177 { |
| 168 if (!WebInspector.TextSourceMap.prototype._base64Map) { | 178 if (!WebInspector.TextSourceMap.prototype._base64Map) { |
| 169 const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/"; | 179 const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/"; |
| 170 WebInspector.TextSourceMap.prototype._base64Map = {}; | 180 WebInspector.TextSourceMap.prototype._base64Map = {}; |
| 171 for (var i = 0; i < base64Digits.length; ++i) | 181 for (var i = 0; i < base64Digits.length; ++i) |
| 172 WebInspector.TextSourceMap.prototype._base64Map[base64Digits.charAt( i)] = i; | 182 WebInspector.TextSourceMap.prototype._base64Map[base64Digits.charAt( i)] = i; |
| 173 } | 183 } |
| 174 | 184 |
| 185 this._json = payload; | |
| 175 this._compiledURL = compiledURL; | 186 this._compiledURL = compiledURL; |
| 176 this._sourceMappingURL = sourceMappingURL; | 187 this._sourceMappingURL = sourceMappingURL; |
| 177 this._reverseMappingsBySourceURL = new Map(); | 188 /** @type {?Array<!WebInspector.SourceMapEntry>} */ |
| 178 this._mappings = []; | 189 this._mappings = null; |
| 179 this._sources = {}; | 190 /** @type {!Map<string, !WebInspector.SourceURLInfo>} */ |
| 180 this._sourceContentByURL = {}; | 191 this._sourceURLInfos = new Map(); |
| 181 this._parseMappingPayload(payload); | 192 this._eachSection(this._parseSources.bind(this)); |
| 182 } | 193 } |
| 183 | 194 |
| 184 /** | 195 /** |
| 185 * @param {string} sourceMapURL | 196 * @param {string} sourceMapURL |
| 186 * @param {string} compiledURL | 197 * @param {string} compiledURL |
| 187 * @return {!Promise<?WebInspector.TextSourceMap>} | 198 * @return {!Promise<?WebInspector.TextSourceMap>} |
| 188 * @this {WebInspector.TextSourceMap} | 199 * @this {WebInspector.TextSourceMap} |
| 189 */ | 200 */ |
| 190 WebInspector.TextSourceMap.load = function(sourceMapURL, compiledURL) | 201 WebInspector.TextSourceMap.load = function(sourceMapURL, compiledURL) |
| 191 { | 202 { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 204 if (!content || statusCode >= 400) { | 215 if (!content || statusCode >= 400) { |
| 205 callback(null); | 216 callback(null); |
| 206 return; | 217 return; |
| 207 } | 218 } |
| 208 | 219 |
| 209 if (content.slice(0, 3) === ")]}") | 220 if (content.slice(0, 3) === ")]}") |
| 210 content = content.substring(content.indexOf("\n")); | 221 content = content.substring(content.indexOf("\n")); |
| 211 try { | 222 try { |
| 212 var payload = /** @type {!SourceMapV3} */ (JSON.parse(content)); | 223 var payload = /** @type {!SourceMapV3} */ (JSON.parse(content)); |
| 213 var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourc eMapURL; | 224 var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourc eMapURL; |
| 214 | |
| 215 callback(new WebInspector.TextSourceMap(compiledURL, baseURL, payloa d)); | 225 callback(new WebInspector.TextSourceMap(compiledURL, baseURL, payloa d)); |
| 216 } catch (e) { | 226 } catch (e) { |
| 217 console.error(e); | 227 console.error(e); |
| 218 WebInspector.console.warn("DevTools failed to parse SourceMap: " + s ourceMapURL); | 228 WebInspector.console.warn("DevTools failed to parse SourceMap: " + s ourceMapURL); |
| 219 callback(null); | 229 callback(null); |
| 220 } | 230 } |
| 221 } | 231 } |
| 222 } | 232 } |
| 223 | 233 |
| 224 WebInspector.TextSourceMap.prototype = { | 234 WebInspector.TextSourceMap.prototype = { |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 239 { | 249 { |
| 240 return this._sourceMappingURL; | 250 return this._sourceMappingURL; |
| 241 }, | 251 }, |
| 242 | 252 |
| 243 /** | 253 /** |
| 244 * @override | 254 * @override |
| 245 * @return {!Array.<string>} | 255 * @return {!Array.<string>} |
| 246 */ | 256 */ |
| 247 sourceURLs: function() | 257 sourceURLs: function() |
| 248 { | 258 { |
| 249 return Object.keys(this._sources); | 259 return this._sourceURLInfos.keysArray(); |
| 250 }, | 260 }, |
| 251 | 261 |
| 252 /** | 262 /** |
| 253 * @override | 263 * @override |
| 254 * @param {string} sourceURL | 264 * @param {string} sourceURL |
| 255 * @param {!WebInspector.ResourceType} contentType | 265 * @param {!WebInspector.ResourceType} contentType |
| 256 * @return {!WebInspector.ContentProvider} | 266 * @return {!WebInspector.ContentProvider} |
| 257 */ | 267 */ |
| 258 sourceContentProvider: function(sourceURL, contentType) | 268 sourceContentProvider: function(sourceURL, contentType) |
| 259 { | 269 { |
| 260 var sourceContent = this._sourceContentByURL[sourceURL]; | 270 var info = this._sourceURLInfos.get(sourceURL); |
| 261 if (sourceContent) | 271 if (info.content) |
| 262 return WebInspector.StaticContentProvider.fromString(sourceURL, cont entType, sourceContent); | 272 return WebInspector.StaticContentProvider.fromString(sourceURL, cont entType, info.content); |
| 263 return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType); | 273 return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType); |
| 264 }, | 274 }, |
| 265 | 275 |
| 266 /** | 276 /** |
| 267 * @override | 277 * @override |
| 268 * @return {boolean} | 278 * @return {boolean} |
| 269 */ | 279 */ |
| 270 editable: function() | 280 editable: function() |
| 271 { | 281 { |
| 272 return false; | 282 return false; |
| 273 }, | 283 }, |
| 274 | 284 |
| 275 /** | 285 /** |
| 276 * @override | 286 * @override |
| 277 * @param {!Array<!WebInspector.TextRange>} ranges | 287 * @param {!Array<!WebInspector.TextRange>} ranges |
| 278 * @param {!Array<string>} texts | 288 * @param {!Array<string>} texts |
| 279 * @return {!Promise<?WebInspector.SourceMap.EditResult>} | 289 * @return {!Promise<?WebInspector.SourceMap.EditResult>} |
| 280 */ | 290 */ |
| 281 editCompiled: function(ranges, texts) | 291 editCompiled: function(ranges, texts) |
| 282 { | 292 { |
| 283 return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */ (null)); | 293 return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */ (null)); |
| 284 }, | 294 }, |
| 285 | 295 |
| 286 /** | 296 /** |
| 287 * @param {!SourceMapV3} mappingPayload | |
| 288 */ | |
| 289 _parseMappingPayload: function(mappingPayload) | |
| 290 { | |
| 291 if (mappingPayload.sections) | |
| 292 this._parseSections(mappingPayload.sections); | |
| 293 else | |
| 294 this._parseMap(mappingPayload, 0, 0); | |
| 295 }, | |
| 296 | |
| 297 /** | |
| 298 * @param {!Array.<!SourceMapV3.Section>} sections | |
| 299 */ | |
| 300 _parseSections: function(sections) | |
| 301 { | |
| 302 for (var i = 0; i < sections.length; ++i) { | |
| 303 var section = sections[i]; | |
| 304 this._parseMap(section.map, section.offset.line, section.offset.colu mn); | |
| 305 } | |
| 306 }, | |
| 307 | |
| 308 /** | |
| 309 * @override | 297 * @override |
| 310 * @param {number} lineNumber in compiled resource | 298 * @param {number} lineNumber in compiled resource |
| 311 * @param {number} columnNumber in compiled resource | 299 * @param {number} columnNumber in compiled resource |
| 312 * @return {?WebInspector.SourceMapEntry} | 300 * @return {?WebInspector.SourceMapEntry} |
| 313 */ | 301 */ |
| 314 findEntry: function(lineNumber, columnNumber) | 302 findEntry: function(lineNumber, columnNumber) |
| 315 { | 303 { |
| 316 var first = 0; | 304 var first = 0; |
| 317 var count = this._mappings.length; | 305 var mappings = this.mappings(); |
| 306 var count = mappings.length; | |
| 318 while (count > 1) { | 307 while (count > 1) { |
| 319 var step = count >> 1; | 308 var step = count >> 1; |
| 320 var middle = first + step; | 309 var middle = first + step; |
| 321 var mapping = this._mappings[middle]; | 310 var mapping = mappings[middle]; |
| 322 if (lineNumber < mapping.lineNumber || (lineNumber === mapping.lineN umber && columnNumber < mapping.columnNumber)) { | 311 if (lineNumber < mapping.lineNumber || (lineNumber === mapping.lineN umber && columnNumber < mapping.columnNumber)) { |
| 323 count = step; | 312 count = step; |
| 324 } else { | 313 } else { |
| 325 first = middle; | 314 first = middle; |
| 326 count -= step; | 315 count -= step; |
| 327 } | 316 } |
| 328 } | 317 } |
| 329 var entry = this._mappings[first]; | 318 var entry = mappings[first]; |
| 330 if (!first && entry && (lineNumber < entry.lineNumber || (lineNumber === entry.lineNumber && columnNumber < entry.columnNumber))) | 319 if (!first && entry && (lineNumber < entry.lineNumber || (lineNumber === entry.lineNumber && columnNumber < entry.columnNumber))) |
| 331 return null; | 320 return null; |
| 332 return entry; | 321 return entry; |
| 333 }, | 322 }, |
| 334 | 323 |
| 335 /** | 324 /** |
| 336 * @param {string} sourceURL | 325 * @param {string} sourceURL |
| 337 * @param {number} lineNumber | 326 * @param {number} lineNumber |
| 338 * @return {?WebInspector.SourceMapEntry} | 327 * @return {?WebInspector.SourceMapEntry} |
| 339 */ | 328 */ |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 354 { | 343 { |
| 355 return lineNumber - mapping.sourceLineNumber; | 344 return lineNumber - mapping.sourceLineNumber; |
| 356 } | 345 } |
| 357 }, | 346 }, |
| 358 | 347 |
| 359 /** | 348 /** |
| 360 * @return {!Array<!WebInspector.SourceMapEntry>} | 349 * @return {!Array<!WebInspector.SourceMapEntry>} |
| 361 */ | 350 */ |
| 362 mappings: function() | 351 mappings: function() |
| 363 { | 352 { |
| 364 return this._mappings; | 353 if (this._mappings === null) { |
| 354 this._mappings = []; | |
| 355 this._eachSection(this._parseMap.bind(this)); | |
| 356 this._json = null; | |
| 357 } | |
| 358 return /** @type {!Array<!WebInspector.SourceMapEntry>} */ (this._mappin gs); | |
| 365 }, | 359 }, |
| 366 | 360 |
| 367 /** | 361 /** |
| 368 * @param {string} sourceURL | 362 * @param {string} sourceURL |
| 369 * @return {!Array.<!WebInspector.SourceMapEntry>} | 363 * @return {!Array.<!WebInspector.SourceMapEntry>} |
| 370 */ | 364 */ |
| 371 _reversedMappings: function(sourceURL) | 365 _reversedMappings: function(sourceURL) |
| 372 { | 366 { |
| 373 var mappings = this._reverseMappingsBySourceURL.get(sourceURL); | 367 if (this._sourceURLInfos && !this._sourceURLInfos.has(sourceURL)) |
|
lushnikov
2016/09/29 16:29:52
how could this._sourceURLInfos be null?
eostroukhov
2016/09/30 19:14:41
It can't. Thanks for pointing out.
| |
| 374 if (!mappings) | |
| 375 return []; | 368 return []; |
| 376 if (!mappings._sorted) { | 369 var mappings = this.mappings(); |
| 377 mappings.sort(sourceMappingComparator); | 370 var info = this._sourceURLInfos.get(sourceURL); |
| 378 mappings._sorted = true; | 371 if (info.reverseMappings === null) |
| 379 } | 372 info.reverseMappings = mappings.filter((mapping) => mapping.sourceUR L === sourceURL).sort(sourceMappingComparator); |
| 380 return mappings; | 373 return info.reverseMappings; |
| 381 | 374 |
| 382 /** | 375 /** |
| 383 * @param {!WebInspector.SourceMapEntry} a | 376 * @param {!WebInspector.SourceMapEntry} a |
| 384 * @param {!WebInspector.SourceMapEntry} b | 377 * @param {!WebInspector.SourceMapEntry} b |
| 385 * @return {number} | 378 * @return {number} |
| 386 */ | 379 */ |
| 387 function sourceMappingComparator(a, b) | 380 function sourceMappingComparator(a, b) |
| 388 { | 381 { |
| 389 if (a.sourceLineNumber !== b.sourceLineNumber) | 382 if (a.sourceLineNumber !== b.sourceLineNumber) |
| 390 return a.sourceLineNumber - b.sourceLineNumber; | 383 return a.sourceLineNumber - b.sourceLineNumber; |
| 391 if (a.sourceColumnNumber !== b.sourceColumnNumber) | 384 if (a.sourceColumnNumber !== b.sourceColumnNumber) |
| 392 return a.sourceColumnNumber - b.sourceColumnNumber; | 385 return a.sourceColumnNumber - b.sourceColumnNumber; |
| 393 | 386 |
| 394 if (a.lineNumber !== b.lineNumber) | 387 if (a.lineNumber !== b.lineNumber) |
| 395 return a.lineNumber - b.lineNumber; | 388 return a.lineNumber - b.lineNumber; |
| 396 | 389 |
| 397 return a.columnNumber - b.columnNumber; | 390 return a.columnNumber - b.columnNumber; |
| 398 } | 391 } |
| 399 }, | 392 }, |
| 400 | 393 |
| 401 /** | 394 /** |
| 395 * @param {function(!SourceMapV3, number, number)} callback | |
| 396 */ | |
| 397 _eachSection: function(callback) { | |
| 398 if (!this._json.sections) { | |
| 399 callback(this._json, 0, 0); | |
| 400 return; | |
| 401 } | |
| 402 for (var section of this._json.sections) | |
| 403 callback(section.map, section.offset.line, section.offset.column); | |
| 404 }, | |
| 405 | |
| 406 /** | |
| 407 * @param {!SourceMapV3} sourceMap | |
| 408 * @param {function(string, ?string)} callback | |
| 409 */ | |
| 410 _visitSourceURLs: function(sourceMap, callback) { | |
| 411 var sourceRoot = sourceMap.sourceRoot || ""; | |
| 412 if (sourceRoot && !sourceRoot.endsWith("/")) | |
| 413 sourceRoot += "/"; | |
| 414 for (var i = 0; i < sourceMap.sources.length; ++i) { | |
| 415 var href = sourceRoot + sourceMap.sources[i]; | |
| 416 var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href; | |
| 417 var source = sourceMap.sourcesContent && sourceMap.sourcesContent[i] ; | |
| 418 if (url === this._compiledURL && source) | |
| 419 url += WebInspector.UIString(" [sm]"); | |
| 420 callback(url, source); | |
| 421 } | |
| 422 }, | |
| 423 | |
| 424 /** | |
| 425 * @param {!SourceMapV3} sourceMap | |
| 426 */ | |
| 427 _parseSources: function(sourceMap) { | |
|
lushnikov
2016/09/29 16:29:53
yeah, callback is unfortunate
Let's do the follow
eostroukhov
2016/09/30 19:14:41
Done.
| |
| 428 this._visitSourceURLs(sourceMap, | |
| 429 (url, source) => this._sourceURLInfos.set(url, new WebInspector.Sour ceURLInfo(source, null))); | |
| 430 }, | |
| 431 | |
| 432 /** | |
| 402 * @param {!SourceMapV3} map | 433 * @param {!SourceMapV3} map |
| 403 * @param {number} lineNumber | 434 * @param {number} lineNumber |
| 404 * @param {number} columnNumber | 435 * @param {number} columnNumber |
| 405 */ | 436 */ |
| 406 _parseMap: function(map, lineNumber, columnNumber) | 437 _parseMap: function(map, lineNumber, columnNumber) |
| 407 { | 438 { |
| 408 var sourceIndex = 0; | 439 var sourceIndex = 0; |
| 409 var sourceLineNumber = 0; | 440 var sourceLineNumber = 0; |
| 410 var sourceColumnNumber = 0; | 441 var sourceColumnNumber = 0; |
| 411 var nameIndex = 0; | 442 var nameIndex = 0; |
| 412 | |
| 413 var sources = []; | 443 var sources = []; |
| 414 var names = map.names || []; | 444 var names = map.names || []; |
| 415 var sourceRoot = map.sourceRoot || ""; | |
| 416 if (sourceRoot && !sourceRoot.endsWith("/")) | |
| 417 sourceRoot += "/"; | |
| 418 for (var i = 0; i < map.sources.length; ++i) { | |
| 419 var href = sourceRoot + map.sources[i]; | |
| 420 var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href; | |
| 421 var hasSource = map.sourcesContent && map.sourcesContent[i]; | |
| 422 if (url === this._compiledURL && hasSource) | |
| 423 url += WebInspector.UIString(" [sm]"); | |
| 424 sources.push(url); | |
| 425 this._sources[url] = true; | |
| 426 | |
| 427 if (hasSource) | |
| 428 this._sourceContentByURL[url] = map.sourcesContent[i]; | |
| 429 } | |
| 430 | |
| 431 var stringCharIterator = new WebInspector.TextSourceMap.StringCharIterat or(map.mappings); | 445 var stringCharIterator = new WebInspector.TextSourceMap.StringCharIterat or(map.mappings); |
| 446 this._visitSourceURLs(map, (url) => sources.push(url)); | |
| 432 var sourceURL = sources[sourceIndex]; | 447 var sourceURL = sources[sourceIndex]; |
| 433 | 448 |
| 434 while (true) { | 449 while (true) { |
| 435 if (stringCharIterator.peek() === ",") | 450 if (stringCharIterator.peek() === ",") |
| 436 stringCharIterator.next(); | 451 stringCharIterator.next(); |
| 437 else { | 452 else { |
| 438 while (stringCharIterator.peek() === ";") { | 453 while (stringCharIterator.peek() === ";") { |
| 439 lineNumber += 1; | 454 lineNumber += 1; |
| 440 columnNumber = 0; | 455 columnNumber = 0; |
| 441 stringCharIterator.next(); | 456 stringCharIterator.next(); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 459 sourceColumnNumber += this._decodeVLQ(stringCharIterator); | 474 sourceColumnNumber += this._decodeVLQ(stringCharIterator); |
| 460 | 475 |
| 461 if (!stringCharIterator.hasNext() || this._isSeparator(stringCharIte rator.peek())) { | 476 if (!stringCharIterator.hasNext() || this._isSeparator(stringCharIte rator.peek())) { |
| 462 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber)); | 477 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber)); |
| 463 continue; | 478 continue; |
| 464 } | 479 } |
| 465 | 480 |
| 466 nameIndex += this._decodeVLQ(stringCharIterator); | 481 nameIndex += this._decodeVLQ(stringCharIterator); |
| 467 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, colu mnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex])); | 482 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, colu mnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex])); |
| 468 } | 483 } |
| 469 | |
| 470 for (var i = 0; i < this._mappings.length; ++i) { | |
| 471 var mapping = this._mappings[i]; | |
| 472 var url = mapping.sourceURL; | |
| 473 if (!url) | |
| 474 continue; | |
| 475 if (!this._reverseMappingsBySourceURL.has(url)) | |
| 476 this._reverseMappingsBySourceURL.set(url, []); | |
| 477 var reverseMappings = this._reverseMappingsBySourceURL.get(url); | |
| 478 reverseMappings.push(mapping); | |
| 479 } | |
| 480 }, | 484 }, |
| 481 | 485 |
| 482 /** | 486 /** |
| 483 * @param {string} char | 487 * @param {string} char |
| 484 * @return {boolean} | 488 * @return {boolean} |
| 485 */ | 489 */ |
| 486 _isSeparator: function(char) | 490 _isSeparator: function(char) |
| 487 { | 491 { |
| 488 return char === "," || char === ";"; | 492 return char === "," || char === ";"; |
| 489 }, | 493 }, |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 }, | 575 }, |
| 572 | 576 |
| 573 /** | 577 /** |
| 574 * @return {boolean} | 578 * @return {boolean} |
| 575 */ | 579 */ |
| 576 hasNext: function() | 580 hasNext: function() |
| 577 { | 581 { |
| 578 return this._position < this._string.length; | 582 return this._position < this._string.length; |
| 579 } | 583 } |
| 580 } | 584 } |
| OLD | NEW |