1Demo = (function () { 2 'use strict'; 3 4 function $$(id) { 5 return window.document.getElementById(id); 6 } 7 8 function StatusInfo(configs) { 9 this.configs = configs; 10 this.element = null; 11 12 var self = this; 13 14 this.showInfo = function (message) { 15 var el = $$(self.configs.container); 16 typeof el.innerText === 'string' ? (el.innerText = message) : (el.textContent = message); 17 el.setAttribute('class', 'alert-box success'); 18 }; 19 20 this.showWarning = function (message) { 21 var el = $$(self.configs.container); 22 typeof el.innerText === 'string' ? (el.innerText = message) : (el.textContent = message); 23 el.setAttribute('class', 'alert-box secondary'); 24 }; 25 26 this.showError = function (message) { 27 var el = $$(self.configs.container); 28 typeof el.innerText === 'string' ? (el.innerText = message) : (el.textContent = message); 29 el.setAttribute('class', 'alert-box alert'); 30 }; 31 } 32 33 function CodeEditor(configs, listeners) { 34 this.configs = configs || {}; 35 this.listeners = listeners || {}; 36 37 this.editor = null; 38 this.debounceTimer = null; 39 40 var self = this; 41 42 function registerCustomLanguage(language) { 43 monaco.languages.register({ id: language }); 44 // Mimic Monaco's built-in Monarch for JavaScript 45 // https://github.com/microsoft/monaco-languages 46 monaco.languages.setMonarchTokensProvider(language, { 47 defaultToken: 'invalid', 48 tokenPostfix: '.js', 49 50 keywords: [ 51 'break', 52 'case', 53 'catch', 54 'class', 55 'continue', 56 'const', 57 'constructor', 58 'debugger', 59 'default', 60 'delete', 61 'do', 62 'else', 63 'export', 64 'extends', 65 'false', 66 'finally', 67 'for', 68 'from', 69 'function', 70 'get', 71 'if', 72 'import', 73 'in', 74 'instanceof', 75 'let', 76 'new', 77 'null', 78 'return', 79 'set', 80 'super', 81 'switch', 82 'symbol', 83 'this', 84 'throw', 85 'true', 86 'try', 87 'typeof', 88 'undefined', 89 'var', 90 'void', 91 'while', 92 'with', 93 'yield', 94 'async', 95 'await', 96 'of' 97 ], 98 99 operators: [ 100 '<=', 101 '>=', 102 '==', 103 '!=', 104 '===', 105 '!==', 106 '=>', 107 '+', 108 '-', 109 '**', 110 '*', 111 '/', 112 '%', 113 '++', 114 '--', 115 '<<', 116 '</', 117 '>>', 118 '>>>', 119 '&', 120 '|', 121 '^', 122 '!', 123 '~', 124 '&&', 125 '||', 126 '?', 127 ':', 128 '=', 129 '+=', 130 '-=', 131 '*=', 132 '**=', 133 '/=', 134 '%=', 135 '<<=', 136 '>>=', 137 '>>>=', 138 '&=', 139 '|=', 140 '^=', 141 '@' 142 ], 143 144 symbols: /[=><!~?:&|+\-*\/\^%]+/, 145 escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/, 146 digits: /\d+(_+\d+)*/, 147 octaldigits: /[0-7]+(_+[0-7]+)*/, 148 binarydigits: /[0-1]+(_+[0-1]+)*/, 149 hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/, 150 151 regexpctl: /[(){}\[\]\$\^|\-*+?\.]/, 152 regexpesc: /\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/, 153 154 tokenizer: { 155 root: [[/[{}]/, 'delimiter.bracket'], { include: 'common' }], 156 157 common: [
158 // identifiers and keywords 159 [ 160 /[a-z_$][\w$]*/, 161 { 162 cases: { 163 '@keywords': 'keyword', 164 '@default': 'identifier' 165 } 166 } 167 ], 168 [/[A-Z][\w\$]*/, 'type.identifier'], // to show class names nicely 169 // [/[A-Z][\w\$]*/, 'identifier'], 170 171 // whitespace 172 { include: '@whitespace' }, 173 174 // regular expression: ensure it is terminated before beginning (otherwise it is an opeator) 175 [ 176 /\/(?=([^\\\/]|\\.)+\/([gimsuy]*)(\s*)(\.|;|\/|,|\)|\]|\}|$))/, 177 { token: 'regexp', bracket: '@open', next: '@regexp' } 178 ], 179 180 // delimiters and operators 181 [/[()\[\]]/, '@brackets'], 182 [/[<>](?!@symbols)/, '@brackets'], 183 [ 184 /@symbols/, 185 { 186 cases: { 187 '@operators': 'delimiter', 188 '@default': '' 189 } 190 } 191 ], 192 193 // numbers 194 [/(@digits)[eE]([\-+]?(@digits))?/, 'number.float'], 195 [/(@digits)\.(@digits)([eE][\-+]?(@digits))?/, 'number.float'], 196 [/0[xX](@hexdigits)/, 'number.hex'], 197 [/0[oO]?(@octaldigits)/, 'number.octal'], 198 [/0[bB](@binarydigits)/, 'number.binary'], 199 [/(@digits)/, 'number'], 200 201 // delimiter: after number because of .\d floats 202 [/[;,.]/, 'delimiter'], 203 204 // strings 205 [/"([^"\\]|\\.)*$/, 'string.invalid'], // non-teminated string 206 [/'([^'\\]|\\.)*$/, 'string.invalid'], // non-teminated string 207 [/"/, 'string', '@string_double'], 208 [/'/, 'string', '@string_single'], 209 [/`/, 'string', '@string_backtick'] 210 ], 211 212 whitespace: [ 213 [/[ \t\r\n]+/, ''], 214 [/\/\*/, 'comment', '@comment'], 215 [/\/\/.*$/, 'comment'] 216 ], 217 218 comment: [ 219 [/[^\/*]+/, 'comment'], 220 [/\*\//, 'comment', '@pop'], 221 [/[\/*]/, 'comment'] 222 ], 223 224 // We match regular expression quite precisely 225 regexp: [ 226 [ 227 /(\{)(\d+(?:,\d*)?)(\})/, 228 ['regexp.escape.control', 'regexp.escape.control', 'regexp.escape.control'] 229 ], 230 [ 231 /(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/, 232 ['regexp.escape.control', { token: 'regexp.escape.control', next: '@regexrange' }] 233 ], 234 [/(\()(\?:|\?=|\?!)/, ['regexp.escape.control', 'regexp.escape.control']], 235 [/[()]/, 'regexp.escape.control'], 236 [/@regexpctl/, 'regexp.escape.control'], 237 [/[^\\\/]/, 'regexp'], 238 [/@regexpesc/, 'regexp.escape'], 239 [/\\\./, 'regexp.invalid'], 240 [/(\/)([gimsuy]*)/, [{ token: 'regexp', bracket: '@close', next: '@pop' }, 'keyword.other']] 241 ], 242 243 regexrange: [ 244 [/-/, 'regexp.escape.control'], 245 [/\^/, 'regexp.invalid'], 246 [/@regexpesc/, 'regexp.escape'], 247 [/[^\]]/, 'regexp'], 248 [/\]/, { token: 'regexp.escape.control', next: '@pop', bracket: '@close' }] 249 ], 250
251 string_double: [ 252 [/[^\\"]+/, 'string'], 253 [/@escapes/, 'string.escape'], 254 [/\\./, 'string.escape.invalid'], 255 [/"/, 'string', '@pop'] 256 ], 257 258 string_single: [ 259 [/[^\\']+/, 'string'], 260 [/@escapes/, 'string.escape'], 261 [/\\./, 'string.escape.invalid'], 262 [/'/, 'string', '@pop'] 263 ], 264 265 string_backtick: [ 266 [/\$\{/, { token: 'delimiter.bracket', next: '@bracketCounting' }], 267 [/[^\\`$]+/, 'string'], 268 [/@escapes/, 'string.escape'], 269 [/\\./, 'string.escape.invalid'], 270 [/`/, 'string', '@pop'] 271 ], 272 273 bracketCounting: [ 274 [/\{/, 'delimiter.bracket', '@bracketCounting'], 275 [/\}/, 'delimiter.bracket', '@pop'], 276 { include: 'common' } 277 ] 278 } 279 }); 280 } 281 282 require.config({ paths: { vs: '../assets/monaco/min/vs' } }); 283 require(['vs/editor/editor.main'], function () { 284 var language = self.configs.language; 285 if (language) { 286 registerCustomLanguage(language); 287 } else { 288 language = 'javascript'; 289 } 290 291 monaco.editor.defineTheme('custom', { 292 base: 'vs', 293 inherit: true, 294 rules: [{ token: 'comment', foreground: 'aaaaaa', fontStyle: 'italic' }], 295 colors: { 296 'editorLineNumber.foreground': '#ccc',
297 'editor.lineHighlightBackground': '#eaf2fe' 298 } 299 }); 300 301 var element = $$(self.configs.container); 302 303 self.editor = monaco.editor.create(element, { 304 language: language, 305 folding: false, 306 lineNumbersMinChars: 3, 307 fontSize: 14, 308 minimap: { 309 enabled: false 310 }, 311 theme: 'custom', 312 wordBasedSuggestions: false, 313 quickSuggestions: false 314 }); 315 316 if (self.configs.value) self.editor.getModel().setValue($$(self.configs.value).textContent); 317 318 self.editor.getModel().onDidChangeContent(function () { 319 if (self.editor && self.listeners.contentChanged) { 320 if (self.debounceTimer) window.clearTimeout(self.debounceTimer); 321 self.debounceTimer = window.setTimeout(self.listeners.contentChanged, 200); 322 } 323 }); 324 325 if (self.listeners.cursorMoved) self.editor.onDidChangeCursorPosition(self.listeners.cursorMoved); 326 327 if (self.listeners.ready) self.listeners.ready(); 328 self.editor.focus(); 329 }); 330 331 this.getValue = function () { 332 return self.editor.getModel().getValue(); 333 }; 334 335 this.setValue = function (value) { 336 self.editor.getModel().setValue(value); 337 }; 338 339 this.getCursorOffset = function () { 340 var selection = self.editor.getSelection(); 341 return self.editor.getModel().getOffsetAt(selection.getPosition()); 342 }; 343 344 this.setCursorOffset = function (offset) { 345 var pos = self.editor.getModel().getPositionAt(offset); 346 self.editor.setSelection(new monaco.Selection(pos.lineNumber, pos.column, pos.lineNumber, pos.column)); 347 }; 348 349 this.getPositionAt = function (offset) { 350 return self.editor.getModel().getPositionAt(offset); 351 }; 352 353 this.markers = []; 354 355 this.clearMarkers = function () { 356 self.markers = []; 357 monaco.editor.setModelMarkers(self.editor.getModel(), 'code-editor', this.markers); 358 }; 359 360 this.addErrorMarker = function (message, lineNumber, column) { 361 self.markers.push({ 362 severity: monaco.MarkerSeverity.Error, 363 startLineNumber: lineNumber, 364 startColumn: column, 365 endLineNumber: lineNumber, 366 endColumn: column, 367 message: message 368 }); 369 monaco.editor.setModelMarkers(self.editor.getModel(), 'code-editor', this.markers); 370 }; 371 372 this.addInfoMarker = function (message, lineNumber, column) { 373 self.markers.push({ 374 severity: monaco.MarkerSeverity.Info, 375 startLineNumber: lineNumber, 376 startColumn: column, 377 endLineNumber: lineNumber, 378 endColumn: column, 379 message: message 380 }); 381 monaco.editor.setModelMarkers(self.editor.getModel(), 'code-editor', this.markers); 382 }; 383 384 this.previousDecorations = []; 385 386 this.clearDecorations = function () { 387 self.previousDecorations = self.editor.deltaDecorations(self.previousDecorations, []); 388 }; 389 390 this.createDecoration = function (className, loc) { 391 return { 392 range: new monaco.Range(loc.start.line, loc.start.column + 1, loc.end.line, loc.end.column + 1), 393 options: { 394 inlineClassName: className 395 } 396 }; 397 }; 398 399 this.applyDecorations = function (highlights) { 400 self.previousDecorations = self.editor.deltaDecorations(self.previousDecorations, highlights); 401 }; 402 } 403 404 function TreeViz(configs, listeners) { 405 this.configs = configs || {}; 406 this.listeners = listeners || {}; 407 408 var self = this; 409 410 $$('show_tree').onclick = function () { 411 $$('tab_tree').className = $$('show_tree').className = 'active'; 412 $$('tab_syntax').className = $$('show_syntax').className = ''; 413 $$('tab_tokens').className = $$('show_tokens').className = ''; 414 $$('tab_tree').style.display = ''; 415 $$('tab_syntax').style.display = 'none'; 416 $$('tab_tokens').style.display = 'none'; 417 if (self.listeners.tabSwitched) self.listeners.tabSwitched(); 418 }; 419 420 $$('show_syntax').onclick = function () { 421 $$('tab_tree').className = $$('show_tree').className = ''; 422 $$('tab_syntax').className = $$('show_syntax').className = 'active'; 423 $$('tab_tokens').className = $$('show_tokens').className = ''; 424 $$('tab_tree').style.display = 'none'; 425 $$('tab_syntax').style.display = ''; 426 $$('tab_tokens').style.display = 'none'; 427 if (self.listeners.tabSwitched) self.listeners.tabSwitched(); 428 }; 429 430 $$('show_tokens').onclick = function () { 431 $$('tab_tree').className = $$('show_tree').className = ''; 432 $$('tab_syntax').className = $$('show_syntax').className = ''; 433 $$('tab_tokens').className = $$('show_tokens').className = 'active'; 434 $$('tab_tree').style.display = 'none'; 435 $$('tab_syntax').style.display = 'none'; 436 $$('tab_tokens').style.display = ''; 437 if (self.listeners.tabSwitched) self.listeners.tabSwitched(); 438 }; 439 440 $('#syntaxtree').tree({ 441 autoOpen: true, 442 dragAndDrop: false, 443 openedIcon: '-', 444 closedIcon: '+' 445 }); 446 447 // Special handling for regular expression literal since we need to 448 // convert it to a string literal, otherwise it will be decoded 449 // as object "{}" and the regular expression would be lost. 450 function adjustRegexLiteral(key, value) { 451 if (key === 'value' && value instanceof RegExp) { 452 value = value.toString(); 453 } 454 return value; 455 } 456 457 this.updateSyntax = function (syntax) { 458 $$('syntax').value = JSON.stringify(syntax, adjustRegexLiteral, 2); 459 }; 460 461 this.updateTokens = function (tokens) { 462 $$('tokens').value = JSON.stringify(tokens, adjustRegexLiteral, 4); 463 }; 464 465 this.updateTree = function (syntax) { 466 if (typeof syntax === 'undefined') { 467 $('#syntaxtree').tree('loadData', []); 468 return; 469 } 470 471 function isArray(o) { 472 return typeof Array.isArray === 'function' 473 ? Array.isArray(o) 474 : Object.prototype.toString.apply(o) === '[object Array]'; 475 } 476 477 function convert(node) { 478 var data = [], 479 value; 480 if (isArray(node)) { 481 for (var i = 0; i < node.length; ++i) { 482 value = node[i]; 483 data.push({ 484 name: '#' + (i + 1), 485 children: convert(value) 486 }); 487 } 488 } else { 489 for (var key in node) { 490 if (Object.prototype.hasOwnProperty.call(node, key)) { 491 value = node[key]; 492 switch (typeof value) { 493 case 'string': 494 case 'number': 495 case 'boolean': 496 data.push({ name: key + ': ' + value.toString() }); 497 break; 498 case 'object': 499 if (node instanceof RegExp) { 500 data.push({ name: key + ': ' + value.toString() }); 501 } else if (key === 'range' && isArray(value) && value.length === 2) { 502 data.push({ name: key + ': [' + value[0] + ', ' + value[1] + ']' }); 503 } else { 504 data.push({ name: key, children: convert(value) }); 505 } 506 break; 507 default: 508 break; 509 } 510 } 511 } 512 } 513 return data; 514 } 515 516 $('#syntaxtree').tree('loadData', convert(syntax)); 517 }; 518 } 519 520 function deduceSourceType(code) { 521 var type = 'module';
522 523 try { 524 esprima.parse(code, { sourceType: 'script' }); 525 type = 'script'; 526 } catch (e) {} 527 528 return type; 529 } 530 531 // parse.html 532 function visualizer() { 533 var statusInfo = new StatusInfo({ container: 'info' }); 534 535 var codeEditor = new CodeEditor( 536 { container: 'editor', value: 'init' }, 537 { 538 ready: initialize, 539 contentChanged: visualize 540 } 541 ); 542 543 var treeViz = new TreeViz(); 544 545 function initialize() { 546 window.setTimeout(visualize, 0); // first time 547 548 statusInfo.showInfo('Ready.'); 549 550 $$('comment').onchange = visualize; 551 $$('range').onchange = visualize; 552 $$('loc').onchange = visualize; 553 554 // Special handling for IE. 555 $$('comment').onclick = visualize; 556 $$('range').onclick = visualize; 557 $$('loc').onclick = visualize; 558 559 if (location.search) { 560 var queries = {}; 561 var elements = location.search.substring(1).split(/[;&]/); 562 for (var i = 0; i < elements.length; i += 1) { 563 var pair = elements[i].split('='); 564 if (pair.length === 2) { 565 queries[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); 566 } 567 } 568 var code = queries.code; 569 if (code) { 570 codeEditor.setValue(code); 571 visualize(); 572 } 573 } 574 } 575 576 function visualize() { 577 var code = codeEditor.getValue(); 578 579 var loc = window.location; 580 $$('url').value = loc.protocol + '//' + loc.host + loc.pathname + '?code=' + encodeURIComponent(code); 581 582 var options = { 583 attachComment: $$('comment').checked, 584 range: $$('range').checked, 585 loc: $$('loc').checked, 586 sourceType: deduceSourceType(code) 587 }; 588 try { 589 var syntax = esprima.parse(code, options); 590 treeViz.updateTree(syntax); 591 treeViz.updateSyntax(syntax); 592 options.tokens = true; 593 treeViz.updateTokens(esprima.parse(code, options).tokens); 594 statusInfo.showInfo('No error.'); 595 } catch (e) { 596 statusInfo.showError(e.toString()); 597 } 598 } 599 } 600 601 // validate.html 602 function validator() { 603 var statusInfo = new StatusInfo({ container: 'info' }); 604 605 var codeEditor = new CodeEditor( 606 { container: 'editor', value: 'init' }, 607 { 608 ready: initialize, 609 contentChanged: validate 610 } 611 ); 612 613 function initialize() { 614 statusInfo.showInfo('Ready.'); 615 validate(); // first time 616 } 617 618 function validate() { 619 try { 620 codeEditor.clearMarkers(); 621 var code = codeEditor.getValue(); 622 var syntax = esprima.parse(code, { tolerant: true, loc: true, range: true }); 623 if (syntax.errors.length > 0) { 624 for (var i = 0; i < syntax.errors.length; ++i) { 625 var e = syntax.errors[i]; 626 codeEditor.addErrorMarker(e.description, e.lineNumber, e.column); 627 } 628 statusInfo.showError('Invalid code. Total issues: ' + syntax.errors.length); 629 } else { 630 if (syntax.body.length === 0) { 631 statusInfo.showWarning('info', 'Empty code. Nothing to validate.'); 632 } 633 statusInfo.showInfo('Code is syntactically valid.'); 634 } 635 } catch (e) { 636 codeEditor.addErrorMarker(e.toString(), e.lineNumber, e.column); 637 statusInfo.showError(e.toString()); 638 } 639 } 640 } 641 642 // collector.html 643 function regexCollector() { 644 var statusInfo = new StatusInfo({ container: 'info' }); 645 646 var codeEditor = new CodeEditor( 647 { container: 'editor', value: 'init' }, 648 { 649 ready: initialize, 650 contentChanged: collect 651 } 652 ); 653 654 function initialize() { 655 statusInfo.showInfo('Ready.'); 656 collect();
656 // first time 657 } 658 659 function collect() { 660 function createRegex(pattern, mode) { 661 var literal; 662 try { 663 literal = new RegExp(pattern, mode); 664 } catch (e) { 665 // Invalid regular expression. 666 return; 667 } 668 return literal; 669 } 670 671 codeEditor.clearDecorations(); 672 try { 673 var highlights = []; 674 var code = codeEditor.getValue(); 675 676 esprima.parse(code, { tolerant: true, loc: true }, function (node) { 677 if (node.type === 'Literal') { 678 if (node.value instanceof RegExp) 679 highlights.push(codeEditor.createDecoration('highlight-decoration', node.loc)); 680 } 681 if (node.type === 'NewExpression' || node.type === 'CallExpression') { 682 if (node.callee.type === 'Identifier' && node.callee.name === 'RegExp') { 683 var arg = node['arguments']; 684 if (arg.length === 1 && arg[0].type === 'Literal') { 685 if (typeof arg[0].value === 'string') { 686 if (createRegex(arg[0].value)) 687 highlights.push(codeEditor.createDecoration('highlight-decoration', node.loc)); 688 } 689 } 690 if (arg.length === 2 && arg[0].type === 'Literal' && arg[1].type === 'Literal') { 691 if (typeof arg[0].value === 'string' && typeof arg[1].value === 'string') { 692 if (createRegex(arg[0].value, arg[1].value)) 693 highlights.push(codeEditor.createDecoration('highlight-decoration', node.loc)); 694 } 695 } 696 } 697 } 698 }); 699 if (highlights.length > 0) { 700 codeEditor.applyDecorations(highlights); 701 statusInfo.showInfo('Total regular expressions: ' + highlights.length); 702 } else { 703 statusInfo.showWarning('No regex found.'); 704 } 705 } catch (e) { 706 statusInfo.showError(e.toString()); 707 } 708 } 709 } 710 711 // functiontrace.html 712 function functionTracer() { 713 var statusInfo = new StatusInfo({ container: 'info' }); 714 715 var codeEditor = new CodeEditor( 716 { container: 'editor', value: 'init' }, 717 { 718 ready: function () { 719 statusInfo.showWarning('Ready.'); 720 $$('run').onclick = traceRun; 721 }, 722 contentChanged: function () { 723 statusInfo.showWarning('Ready.'); 724 } 725 } 726 ); 727 728 function traceRun() { 729 try { 730 statusInfo.showWarning('Executing...'); 731 createTraceCollector(); 732 var code = traceInstrument(); 733 var timestamp = +new Date(); 734 eval(code); 735 var elapsed = +new Date() - timestamp; 736 showResult(); 737 statusInfo.showInfo('Tracing completed in ' + (1 + elapsed) + ' ms'); 738 } catch (e) { 739 statusInfo.showError(e.toString()); 740 } 741 } 742 743 function createTraceCollector() { 744 window.TRACE = { 745 hits: {}, 746 enterFunction: function (info) { 747 var key = info.name + ':' + info.range[0]; 748 if (this.hits.hasOwnProperty(key)) { 749 this.hits[key] = this.hits[key] + 1; 750 } else { 751 this.hits[key] = 1; 752 } 753 }, 754 getHistogram: function () { 755 var histogram = []; 756 for (var entry in this.hits) { 757 if (this.hits.hasOwnProperty(entry)) { 758 histogram.push({ name: entry, count: this.hits[entry] }); 759 } 760 } 761 histogram.sort(function (a, b) {
762 return b.count - a.count; 763 }); 764 return histogram; 765 } 766 }; 767 } 768 769 function traceInstrument() { 770 var code = codeEditor.getValue(); 771 var tracer = window.esmorph.Tracer.FunctionEntrance(function (fn) { 772 var signature = 'window.TRACE.enterFunction({ '; 773 signature += 'name: "' + fn.name + '", '; 774 signature += 'lineNumber: ' + fn.loc.start.line + ', '; 775 signature += 'range: [' + fn.range[0] + ',' + fn.range[1] + ']'; 776 signature += ' });'; 777 return signature; 778 }); 779 780 code = window.esmorph.modify(code, tracer); 781 782 // Enclose in IIFE. 783 code = '(function() {\n' + code + '\n}())'; 784 785 return code; 786 } 787 788 function showResult() { 789 var histogram = window.TRACE.getHistogram(); 790 codeEditor.clearMarkers(); 791 for (var i = 0; i < histogram.length; i += 1) { 792 var entry = histogram[i]; 793 var name = entry.name.split(':')[0]; 794 var offset = parseInt(entry.name.split(':')[1], 10); 795 var pos = codeEditor.getPositionAt(offset); 796 var msg = name + ' is called ' + entry.count.toString() + ' time'; 797 if (entry.count > 1) msg += 's'; 798 codeEditor.addInfoMarker(msg, pos.lineNumber, pos.column); 799 } 800 } 801 } 802 803 // rewrite.html 804 function rewriter() { 805 var statusInfo = new StatusInfo({ container: 'info' }); 806 807 var codeEditor = new CodeEditor( 808 { container: 'editor', value: 'init' }, 809 { 810 ready: function () { 811 statusInfo.showWarning('Ready.'); 812 $$('rewrite').onclick = sourceRewrite; 813 }, 814 contentChanged: function () { 815 statusInfo.showWarning('Ready.'); 816 } 817 } 818 ); 819 820 function sourceRewrite() { 821 var code = codeEditor.getValue(); 822 823 var indent = ''; 824 if ($$('onetab').checked) { 825 indent = '\t'; 826 } else if ($$('twospaces').checked) { 827 indent = ' '; 828 } else if ($$('fourspaces').checked) { 829 indent = ' '; 830 } 831 832 var quotes = 'auto'; 833 if ($$('singlequotes').checked) { 834 quotes = 'single'; 835 } else if ($$('doublequotes').checked) { 836 quotes = 'double'; 837 } 838 839 var option = { 840 comment: true, 841 format: { 842 indent: { 843 style: indent 844 }, 845 quotes: quotes 846 } 847 }; 848 849 try { 850 var syntax = window.esprima.parse(code, { raw: true, tokens: true, range: true, comment: true }); 851 syntax = window.escodegen.attachComments(syntax, syntax.comments, syntax.tokens); 852 code = window.escodegen.generate(syntax, option); 853 codeEditor.setValue(code); 854 statusInfo.showInfo('Rewriting was successful.'); 855 } catch (e) { 856 statusInfo.showError(e.toString()); 857 } 858 } 859 } 860 861 // minify.html 862 function minifier() { 863 var statusInfo = new StatusInfo({ container: 'info' }); 864 865 var codeEditor = new CodeEditor( 866 { container: 'editor', value: 'init' }, 867 { 868 ready: function () { 869 statusInfo.showWarning('Ready.'); 870 $$('minify').onclick = minify; 871 }, 872 contentChanged: function () { 873 statusInfo.showWarning('Ready.'); 874 } 875 } 876 ); 877 878 function createPipeline() { 879 var passes, pipeline, inputs, i, el, optimizer; 880 881 passes = { 882 'eliminate-dead-code': 'pass/dead-code-elimination', 883 'fold-constant': 'pass/tree-based-constant-folding', 884 'remove-unreachable-branch': 'pass/remove-unreachable-branch', 885 'remove-unused-vars': 'pass/drop-variable-definition' 886 }; 887 888 pipeline = [ 889 'pass/hoist-variable-to-arguments', 890 'pass/transform-dynamic-to-static-property-access', 891 'pass/transform-dynamic-to-static-property-definition', 892 'pass/transform-immediate-function-call', 893 'pass/transform-logical-association', 894 'pass/reordering-function-declarations', 895 'pass/remove-unused-label', 896 'pass/remove-empty-statement', 897 'pass/remove-wasted-blocks', 898 'pass/transform-to-compound-assignment', 899 'pass/transform-to-sequence-expression', 900 'pass/transform-branch-to-expression', 901 'pass/transform-typeof-undefined', 902 'pass/reduce-sequence-expression', 903 'pass/reduce-branch-jump', 904 'pass/reduce-multiple-if-statements', 905 'pass/dead-code-elimination', 906 'pass/remove-side-effect-free-expressions', 907 'pass/remove-context-sensitive-expressions', 908 'pass/tree-based-constant-folding', 909 'pass/drop-variable-definition', 910 'pass/remove-unreachable-branch' 911 ]; 912 913 inputs = document.getElementsByTagName('input'); 914 for (i = 0; i < inputs.length; i += 1) { 915 el = inputs[i]; 916 optimizer = passes[el.id]; 917 if (optimizer && el.checked === false) { 918 pipeline.splice(pipeline.indexOf(optimizer), 1); 919 } 920 } 921 922 pipeline = pipeline.map(window.esmangle.require); 923 pipeline = [pipeline]; 924 pipeline.push({ 925 once: true, 926 pass: [ 927 'post/transform-static-to-dynamic-property-access', 928 'post/transform-infinity', 929 'post/rewrite-boolean', 930 'post/rewrite-conditional-expression' 931 ].map(window.esmangle.require) 932 }); 933 934 return pipeline; 935 } 936 937 function obfuscate(syntax) { 938 var result = window.esmangle.optimize(syntax, createPipeline()); 939 940 if ($$('mangle').checked) { 941 result = window.esmangle.mangle(result); 942 } 943 944 return result; 945 } 946 947 function minify() { 948 var code = codeEditor.getValue(); 949 950 var option = { 951 format: { 952 indent: { 953 style: '' 954 }, 955 quotes: 'auto', 956 compact: true 957 } 958 }; 959 960 try { 961 var before = code.length; 962 var syntax = window.esprima.parse(code, { raw: true, loc: true }); 963 syntax = obfuscate(syntax); 964 code = window.escodegen.generate(syntax, option); 965 var after = code.length; 966 if (before > after) { 967 codeEditor.setValue(code); 968 statusInfo.showInfo('No error. Minifying ' + before + ' bytes to ' + after + ' bytes.'); 969 } else { 970 statusInfo.showWarning('Can not minify further, code is already optimized.'); 971 } 972 } catch (e) { 973 statusInfo.showError(e.toString()); 974 } 975 } 976 } 977 978 // highlight.html 979 function highlighter() { 980 var statusInfo = new StatusInfo({ container: 'info' }); 981 982 var codeEditor = new CodeEditor( 983 { container: 'editor', value: 'init' }, 984 { 985 ready: initialize, 986 contentChanged: reparse, 987 cursorMoved: trackCursor 988 } 989 ); 990 991 function initialize() { 992 reparse();
992 // first time 993 codeEditor.setCursorOffset(300); 994 } 995 996 var context; 997 998 function reparse() { 999 var code = codeEditor.getValue(); 1000 var options = { 1001 tolerant: true, 1002 range: true, 1003 loc: true, 1004 sourceType: deduceSourceType(code) 1005 }; 1006 1007 try { 1008 if (!context) { 1009 context = new esrefactor.Context(); 1010 } 1011 var syntax = esprima.parse(code, options); 1012 context.setCode(syntax); 1013 statusInfo.showInfo('Ready'); 1014 } catch (e) { 1015 statusInfo.showError(e.toString()); 1016 } 1017 } 1018 1019 function trackCursor() { 1020 var decorations = []; 1021 1022 var offset = codeEditor.getCursorOffset(); 1023 var identification = context.identify(offset); 1024 if (identification) { 1025 var identifier = identification.identifier; 1026 var declaration = identification.declaration; 1027 var references = identification.references; 1028 if (declaration) { 1029 if (declaration.range !== identifier.range) 1030 decorations.push(codeEditor.createDecoration('declaration-decoration', declaration.loc)); 1031 statusInfo.showInfo( 1032 "Identifier '" + identifier.name + "' is declared in line " + declaration.loc.start.line + '.' 1033 ); 1034 } else { 1035 statusInfo.showWarning("Warning: No declaration is found for '" + identifier.name + "'."); 1036 } 1037 for (var i = 0; i < references.length; ++i) { 1038 decorations.push(codeEditor.createDecoration('reference-decoration', references[i].loc)); 1039 } 1040 } else { 1041 statusInfo.showInfo('Ready.'); 1042 } 1043 1044 codeEditor.applyDecorations(decorations); 1045 } 1046 } 1047 1048 // rename.html 1049 function renamer() { 1050 var statusInfo = new StatusInfo({ container: 'info' }); 1051 1052 var codeEditor = new CodeEditor( 1053 { container: 'editor', value: 'init' }, 1054 { 1055 ready: initialize, 1056 contentChanged: parse, 1057 cursorMoved: trackCursor 1058 } 1059 ); 1060 1061 // important state 1062 var context, code, cursorOffset, syntax, identification; 1063 1064 function initialize() { 1065 context = new esrefactor.Context(); 1066 parse(); 1067 codeEditor.setCursorOffset(300); 1068 } 1069 1070 function parse() { 1071 if (!context) { 1072 context = new esrefactor.Context(); 1073 } 1074 1075 if (identification && identification.identifier) rename(); 1076 1077 code = codeEditor.getValue(); 1078 var options = { 1079 tolerant: true, 1080 range: true, 1081 loc: true, 1082 tokens: true, 1083 sourceType: deduceSourceType(code) 1084 }; 1085 1086 try { 1087 codeEditor.clearDecorations(); 1088 identification = null; 1089 syntax = esprima.parse(code, options); 1090 context.setCode(syntax); 1091 statusInfo.showInfo('Ready.'); 1092 } catch (e) { 1093 statusInfo.showError(e.toString()); 1094 } finally { 1095 trackCursor(); 1096 } 1097 } 1098 1099 function rename() { 1100 if (!identification || !syntax) { 1101 contextify(); 1102 return; 1103 } 1104 1105 // Locate the token corresponding to the renamed identifier. 1106 var index = -1, 1107 offset, 1108 charOffset; 1109 for (var i = 0; i < syntax.tokens.length; ++i) { 1110 var token = syntax.tokens[i]; 1111 if (token.range[0] === identification.identifier.range[0]) { 1112 index = i; 1113 offset = identification.identifier.range[1] - cursorOffset; 1114 charOffset = cursorOffset - token.range[0]; 1115 break; 1116 } 1117 } 1118 1119 var new_code = codeEditor.getValue(); 1120 if (code === new_code) { 1121 return; 1122 } 1123 var options = { 1124 tolerant: true, 1125 range: true, 1126 loc: true, 1127 tokens: true, 1128 sourceType: deduceSourceType(new_code) 1129 }; 1130 var new_tokens = esprima.parse(new_code, options).tokens; 1131 1132 // Get the new name for that identifier. 1133 token = new_tokens[index]; 1134 if (token && token.value !== identification.identifier.name) { 1135 // Check that other tokens haven't changed. 1136 var old_p = code.substr(0, identification.identifier.range[0]); 1137 var old_q = code.substr(identification.identifier.range[1]); 1138 var new_p = new_code.substr(0, token.range[0]); 1139 var new_q = new_code.substr(token.range[1]); 1140 if (old_p === new_p || old_q === new_q) { 1141 // Rename the identifier, adjust the source and the cursor. 1142 context.setCode(code); 1143 var renamed = context.rename(identification, token.value); 1144 if (renamed) { 1145 new_tokens = esprima.parse(renamed, options).tokens; 1146 var new_cursorOffset = new_tokens[index].range[0] + charOffset; 1147 codeEditor.setValue(renamed); 1148 codeEditor.setCursorOffset(new_cursorOffset); 1149 } 1150 } 1151 } 1152 } 1153 1154 function trackCursor() { 1155 var decorations = []; 1156 1157 cursorOffset = codeEditor.getCursorOffset();
1158 identification = context ? context.identify(cursorOffset) : null; 1159 if (identification) { 1160 var identifier = identification.identifier; 1161 var declaration = identification.declaration; 1162 var references = identification.references; 1163 if (declaration && declaration.loc) { 1164 if (declaration.range !== identifier.range) 1165 decorations.push(codeEditor.createDecoration('declaration-decoration', declaration.loc)); 1166 statusInfo.showInfo( 1167 "Identifier '" + identifier.name + "' is declared in line " + declaration.loc.start.line + '.' 1168 ); 1169 } else { 1170 statusInfo.showWarning("Warning: No declaration is found for '" + identifier.name + "'."); 1171 } 1172 for (var i = 0; i < references.length; ++i) { 1173 if (references[i] && references[i].loc) 1174 decorations.push(codeEditor.createDecoration('reference-decoration', references[i].loc)); 1175 } 1176 } else { 1177 statusInfo.showInfo('Ready.'); 1178 } 1179 1180 codeEditor.applyDecorations(decorations); 1181 } 1182 } 1183 1184 // autocomplete.html 1185 function autoCompleter() { 1186 var statusInfo = new StatusInfo({ container: 'info' }); 1187 1188 var codeEditor = new CodeEditor( 1189 { container: 'editor', value: 'init', language: 'AksaraJawa' }, 1190 { 1191 ready: initialize, 1192 cursorMoved: trackCursor 1193 } 1194 ); 1195 1196 // important state 1197 var aulx = new Aulx.JS(); 1198 var previousLine = -1; 1199 1200 function initialize() { 1201 registerAutocompletion(); 1202 trackCursor(); 1203 } 1204 1205 function registerAutocompletion() { 1206 monaco.languages.registerCompletionItemProvider('AksaraJawa', { 1207 triggerCharacters: ['.'], 1208 provideCompletionItems: function (model, position) { 1209 var code = model.getValue(); 1210 var caret = { 1211 line: position.lineNumber - 1, 1212 ch: position.column - 1 1213 }; 1214 var completion = aulx.complete(code, caret); 1215 var suggestions = []; 1216 if (completion && completion.candidates && completion.candidates.length) { 1217 for (var i = 0; i < completion.candidates.length; ++i) { 1218 var display = completion.candidates[i].display; 1219 suggestions.push({ 1220 label: display, 1221 insertText: display 1222 }); 1223 } 1224 statusInfo.showInfo('Suggestions: ' + completion.candidates.length); 1225 } else { 1226 statusInfo.showWarning('No suggestion'); 1227 } 1228 return { suggestions: suggestions }; 1229 } 1230 }); 1231 } 1232 1233 function trackCursor() { 1234 statusInfo.showWarning('Ready.'); 1235 var code = codeEditor.getValue(); 1236 var offset = codeEditor.getCursorOffset(); 1237 var position = codeEditor.getPositionAt(offset); 1238 if (position.lineNumber != previousLine) { 1239 previousLine = position.lineNumber; 1240 var caret = { 1241 line: position.lineNumber - 1, 1242 ch: position.column - 1 1243 }; 1244 aulx.fireStaticAnalysis(code, caret); 1245 } 1246 } 1247 } 1248 1249 // precedence.html 1250 function precedenceComparator() { 1251 var compareId; 1252 function compare() { 1253 if (compareId) { 1254 window.clearTimeout(compareId); 1255 } 1256 1257 function stringify(node) { 1258 var result; 1259 1260 if (typeof node !== 'object') { 1261 throw new Error('Node is not valid'); 1262 } 1263 if (typeof node.type !== 'string') { 1264 throw new Error('Node does not have type property'); 1265 } 1266 1267 switch (node.type) { 1268 case 'Program': 1269 if (node.body.length !== 1) { 1270 throw new Error('Expression is too complex'); 1271 } 1272 result = stringify(node.body[0]); 1273 if (result[0] === '(' && result[result.length - 1] === ')') { 1274 result = result.substr(1, result.length - 2); 1275 } 1276 break; 1277 1278 case 'ExpressionStatement': 1279 result = stringify(node.expression); 1280 break; 1281 1282 case 'BinaryExpression': 1283 case 'LogicalExpression': 1284 result = '(' + stringify(node.left) + ' ' + node.operator + ' ' + stringify(node.right) + ')'; 1285 break; 1286 1287 case 'UnaryExpression': 1288 result = '(' + node.operator; 1289 if (node.operator.length > 2) { 1290 // delete void typeof 1291 result += ' '; 1292 } 1293 result += stringify(node.argument) + ')'; 1294 break; 1295
1296 case 'UpdateExpression': 1297 result = stringify(node.argument); 1298 if (node.prefix) { 1299 result = node.operator + result; 1300 } else { 1301 result = result + node.operator; 1302 } 1303 result = '(' + result + ')'; 1304 break; 1305 1306 case 'Literal': 1307 result = node.value.toString(); 1308 if (typeof node.value === 'string') { 1309 result = '"' + node.value + '"'; 1310 } 1311 break; 1312 1313 case 'Identifier': 1314 result = node.name; 1315 break; 1316 1317 default: 1318 break; 1319 } 1320 1321 if (!result) { 1322 throw new Error('Unknown node type: ' + node.type); 1323 } 1324 1325 return result; 1326 } 1327 1328 function setText(el, str) { 1329 if (typeof el.innerText === 'string') { 1330 el.innerText = str; 1331 } else { 1332 el.textContent = str; 1333 } 1334 } 1335 1336 compareId = window.setTimeout(function () { 1337 var a, b, answer, status, expr, left, right, suggest; 1338 1339 a = $$('a'); 1340 b = $$('b'); 1341 answer = $$('answer'); 1342 status = $$('status'); 1343 expr = $$('expr'); 1344 1345 a.setAttribute('class', ''); 1346 b.setAttribute('class', ''); 1347 answer.setAttribute('class', ''); 1348 1349 setText(answer, ''); 1350 setText(status, ''); 1351 setText(expr, ''); 1352 1353 try { 1354 left = esprima.parse(typeof a.innerText === 'string' ? a.innerText : a.textContent); 1355 } catch (e_left) { 1356 a.setAttribute('class', 'lightred'); 1357 } 1358 1359 try { 1360 right = esprima.parse(typeof b.innerText === 'string' ? b.innerText : b.textContent); 1361 } catch (e_right) { 1362 b.setAttribute('class', 'lightred'); 1363 } 1364 1365 try { 1366 suggest = stringify(left); 1367 } catch (e_suggest) { 1368 a.setAttribute('class', 'lightred'); 1369 } 1370 1371 if (left && right) { 1372 if (JSON.stringify(left) === JSON.stringify(right)) { 1373 setText(answer, 'Yes'); 1374 answer.setAttribute('class', 'yes'); 1375 } else { 1376 setText(answer, 'No'); 1377 answer.setAttribute('class', 'no'); 1378 setText(status, suggest ? 'It is more like ' : ''); 1379 setText(expr, suggest || ''); 1380 } 1381 } else { 1382 answer.setAttribute('class', ''); 1383 } 1384 1385 compareId = undefined; 1386 }, 57); 1387 } 1388 1389 if (typeof document.body.attachEvent === 'object') { 1390 // Workaround for old Internet Explorer. 1391 // Until there is a reliable way to track the modification to the editable 1392 // inputs, manually track the change periodically. 1393 window.setInterval(compare, 500); 1394 } 1395 1396 // See http://mathiasbynens.be/notes/oninput for details. 1397 $$('a').onkeyup = compare; 1398 $$('a').oninput = function () { 1399 this.onkeyup = null; 1400 compare(); 1401 }; 1402 $$('b').onkeyup = compare; 1403 $$('b').oninput = function () { 1404 this.onkeyup = null; 1405 compare(); 1406 }; 1407 compare(); 1408 } 1409 1410 return { 1411 visualizer: visualizer, 1412 validator: validator, 1413 regexCollector: regexCollector, 1414 functionTracer: functionTracer, 1415 rewriter: rewriter, 1416 minifier: minifier, 1417 highlighter: highlighter, 1418 renamer: renamer, 1419 autoCompleter: autoCompleter, 1420 precedenceComparator: precedenceComparator 1421 }; 1422})(window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.