Code Behind the Button:

  1. var experience = {
  2. 1985: [ 'Applesoft BASIC', 'Apple DOS 3.3' ],
  3. 1987: '6502 assembly',
  4. 1989: [ 'Unix', 'the Internet' ],
  5. 1990: 'Pascal',
  6. 1991: [ 'C', 'MS-DOS' ],
  7. 1992: 'pre-Standard C++',
  8. 1993: [ '8086 assembly', 'Linux', 'Bourne shell scripting' ],
  9. 1994: [ 'HTML', 'Perl 4', 'Win16', 'Winsock', 'Borland OWL' ],
  10. 1995: [ 'patterns', 'Perl 5 (bless $me!)', '2D vector drawing' ],
  11. 1996: 'Java',
  12. 1997: [ 'Visual C++', 'Win32', 'Photoshop' ],
  13. 1998: [ 'C++98', 'Tcl/Tk 8.0', 'SQL' ],
  14. 1999: 'JavaScript',
  15. 2000: [ 'Python', 'MFC', 'Extreme Programming' ],
  16. 2001: 'analog electronics',
  17. 2002: 'MySQL',
  18. 2003: [ 'GNU Bash scripting', 'EAGLE' ],
  19. 2004: [ 'digital electronics', 'PIC assembly', 'Mathematica' ],
  20. 2005: [ 'UML', '3D modeling & rendering' ],
  21. 2006: [ 'Ruby', 'C.O.F.F.E.E.', 'wxPython', 'Ajax/Prototype' ],
  22. 2007: [ 'Flash', 'ActionScript', 'C++/CLI' ],
  23. 2008: 'Erlang',
  24. 2009: 'Lua',
  25. 2010: [ 'R', 'Arduino' ],
  26. 2011: [ 'Haskell', 'C++11' ],
  27. 2012: 'RPL',
  28. 2013: [ 'F#', '.NET', 'MS Office Automation', 'OCaml' ],
  29. 2014: [ 'jQuery', 'Dancer', 'Fossil (DVCS)' ],
  30. 2015: 'C++14',
  31. 2016: [ 'SQLite', 'PDP-8 assembly' ],
  32. 2017: [ 'Go (language)', 'FOCAL', 'FORTRAN II', 'FORTRAN IV' ],
  33. 2018: [ 'C++17', 'Tcl 8.6', 'HD-SDI' ],
  34. 2019: [ 'Cinema 4D', 'Jupyter' ],
  35. 2020: 'C++20',
  36. 2021: [ 'RouterOS', 'network engineering' ],
  37. 2022: [ 'Docker', 'Swarm', 'systemd-nspawn' ],
  38. 2023: [ 'dvLED Walls', 'Podman' ],
  39. 2024: 'container distribution',
  40.  
  41. format: function(year) {
  42. var what = this[year];
  43. if (what === undefined) return '';
  44. if (!(what instanceof Array)) return what;
  45. if (what.length < 2) return '';
  46.  
  47. var s = '';
  48. var wtmp = what.slice(0).reverse();
  49. while (wtmp.length > 2) s += wtmp.pop() + ', ';
  50.  
  51. return s + wtmp[1] + ' and ' + wtmp[0];
  52. },
  53.  
  54. init: function() {
  55. experience.test();
  56.  
  57. var expBlock = $('#experience');
  58. var btn = $('<button></button>').
  59. text('So, Warren, what have you learned?').
  60. click(function() {
  61. expBlock.after(experience.résumé()).show();
  62. });
  63. expBlock.after(btn);
  64.  
  65. $.get('js/resume.js', function(reply) {
  66. $('#resumeText').text(reply);
  67. prettyPrint();
  68. });
  69. },
  70.  
  71. résumé: function() {
  72. var html = '';
  73. Object.keys(this).filter(function(k) {
  74. return parseInt(k, 10) > 0;
  75. }).sort().forEach(function(y) {
  76. var what = experience.format(y);
  77. html +=
  78. '<li>' +
  79. 'I learned ' + what + ' in ' + y + '.' +
  80. '</li>';
  81. });
  82. return '<ul>' + html + '</ul>';
  83. },
  84.  
  85. test: function() {
  86. function assert(actual, expected) {
  87. if (actual != expected) {
  88. alert('FAIL: "' + actual + '" != "' + expected + '"!');
  89. throw 'things and sulk';
  90. }
  91. }
  92.  
  93. assert(this.format(1969), ''); // error
  94. assert(this.format(1990), 'Pascal'); // singleton
  95. assert(this.format(1991).indexOf(' and '), 1); // pair
  96. assert(this.format(1998).indexOf(', '), 5); // triplet
  97. assert(this.format(2006).indexOf(' and '), 28); // tuple
  98. if (window.console) console.log('All good!');
  99. }
  100. };