test_promise.html (21963B)
1 <!-- 2 Any copyright is dedicated to the Public Domain. 3 http://creativecommons.org/publicdomain/zero/1.0/ 4 --> 5 <html> 6 <head> 7 <title>Basic Promise Test</title> 8 <script src="/tests/SimpleTest/SimpleTest.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 <p id="display"></p> 13 <div id="content" style="display: none"> 14 15 </div> 16 <pre id="test"> 17 <script type="application/javascript"><!-- 18 19 function promiseResolve() { 20 ok(Promise, "Promise object should exist"); 21 22 var promise = new Promise(function(resolve, reject) { 23 ok(resolve, "Promise.resolve exists"); 24 ok(reject, "Promise.reject exists"); 25 26 resolve(42); 27 }); 28 promise.then(function(what) { 29 ok(true, "Then - resolveCb has been called"); 30 is(what, 42, "ResolveCb received 42"); 31 runTest(); 32 }, function() { 33 ok(false, "Then - rejectCb has been called"); 34 runTest(); 35 }); 36 } 37 38 function promiseResolveNoArg() { 39 var promise = new Promise(function(resolve, reject) { 40 ok(resolve, "Promise.resolve exists"); 41 ok(reject, "Promise.reject exists"); 42 43 resolve(); 44 }); 45 promise.then(function(what) { 46 ok(true, "Then - resolveCb has been called"); 47 is(what, undefined, "ResolveCb received undefined"); 48 runTest(); 49 }, function() { 50 ok(false, "Then - rejectCb has been called"); 51 runTest(); 52 }); 53 } 54 55 function promiseReject() { 56 var promise = new Promise(function(resolve, reject) { 57 reject(42); 58 }); 59 promise.then(function() { 60 ok(false, "Then - resolveCb has been called"); 61 runTest(); 62 }, function(what) { 63 ok(true, "Then - rejectCb has been called"); 64 is(what, 42, "RejectCb received 42"); 65 runTest(); 66 }); 67 } 68 69 function promiseRejectNoHandler() { 70 // This test only checks that the code that reports unhandled errors in the 71 // Promises implementation does not crash or leak. 72 73 new Promise(function() { 74 // eslint-disable-next-line no-undef 75 noSuchMethod(); 76 }); 77 runTest(); 78 } 79 80 function promiseRejectNoArg() { 81 var promise = new Promise(function(resolve, reject) { 82 reject(); 83 }); 84 promise.then(function() { 85 ok(false, "Then - resolveCb has been called"); 86 runTest(); 87 }, function(what) { 88 ok(true, "Then - rejectCb has been called"); 89 is(what, undefined, "RejectCb received undefined"); 90 runTest(); 91 }); 92 } 93 94 function promiseException() { 95 var promise = new Promise(function() { 96 // eslint-disable-next-line no-throw-literal 97 throw 42; 98 }); 99 promise.then(function() { 100 ok(false, "Then - resolveCb has been called"); 101 runTest(); 102 }, function(what) { 103 ok(true, "Then - rejectCb has been called"); 104 is(what, 42, "RejectCb received 42"); 105 runTest(); 106 }); 107 } 108 109 function promiseGC() { 110 var resolve; 111 var promise = new Promise(function(r1) { 112 resolve = r1; 113 }); 114 promise.then(function() { 115 ok(true, "Then - promise is still alive"); 116 runTest(); 117 }); 118 119 promise = null; 120 121 SpecialPowers.gc(); 122 SpecialPowers.forceGC(); 123 SpecialPowers.forceCC(); 124 125 resolve(42); 126 } 127 128 function promiseAsync_TimeoutResolveThen() { 129 var handlerExecuted = false; 130 131 setTimeout(function() { 132 ok(handlerExecuted, "Handler should have been called before the timeout."); 133 134 // Allow other assertions to run so the test could fail before the next one. 135 setTimeout(runTest, 0); 136 }, 0); 137 138 Promise.resolve().then(function() { 139 handlerExecuted = true; 140 }); 141 142 ok(!handlerExecuted, "Handlers are not called before 'then' returns."); 143 } 144 145 function promiseAsync_ResolveTimeoutThen() { 146 var handlerExecuted = false; 147 148 var promise = Promise.resolve(); 149 150 setTimeout(function() { 151 ok(handlerExecuted, "Handler should have been called before the timeout."); 152 153 // Allow other assertions to run so the test could fail before the next one. 154 setTimeout(runTest, 0); 155 }, 0); 156 157 promise.then(function() { 158 handlerExecuted = true; 159 }); 160 161 ok(!handlerExecuted, "Handlers are not called before 'then' returns."); 162 } 163 164 function promiseAsync_ResolveThenTimeout() { 165 var handlerExecuted = false; 166 167 Promise.resolve().then(function() { 168 handlerExecuted = true; 169 }); 170 171 setTimeout(function() { 172 ok(handlerExecuted, "Handler should have been called before the timeout."); 173 174 // Allow other assertions to run so the test could fail before the next one. 175 setTimeout(runTest, 0); 176 }, 0); 177 178 ok(!handlerExecuted, "Handlers are not called before 'then' returns."); 179 } 180 181 function promiseAsync_SyncXHR() { 182 var handlerExecuted = false; 183 184 Promise.resolve().then(function() { 185 handlerExecuted = true; 186 187 // Allow other assertions to run so the test could fail before the next one. 188 setTimeout(runTest, 0); 189 }); 190 191 ok(!handlerExecuted, "Handlers are not called until the next microtask."); 192 193 var xhr = new XMLHttpRequest(); 194 xhr.open("GET", "testXHR.txt", false); 195 xhr.send(null); 196 197 ok(!handlerExecuted, "Sync XHR should not trigger microtask execution."); 198 } 199 200 function promiseDoubleThen() { 201 var steps = 0; 202 var promise = new Promise(function(r1) { 203 r1(42); 204 }); 205 206 promise.then(function(what) { 207 ok(true, "Then.resolve has been called"); 208 is(what, 42, "Value == 42"); 209 steps++; 210 }, function() { 211 ok(false, "Then.reject has been called"); 212 }); 213 214 promise.then(function(what) { 215 ok(true, "Then.resolve has been called"); 216 is(steps, 1, "Then.resolve - step == 1"); 217 is(what, 42, "Value == 42"); 218 runTest(); 219 }, function() { 220 ok(false, "Then.reject has been called"); 221 }); 222 } 223 224 function promiseThenException() { 225 var promise = new Promise(function(resolve) { 226 resolve(42); 227 }); 228 229 promise.then(function() { 230 ok(true, "Then.resolve has been called"); 231 // eslint-disable-next-line no-throw-literal 232 throw "booh"; 233 }).catch(function() { 234 ok(true, "window.onerror has been called!"); 235 runTest(); 236 }); 237 } 238 239 function promiseThenCatchThen() { 240 var promise = new Promise(function(resolve) { 241 resolve(42); 242 }); 243 244 var promise2 = promise.then(function(what) { 245 ok(true, "Then.resolve has been called"); 246 is(what, 42, "Value == 42"); 247 return what + 1; 248 }, function() { 249 ok(false, "Then.reject has been called"); 250 }); 251 252 isnot(promise, promise2, "These 2 promise objs are different"); 253 254 promise2.then(function(what) { 255 ok(true, "Then.resolve has been called"); 256 is(what, 43, "Value == 43"); 257 return what + 1; 258 }, function() { 259 ok(false, "Then.reject has been called"); 260 }).catch(function() { 261 ok(false, "Catch has been called"); 262 }).then(function(what) { 263 ok(true, "Then.resolve has been called"); 264 is(what, 44, "Value == 44"); 265 runTest(); 266 }, function() { 267 ok(false, "Then.reject has been called"); 268 }); 269 } 270 271 function promiseThenNoArg() { 272 var promise = new Promise(function(resolve) { 273 resolve(42); 274 }); 275 276 // eslint-disable-next-line promise/valid-params 277 var clone = promise.then(); 278 isnot(promise, clone, "These 2 promise objs are different"); 279 promise.then(function(v) { 280 clone.then(function(cv) { 281 is(v, cv, "Both resolve to the same value"); 282 runTest(); 283 }); 284 }); 285 } 286 287 function promiseThenUndefinedResolveFunction() { 288 var promise = new Promise(function(resolve, reject) { 289 reject(42); 290 }); 291 292 try { 293 promise.then(undefined, function(v) { 294 is(v, 42, "Promise rejected with 42"); 295 runTest(); 296 }); 297 } catch (e) { 298 ok(false, "then should not throw on undefined resolve function"); 299 } 300 } 301 302 function promiseThenNullResolveFunction() { 303 var promise = new Promise(function(resolve, reject) { 304 reject(42); 305 }); 306 307 try { 308 promise.then(null, function(v) { 309 is(v, 42, "Promise rejected with 42"); 310 runTest(); 311 }); 312 } catch (e) { 313 ok(false, "then should not throw on null resolve function"); 314 } 315 } 316 317 function promiseRejectThenCatchThen() { 318 var promise = new Promise(function(resolve, reject) { 319 reject(42); 320 }); 321 322 var promise2 = promise.then(function() { 323 ok(false, "Then.resolve has been called"); 324 }, function(what) { 325 ok(true, "Then.reject has been called"); 326 is(what, 42, "Value == 42"); 327 return what + 1; 328 }); 329 330 isnot(promise, promise2, "These 2 promise objs are different"); 331 332 promise2.then(function(what) { 333 ok(true, "Then.resolve has been called"); 334 is(what, 43, "Value == 43"); 335 return what + 1; 336 }).catch(function() { 337 ok(false, "Catch has been called"); 338 }).then(function(what) { 339 ok(true, "Then.resolve has been called"); 340 is(what, 44, "Value == 44"); 341 runTest(); 342 }); 343 } 344 345 function promiseRejectThenCatchThen2() { 346 var promise = new Promise(function(resolve, reject) { 347 reject(42); 348 }); 349 350 promise.then(function(what) { 351 ok(true, "Then.resolve has been called"); 352 is(what, 42, "Value == 42"); 353 return what + 1; 354 }).catch(function(what) { 355 is(what, 42, "Value == 42"); 356 ok(true, "Catch has been called"); 357 return what + 1; 358 }).then(function(what) { 359 ok(true, "Then.resolve has been called"); 360 is(what, 43, "Value == 43"); 361 runTest(); 362 }); 363 } 364 365 function promiseRejectThenCatchExceptionThen() { 366 var promise = new Promise(function(resolve, reject) { 367 reject(42); 368 }); 369 370 promise.then(function() { 371 ok(false, "Then.resolve has been called"); 372 }, function(what) { 373 ok(true, "Then.reject has been called"); 374 is(what, 42, "Value == 42"); 375 // eslint-disable-next-line no-throw-literal 376 throw (what + 1); 377 }).catch(function(what) { 378 ok(true, "Catch has been called"); 379 is(what, 43, "Value == 43"); 380 return what + 1; 381 }).then(function(what) { 382 ok(true, "Then.resolve has been called"); 383 is(what, 44, "Value == 44"); 384 runTest(); 385 }); 386 } 387 388 function promiseThenCatchOrderingResolve() { 389 var global = 0; 390 var f = new Promise(function(r1) { 391 r1(42); 392 }); 393 394 f.then(function() { 395 f.then(function() { 396 global++; 397 }); 398 f.catch(function() { 399 global++; 400 }); 401 f.then(function() { 402 global++; 403 }); 404 setTimeout(function() { 405 is(global, 2, "Many steps... should return 2"); 406 runTest(); 407 }, 0); 408 }); 409 } 410 411 function promiseThenCatchOrderingReject() { 412 var global = 0; 413 var f = new Promise(function(r1, r2) { 414 r2(42); 415 }); 416 417 f.then(function() {}, function() { 418 f.then(function() { 419 global++; 420 }); 421 f.catch(function() { 422 global++; 423 }); 424 f.then(function() {}, function() { 425 global++; 426 }); 427 setTimeout(function() { 428 is(global, 2, "Many steps... should return 2"); 429 runTest(); 430 }, 0); 431 }); 432 } 433 434 function promiseCatchNoArg() { 435 var promise = new Promise(function(resolve, reject) { 436 reject(42); 437 }); 438 439 // eslint-disable-next-line promise/valid-params 440 var clone = promise.catch(); 441 isnot(promise, clone, "These 2 promise objs are different"); 442 promise.catch(function(v) { 443 clone.catch(function(cv) { 444 is(v, cv, "Both reject to the same value"); 445 runTest(); 446 }); 447 }); 448 } 449 450 function promiseNestedPromise() { 451 new Promise(function(resolve) { 452 resolve(new Promise(function(res) { 453 ok(true, "Nested promise is executed"); 454 res(42); 455 })); 456 }).then(function(value) { 457 is(value, 42, "Nested promise is executed and then == 42"); 458 runTest(); 459 }); 460 } 461 462 function promiseNestedNestedPromise() { 463 new Promise(function(resolve) { 464 resolve(new Promise(function(res) { 465 ok(true, "Nested promise is executed"); 466 res(42); 467 }).then(function(what) { return what + 1; })); 468 }).then(function(value) { 469 is(value, 43, "Nested promise is executed and then == 43"); 470 runTest(); 471 }); 472 } 473 474 function promiseWrongNestedPromise() { 475 new Promise(function(resolve, reject) { 476 resolve(new Promise(function(r) { 477 ok(true, "Nested promise is executed"); 478 r(42); 479 })); 480 reject(42); 481 }).then(function(value) { 482 is(value, 42, "Nested promise is executed and then == 42"); 483 runTest(); 484 }, function() { 485 ok(false, "This is wrong"); 486 }); 487 } 488 489 function promiseLoop() { 490 new Promise(function(resolve) { 491 resolve(new Promise(function(res) { 492 ok(true, "Nested promise is executed"); 493 res(new Promise(function(resInner) { 494 ok(true, "Nested nested promise is executed"); 495 resInner(42); 496 })); 497 })); 498 }).then(function(value) { 499 is(value, 42, "Nested nested promise is executed and then == 42"); 500 runTest(); 501 }, function() { 502 ok(false, "This is wrong"); 503 }); 504 } 505 506 function promiseStaticReject() { 507 var promise = Promise.reject(42); 508 promise.then(function() { 509 ok(false, "This should not be called"); 510 }, function(what) { 511 is(what, 42, "Value == 42"); 512 runTest(); 513 }); 514 } 515 516 function promiseStaticResolve() { 517 var promise = Promise.resolve(42); 518 promise.then(function(what) { 519 is(what, 42, "Value == 42"); 520 runTest(); 521 }, function() { 522 ok(false, "This should not be called"); 523 }); 524 } 525 526 function promiseResolveNestedPromise() { 527 var promise = Promise.resolve(new Promise(function(r) { 528 ok(true, "Nested promise is executed"); 529 r(42); 530 }, function() { 531 ok(false, "This should not be called"); 532 })); 533 promise.then(function(what) { 534 is(what, 42, "Value == 42"); 535 runTest(); 536 }, function() { 537 ok(false, "This should not be called"); 538 }); 539 } 540 541 function promiseSimpleThenableResolve() { 542 var thenable = { then(resolve) { resolve(5); } }; 543 var promise = new Promise(function(resolve) { 544 resolve(thenable); 545 }); 546 547 promise.then(function(v) { 548 ok(v === 5, "promiseSimpleThenableResolve"); 549 runTest(); 550 }, function() { 551 ok(false, "promiseSimpleThenableResolve: Should not reject"); 552 }); 553 } 554 555 function promiseSimpleThenableReject() { 556 var thenable = { then(resolve, reject) { reject(5); } }; 557 var promise = new Promise(function(resolve) { 558 resolve(thenable); 559 }); 560 561 promise.then(function() { 562 ok(false, "promiseSimpleThenableReject: Should not resolve"); 563 runTest(); 564 }, function(e) { 565 ok(e === 5, "promiseSimpleThenableReject"); 566 runTest(); 567 }); 568 } 569 570 function promiseThenableThrowsBeforeCallback() { 571 var thenable = { then(resolve) { 572 throw new TypeError("Hi there"); 573 574 // eslint-disable-next-line no-unreachable 575 resolve(5); 576 }}; 577 578 var promise = Promise.resolve(thenable); 579 promise.then(function() { 580 ok(false, "promiseThenableThrowsBeforeCallback: Should've rejected"); 581 runTest(); 582 }, function(e) { 583 ok(e instanceof TypeError, "promiseThenableThrowsBeforeCallback"); 584 runTest(); 585 }); 586 } 587 588 function promiseThenableThrowsAfterCallback() { 589 var thenable = { then(resolve) { 590 resolve(5); 591 throw new TypeError("Hi there"); 592 }}; 593 594 var promise = Promise.resolve(thenable); 595 promise.then(function(v) { 596 ok(v === 5, "promiseThenableThrowsAfterCallback"); 597 runTest(); 598 }, function() { 599 ok(false, "promiseThenableThrowsAfterCallback: Should've resolved"); 600 runTest(); 601 }); 602 } 603 604 function promiseThenableRejectThenResolve() { 605 var thenable = { then(resolve, reject) { 606 reject(new TypeError("Hi there")); 607 resolve(5); 608 }}; 609 610 var promise = Promise.resolve(thenable); 611 promise.then(function() { 612 ok(false, "promiseThenableRejectThenResolve should have rejected"); 613 runTest(); 614 }, function(e) { 615 ok(e instanceof TypeError, "promiseThenableRejectThenResolve"); 616 runTest(); 617 }); 618 } 619 620 function promiseWithThenReplaced() { 621 // Ensure that we call the 'then' on the promise and not the internal then. 622 var promise = new Promise(function(resolve) { 623 resolve(5); 624 }); 625 626 // Rogue `then` always rejects. 627 promise.then = function(onFulfill, onReject) { 628 onReject(new TypeError("Foo")); 629 }; 630 631 var promise2 = Promise.resolve(promise); 632 promise2.then(function() { 633 ok(false, "promiseWithThenReplaced: Should've rejected"); 634 runTest(); 635 }, function(e) { 636 ok(e instanceof TypeError, "promiseWithThenReplaced"); 637 runTest(); 638 }); 639 } 640 641 function promiseStrictHandlers() { 642 var promise = Promise.resolve(5); 643 promise.then(function() { 644 "use strict"; 645 ok(this === undefined, "Strict mode callback should have this === undefined."); 646 runTest(); 647 }); 648 } 649 650 function promiseStrictExecutorThisArg() { 651 new Promise(function() { 652 "use strict"; 653 ok(this === undefined, "thisArg should be undefined."); 654 runTest(); 655 }); 656 } 657 658 function promiseResolveArray() { 659 var p = Promise.resolve([1, 2, 3]); 660 ok(p instanceof Promise, "Should return a Promise."); 661 p.then(function(v) { 662 ok(Array.isArray(v), "Resolved value should be an Array"); 663 is(v.length, 3, "Length should match"); 664 is(v[0], 1, "Resolved value should match original"); 665 is(v[1], 2, "Resolved value should match original"); 666 is(v[2], 3, "Resolved value should match original"); 667 runTest(); 668 }); 669 } 670 671 function promiseResolveThenable() { 672 var p = Promise.resolve({ then(onFulfill) { onFulfill(2); } }); 673 ok(p instanceof Promise, "Should cast to a Promise."); 674 p.then(function(v) { 675 is(v, 2, "Should resolve to 2."); 676 runTest(); 677 }, function() { 678 ok(false, "promiseResolveThenable should've resolved"); 679 runTest(); 680 }); 681 } 682 683 function promiseResolvePromise() { 684 var original = Promise.resolve(true); 685 var cast = Promise.resolve(original); 686 687 ok(cast instanceof Promise, "Should cast to a Promise."); 688 is(cast, original, "Should return original Promise."); 689 cast.then(function(v) { 690 is(v, true, "Should resolve to true."); 691 runTest(); 692 }); 693 } 694 695 // Bug 1009569. 696 // Ensure that thenables are run on a clean stack asynchronously. 697 // Test case adopted from 698 // https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js. 699 function promiseResolveThenableCleanStack() { 700 function immed(s) { x++; s(); } 701 function incX() { x++; } 702 703 var x = 0; 704 var thenable = { then: immed }; 705 var results = []; 706 707 var p = Promise.resolve(thenable).then(incX); 708 results.push(x); 709 710 // check what happens after all "next cycle" steps 711 // have had a chance to complete 712 setTimeout(function() { 713 // Result should be [0, 2] since `thenable` will be called async. 714 is(results[0], 0, "Expected thenable to be called asynchronously"); 715 // See Bug 1023547 comment 13 for why this check has to be gated on p. 716 p.then(function() { 717 results.push(x); 718 is(results[1], 2, "Expected thenable to be called asynchronously"); 719 runTest(); 720 }); 721 }, 1000); 722 } 723 724 // Bug 1008467 - Promise fails with "too much recursion". 725 // The bug was that the callbacks passed to a thenable would resolve the 726 // promise synchronously when the fulfill handler returned a non-thenable. 727 // 728 // For example: 729 // var p = new Promise(function(resolve) { 730 // resolve(5); 731 // }); 732 // var m = Promise.resolve(p); 733 // 734 // At this point `m` is a Promise that is resolved with a thenable `p`, so it 735 // calls `p.then()` with two callbacks, both of which would synchronously resolve 736 // `m` when `p` invoked them (on account of itself being resolved, possibly 737 // synchronously. A chain of these 'Promise resolved by a Promise' would lead to 738 // stack overflow. 739 function promiseTestAsyncThenableResolution() { 740 var k = 3000; 741 Promise.resolve().then(function next() { 742 k--; 743 if (k > 0) return Promise.resolve().then(next); 744 return undefined; 745 }).then(function() { 746 ok(true, "Resolution of a chain of thenables should not be synchronous."); 747 runTest(); 748 }); 749 } 750 751 // Bug 1062323 752 function promiseWrapperAsyncResolution() { 753 var p = new Promise(function(resolve) { 754 resolve(); 755 }); 756 757 var results = []; 758 var q = p.then(function() { 759 results.push("1-1"); 760 }).then(function() { 761 results.push("1-2"); 762 }).then(function() { 763 results.push("1-3"); 764 }); 765 766 var r = p.then(function() { 767 results.push("2-1"); 768 }).then(function() { 769 results.push("2-2"); 770 }).then(function() { 771 results.push("2-3"); 772 }); 773 774 Promise.all([q, r]).then(function() { 775 var match = results[0] == "1-1" && 776 results[1] == "2-1" && 777 results[2] == "1-2" && 778 results[3] == "2-2" && 779 results[4] == "1-3" && 780 results[5] == "2-3"; 781 info(results); 782 ok(match, "Chained promises should resolve asynchronously."); 783 runTest(); 784 }, function() { 785 ok(false, "promiseWrapperAsyncResolution: One of the promises failed."); 786 runTest(); 787 }); 788 } 789 790 var tests = [ promiseResolve, promiseReject, 791 promiseException, promiseGC, 792 promiseAsync_TimeoutResolveThen, 793 promiseAsync_ResolveTimeoutThen, 794 promiseAsync_ResolveThenTimeout, 795 promiseAsync_SyncXHR, 796 promiseDoubleThen, promiseThenException, 797 promiseThenCatchThen, promiseRejectThenCatchThen, 798 promiseRejectThenCatchThen2, 799 promiseRejectThenCatchExceptionThen, 800 promiseThenCatchOrderingResolve, 801 promiseThenCatchOrderingReject, 802 promiseNestedPromise, promiseNestedNestedPromise, 803 promiseWrongNestedPromise, promiseLoop, 804 promiseStaticReject, promiseStaticResolve, 805 promiseResolveNestedPromise, 806 promiseResolveNoArg, 807 promiseRejectNoArg, 808 promiseThenNoArg, 809 promiseThenUndefinedResolveFunction, 810 promiseThenNullResolveFunction, 811 promiseCatchNoArg, 812 promiseRejectNoHandler, 813 promiseSimpleThenableResolve, 814 promiseSimpleThenableReject, 815 promiseThenableThrowsBeforeCallback, 816 promiseThenableThrowsAfterCallback, 817 promiseThenableRejectThenResolve, 818 promiseWithThenReplaced, 819 promiseStrictHandlers, 820 promiseStrictExecutorThisArg, 821 promiseResolveArray, 822 promiseResolveThenable, 823 promiseResolvePromise, 824 promiseResolveThenableCleanStack, 825 promiseTestAsyncThenableResolution, 826 promiseWrapperAsyncResolution, 827 ]; 828 829 function runTest() { 830 if (!tests.length) { 831 SimpleTest.finish(); 832 return; 833 } 834 835 var test = tests.shift(); 836 test(); 837 } 838 839 SimpleTest.waitForExplicitFinish(); 840 SimpleTest.requestFlakyTimeout("untriaged"); 841 runTest(); 842 // --> 843 </script> 844 </pre> 845 </body> 846 </html>