Browse Source

- New look and feel
- Code completion
- Cog menus in browser list

Nicolas Petton 12 years ago
parent
commit
7f8b8e39f8
73 changed files with 5764 additions and 521 deletions
  1. 215 69
      css/helios.css
  2. BIN
      images/hsplitter.png
  3. BIN
      images/spinner.gif
  4. BIN
      images/vsplitter.png
  5. 377 85
      js/Helios-Browser.deploy.js
  6. 473 101
      js/Helios-Browser.js
  7. 92 0
      js/Helios-Commands-Browser.deploy.js
  8. 128 1
      js/Helios-Commands-Browser.js
  9. 65 0
      js/Helios-Commands-Core.deploy.js
  10. 86 1
      js/Helios-Commands-Core.js
  11. 16 0
      js/Helios-Core.deploy.js
  12. 21 0
      js/Helios-Core.js
  13. 29 0
      js/Helios-Environments.deploy.js
  14. 39 0
      js/Helios-Environments.js
  15. 20 10
      js/Helios-KeyBindings.deploy.js
  16. 23 13
      js/Helios-KeyBindings.js
  17. 8 6
      js/Helios-Layout.deploy.js
  18. 10 8
      js/Helios-Layout.js
  19. 123 8
      js/Helios-Workspace.deploy.js
  20. 151 11
      js/Helios-Workspace.js
  21. 12 0
      js/Kernel-Methods.deploy.js
  22. 17 0
      js/Kernel-Methods.js
  23. 36 0
      js/Kernel-Objects.deploy.js
  24. 51 0
      js/Kernel-Objects.js
  25. 2 0
      js/amber.js
  26. 6 0
      js/boot.js
  27. 32 0
      js/lib/CodeMirror/addon/dialog/dialog.css
  28. 80 0
      js/lib/CodeMirror/addon/dialog/dialog.js
  29. 54 0
      js/lib/CodeMirror/addon/display/placeholder.js
  30. 52 0
      js/lib/CodeMirror/addon/edit/closebrackets.js
  31. 85 0
      js/lib/CodeMirror/addon/edit/closetag.js
  32. 44 0
      js/lib/CodeMirror/addon/edit/continuecomment.js
  33. 25 0
      js/lib/CodeMirror/addon/edit/continuelist.js
  34. 74 0
      js/lib/CodeMirror/addon/edit/matchbrackets.js
  35. 31 0
      js/lib/CodeMirror/addon/fold/brace-fold.js
  36. 32 0
      js/lib/CodeMirror/addon/fold/foldcode.js
  37. 11 0
      js/lib/CodeMirror/addon/fold/indent-fold.js
  38. 64 0
      js/lib/CodeMirror/addon/fold/xml-fold.js
  39. 582 0
      js/lib/CodeMirror/addon/hint/html-hint.js
  40. 140 0
      js/lib/CodeMirror/addon/hint/javascript-hint.js
  41. 117 0
      js/lib/CodeMirror/addon/hint/pig-hint.js
  42. 93 0
      js/lib/CodeMirror/addon/hint/python-hint.js
  43. 38 0
      js/lib/CodeMirror/addon/hint/show-hint.css
  44. 172 0
      js/lib/CodeMirror/addon/hint/show-hint.js
  45. 118 0
      js/lib/CodeMirror/addon/hint/xml-hint.js
  46. 127 0
      js/lib/CodeMirror/addon/lint/javascript-lint.js
  47. 14 0
      js/lib/CodeMirror/addon/lint/json-lint.js
  48. 96 0
      js/lib/CodeMirror/addon/lint/lint.css
  49. 183 0
      js/lib/CodeMirror/addon/lint/lint.js
  50. 51 0
      js/lib/CodeMirror/addon/mode/loadmode.js
  51. 95 0
      js/lib/CodeMirror/addon/mode/multiplex.js
  52. 59 0
      js/lib/CodeMirror/addon/mode/overlay.js
  53. 29 0
      js/lib/CodeMirror/addon/runmode/colorize.js
  54. 130 0
      js/lib/CodeMirror/addon/runmode/runmode-standalone.js
  55. 52 0
      js/lib/CodeMirror/addon/runmode/runmode.js
  56. 89 0
      js/lib/CodeMirror/addon/runmode/runmode.node.js
  57. 60 0
      js/lib/CodeMirror/addon/search/match-highlighter.js
  58. 131 0
      js/lib/CodeMirror/addon/search/search.js
  59. 130 0
      js/lib/CodeMirror/addon/search/searchcursor.js
  60. 39 0
      js/lib/CodeMirror/addon/selection/active-line.js
  61. 34 0
      js/lib/CodeMirror/addon/selection/mark-selection.js
  62. 10 9
      js/lib/CodeMirror/codemirror.css
  63. 264 132
      js/lib/CodeMirror/codemirror.js
  64. 144 43
      st/Helios-Browser.st
  65. 38 2
      st/Helios-Commands-Browser.st
  66. 28 2
      st/Helios-Commands-Core.st
  67. 6 0
      st/Helios-Core.st
  68. 8 0
      st/Helios-Environments.st
  69. 13 9
      st/Helios-KeyBindings.st
  70. 2 2
      st/Helios-Layout.st
  71. 68 9
      st/Helios-Workspace.st
  72. 6 0
      st/Kernel-Methods.st
  73. 14 0
      st/Kernel-Objects.st

+ 215 - 69
css/helios.css

@@ -1,5 +1,6 @@
 body {
-    font-size: 13px;
+    font-size: 11px;
+    background: #f1f1f1;
 }
 
 .clearfix:after {
@@ -27,22 +28,58 @@ a {
     cursor: pointer;
 }
 
+i {
+    opacity: 0.8;
+}
+
+[class^="icon-"], [class*=" icon-"] {
+    margin-top: 0;
+}
+
+.CodeMirror {
+    position: absolute;
+    overflow: hidden;
+    height: 100%;
+    width: 100%;
+    background: #fff;
+}
+
+.CodeMirror-hints {
+    border-radius: 0;
+    font-family: Menlo, Monaco, "Lucida Console", Courier, monospace;
+    font-size: 11px;
+    line-height: 1em;
+    padding: 0;
+    max-height: 120px;
+}
+
+.CodeMirror-hint {
+    border-radius: 0;
+    padding: 0 10px;
+}
+
 .CodeMirror pre {
-    line-height: 16px;
+    font-family: Menlo, Monaco, "Lucida Console", Courier, monospace;
+    line-height: 15px;
 }
 
 .btn, .btn-group > .btn, .btn-group > .dropdown-menu {
-    font-size: 13px;
     padding: 2px 8px;
 }
 
 .navbar .navbar-fixed-top {
-    font-size: 12px;
+    font-size: 11px;
     line-height: 16px;
 }
 
 .navbar-inner {
-    min-height: 26px;
+    min-height: 22px;
+    background-color: #dbdbdb;
+    border: 1px solid #666;
+    background-image: linear-gradient(top, #dbdbdb, #bababa);
+    background-image: -webkit-linear-gradient(top, #dbdbdb, #bababa);
+    background-image: -moz-linear-gradient(top, #dbdbdb, #bababa);
+    background-image: -owebkit-linear-gradient(top, #dbdbdb, #bababa);
 }
 
 
@@ -52,11 +89,25 @@ a {
 
 
 .navbar .nav > li > a {
-    padding: 5px 15px;
+    padding: 3px 8px;
+    font-size: 11px;
+    color: #111;
+    text-shadow: 0 1px 0 #ddd;
+}
+
+.navbar .nav > .active > a, 
+.navbar .nav > .active > a:hover, 
+.navbar .nav > .active > a:focus {
+    background-image: linear-gradient(left, #999 0%, #bababa 4%, #bababa 96%, #999 100%);
+    background-image: -webkit-linear-gradient(left, #999 0%, #bababa 4%, #bababa 96%, #999 100%);
+    background-image: -moz-linear-gradient(left, #999 0%, #bababa 4%, #bababa 96%, #999 100%);
+    background-image: -o-linear-gradient(left, #999 0%, #bababa 4%, #bababa 96%, #999 100%);
+    text-shadow: #ddd 0px 1px 0px; 
+    color: #222;
 }
 
 .navbar-fixed-top i {
-    opacity: 0.2;
+    opacity: 0.4;
     margin-right: 5px;
     height: 12px;
     margin-top: 0;
@@ -70,8 +121,11 @@ a {
     border-radius: 0;
     -webkit-border-radius: 0;
     -moz-border-radius: 0;
-    padding: 3px;
+    padding: 2px 2px 1px 4px;
     margin: 0;
+    font-size: 11px;
+    font-family: Menlo, Monaco, "Lucida Console", Courier, monospace;
+    color: #111;
 }
 
 [class^="icon-"], [class*=" icon-"] {
@@ -79,18 +133,38 @@ a {
 }
 
 .nav-pills > .active > a {
-    background: #ccc;
+    background-color: #ddd;
+    background-image: linear-gradient(top, #ddd, #bababa);
+    background-image: -moz-linear-gradient(top, #ddd, #bababa);
+    background-image: -webkit-linear-gradient(top, #ddd, #bababa);
+    background-image: -o-linear-gradient(top, #ddd, #bababa);
     color: #fff;
 }
 
-.focused .nav-pills > .active > a, .nav-pills > .active > a:hover {
-    background: #08C;
+.focused .nav-pills {
+    background-color: #e3e7eb;
+}
+
+.focused .nav-pills > .active > a, 
+.nav-pills > .active > a:hover,
+.dropdown-menu li > a:hover, 
+.dropdown-menu li > a:focus, 
+.dropdown-submenu:hover > a,
+.dropdown-menu .active > a, 
+.dropdown-menu .active > a:hover,
+.CodeMirror-hint-active {
+    background: #b1bdd5;
+    background-image: linear-gradient(top, #b1bdd5, #8999b8);
+    background-image: -webkit-linear-gradient(top, #b1bdd5, #8999b8);
+    background-image: -moz-linear-gradient(top, #b1bdd5, #8999b8);
+    background-image: -owebkit-linear-gradient(top, #b1bdd5, #8999b8);
+    color: #fff;
 }
 
 
 .tool_container {
     position: fixed;
-    top: 26px;
+    top: 23px;
     bottom: 0;
     left: 0;
     right: 0;
@@ -118,23 +192,67 @@ a {
 .tool_container .panes .pane .nav-pills {
     position: absolute;
     overflow-y: auto;
-    top: 0;
-    bottom: 37px;
+    top: 17px;
+    bottom: 17px;
     width: 100%;
     margin: 0;
 }
 
+.tool_container .list-label {
+    font-size: 11px;
+    border-radius: 0;
+    border-bottom: 1px solid #666;
+    background-color: #e9eaf5;
+    background-image: -webkit-linear-gradient(top, #e9eaf5, #bfc2d2);
+    color: #333;
+    font-weight: bold;
+    text-shadow: 0 0 0;
+    padding-left: 4px;
+    line-height: 16px;
+}
+
+.tool_container .list-label .btn-group.cog {
+    position: absolute;
+    top: 0;
+    right: 0;
+    padding: 0;
+    margin: 0;
+}
+
+.tool_container .list-label .btn-group.open .dropdown-toggle {
+    box-shadow: 0 0 0;
+    border: 0;
+}
+
+.tool_container .list-label .btn-group > .dropdown-menu {
+    padding: 0;
+    font-size: 11px;
+}
+
+.tool_container .list-label .btn-group.cog i {
+    margin-top: 1px;
+}
+
+.tool_container .list-label .cog .btn.dropdown-toggle {
+    padding: 0;
+    margin: 0;
+    line-height: 12px;
+    border: 0;
+    background: transparent;
+}
+
+
 .tool_container .panes .pane .pane_actions {
     position: absolute;
     overflow: hidden;
     width: 100%;
-    height: 26px;
+    height: 16px;
     bottom: 0;
-    background: #E5E5E5;
-    border-top: 1px solid #bbb;
-    padding: 5px 0;
+    background: #dadada;
+    border-top: 1px solid #666;
+    padding: 0;
     margin: 0;
-    text-align: center;
+    background-image: -webkit-linear-gradient(top, #dadada, #bdbdbd);
 }
 
 .tool_container .panes .pane .pane_actions .info {
@@ -150,18 +268,35 @@ a {
     margin-left: 5px;
 }
 
-.tool_container .panes .pane .pane_actions .btn-group .btn {
+.tool_container .panes .pane .pane_actions .btn {
+    background: transparent;
+    border: 0;
+    font-size: 11px;
+    line-height: 16px;
+    padding: 0 5px;
     margin: 0;
+    border-radius: 0;
+    box-shadow: 0 0 0;
+    vertical-align: top;
+    min-width: 50px;
 }
 
-.tool_container .panes .pane .pane_actions .btn-group.switch .btn.active:first-child {
-    background: #5AD;
-    color: white;
-    text-shadow: 0 1px 0 #333;
+.tool_container .panes .pane .pane_actions .btn:hover {
+    background-color: #bbb;
 }
 
-.tool_container .panes .pane .pane_actions .btn {
-    margin-left: 5px;
+.tool_container .panes .pane .pane_actions .btn-group .btn:hover {
+    background-color: transparent;
+}
+
+.tool_container .panes .pane .pane_actions .btn-group .btn.active {
+    background: #999;
+    background-image: linear-gradient(left, #777 0%, #bababa 4%, #bababa 96%, #777 100%);
+    background-image: -webkit-linear-gradient(left, #777 0%, #bababa 4%, #bababa 96%, #777 100%);
+    background-image: -moz-linear-gradient(left, #777 0%, #bababa 4%, #bababa 96%, #777 100%);
+    background-image: -o-linear-gradient(left, #777 0%, #bababa 4%, #bababa 96%, #777 100%);
+    text-shadow: #ddd 0px 1px 0px; 
+    color: #222;
 }
 
 .tool_container .panes .pane .class_side .nav-pills {
@@ -184,26 +319,19 @@ a {
 
 .tool_container .splitter {
     z-index: 10;
-    background: #E6E6E6;
-    border: 1px solid #bbb;
+    background: #888;
 }
 
 .tool_container .splitter.vertical {
-    width: 4px;
+    width: 1px;
     height: 100%;
     float: left;
     cursor: e-resize;
-    background-image: url('/images/vsplitter.png');
-    background-repeat: no-repeat;
-    background-position: center;
 }
 
 .tool_container .splitter.horizontal {
-    height: 4px;
+    height: 1px;
     cursor: s-resize;
-    background-image: url('/images/hsplitter.png');
-    background-repeat: no-repeat;
-    background-position: center;
 }
 
 
@@ -211,9 +339,13 @@ a {
     z-index: 20;
     position: fixed;
     bottom: 0px;
-    background: #ddd;
+    background: #fff;
+    background-image: linear-gradient(top, #fafafa, #f0f0f0 50%, #e1e1e1 51%);
+    background-image: -moz-linear-gradient(top, #fafafa, #f0f0f0 50%, #e1e1e1 51%);
+    background-image: -o-linear-gradient(top, #fafafa, #f0f0f0 50%, #e1e1e1 51%);
+    background-image: -webkit-linear-gradient(top, #fafafa, #f0f0f0 50%, #e1e1e1 51%);
     width: 100%;
-    border-top: 1px solid #AAA;
+    border-top: 1px solid #aaa;
     font-size: 11px;
     height: 22px;
 }
@@ -223,12 +355,12 @@ a {
 }
 
 .key_helper .label {
-    padding: 0 4px;
-    border-top: 1px solid white;
-    border-left: 1px solid white;
-    border-bottom: 1px solid #666;
-    border-right: 1px solid #666;
-    font-family: monospace;
+    padding: 1px 4px;
+    font-family: Menlo, Monaco, "Lucida Console", Courier, monospace;
+    background: transparent;
+    color: #08C;
+    text-shadow: none;
+    border: 0 none;
 }
 
 .key_helper .action {
@@ -237,17 +369,23 @@ a {
 }
 
 .key_helper .selected {
-    background: rgb(7, 195, 221);
+    background-image: linear-gradient(top, #ccc, #aaa);
+    background-image: -moz-linear-gradient(top, #ccc, #aaa);
+    background-image: -o-linear-gradient(top, #ccc, #aaa);
+    background-image: -webkit-linear-gradient(top, #ccc, #aaa);
     height: 30px;
-    padding: 4px;
-    color: white;
+    padding: 0 8px;
+    color: #333;
     font-weight: bold;
-    text-shadow: 0 -1px 0 #666;
+    text-shadow: 0 1px 0 #fff;
+    display: inline-block;
+    border-right: 1px solid #aaa;
 }
 
 .key_helper .close {
     font-size: 14px;
-    line-height: 22px;
+    line-height: 26px;
+    opacity: 0.6;
 }
 
 .key_helper .close:hover {
@@ -256,10 +394,14 @@ a {
 
 .key_helper input {
     outline: none;
-    border: none;
-    font-size: 12px;
-    padding: 2px 4px;
-    background: #EEE;
+    font-size: 11px;
+    padding: 2px 8px;
+    background: #fff;
+    border: 1px solid #a1a1a1;
+    border-radius: 8px;
+    box-shadow: inset 0 0px 2px 0px #aaa;
+    margin: 2px 4px;
+    line-height: 1em;
 }
 
 .key_helper .error .help-inline,
@@ -274,24 +416,10 @@ a {
     bottom: 30px !important;
 }
 
-#keybinding-start-helper {
-    position: fixed;
-    top: 50%;
-    left: 50%;
-    background: rgba(0,0,0, 0.6);
-    color: #fff;
-    width: 300px;
-    margin-left: -150px;
-    text-align: center;
-    padding: 50px 0;
-    font-size: 16px;
-    border-radius: 20px;
-}
-
 #cog-helper {
     position: fixed;
-    bottom: 4px;
-    left: 4px;
+    bottom: 2px;
+    right: 2px;
     z-index: 1000;
     opacity: 0.6;
     transition: all .5s;
@@ -303,4 +431,22 @@ a {
 
 #cog-helper:hover {
     opacity: 1;
+}
+
+#helper {
+    z-index: 300;
+    top: 50%;
+    position: absolute;
+    left: 50%;
+    margin-left: -220px;
+    margin-top: -35px;
+    width: 400px;
+    text-align: center;
+    color: #ddd;
+    font-size: 18px;
+    font-weight: bold;
+    text-shadow: 0 -1px 0 #111;
+    padding: 20px;
+    background: rgba(0,0,0, 0.6);
+    border-radius: 40px;
 }

BIN
images/hsplitter.png


BIN
images/spinner.gif


BIN
images/vsplitter.png


+ 377 - 85
js/Helios-Browser.deploy.js

@@ -76,17 +76,20 @@ smalltalk.method({
 selector: "methodsListWidget",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+function $HLMethodsListWidget(){return smalltalk.HLMethodsListWidget||(typeof HLMethodsListWidget=="undefined"?nil:HLMethodsListWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
 $2=self["@methodsListWidget"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@methodsListWidget"]=_st((smalltalk.HLMethodsListWidget || HLMethodsListWidget))._on_(_st(self)._model());
-$1=self["@methodsListWidget"];
+self["@methodsListWidget"]=_st($HLMethodsListWidget())._on_(_st(self)._model());
+self["@methodsListWidget"];
+$1=_st(self["@methodsListWidget"])._next_(_st(self)._sourceWidget());
 } else {
 $1=$2;
 };
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"methodsListWidget",{}, smalltalk.HLBrowser)})},
-messageSends: ["ifNil:", "on:", "model"]}),
+}, function($ctx1) {$ctx1.fill(self,"methodsListWidget",{},smalltalk.HLBrowser)})},
+messageSends: ["ifNil:", "on:", "model", "next:", "sourceWidget"]}),
 smalltalk.HLBrowser);
 
 smalltalk.addMethod(
@@ -259,15 +262,36 @@ smalltalk.HLBrowser.klass);
 
 smalltalk.addClass('HLBrowserListWidget', smalltalk.HLNavigationListWidget, ['model'], 'Helios-Browser');
 smalltalk.addMethod(
-"_initialize",
+"_label",
 smalltalk.method({
-selector: "initialize",
+selector: "label",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.HLNavigationListWidget.fn.prototype._initialize.apply(_st(self), []);
-_st(self)._observeSystem();
-return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.HLBrowserListWidget)})},
-messageSends: ["initialize", "observeSystem"]}),
+return smalltalk.withContext(function($ctx1) { 
+return "List";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLBrowserListWidget)})},
+messageSends: []}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_menuCommands",
+smalltalk.method({
+selector: "menuCommands",
+fn: function (){
+var self=this;
+function $HLBrowserCommand(){return smalltalk.HLBrowserCommand||(typeof HLBrowserCommand=="undefined"?nil:HLBrowserCommand)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st($HLBrowserCommand())._concreteClasses())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._isValidFor_(_st(self)._selectedItem());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._for_(_st(self)._model());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuCommands",{},smalltalk.HLBrowserListWidget)})},
+messageSends: ["collect:", "for:", "model", "select:", "isValidFor:", "selectedItem", "concreteClasses"]}),
 smalltalk.HLBrowserListWidget);
 
 smalltalk.addMethod(
@@ -289,10 +313,14 @@ smalltalk.method({
 selector: "model:",
 fn: function (aBrowserModel){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self["@model"]=aBrowserModel;
-_st(self)._observeModel();
-return self}, function($ctx1) {$ctx1.fill(self,"model:",{aBrowserModel:aBrowserModel}, smalltalk.HLBrowserListWidget)})},
-messageSends: ["observeModel"]}),
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+self["@model"]=aBrowserModel;
+$1=self;
+_st($1)._observeSystem();
+$2=_st($1)._observeModel();
+return self}, function($ctx1) {$ctx1.fill(self,"model:",{aBrowserModel:aBrowserModel},smalltalk.HLBrowserListWidget)})},
+messageSends: ["observeSystem", "observeModel"]}),
 smalltalk.HLBrowserListWidget);
 
 smalltalk.addMethod(
@@ -315,6 +343,118 @@ return smalltalk.withContext(function($ctx1) { 
return self}, function($ctx1) {$
 messageSends: []}),
 smalltalk.HLBrowserListWidget);
 
+smalltalk.addMethod(
+"_renderContentOn_",
+smalltalk.method({
+selector: "renderContentOn:",
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self)._renderHeadOn_(html);
+smalltalk.HLNavigationListWidget.fn.prototype._renderContentOn_.apply(_st(self), [html]);
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.HLBrowserListWidget)})},
+messageSends: ["renderHeadOn:", "renderContentOn:"]}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_renderHeadOn_",
+smalltalk.method({
+selector: "renderHeadOn:",
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_("list-label");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(html)._with_(_st(self)._label());
+return _st(self)._renderMenuOn_(html);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderHeadOn:",{html:html},smalltalk.HLBrowserListWidget)})},
+messageSends: ["class:", "div", "with:", "label", "renderMenuOn:"]}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_renderMenuOn_",
+smalltalk.method({
+selector: "renderMenuOn:",
+fn: function (html){
+var self=this;
+var commands;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$5,$6,$7,$9,$10,$8,$4;
+commands=_st(self)._menuCommands();
+$1=_st(commands)._isEmpty();
+if(smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(html)._div();
+_st($3)._class_("btn-group cog");
+$4=_st($3)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$5=_st(html)._a();
+_st($5)._class_("btn dropdown-toggle");
+_st($5)._at_put_("data-toggle","dropdown");
+$6=_st($5)._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(html)._tag_("i"))._class_("icon-cog");
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+$6;
+$7=_st(html)._ul();
+_st($7)._class_("dropdown-menu pull-right");
+$8=_st($7)._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(self)._menuCommands())._do_((function(each){
+return smalltalk.withContext(function($ctx4) {
+return _st(_st(html)._li())._with_((function(){
+return smalltalk.withContext(function($ctx5) {
+$9=_st(html)._a();
+_st($9)._with_(_st(each)._menuLabel());
+$10=_st($9)._onClick_((function(){
+return smalltalk.withContext(function($ctx6) {
+return _st(self)._execute_(each);
+}, function($ctx6) {$ctx6.fillBlock({},$ctx1)})}));
+return $10;
+}, function($ctx5) {$ctx5.fillBlock({},$ctx1)})}));
+}, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+return $8;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderMenuOn:",{html:html,commands:commands},smalltalk.HLBrowserListWidget)})},
+messageSends: ["menuCommands", "ifTrue:", "isEmpty", "class:", "div", "with:", "a", "at:put:", "tag:", "ul", "do:", "menuLabel", "onClick:", "execute:", "li"]}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_selectedItem_",
+smalltalk.method({
+selector: "selectedItem:",
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLNavigationListWidget.fn.prototype._selectedItem_.apply(_st(self), [anItem]);
+_st(self)._updateMenu();
+return self}, function($ctx1) {$ctx1.fill(self,"selectedItem:",{anItem:anItem},smalltalk.HLBrowserListWidget)})},
+messageSends: ["selectedItem:", "updateMenu"]}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_updateMenu",
+smalltalk.method({
+selector: "updateMenu",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(_st(_st(self)._wrapper())._asJQuery())._find_(".cog"))._remove();
+_st((function(html){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._renderMenuOn_(html);
+}, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st(_st(_st(self)._wrapper())._asJQuery())._find_(".list-label"));
+return self}, function($ctx1) {$ctx1.fill(self,"updateMenu",{},smalltalk.HLBrowserListWidget)})},
+messageSends: ["remove", "find:", "asJQuery", "wrapper", "appendToJQuery:", "renderMenuOn:"]}),
+smalltalk.HLBrowserListWidget);
+
 
 smalltalk.addMethod(
 "_on_",
@@ -404,6 +544,18 @@ return $1;
 messageSends: ["ifFalse:ifTrue:", "isEmpty", "comment", "theNonMetaClass"]}),
 smalltalk.HLClassesListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Classes";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLClassesListWidget)})},
+messageSends: []}),
+smalltalk.HLClassesListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -434,16 +586,21 @@ smalltalk.method({
 selector: "observeSystem",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ClassAdded || ClassAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onClassAdded_(_st(ann)._theClass());
+function $ClassAdded(){return smalltalk.ClassAdded||(typeof ClassAdded=="undefined"?nil:ClassAdded)}
+function $ClassRemoved(){return smalltalk.ClassRemoved||(typeof ClassRemoved=="undefined"?nil:ClassRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ClassAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onClassAdded_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.ClassRemoved || ClassRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onClassRemoved_(_st(ann)._theClass());
+$2=_st($1)._on_do_($ClassRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onClassRemoved_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{}, smalltalk.HLClassesListWidget)})},
-messageSends: ["on:do:", "onClassAdded:", "theClass", "current", "onClassRemoved:"]}),
+return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLClassesListWidget)})},
+messageSends: ["on:do:", "onClassAdded:", "theClass", "systemAnnouncer", "model", "onClassRemoved:"]}),
 smalltalk.HLClassesListWidget);
 
 smalltalk.addMethod(
@@ -555,14 +712,18 @@ smalltalk.method({
 selector: "renderButtonsOn:",
 fn: function (html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$6,$7,$8,$2,$9,$10;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$5,$6,$7,$8,$2;
 $1=_st(html)._div();
 _st($1)._class_("btn-group");
 _st($1)._at_put_("data-toggle","buttons-radio");
 $2=_st($1)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(html)._button();
-_st($3)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
_st(str)._nextPutAll_("btn");
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_(_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx3) {
+_st(str)._nextPutAll_("btn");
 $4=_st(self)._showInstance();
 if(smalltalk.assert($4)){
 return _st(str)._nextPutAll_(" active");
@@ -570,12 +731,14 @@ return _st(str)._nextPutAll_(" active");
 }, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
 _st($3)._with_("Instance");
 $5=_st($3)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
return _st(self)._showInstance_(true);
+return smalltalk.withContext(function($ctx3) {
+return _st(self)._showInstance_(true);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
 $5;
 $6=_st(html)._button();
-_st($6)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
_st(str)._nextPutAll_("btn");
+_st($6)._class_(_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx3) {
+_st(str)._nextPutAll_("btn");
 $7=_st(_st(self)._model())._showInstance();
 if(! smalltalk.assert($7)){
 return _st(str)._nextPutAll_(" active");
@@ -583,15 +746,12 @@ return _st(str)._nextPutAll_(" active");
 }, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
 _st($6)._with_("Class");
 $8=_st($6)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
return _st(_st(self)._model())._showInstance_(false);
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(self)._model())._showInstance_(false);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
 return $8;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-$9=_st(html)._button();
-_st($9)._class_("btn");
-_st($9)._at_put_("data-toggle","button");
-$10=_st($9)._with_("Comment");
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html}, smalltalk.HLClassesListWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLClassesListWidget)})},
 messageSends: ["class:", "div", "at:put:", "with:", "streamContents:", "nextPutAll:", "ifTrue:", "showInstance", "button", "onClick:", "showInstance:", "ifFalse:", "model"]}),
 smalltalk.HLClassesListWidget);
 
@@ -846,6 +1006,18 @@ return $1;
 messageSends: ["isOverride:", "selectorsCache"]}),
 smalltalk.HLMethodsListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Methods";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLMethodsListWidget)})},
+messageSends: []}),
+smalltalk.HLMethodsListWidget);
+
 smalltalk.addMethod(
 "_methodForSelector_",
 smalltalk.method({
@@ -913,22 +1085,31 @@ smalltalk.method({
 selector: "observeSystem",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ProtocolAdded || ProtocolAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolAdded_(_st(ann)._theClass());
+function $ProtocolAdded(){return smalltalk.ProtocolAdded||(typeof ProtocolAdded=="undefined"?nil:ProtocolAdded)}
+function $ProtocolRemoved(){return smalltalk.ProtocolRemoved||(typeof ProtocolRemoved=="undefined"?nil:ProtocolRemoved)}
+function $MethodAdded(){return smalltalk.MethodAdded||(typeof MethodAdded=="undefined"?nil:MethodAdded)}
+function $MethodRemoved(){return smalltalk.MethodRemoved||(typeof MethodRemoved=="undefined"?nil:MethodRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ProtocolAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolAdded_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-_st($1)._on_do_((smalltalk.ProtocolRemoved || ProtocolRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolRemoved_(_st(ann)._theClass());
+_st($1)._on_do_($ProtocolRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolRemoved_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-_st($1)._on_do_((smalltalk.MethodAdded || MethodAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onMethodAdded_(_st(ann)._method());
+_st($1)._on_do_($MethodAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onMethodAdded_(_st(ann)._method());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.MethodRemoved || MethodRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onMethodRemoved_(_st(ann)._method());
+$2=_st($1)._on_do_($MethodRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onMethodRemoved_(_st(ann)._method());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLMethodsListWidget)})},
-messageSends: ["on:do:", "onProtocolAdded:", "theClass", "current", "onProtocolRemoved:", "onMethodAdded:", "method", "onMethodRemoved:"]}),
+messageSends: ["on:do:", "onProtocolAdded:", "theClass", "systemAnnouncer", "model", "onProtocolRemoved:", "onMethodAdded:", "method", "onMethodRemoved:"]}),
 smalltalk.HLMethodsListWidget);
 
 smalltalk.addMethod(
@@ -1239,6 +1420,18 @@ smalltalk.HLMethodsListWidget.klass);
 
 
 smalltalk.addClass('HLPackagesListWidget', smalltalk.HLBrowserListWidget, [], 'Helios-Browser');
+smalltalk.addMethod(
+"_commitPackage",
+smalltalk.method({
+selector: "commitPackage",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(self)._model())._commitPackage();
+return self}, function($ctx1) {$ctx1.fill(self,"commitPackage",{},smalltalk.HLPackagesListWidget)})},
+messageSends: ["commitPackage", "model"]}),
+smalltalk.HLPackagesListWidget);
+
 smalltalk.addMethod(
 "_focusClassesListWidget",
 smalltalk.method({
@@ -1256,13 +1449,15 @@ smalltalk.method({
 selector: "initializeItems",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
 self["@items"]=_st(_st(_st(self)._model())._packages())._sort_((function(a,b){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(a)._name()).__lt(_st(b)._name());
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(a)._name()).__lt(_st(b)._name());
 }, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})}));
 $1=self["@items"];
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"initializeItems",{}, smalltalk.HLPackagesListWidget)})},
+}, function($ctx1) {$ctx1.fill(self,"initializeItems",{},smalltalk.HLPackagesListWidget)})},
 messageSends: ["sort:", "<", "name", "packages", "model"]}),
 smalltalk.HLPackagesListWidget);
 
@@ -1284,6 +1479,18 @@ return $1;
 messageSends: ["ifNil:", "initializeItems"]}),
 smalltalk.HLPackagesListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Packages";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLPackagesListWidget)})},
+messageSends: []}),
+smalltalk.HLPackagesListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -1338,32 +1545,35 @@ smalltalk.method({
 selector: "renderButtonsOn:",
 fn: function (html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$5,$6,$7,$8,$4,$9,$10;
-$1=_st(html)._span();
-_st($1)._class_("info");
-$2=_st($1)._with_("Auto commit");
-$3=_st(html)._div();
-_st($3)._class_("btn-group switch");
-_st($3)._at_put_("data-toggle","buttons-radio");
-$4=_st($3)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(html)._button();
-_st($5)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
return _st(str)._nextPutAll_("btn");
-}, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
-$6=_st($5)._with_("On");
-$6;
-$7=_st(html)._button();
-_st($7)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
return _st(str)._nextPutAll_("btn active");
-}, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
-$8=_st($7)._with_("Off");
-return $8;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$2;
+$1=_st(html)._div();
+_st($1)._class_("buttons");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_("btn");
+_st($3)._with_("Commit");
+$4=_st($3)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(self)._commitPackage();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+return $4;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-$9=_st(html)._a();
-_st($9)._class_("btn");
-$10=_st($9)._with_("Commit");
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html}, smalltalk.HLPackagesListWidget)})},
-messageSends: ["class:", "span", "with:", "div", "at:put:", "streamContents:", "nextPutAll:", "button", "a"]}),
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLPackagesListWidget)})},
+messageSends: ["class:", "div", "with:", "button", "onClick:", "commitPackage"]}),
+smalltalk.HLPackagesListWidget);
+
+smalltalk.addMethod(
+"_renderItemLabel_on_",
+smalltalk.method({
+selector: "renderItemLabel:on:",
+fn: function (aPackage,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(html)._with_(_st(aPackage)._name());
+return self}, function($ctx1) {$ctx1.fill(self,"renderItemLabel:on:",{aPackage:aPackage,html:html},smalltalk.HLPackagesListWidget)})},
+messageSends: ["with:", "name"]}),
 smalltalk.HLPackagesListWidget);
 
 smalltalk.addMethod(
@@ -1393,6 +1603,18 @@ return $1;
 messageSends: ["allProtocol", "model"]}),
 smalltalk.HLProtocolsListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Protocols";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLProtocolsListWidget)})},
+messageSends: []}),
+smalltalk.HLProtocolsListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -1423,16 +1645,21 @@ smalltalk.method({
 selector: "observeSystem",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ProtocolAdded || ProtocolAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolAdded_to_(_st(ann)._protocol(),_st(ann)._theClass());
+function $ProtocolAdded(){return smalltalk.ProtocolAdded||(typeof ProtocolAdded=="undefined"?nil:ProtocolAdded)}
+function $ProtocolRemoved(){return smalltalk.ProtocolRemoved||(typeof ProtocolRemoved=="undefined"?nil:ProtocolRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ProtocolAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolAdded_to_(_st(ann)._protocol(),_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.ProtocolRemoved || ProtocolRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolRemoved_from_(_st(ann)._protocol(),_st(ann)._theClass());
+$2=_st($1)._on_do_($ProtocolRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolRemoved_from_(_st(ann)._protocol(),_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{}, smalltalk.HLProtocolsListWidget)})},
-messageSends: ["on:do:", "onProtocolAdded:to:", "protocol", "theClass", "current", "onProtocolRemoved:from:"]}),
+return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLProtocolsListWidget)})},
+messageSends: ["on:do:", "onProtocolAdded:to:", "protocol", "theClass", "systemAnnouncer", "model", "onProtocolRemoved:from:"]}),
 smalltalk.HLProtocolsListWidget);
 
 smalltalk.addMethod(
@@ -1686,9 +1913,13 @@ smalltalk.method({
 selector: "commitPackage",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._environment())._commitPackage_(_st(self)._selectedPackage());
+return smalltalk.withContext(function($ctx1) { 
+_st(self)._withHelperLabelled_do_(_st(_st("Committing package ").__comma(_st(_st(self)._selectedPackage())._name())).__comma("..."),(function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(self)._environment())._commitPackage_(_st(self)._selectedPackage());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"commitPackage",{},smalltalk.HLBrowserModel)})},
-messageSends: ["commitPackage:", "selectedPackage", "environment"]}),
+messageSends: ["withHelperLabelled:do:", ",", "name", "selectedPackage", "commitPackage:", "environment"]}),
 smalltalk.HLBrowserModel);
 
 smalltalk.addMethod(
@@ -2258,6 +2489,20 @@ return self}, function($ctx1) {$ctx1.fill(self,"showInstance:",{aBoolean:aBoolea
 messageSends: ["ifNotNil:", "selectedClass:", "ifTrue:ifFalse:", "theNonMetaClass", "selectedClass", "theMetaClass", "announce:", "new", "announcer"]}),
 smalltalk.HLBrowserModel);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._environment())._systemAnnouncer();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLBrowserModel)})},
+messageSends: ["systemAnnouncer", "environment"]}),
+smalltalk.HLBrowserModel);
+
 smalltalk.addMethod(
 "_unclassifiedProtocol",
 smalltalk.method({
@@ -2290,6 +2535,31 @@ return self}, function($ctx1) {$ctx1.fill(self,"withCompileErrorHandling:",{aBlo
 messageSends: ["on:do:", "handleCompileError:", "handleUnkownVariableError:", "handleParseError:"]}),
 smalltalk.HLBrowserModel);
 
+smalltalk.addMethod(
+"_withHelperLabelled_do_",
+smalltalk.method({
+selector: "withHelperLabelled:do:",
+fn: function (aString,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+_st(_st(window)._jQuery_("#helper"))._remove();
+_st((function(html){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(html)._div();
+_st($1)._id_("helper");
+$2=_st($1)._with_(aString);
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st("body")._asJQuery());
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(aBlock)._value();
+return _st(_st(window)._jQuery_("#helper"))._remove();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((10));
+return self}, function($ctx1) {$ctx1.fill(self,"withHelperLabelled:do:",{aString:aString,aBlock:aBlock},smalltalk.HLBrowserModel)})},
+messageSends: ["remove", "jQuery:", "appendToJQuery:", "asJQuery", "id:", "div", "with:", "valueWithTimeout:", "value"]}),
+smalltalk.HLBrowserModel);
+
 
 smalltalk.addMethod(
 "_on_",
@@ -2591,6 +2861,28 @@ return self}, function($ctx1) {$ctx1.fill(self,"onSourceCodeFocusRequested",{},
 messageSends: ["focus"]}),
 smalltalk.HLBrowserSourceWidget);
 
+smalltalk.addMethod(
+"_previous",
+smalltalk.method({
+selector: "previous",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"previous",{},smalltalk.HLBrowserSourceWidget)})},
+messageSends: []}),
+smalltalk.HLBrowserSourceWidget);
+
+smalltalk.addMethod(
+"_previous_",
+smalltalk.method({
+selector: "previous:",
+fn: function (aWidget){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"previous:",{aWidget:aWidget},smalltalk.HLBrowserSourceWidget)})},
+messageSends: []}),
+smalltalk.HLBrowserSourceWidget);
+
 smalltalk.addMethod(
 "_refresh",
 smalltalk.method({

+ 473 - 101
js/Helios-Browser.js

@@ -102,19 +102,22 @@ selector: "methodsListWidget",
 category: 'widgets',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+function $HLMethodsListWidget(){return smalltalk.HLMethodsListWidget||(typeof HLMethodsListWidget=="undefined"?nil:HLMethodsListWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
 $2=self["@methodsListWidget"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@methodsListWidget"]=_st((smalltalk.HLMethodsListWidget || HLMethodsListWidget))._on_(_st(self)._model());
-$1=self["@methodsListWidget"];
+self["@methodsListWidget"]=_st($HLMethodsListWidget())._on_(_st(self)._model());
+self["@methodsListWidget"];
+$1=_st(self["@methodsListWidget"])._next_(_st(self)._sourceWidget());
 } else {
 $1=$2;
 };
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"methodsListWidget",{}, smalltalk.HLBrowser)})},
+}, function($ctx1) {$ctx1.fill(self,"methodsListWidget",{},smalltalk.HLBrowser)})},
 args: [],
-source: "methodsListWidget\x0a\x09^ methodsListWidget ifNil: [\x0a      \x09methodsListWidget := HLMethodsListWidget on: self model ]",
-messageSends: ["ifNil:", "on:", "model"],
+source: "methodsListWidget\x0a\x09^ methodsListWidget ifNil: [\x0a      \x09methodsListWidget := HLMethodsListWidget on: self model.\x0a\x09\x09methodsListWidget next: self sourceWidget]",
+messageSends: ["ifNil:", "on:", "model", "next:", "sourceWidget"],
 referencedClasses: ["HLMethodsListWidget"]
 }),
 smalltalk.HLBrowser);
@@ -344,22 +347,48 @@ smalltalk.HLBrowser.klass);
 
 smalltalk.addClass('HLBrowserListWidget', smalltalk.HLNavigationListWidget, ['model'], 'Helios-Browser');
 smalltalk.addMethod(
-"_initialize",
+"_label",
 smalltalk.method({
-selector: "initialize",
-category: 'initialization',
+selector: "label",
+category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.HLNavigationListWidget.fn.prototype._initialize.apply(_st(self), []);
-_st(self)._observeSystem();
-return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.HLBrowserListWidget)})},
+return smalltalk.withContext(function($ctx1) { 
+return "List";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLBrowserListWidget)})},
 args: [],
-source: "initialize\x0a\x09super initialize.\x0a    \x0a    self observeSystem",
-messageSends: ["initialize", "observeSystem"],
+source: "label\x0a\x09^ 'List'",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.HLBrowserListWidget);
 
+smalltalk.addMethod(
+"_menuCommands",
+smalltalk.method({
+selector: "menuCommands",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $HLBrowserCommand(){return smalltalk.HLBrowserCommand||(typeof HLBrowserCommand=="undefined"?nil:HLBrowserCommand)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st($HLBrowserCommand())._concreteClasses())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._isValidFor_(_st(self)._selectedItem());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._for_(_st(self)._model());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuCommands",{},smalltalk.HLBrowserListWidget)})},
+args: [],
+source: "menuCommands\x0a\x09\x22Answer a collection of commands to be put in the cog menu\x22\x0a\x09\x0a\x09^ (HLBrowserCommand concreteClasses \x0a\x09\x09select: [ :each | each isValidFor: self selectedItem ])\x0a\x09\x09collect: [ :each | each for: self model ]",
+messageSends: ["collect:", "for:", "model", "select:", "isValidFor:", "selectedItem", "concreteClasses"],
+referencedClasses: ["HLBrowserCommand"]
+}),
+smalltalk.HLBrowserListWidget);
+
 smalltalk.addMethod(
 "_model",
 smalltalk.method({
@@ -385,12 +414,16 @@ selector: "model:",
 category: 'accessing',
 fn: function (aBrowserModel){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self["@model"]=aBrowserModel;
-_st(self)._observeModel();
-return self}, function($ctx1) {$ctx1.fill(self,"model:",{aBrowserModel:aBrowserModel}, smalltalk.HLBrowserListWidget)})},
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+self["@model"]=aBrowserModel;
+$1=self;
+_st($1)._observeSystem();
+$2=_st($1)._observeModel();
+return self}, function($ctx1) {$ctx1.fill(self,"model:",{aBrowserModel:aBrowserModel},smalltalk.HLBrowserListWidget)})},
 args: ["aBrowserModel"],
-source: "model: aBrowserModel\x0a\x09model := aBrowserModel.\x0a    \x0a    self observeModel",
-messageSends: ["observeModel"],
+source: "model: aBrowserModel\x0a\x09model := aBrowserModel.\x0a    \x0a    self \x0a\x09\x09observeSystem;\x0a\x09\x09observeModel",
+messageSends: ["observeSystem", "observeModel"],
 referencedClasses: []
 }),
 smalltalk.HLBrowserListWidget);
@@ -425,6 +458,143 @@ referencedClasses: []
 }),
 smalltalk.HLBrowserListWidget);
 
+smalltalk.addMethod(
+"_renderContentOn_",
+smalltalk.method({
+selector: "renderContentOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self)._renderHeadOn_(html);
+smalltalk.HLNavigationListWidget.fn.prototype._renderContentOn_.apply(_st(self), [html]);
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.HLBrowserListWidget)})},
+args: ["html"],
+source: "renderContentOn: html\x0a\x09self renderHeadOn: html.\x09\x0a\x09super renderContentOn: html",
+messageSends: ["renderHeadOn:", "renderContentOn:"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_renderHeadOn_",
+smalltalk.method({
+selector: "renderHeadOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_("list-label");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(html)._with_(_st(self)._label());
+return _st(self)._renderMenuOn_(html);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderHeadOn:",{html:html},smalltalk.HLBrowserListWidget)})},
+args: ["html"],
+source: "renderHeadOn: html\x0a\x09html div \x0a\x09\x09class: 'list-label';\x0a\x09\x09with: [\x0a\x09\x09\x09html with: self label.\x0a\x09\x09\x09self renderMenuOn: html ]",
+messageSends: ["class:", "div", "with:", "label", "renderMenuOn:"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_renderMenuOn_",
+smalltalk.method({
+selector: "renderMenuOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+var commands;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$5,$6,$7,$9,$10,$8,$4;
+commands=_st(self)._menuCommands();
+$1=_st(commands)._isEmpty();
+if(smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(html)._div();
+_st($3)._class_("btn-group cog");
+$4=_st($3)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$5=_st(html)._a();
+_st($5)._class_("btn dropdown-toggle");
+_st($5)._at_put_("data-toggle","dropdown");
+$6=_st($5)._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(html)._tag_("i"))._class_("icon-cog");
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+$6;
+$7=_st(html)._ul();
+_st($7)._class_("dropdown-menu pull-right");
+$8=_st($7)._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(self)._menuCommands())._do_((function(each){
+return smalltalk.withContext(function($ctx4) {
+return _st(_st(html)._li())._with_((function(){
+return smalltalk.withContext(function($ctx5) {
+$9=_st(html)._a();
+_st($9)._with_(_st(each)._menuLabel());
+$10=_st($9)._onClick_((function(){
+return smalltalk.withContext(function($ctx6) {
+return _st(self)._execute_(each);
+}, function($ctx6) {$ctx6.fillBlock({},$ctx1)})}));
+return $10;
+}, function($ctx5) {$ctx5.fillBlock({},$ctx1)})}));
+}, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+return $8;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderMenuOn:",{html:html,commands:commands},smalltalk.HLBrowserListWidget)})},
+args: ["html"],
+source: "renderMenuOn: html\x0a\x09| commands |\x0a\x09\x0a\x09commands := self menuCommands.\x0a\x09commands isEmpty ifTrue: [ ^ self ].\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'btn-group cog';\x0a\x09\x09with: [\x0a\x09\x09\x09html a\x0a\x09\x09\x09\x09class: 'btn dropdown-toggle';\x0a\x09\x09\x09\x09at: 'data-toggle' put: 'dropdown';\x0a\x09\x09\x09\x09with: [ (html tag: 'i') class: 'icon-cog' ].\x0a\x09\x09html ul \x0a\x09\x09\x09class: 'dropdown-menu pull-right';\x0a\x09\x09\x09with: [ \x0a\x09\x09\x09\x09self menuCommands do: [ :each | \x0a\x09\x09\x09\x09\x09html li with: [ html a \x0a\x09\x09\x09\x09\x09\x09with: each menuLabel;\x0a\x09\x09\x09\x09\x09\x09onClick: [ self execute: each ] ] ] ] ]",
+messageSends: ["menuCommands", "ifTrue:", "isEmpty", "class:", "div", "with:", "a", "at:put:", "tag:", "ul", "do:", "menuLabel", "onClick:", "execute:", "li"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_selectedItem_",
+smalltalk.method({
+selector: "selectedItem:",
+category: 'accessing',
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLNavigationListWidget.fn.prototype._selectedItem_.apply(_st(self), [anItem]);
+_st(self)._updateMenu();
+return self}, function($ctx1) {$ctx1.fill(self,"selectedItem:",{anItem:anItem},smalltalk.HLBrowserListWidget)})},
+args: ["anItem"],
+source: "selectedItem: anItem\x0a\x09\x22Selection changed, update the cog menu\x22\x0a\x09\x0a\x09super selectedItem: anItem.\x0a\x09self updateMenu",
+messageSends: ["selectedItem:", "updateMenu"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserListWidget);
+
+smalltalk.addMethod(
+"_updateMenu",
+smalltalk.method({
+selector: "updateMenu",
+category: 'updating',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(_st(_st(self)._wrapper())._asJQuery())._find_(".cog"))._remove();
+_st((function(html){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._renderMenuOn_(html);
+}, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st(_st(_st(self)._wrapper())._asJQuery())._find_(".list-label"));
+return self}, function($ctx1) {$ctx1.fill(self,"updateMenu",{},smalltalk.HLBrowserListWidget)})},
+args: [],
+source: "updateMenu\x0a\x09(self wrapper asJQuery find: '.cog') remove.\x0a\x09\x0a\x09[ :html | self renderMenuOn: html ] \x0a\x09\x09appendToJQuery: (self wrapper asJQuery find: '.list-label')",
+messageSends: ["remove", "find:", "asJQuery", "wrapper", "appendToJQuery:", "renderMenuOn:"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserListWidget);
+
 
 smalltalk.addMethod(
 "_on_",
@@ -544,6 +714,23 @@ referencedClasses: []
 }),
 smalltalk.HLClassesListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Classes";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLClassesListWidget)})},
+args: [],
+source: "label\x0a\x09^ 'Classes'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLClassesListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -580,19 +767,24 @@ selector: "observeSystem",
 category: 'actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ClassAdded || ClassAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onClassAdded_(_st(ann)._theClass());
+function $ClassAdded(){return smalltalk.ClassAdded||(typeof ClassAdded=="undefined"?nil:ClassAdded)}
+function $ClassRemoved(){return smalltalk.ClassRemoved||(typeof ClassRemoved=="undefined"?nil:ClassRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ClassAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onClassAdded_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.ClassRemoved || ClassRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onClassRemoved_(_st(ann)._theClass());
+$2=_st($1)._on_do_($ClassRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onClassRemoved_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{}, smalltalk.HLClassesListWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLClassesListWidget)})},
 args: [],
-source: "observeSystem\x0a\x09SystemAnnouncer current\x0a    \x09on: ClassAdded\x0a        do: [ :ann | self onClassAdded: ann theClass ];\x0a        on: ClassRemoved\x0a        do: [ :ann | self onClassRemoved: ann theClass ]",
-messageSends: ["on:do:", "onClassAdded:", "theClass", "current", "onClassRemoved:"],
-referencedClasses: ["ClassAdded", "SystemAnnouncer", "ClassRemoved"]
+source: "observeSystem\x0a\x09self model systemAnnouncer\x0a    \x09on: ClassAdded\x0a        do: [ :ann | self onClassAdded: ann theClass ];\x0a        on: ClassRemoved\x0a        do: [ :ann | self onClassRemoved: ann theClass ]",
+messageSends: ["on:do:", "onClassAdded:", "theClass", "systemAnnouncer", "model", "onClassRemoved:"],
+referencedClasses: ["ClassAdded", "ClassRemoved"]
 }),
 smalltalk.HLClassesListWidget);
 
@@ -736,14 +928,18 @@ selector: "renderButtonsOn:",
 category: 'rendering',
 fn: function (html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$6,$7,$8,$2,$9,$10;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$5,$6,$7,$8,$2;
 $1=_st(html)._div();
 _st($1)._class_("btn-group");
 _st($1)._at_put_("data-toggle","buttons-radio");
 $2=_st($1)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(html)._button();
-_st($3)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
_st(str)._nextPutAll_("btn");
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_(_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx3) {
+_st(str)._nextPutAll_("btn");
 $4=_st(self)._showInstance();
 if(smalltalk.assert($4)){
 return _st(str)._nextPutAll_(" active");
@@ -751,12 +947,14 @@ return _st(str)._nextPutAll_(" active");
 }, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
 _st($3)._with_("Instance");
 $5=_st($3)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
return _st(self)._showInstance_(true);
+return smalltalk.withContext(function($ctx3) {
+return _st(self)._showInstance_(true);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
 $5;
 $6=_st(html)._button();
-_st($6)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
_st(str)._nextPutAll_("btn");
+_st($6)._class_(_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx3) {
+_st(str)._nextPutAll_("btn");
 $7=_st(_st(self)._model())._showInstance();
 if(! smalltalk.assert($7)){
 return _st(str)._nextPutAll_(" active");
@@ -764,17 +962,14 @@ return _st(str)._nextPutAll_(" active");
 }, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
 _st($6)._with_("Class");
 $8=_st($6)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
return _st(_st(self)._model())._showInstance_(false);
+return smalltalk.withContext(function($ctx3) {
+return _st(_st(self)._model())._showInstance_(false);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
 return $8;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-$9=_st(html)._button();
-_st($9)._class_("btn");
-_st($9)._at_put_("data-toggle","button");
-$10=_st($9)._with_("Comment");
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html}, smalltalk.HLClassesListWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLClassesListWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html\x0a\x09html div \x0a        class: 'btn-group';\x0a\x09\x09at: 'data-toggle' put: 'buttons-radio';\x0a\x09\x09with: [ \x0a           \x09html button \x0a                class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn'.\x0a                    self showInstance ifTrue: [ \x0a                    \x09str nextPutAll: ' active'] ]);\x0a  \x09\x09\x09\x09with: 'Instance';\x0a                onClick: [ self showInstance: true ].\x0a  \x09\x09\x09html button\x0a  \x09\x09\x09\x09class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn'.\x0a                    self model showInstance ifFalse: [ \x0a                    \x09str nextPutAll: ' active'] ]);\x0a  \x09\x09\x09\x09with: 'Class';\x0a\x09\x09\x09\x09onClick: [ self model showInstance: false ] ].\x0a                 \x0a  \x09html button \x0a           \x09class: 'btn';\x0a            at: 'data-toggle' put: 'button';\x0a  \x09\x09\x09with: 'Comment'",
+source: "renderButtonsOn: html\x0a\x09html div \x0a        class: 'btn-group';\x0a\x09\x09at: 'data-toggle' put: 'buttons-radio';\x0a\x09\x09with: [ \x0a           \x09html button \x0a                class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn'.\x0a                    self showInstance ifTrue: [ \x0a                    \x09str nextPutAll: ' active'] ]);\x0a  \x09\x09\x09\x09with: 'Instance';\x0a                onClick: [ self showInstance: true ].\x0a  \x09\x09\x09html button\x0a  \x09\x09\x09\x09class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn'.\x0a                    self model showInstance ifFalse: [ \x0a                    \x09str nextPutAll: ' active'] ]);\x0a  \x09\x09\x09\x09with: 'Class';\x0a\x09\x09\x09\x09onClick: [ self model showInstance: false ] ]",
 messageSends: ["class:", "div", "at:put:", "with:", "streamContents:", "nextPutAll:", "ifTrue:", "showInstance", "button", "onClick:", "showInstance:", "ifFalse:", "model"],
 referencedClasses: ["String"]
 }),
@@ -1111,6 +1306,23 @@ referencedClasses: []
 }),
 smalltalk.HLMethodsListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Methods";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLMethodsListWidget)})},
+args: [],
+source: "label\x0a\x09^ 'Methods'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLMethodsListWidget);
+
 smalltalk.addMethod(
 "_methodForSelector_",
 smalltalk.method({
@@ -1194,25 +1406,34 @@ selector: "observeSystem",
 category: 'actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ProtocolAdded || ProtocolAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolAdded_(_st(ann)._theClass());
+function $ProtocolAdded(){return smalltalk.ProtocolAdded||(typeof ProtocolAdded=="undefined"?nil:ProtocolAdded)}
+function $ProtocolRemoved(){return smalltalk.ProtocolRemoved||(typeof ProtocolRemoved=="undefined"?nil:ProtocolRemoved)}
+function $MethodAdded(){return smalltalk.MethodAdded||(typeof MethodAdded=="undefined"?nil:MethodAdded)}
+function $MethodRemoved(){return smalltalk.MethodRemoved||(typeof MethodRemoved=="undefined"?nil:MethodRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ProtocolAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolAdded_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-_st($1)._on_do_((smalltalk.ProtocolRemoved || ProtocolRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolRemoved_(_st(ann)._theClass());
+_st($1)._on_do_($ProtocolRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolRemoved_(_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-_st($1)._on_do_((smalltalk.MethodAdded || MethodAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onMethodAdded_(_st(ann)._method());
+_st($1)._on_do_($MethodAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onMethodAdded_(_st(ann)._method());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.MethodRemoved || MethodRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onMethodRemoved_(_st(ann)._method());
+$2=_st($1)._on_do_($MethodRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onMethodRemoved_(_st(ann)._method());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLMethodsListWidget)})},
 args: [],
-source: "observeSystem\x0a\x09SystemAnnouncer current \x0a    \x09on: ProtocolAdded\x0a        do: [ :ann | self onProtocolAdded: ann theClass ];\x0a    \x09on: ProtocolRemoved\x0a        do: [ :ann | self onProtocolRemoved: ann theClass ];\x0a    \x09on: MethodAdded \x0a        do: [ :ann | self onMethodAdded: ann method ];\x0a        on: MethodRemoved \x0a        do: [ :ann | self onMethodRemoved: ann method ]",
-messageSends: ["on:do:", "onProtocolAdded:", "theClass", "current", "onProtocolRemoved:", "onMethodAdded:", "method", "onMethodRemoved:"],
-referencedClasses: ["ProtocolAdded", "SystemAnnouncer", "ProtocolRemoved", "MethodAdded", "MethodRemoved"]
+source: "observeSystem\x0a\x09self model systemAnnouncer \x0a    \x09on: ProtocolAdded\x0a        do: [ :ann | self onProtocolAdded: ann theClass ];\x0a    \x09on: ProtocolRemoved\x0a        do: [ :ann | self onProtocolRemoved: ann theClass ];\x0a    \x09on: MethodAdded \x0a        do: [ :ann | self onMethodAdded: ann method ];\x0a        on: MethodRemoved \x0a        do: [ :ann | self onMethodRemoved: ann method ]",
+messageSends: ["on:do:", "onProtocolAdded:", "theClass", "systemAnnouncer", "model", "onProtocolRemoved:", "onMethodAdded:", "method", "onMethodRemoved:"],
+referencedClasses: ["ProtocolAdded", "ProtocolRemoved", "MethodAdded", "MethodRemoved"]
 }),
 smalltalk.HLMethodsListWidget);
 
@@ -1609,6 +1830,23 @@ smalltalk.HLMethodsListWidget.klass);
 
 
 smalltalk.addClass('HLPackagesListWidget', smalltalk.HLBrowserListWidget, [], 'Helios-Browser');
+smalltalk.addMethod(
+"_commitPackage",
+smalltalk.method({
+selector: "commitPackage",
+category: 'actions',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(self)._model())._commitPackage();
+return self}, function($ctx1) {$ctx1.fill(self,"commitPackage",{},smalltalk.HLPackagesListWidget)})},
+args: [],
+source: "commitPackage\x0a\x09self model commitPackage",
+messageSends: ["commitPackage", "model"],
+referencedClasses: []
+}),
+smalltalk.HLPackagesListWidget);
+
 smalltalk.addMethod(
 "_focusClassesListWidget",
 smalltalk.method({
@@ -1629,18 +1867,20 @@ smalltalk.addMethod(
 "_initializeItems",
 smalltalk.method({
 selector: "initializeItems",
-category: 'accessing',
+category: 'initialization',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
 self["@items"]=_st(_st(_st(self)._model())._packages())._sort_((function(a,b){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(a)._name()).__lt(_st(b)._name());
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(a)._name()).__lt(_st(b)._name());
 }, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})}));
 $1=self["@items"];
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"initializeItems",{}, smalltalk.HLPackagesListWidget)})},
+}, function($ctx1) {$ctx1.fill(self,"initializeItems",{},smalltalk.HLPackagesListWidget)})},
 args: [],
-source: "initializeItems\x0a\x09^ items := self model packages sort:[:a :b|\x0a\x09\x09\x09\x09\x09\x09a name < b name]",
+source: "initializeItems\x0a\x09^ items := self model packages \x0a\x09\x09sort: [ :a :b | a name < b name ]",
 messageSends: ["sort:", "<", "name", "packages", "model"],
 referencedClasses: []
 }),
@@ -1669,6 +1909,23 @@ referencedClasses: []
 }),
 smalltalk.HLPackagesListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Packages";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLPackagesListWidget)})},
+args: [],
+source: "label\x0a\x09^ 'Packages'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLPackagesListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -1739,35 +1996,43 @@ selector: "renderButtonsOn:",
 category: 'rendering',
 fn: function (html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$5,$6,$7,$8,$4,$9,$10;
-$1=_st(html)._span();
-_st($1)._class_("info");
-$2=_st($1)._with_("Auto commit");
-$3=_st(html)._div();
-_st($3)._class_("btn-group switch");
-_st($3)._at_put_("data-toggle","buttons-radio");
-$4=_st($3)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(html)._button();
-_st($5)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
return _st(str)._nextPutAll_("btn");
-}, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
-$6=_st($5)._with_("On");
-$6;
-$7=_st(html)._button();
-_st($7)._class_(_st((smalltalk.String || String))._streamContents_((function(str){
-return smalltalk.withContext(function($ctx3) {
return _st(str)._nextPutAll_("btn active");
-}, function($ctx3) {$ctx3.fillBlock({str:str},$ctx1)})})));
-$8=_st($7)._with_("Off");
-return $8;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$2;
+$1=_st(html)._div();
+_st($1)._class_("buttons");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_("btn");
+_st($3)._with_("Commit");
+$4=_st($3)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(self)._commitPackage();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+return $4;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-$9=_st(html)._a();
-_st($9)._class_("btn");
-$10=_st($9)._with_("Commit");
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html}, smalltalk.HLPackagesListWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLPackagesListWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html\x0a\x0a\x09html span class: 'info'; with: 'Auto commit'.\x0a\x09html div \x0a        class: 'btn-group switch';\x0a\x09\x09at: 'data-toggle' put: 'buttons-radio';\x0a\x09\x09with: [ \x0a           \x09html button \x0a                class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn' ]);\x0a  \x09\x09\x09\x09with: 'On'.\x0a  \x09\x09\x09html button\x0a  \x09\x09\x09\x09class: (String streamContents: [ :str |\x0a                \x09str nextPutAll: 'btn active' ]);\x0a  \x09\x09\x09\x09with: 'Off' ].\x0a                \x0a    html a \x0a         \x09class: 'btn';\x0a\x09\x09\x09with: 'Commit'.",
-messageSends: ["class:", "span", "with:", "div", "at:put:", "streamContents:", "nextPutAll:", "button", "a"],
-referencedClasses: ["String"]
+source: "renderButtonsOn: html\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button \x0a\x09\x09\x09\x09class: 'btn';\x0a\x09\x09\x09\x09with: 'Commit';\x0a\x09\x09\x09\x09onClick: [ self commitPackage ] ]",
+messageSends: ["class:", "div", "with:", "button", "onClick:", "commitPackage"],
+referencedClasses: []
+}),
+smalltalk.HLPackagesListWidget);
+
+smalltalk.addMethod(
+"_renderItemLabel_on_",
+smalltalk.method({
+selector: "renderItemLabel:on:",
+category: 'rendering',
+fn: function (aPackage,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(html)._with_(_st(aPackage)._name());
+return self}, function($ctx1) {$ctx1.fill(self,"renderItemLabel:on:",{aPackage:aPackage,html:html},smalltalk.HLPackagesListWidget)})},
+args: ["aPackage", "html"],
+source: "renderItemLabel: aPackage on: html\x0a\x09html with: aPackage name",
+messageSends: ["with:", "name"],
+referencedClasses: []
 }),
 smalltalk.HLPackagesListWidget);
 
@@ -1808,6 +2073,23 @@ referencedClasses: []
 }),
 smalltalk.HLProtocolsListWidget);
 
+smalltalk.addMethod(
+"_label",
+smalltalk.method({
+selector: "label",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Protocols";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLProtocolsListWidget)})},
+args: [],
+source: "label\x0a\x09^ 'Protocols'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLProtocolsListWidget);
+
 smalltalk.addMethod(
 "_observeModel",
 smalltalk.method({
@@ -1844,19 +2126,24 @@ selector: "observeSystem",
 category: 'actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$1=_st((smalltalk.SystemAnnouncer || SystemAnnouncer))._current();
-_st($1)._on_do_((smalltalk.ProtocolAdded || ProtocolAdded),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolAdded_to_(_st(ann)._protocol(),_st(ann)._theClass());
+function $ProtocolAdded(){return smalltalk.ProtocolAdded||(typeof ProtocolAdded=="undefined"?nil:ProtocolAdded)}
+function $ProtocolRemoved(){return smalltalk.ProtocolRemoved||(typeof ProtocolRemoved=="undefined"?nil:ProtocolRemoved)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st(self)._model())._systemAnnouncer();
+_st($1)._on_do_($ProtocolAdded(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolAdded_to_(_st(ann)._protocol(),_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-$2=_st($1)._on_do_((smalltalk.ProtocolRemoved || ProtocolRemoved),(function(ann){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._onProtocolRemoved_from_(_st(ann)._protocol(),_st(ann)._theClass());
+$2=_st($1)._on_do_($ProtocolRemoved(),(function(ann){
+return smalltalk.withContext(function($ctx2) {
+return _st(self)._onProtocolRemoved_from_(_st(ann)._protocol(),_st(ann)._theClass());
 }, function($ctx2) {$ctx2.fillBlock({ann:ann},$ctx1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{}, smalltalk.HLProtocolsListWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"observeSystem",{},smalltalk.HLProtocolsListWidget)})},
 args: [],
-source: "observeSystem\x0a    SystemAnnouncer current\x0a    \x09on: ProtocolAdded \x0a        do: [ :ann | self onProtocolAdded: ann protocol to: ann theClass ];\x0a        on: ProtocolRemoved\x0a        do: [ :ann | self onProtocolRemoved: ann protocol from: ann theClass ]",
-messageSends: ["on:do:", "onProtocolAdded:to:", "protocol", "theClass", "current", "onProtocolRemoved:from:"],
-referencedClasses: ["ProtocolAdded", "SystemAnnouncer", "ProtocolRemoved"]
+source: "observeSystem\x0a\x09self model systemAnnouncer\x0a\x09\x09on: ProtocolAdded \x0a\x09    do: [ :ann | self onProtocolAdded: ann protocol to: ann theClass ];\x0a\x09    on: ProtocolRemoved\x0a\x09    do: [ :ann | self onProtocolRemoved: ann protocol from: ann theClass ]",
+messageSends: ["on:do:", "onProtocolAdded:to:", "protocol", "theClass", "systemAnnouncer", "model", "onProtocolRemoved:from:"],
+referencedClasses: ["ProtocolAdded", "ProtocolRemoved"]
 }),
 smalltalk.HLProtocolsListWidget);
 
@@ -2187,11 +2474,15 @@ selector: "commitPackage",
 category: 'commands actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._environment())._commitPackage_(_st(self)._selectedPackage());
+return smalltalk.withContext(function($ctx1) { 
+_st(self)._withHelperLabelled_do_(_st(_st("Committing package ").__comma(_st(_st(self)._selectedPackage())._name())).__comma("..."),(function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(self)._environment())._commitPackage_(_st(self)._selectedPackage());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"commitPackage",{},smalltalk.HLBrowserModel)})},
 args: [],
-source: "commitPackage\x0a\x09self environment commitPackage: self selectedPackage",
-messageSends: ["commitPackage:", "selectedPackage", "environment"],
+source: "commitPackage\x0a\x09self \x0a\x09\x09withHelperLabelled: 'Committing package ', self selectedPackage name, '...'\x0a\x09\x09do: [ self environment commitPackage: self selectedPackage ]",
+messageSends: ["withHelperLabelled:do:", ",", "name", "selectedPackage", "commitPackage:", "environment"],
 referencedClasses: []
 }),
 smalltalk.HLBrowserModel);
@@ -2938,6 +3229,25 @@ referencedClasses: ["HLShowInstanceToggled"]
 }),
 smalltalk.HLBrowserModel);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._environment())._systemAnnouncer();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLBrowserModel)})},
+args: [],
+source: "systemAnnouncer\x0a\x09^ self environment systemAnnouncer",
+messageSends: ["systemAnnouncer", "environment"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserModel);
+
 smalltalk.addMethod(
 "_unclassifiedProtocol",
 smalltalk.method({
@@ -2980,6 +3290,36 @@ referencedClasses: ["CompilerError", "UnknownVariableError", "ParseError"]
 }),
 smalltalk.HLBrowserModel);
 
+smalltalk.addMethod(
+"_withHelperLabelled_do_",
+smalltalk.method({
+selector: "withHelperLabelled:do:",
+category: 'private',
+fn: function (aString,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+_st(_st(window)._jQuery_("#helper"))._remove();
+_st((function(html){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(html)._div();
+_st($1)._id_("helper");
+$2=_st($1)._with_(aString);
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st("body")._asJQuery());
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(aBlock)._value();
+return _st(_st(window)._jQuery_("#helper"))._remove();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((10));
+return self}, function($ctx1) {$ctx1.fill(self,"withHelperLabelled:do:",{aString:aString,aBlock:aBlock},smalltalk.HLBrowserModel)})},
+args: ["aString", "aBlock"],
+source: "withHelperLabelled: aString do: aBlock\x0a\x09\x22TODO: doesn't belong here\x22\x0a\x0a\x09(window jQuery: '#helper') remove.\x0a\x0a\x09[ :html |\x0a\x09\x09html div \x0a\x09\x09\x09id: 'helper';\x0a\x09\x09\x09with: aString ] appendToJQuery: 'body' asJQuery.\x0a\x09\x0a\x09[\x0a\x09\x09aBlock value.\x0a\x09\x09(window jQuery: '#helper') remove\x0a\x09] \x0a\x09\x09valueWithTimeout: 10",
+messageSends: ["remove", "jQuery:", "appendToJQuery:", "asJQuery", "id:", "div", "with:", "valueWithTimeout:", "value"],
+referencedClasses: []
+}),
+smalltalk.HLBrowserModel);
+
 
 smalltalk.addMethod(
 "_on_",
@@ -3376,6 +3716,38 @@ referencedClasses: []
 }),
 smalltalk.HLBrowserSourceWidget);
 
+smalltalk.addMethod(
+"_previous",
+smalltalk.method({
+selector: "previous",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"previous",{},smalltalk.HLBrowserSourceWidget)})},
+args: [],
+source: "previous\x0a\x09\x22for browser lists widget\x22",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLBrowserSourceWidget);
+
+smalltalk.addMethod(
+"_previous_",
+smalltalk.method({
+selector: "previous:",
+category: 'accessing',
+fn: function (aWidget){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"previous:",{aWidget:aWidget},smalltalk.HLBrowserSourceWidget)})},
+args: ["aWidget"],
+source: "previous: aWidget\x0a\x09\x22for browser lists widget\x22",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLBrowserSourceWidget);
+
 smalltalk.addMethod(
 "_refresh",
 smalltalk.method({

+ 92 - 0
js/Helios-Commands-Browser.deploy.js

@@ -247,6 +247,20 @@ messageSends: []}),
 smalltalk.HLCommitPackageCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isPackage();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLCommitPackageCommand.klass)})},
+messageSends: ["isPackage"]}),
+smalltalk.HLCommitPackageCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -499,6 +513,20 @@ messageSends: []}),
 smalltalk.HLMoveMethodToClassCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLMoveMethodToClassCommand.klass)})},
+messageSends: ["isCompiledMethod"]}),
+smalltalk.HLMoveMethodToClassCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -521,6 +549,18 @@ return smalltalk.withContext(function($ctx1) { 
return "to class";
 messageSends: []}),
 smalltalk.HLMoveMethodToClassCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Move to class...";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLMoveMethodToClassCommand.klass)})},
+messageSends: []}),
+smalltalk.HLMoveMethodToClassCommand.klass);
+
 
 smalltalk.addClass('HLMoveMethodToProtocolCommand', smalltalk.HLMoveMethodToCommand, [], 'Helios-Commands-Browser');
 smalltalk.addMethod(
@@ -581,6 +621,20 @@ messageSends: []}),
 smalltalk.HLMoveMethodToProtocolCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLMoveMethodToProtocolCommand.klass)})},
+messageSends: ["isCompiledMethod"]}),
+smalltalk.HLMoveMethodToProtocolCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -603,6 +657,18 @@ return smalltalk.withContext(function($ctx1) { 
return "to protocol";
 messageSends: []}),
 smalltalk.HLMoveMethodToProtocolCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Move to protocol...";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLMoveMethodToProtocolCommand.klass)})},
+messageSends: []}),
+smalltalk.HLMoveMethodToProtocolCommand.klass);
+
 
 smalltalk.addClass('HLRemoveCommand', smalltalk.HLBrowserCommand, [], 'Helios-Commands-Browser');
 
@@ -655,6 +721,20 @@ messageSends: ["notNil", "selectedMethod", "model"]}),
 smalltalk.HLRemoveMethodCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLRemoveMethodCommand.klass)})},
+messageSends: ["isCompiledMethod"]}),
+smalltalk.HLRemoveMethodCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -677,6 +757,18 @@ return smalltalk.withContext(function($ctx1) { 
return "Method";
 messageSends: []}),
 smalltalk.HLRemoveMethodCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Remove method";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLRemoveMethodCommand.klass)})},
+messageSends: []}),
+smalltalk.HLRemoveMethodCommand.klass);
+
 
 smalltalk.addClass('HLToggleCommand', smalltalk.HLBrowserCommand, [], 'Helios-Commands-Browser');
 

+ 128 - 1
js/Helios-Commands-Browser.js

@@ -347,6 +347,25 @@ referencedClasses: []
 smalltalk.HLCommitPackageCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+category: 'testing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isPackage();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLCommitPackageCommand.klass)})},
+args: ["anObject"],
+source: "isValidFor: anObject\x0a\x09^ anObject isPackage",
+messageSends: ["isPackage"],
+referencedClasses: []
+}),
+smalltalk.HLCommitPackageCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -704,6 +723,25 @@ referencedClasses: []
 smalltalk.HLMoveMethodToClassCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+category: 'testing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLMoveMethodToClassCommand.klass)})},
+args: ["anObject"],
+source: "isValidFor: anObject\x0a\x09^ anObject isCompiledMethod",
+messageSends: ["isCompiledMethod"],
+referencedClasses: []
+}),
+smalltalk.HLMoveMethodToClassCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -736,6 +774,23 @@ referencedClasses: []
 }),
 smalltalk.HLMoveMethodToClassCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Move to class...";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLMoveMethodToClassCommand.klass)})},
+args: [],
+source: "menuLabel\x09\x0a\x09^ 'Move to class...'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLMoveMethodToClassCommand.klass);
+
 
 smalltalk.addClass('HLMoveMethodToProtocolCommand', smalltalk.HLMoveMethodToCommand, [], 'Helios-Commands-Browser');
 smalltalk.addMethod(
@@ -821,6 +876,25 @@ referencedClasses: []
 smalltalk.HLMoveMethodToProtocolCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+category: 'testing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLMoveMethodToProtocolCommand.klass)})},
+args: ["anObject"],
+source: "isValidFor: anObject\x0a\x09^ anObject isCompiledMethod",
+messageSends: ["isCompiledMethod"],
+referencedClasses: []
+}),
+smalltalk.HLMoveMethodToProtocolCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -853,6 +927,23 @@ referencedClasses: []
 }),
 smalltalk.HLMoveMethodToProtocolCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Move to protocol...";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLMoveMethodToProtocolCommand.klass)})},
+args: [],
+source: "menuLabel\x0a\x09^ 'Move to protocol...'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLMoveMethodToProtocolCommand.klass);
+
 
 smalltalk.addClass('HLRemoveCommand', smalltalk.HLBrowserCommand, [], 'Helios-Commands-Browser');
 
@@ -925,11 +1016,30 @@ referencedClasses: []
 smalltalk.HLRemoveMethodCommand);
 
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(anObject)._isCompiledMethod();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{anObject:anObject},smalltalk.HLRemoveMethodCommand.klass)})},
+args: ["anObject"],
+source: "isValidFor: anObject\x0a\x09^ anObject isCompiledMethod",
+messageSends: ["isCompiledMethod"],
+referencedClasses: []
+}),
+smalltalk.HLRemoveMethodCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
 selector: "key",
-category: 'accessing',
+category: 'testing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
return (77);
@@ -957,6 +1067,23 @@ referencedClasses: []
 }),
 smalltalk.HLRemoveMethodCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Remove method";
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLRemoveMethodCommand.klass)})},
+args: [],
+source: "menuLabel\x0a\x09^ 'Remove method'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLRemoveMethodCommand.klass);
+
 
 smalltalk.addClass('HLToggleCommand', smalltalk.HLBrowserCommand, [], 'Helios-Commands-Browser');
 

+ 65 - 0
js/Helios-Commands-Core.deploy.js

@@ -193,6 +193,20 @@ return $1;
 messageSends: ["label", "class"]}),
 smalltalk.HLCommand);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._class())._menuLabel();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLCommand)})},
+messageSends: ["menuLabel", "class"]}),
+smalltalk.HLCommand);
+
 smalltalk.addMethod(
 "_registerOn_",
 smalltalk.method({
@@ -207,6 +221,31 @@ messageSends: ["add:", "asBinding"]}),
 smalltalk.HLCommand);
 
 
+smalltalk.addMethod(
+"_concreteClasses",
+smalltalk.method({
+selector: "concreteClasses",
+fn: function (){
+var self=this;
+var classes;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+classes=_st($OrderedCollection())._new();
+$1=_st(self)._isConcrete();
+if(smalltalk.assert($1)){
+_st(classes)._add_(self);
+};
+_st(_st(self)._subclasses())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(classes)._addAll_(_st(each)._concreteClasses());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+$2=classes;
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"concreteClasses",{classes:classes},smalltalk.HLCommand.klass)})},
+messageSends: ["new", "ifTrue:", "add:", "isConcrete", "do:", "addAll:", "concreteClasses", "subclasses"]}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_documentation",
 smalltalk.method({
@@ -231,6 +270,18 @@ return $1;
 messageSends: ["notNil", "key"]}),
 smalltalk.HLCommand.klass);
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+fn: function (aModel){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{aModel:aModel},smalltalk.HLCommand.klass)})},
+messageSends: []}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -253,6 +304,20 @@ return smalltalk.withContext(function($ctx1) { 
return "";
 messageSends: []}),
 smalltalk.HLCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._label();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLCommand.klass)})},
+messageSends: ["label"]}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_registerConcreteClassesOn_",
 smalltalk.method({

+ 86 - 1
js/Helios-Commands-Core.js

@@ -268,6 +268,25 @@ referencedClasses: []
 }),
 smalltalk.HLCommand);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._class())._menuLabel();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLCommand)})},
+args: [],
+source: "menuLabel\x0a\x09^ self class menuLabel",
+messageSends: ["menuLabel", "class"],
+referencedClasses: []
+}),
+smalltalk.HLCommand);
+
 smalltalk.addMethod(
 "_registerOn_",
 smalltalk.method({
@@ -287,6 +306,36 @@ referencedClasses: []
 smalltalk.HLCommand);
 
 
+smalltalk.addMethod(
+"_concreteClasses",
+smalltalk.method({
+selector: "concreteClasses",
+category: 'registration',
+fn: function (){
+var self=this;
+var classes;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+classes=_st($OrderedCollection())._new();
+$1=_st(self)._isConcrete();
+if(smalltalk.assert($1)){
+_st(classes)._add_(self);
+};
+_st(_st(self)._subclasses())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(classes)._addAll_(_st(each)._concreteClasses());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+$2=classes;
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"concreteClasses",{classes:classes},smalltalk.HLCommand.klass)})},
+args: [],
+source: "concreteClasses\x0a\x09| classes |\x0a\x09\x0a\x09classes := OrderedCollection new.\x0a\x09\x0a\x09self isConcrete\x0a\x09\x09ifTrue: [ classes add: self ].\x0a\x09\x09\x0a\x09self subclasses do: [ :each | \x0a\x09\x09classes addAll: each concreteClasses ].\x0a\x09\x09\x0a\x09^ classes",
+messageSends: ["new", "ifTrue:", "add:", "isConcrete", "do:", "addAll:", "concreteClasses", "subclasses"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_documentation",
 smalltalk.method({
@@ -321,6 +370,23 @@ referencedClasses: []
 }),
 smalltalk.HLCommand.klass);
 
+smalltalk.addMethod(
+"_isValidFor_",
+smalltalk.method({
+selector: "isValidFor:",
+category: 'testing',
+fn: function (aModel){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isValidFor:",{aModel:aModel},smalltalk.HLCommand.klass)})},
+args: ["aModel"],
+source: "isValidFor: aModel\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_key",
 smalltalk.method({
@@ -353,11 +419,30 @@ referencedClasses: []
 }),
 smalltalk.HLCommand.klass);
 
+smalltalk.addMethod(
+"_menuLabel",
+smalltalk.method({
+selector: "menuLabel",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._label();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"menuLabel",{},smalltalk.HLCommand.klass)})},
+args: [],
+source: "menuLabel\x0a\x09^ self label",
+messageSends: ["label"],
+referencedClasses: []
+}),
+smalltalk.HLCommand.klass);
+
 smalltalk.addMethod(
 "_registerConcreteClassesOn_",
 smalltalk.method({
 selector: "registerConcreteClassesOn:",
-category: 'registration',
+category: 'accessing',
 fn: function (aBinding){
 var self=this;
 var newBinding;

+ 16 - 0
js/Helios-Core.deploy.js

@@ -286,6 +286,22 @@ return $1;
 messageSends: ["confirm:"]}),
 smalltalk.HLWidget);
 
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+fn: function (aCommand){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st($HLManager())._current())._keyBinder();
+_st($1)._activate();
+$2=_st($1)._applyBinding_(_st(aCommand)._asBinding());
+return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aCommand:aCommand},smalltalk.HLWidget)})},
+messageSends: ["activate", "keyBinder", "current", "applyBinding:", "asBinding"]}),
+smalltalk.HLWidget);
+
 smalltalk.addMethod(
 "_manager",
 smalltalk.method({

+ 21 - 0
js/Helios-Core.js

@@ -386,6 +386,27 @@ referencedClasses: []
 }),
 smalltalk.HLWidget);
 
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+category: 'actions',
+fn: function (aCommand){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(_st($HLManager())._current())._keyBinder();
+_st($1)._activate();
+$2=_st($1)._applyBinding_(_st(aCommand)._asBinding());
+return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aCommand:aCommand},smalltalk.HLWidget)})},
+args: ["aCommand"],
+source: "execute: aCommand\x0a\x09HLManager current keyBinder\x0a\x09\x09activate;\x0a\x09\x09applyBinding: aCommand asBinding",
+messageSends: ["activate", "keyBinder", "current", "applyBinding:", "asBinding"],
+referencedClasses: ["HLManager"]
+}),
+smalltalk.HLWidget);
+
 smalltalk.addMethod(
 "_manager",
 smalltalk.method({

+ 29 - 0
js/Helios-Environments.deploy.js

@@ -164,6 +164,20 @@ return self}, function($ctx1) {$ctx1.fill(self,"removeMethod:",{aMethod:aMethod}
 messageSends: ["sublcassResponsibility"]}),
 smalltalk.HLEnvironment);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._subclassResponsibility();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLEnvironment)})},
+messageSends: ["subclassResponsibility"]}),
+smalltalk.HLEnvironment);
+
 
 
 smalltalk.addClass('HLLocalEnvironment', smalltalk.HLEnvironment, [], 'Helios-Environments');
@@ -332,6 +346,21 @@ return self}, function($ctx1) {$ctx1.fill(self,"removeMethod:",{aMethod:aMethod}
 messageSends: ["forsakeMethod:", "methodClass"]}),
 smalltalk.HLLocalEnvironment);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+fn: function (){
+var self=this;
+function $SystemAnnouncer(){return smalltalk.SystemAnnouncer||(typeof SystemAnnouncer=="undefined"?nil:SystemAnnouncer)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st($SystemAnnouncer())._current();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLLocalEnvironment)})},
+messageSends: ["current"]}),
+smalltalk.HLLocalEnvironment);
+
 
 
 smalltalk.addClass('HLRemoteEnvironment', smalltalk.HLEnvironment, [], 'Helios-Environments');

+ 39 - 0
js/Helios-Environments.js

@@ -235,6 +235,25 @@ referencedClasses: []
 }),
 smalltalk.HLEnvironment);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._subclassResponsibility();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLEnvironment)})},
+args: [],
+source: "systemAnnouncer\x0a\x09^ self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
+referencedClasses: []
+}),
+smalltalk.HLEnvironment);
+
 
 
 smalltalk.addClass('HLLocalEnvironment', smalltalk.HLEnvironment, [], 'Helios-Environments');
@@ -453,6 +472,26 @@ referencedClasses: []
 }),
 smalltalk.HLLocalEnvironment);
 
+smalltalk.addMethod(
+"_systemAnnouncer",
+smalltalk.method({
+selector: "systemAnnouncer",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $SystemAnnouncer(){return smalltalk.SystemAnnouncer||(typeof SystemAnnouncer=="undefined"?nil:SystemAnnouncer)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st($SystemAnnouncer())._current();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"systemAnnouncer",{},smalltalk.HLLocalEnvironment)})},
+args: [],
+source: "systemAnnouncer\x0a\x09^ SystemAnnouncer current",
+messageSends: ["current"],
+referencedClasses: ["SystemAnnouncer"]
+}),
+smalltalk.HLLocalEnvironment);
+
 
 
 smalltalk.addClass('HLRemoteEnvironment', smalltalk.HLEnvironment, [], 'Helios-Environments');

+ 20 - 10
js/Helios-KeyBindings.deploy.js

@@ -744,7 +744,8 @@ smalltalk.method({
 selector: "renderOn:html:",
 fn: function (aBinder,html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$5,$6,$7,$3;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$4,$5,$6,$7,$3;
 self["@binder"]=aBinder;
 $1=self["@wrapper"];
 if(($receiver = $1) == nil || $receiver == undefined){
@@ -756,7 +757,8 @@ $1;
 $2=self["@wrapper"];
 _st($2)._class_(_st(self)._status());
 $3=_st($2)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(html)._input();
+return smalltalk.withContext(function($ctx2) {
+$4=_st(html)._input();
 _st($4)._placeholder_(_st(self)._ghostText());
 $5=_st($4)._yourself();
 self["@input"]=$5;
@@ -769,9 +771,12 @@ $7=_st($6)._yourself();
 self["@messageTag"]=$7;
 return self["@messageTag"];
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-_st(_st(self["@input"])._asJQuery())._focus();
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(self["@input"])._asJQuery())._focus();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((10));
 return self}, function($ctx1) {$ctx1.fill(self,"renderOn:html:",{aBinder:aBinder,html:html},smalltalk.HLBindingInput)})},
-messageSends: ["ifNil:", "span", "class:", "status", "with:", "placeholder:", "ghostText", "input", "yourself", "typeahead:", "->", "inputCompletion", "asJQuery", "message", "focus"]}),
+messageSends: ["ifNil:", "span", "class:", "status", "with:", "placeholder:", "ghostText", "input", "yourself", "typeahead:", "->", "inputCompletion", "asJQuery", "message", "valueWithTimeout:", "focus"]}),
 smalltalk.HLBindingInput);
 
 smalltalk.addMethod(
@@ -835,7 +840,8 @@ smalltalk.method({
 selector: "activationKeyLabel",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return "ctrl + space";
+return smalltalk.withContext(function($ctx1) { 
+return "ctrl + space";
 }, function($ctx1) {$ctx1.fill(self,"activationKeyLabel",{},smalltalk.HLKeyBinder)})},
 messageSends: []}),
 smalltalk.HLKeyBinder);
@@ -992,7 +998,8 @@ smalltalk.method({
 selector: "handleInactiveKeyDown:",
 fn: function (event){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 $1=_st(_st(event)._which()).__eq(_st(self)._activationKey());
 if(smalltalk.assert($1)){
 $2=_st(event)._ctrlKey();
@@ -1317,15 +1324,18 @@ smalltalk.method({
 selector: "renderStart",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 _st((function(html){
-return smalltalk.withContext(function($ctx2) {
$1=_st(html)._div();
-_st($1)._id_("keybinding-start-helper");
+return smalltalk.withContext(function($ctx2) {
+$1=_st(html)._div();
+_st($1)._id_("helper");
 $2=_st($1)._with_(_st(_st("Press ").__comma(_st(_st(self)._keyBinder())._activationKeyLabel())).__comma(" to start"));
 return $2;
 }, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st("body")._asJQuery());
 _st((function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(window)._jQuery_("#keybinding-start-helper"))._fadeOut_((1000));
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(window)._jQuery_("#helper"))._fadeOut_((1000));
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((2000));
 return self}, function($ctx1) {$ctx1.fill(self,"renderStart",{},smalltalk.HLKeyBinderHelper)})},
 messageSends: ["appendToJQuery:", "asJQuery", "id:", "div", "with:", ",", "activationKeyLabel", "keyBinder", "valueWithTimeout:", "fadeOut:", "jQuery:"]}),

+ 23 - 13
js/Helios-KeyBindings.js

@@ -1000,7 +1000,8 @@ selector: "renderOn:html:",
 category: 'rendering',
 fn: function (aBinder,html){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$5,$6,$7,$3;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$4,$5,$6,$7,$3;
 self["@binder"]=aBinder;
 $1=self["@wrapper"];
 if(($receiver = $1) == nil || $receiver == undefined){
@@ -1012,7 +1013,8 @@ $1;
 $2=self["@wrapper"];
 _st($2)._class_(_st(self)._status());
 $3=_st($2)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(html)._input();
+return smalltalk.withContext(function($ctx2) {
+$4=_st(html)._input();
 _st($4)._placeholder_(_st(self)._ghostText());
 $5=_st($4)._yourself();
 self["@input"]=$5;
@@ -1025,11 +1027,14 @@ $7=_st($6)._yourself();
 self["@messageTag"]=$7;
 return self["@messageTag"];
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-_st(_st(self["@input"])._asJQuery())._focus();
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(self["@input"])._asJQuery())._focus();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((10));
 return self}, function($ctx1) {$ctx1.fill(self,"renderOn:html:",{aBinder:aBinder,html:html},smalltalk.HLBindingInput)})},
 args: ["aBinder", "html"],
-source: "renderOn: aBinder html: html\x0a\x09binder := aBinder.\x0a\x09wrapper ifNil: [ wrapper := html span ].\x0a\x09\x0a\x09wrapper \x0a\x09\x09class: self status;\x0a\x09\x09with: [\x0a\x09\x09\x09input := html input\x0a\x09\x09\x09\x09placeholder: self ghostText;\x0a\x09\x09\x09\x09yourself.\x0a\x09\x09\x09input asJQuery \x0a\x09\x09\x09\x09typeahead: #{ 'source' -> self inputCompletion }.\x0a\x09\x09\x09messageTag := (html span\x0a\x09\x09\x09\x09class: 'help-inline';\x0a\x09\x09\x09\x09with: self message;\x0a\x09\x09\x09\x09yourself) ].\x0a\x09\x09\x09\x0a\x09input asJQuery focus\x0a",
-messageSends: ["ifNil:", "span", "class:", "status", "with:", "placeholder:", "ghostText", "input", "yourself", "typeahead:", "->", "inputCompletion", "asJQuery", "message", "focus"],
+source: "renderOn: aBinder html: html\x0a\x09binder := aBinder.\x0a\x09wrapper ifNil: [ wrapper := html span ].\x0a\x09\x0a\x09wrapper \x0a\x09\x09class: self status;\x0a\x09\x09with: [\x0a\x09\x09\x09input := html input\x0a\x09\x09\x09\x09placeholder: self ghostText;\x0a\x09\x09\x09\x09yourself.\x0a\x09\x09\x09input asJQuery \x0a\x09\x09\x09\x09typeahead: #{ 'source' -> self inputCompletion }.\x0a\x09\x09\x09messageTag := (html span\x0a\x09\x09\x09\x09class: 'help-inline';\x0a\x09\x09\x09\x09with: self message;\x0a\x09\x09\x09\x09yourself) ].\x0a\x09\x0a\x09\x22Evaluate with a timeout to ensure focus.\x0a\x09Commands can be executed from a menu, clicking on the menu to\x0a\x09evaluate the command would give it the focus otherwise\x22\x0a\x09\x0a\x09[ input asJQuery focus ] valueWithTimeout: 10",
+messageSends: ["ifNil:", "span", "class:", "status", "with:", "placeholder:", "ghostText", "input", "yourself", "typeahead:", "->", "inputCompletion", "asJQuery", "message", "valueWithTimeout:", "focus"],
 referencedClasses: []
 }),
 smalltalk.HLBindingInput);
@@ -1116,7 +1121,8 @@ selector: "activationKeyLabel",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return "ctrl + space";
+return smalltalk.withContext(function($ctx1) { 
+return "ctrl + space";
 }, function($ctx1) {$ctx1.fill(self,"activationKeyLabel",{},smalltalk.HLKeyBinder)})},
 args: [],
 source: "activationKeyLabel\x0a\x09^ 'ctrl + space'",
@@ -1318,7 +1324,8 @@ selector: "handleInactiveKeyDown:",
 category: 'events',
 fn: function (event){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 $1=_st(_st(event)._which()).__eq(_st(self)._activationKey());
 if(smalltalk.assert($1)){
 $2=_st(event)._ctrlKey();
@@ -1330,7 +1337,7 @@ return false;
 };
 return self}, function($ctx1) {$ctx1.fill(self,"handleInactiveKeyDown:",{event:event},smalltalk.HLKeyBinder)})},
 args: ["event"],
-source: "handleInactiveKeyDown: event\x0a      event which = self activationKey ifTrue: [\x0a      \x09\x09event ctrlKey  ifTrue: [\x0a\x09\x09\x09\x09\x09self activate. \x0a               \x09\x09 event preventDefault. \x0a                \x09^ false ] ]",
+source: "handleInactiveKeyDown: event\x0a\x09event which = self activationKey ifTrue: [\x0a    \x09event ctrlKey ifTrue: [\x0a\x09\x09\x09self activate. \x0a            event preventDefault. \x0a            ^ false ] ]",
 messageSends: ["ifTrue:", "activate", "preventDefault", "ctrlKey", "=", "activationKey", "which"],
 referencedClasses: []
 }),
@@ -1748,19 +1755,22 @@ selector: "renderStart",
 category: 'rendering',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 _st((function(html){
-return smalltalk.withContext(function($ctx2) {
$1=_st(html)._div();
-_st($1)._id_("keybinding-start-helper");
+return smalltalk.withContext(function($ctx2) {
+$1=_st(html)._div();
+_st($1)._id_("helper");
 $2=_st($1)._with_(_st(_st("Press ").__comma(_st(_st(self)._keyBinder())._activationKeyLabel())).__comma(" to start"));
 return $2;
 }, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1)})}))._appendToJQuery_(_st("body")._asJQuery());
 _st((function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(window)._jQuery_("#keybinding-start-helper"))._fadeOut_((1000));
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(window)._jQuery_("#helper"))._fadeOut_((1000));
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((2000));
 return self}, function($ctx1) {$ctx1.fill(self,"renderStart",{},smalltalk.HLKeyBinderHelper)})},
 args: [],
-source: "renderStart\x0a\x09[ :html |\x0a\x09\x09html div \x0a\x09\x09\x09id: 'keybinding-start-helper';\x0a\x09\x09\x09with: 'Press ', self keyBinder activationKeyLabel, ' to start' ] appendToJQuery: 'body' asJQuery.\x0a\x09\x0a\x09[ (window jQuery: '#keybinding-start-helper') fadeOut: 1000 ] \x0a\x09\x09valueWithTimeout: 2000",
+source: "renderStart\x0a\x09[ :html |\x0a\x09\x09html div \x0a\x09\x09\x09id: 'helper';\x0a\x09\x09\x09with: 'Press ', self keyBinder activationKeyLabel, ' to start' ] appendToJQuery: 'body' asJQuery.\x0a\x09\x0a\x09[ (window jQuery: '#helper') fadeOut: 1000 ] \x0a\x09\x09valueWithTimeout: 2000",
 messageSends: ["appendToJQuery:", "asJQuery", "id:", "div", "with:", ",", "activationKeyLabel", "keyBinder", "valueWithTimeout:", "fadeOut:", "jQuery:"],
 referencedClasses: []
 }),

+ 8 - 6
js/Helios-Layout.deploy.js

@@ -264,12 +264,13 @@ selector: "resize:",
 fn: function (anInteger){
 var self=this;
 var container,position;
-return smalltalk.withContext(function($ctx1) { 
container=_st(_st(self["@firstPane"])._asJQuery())._parent();
+return smalltalk.withContext(function($ctx1) { 
+container=_st(_st(self["@firstPane"])._asJQuery())._parent();
 position=_st(anInteger).__minus(_st(_st(container)._offset())._top());
 _st(_st(self["@firstPane"])._asJQuery())._height_(_st(_st(position)._min_(_st(_st(container)._height()).__minus((100))))._max_((100)));
-_st(_st(self["@secondPane"])._asJQuery())._height_(_st(_st(_st(_st(_st(container)._height()).__minus(position))._min_(_st(_st(container)._height()).__minus((100))))._max_((100))).__minus((6)));
+_st(_st(self["@secondPane"])._asJQuery())._height_(_st(_st(_st(_st(_st(container)._height()).__minus(position))._min_(_st(_st(container)._height()).__minus((100))))._max_((100))).__minus((1)));
 smalltalk.HLSplitter.fn.prototype._resize.apply(_st(self), []);
-return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position}, smalltalk.HLHorizontalSplitter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position},smalltalk.HLHorizontalSplitter)})},
 messageSends: ["parent", "asJQuery", "-", "top", "offset", "height:", "max:", "min:", "height", "resize"]}),
 smalltalk.HLHorizontalSplitter);
 
@@ -346,12 +347,13 @@ selector: "resize:",
 fn: function (anInteger){
 var self=this;
 var container,position;
-return smalltalk.withContext(function($ctx1) { 
container=_st(_st(self["@firstPane"])._asJQuery())._parent();
+return smalltalk.withContext(function($ctx1) { 
+container=_st(_st(self["@firstPane"])._asJQuery())._parent();
 position=_st(anInteger).__minus(_st(_st(container)._offset())._left());
 _st(_st(self["@firstPane"])._asJQuery())._width_(_st(_st(position)._min_(_st(_st(container)._width()).__minus((100))))._max_((100)));
-_st(_st(self["@secondPane"])._asJQuery())._width_(_st(_st(_st(_st(_st(container)._width()).__minus(position))._min_(_st(_st(container)._width()).__minus((100))))._max_((100))).__minus((6)));
+_st(_st(self["@secondPane"])._asJQuery())._width_(_st(_st(_st(_st(_st(container)._width()).__minus(position))._min_(_st(_st(container)._width()).__minus((100))))._max_((100))).__minus((1)));
 smalltalk.HLSplitter.fn.prototype._resize.apply(_st(self), []);
-return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position}, smalltalk.HLVerticalSplitter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position},smalltalk.HLVerticalSplitter)})},
 messageSends: ["parent", "asJQuery", "-", "left", "offset", "width:", "max:", "min:", "width", "resize"]}),
 smalltalk.HLVerticalSplitter);
 

+ 10 - 8
js/Helios-Layout.js

@@ -355,14 +355,15 @@ category: 'actions',
 fn: function (anInteger){
 var self=this;
 var container,position;
-return smalltalk.withContext(function($ctx1) { 
container=_st(_st(self["@firstPane"])._asJQuery())._parent();
+return smalltalk.withContext(function($ctx1) { 
+container=_st(_st(self["@firstPane"])._asJQuery())._parent();
 position=_st(anInteger).__minus(_st(_st(container)._offset())._top());
 _st(_st(self["@firstPane"])._asJQuery())._height_(_st(_st(position)._min_(_st(_st(container)._height()).__minus((100))))._max_((100)));
-_st(_st(self["@secondPane"])._asJQuery())._height_(_st(_st(_st(_st(_st(container)._height()).__minus(position))._min_(_st(_st(container)._height()).__minus((100))))._max_((100))).__minus((6)));
+_st(_st(self["@secondPane"])._asJQuery())._height_(_st(_st(_st(_st(_st(container)._height()).__minus(position))._min_(_st(_st(container)._height()).__minus((100))))._max_((100))).__minus((1)));
 smalltalk.HLSplitter.fn.prototype._resize.apply(_st(self), []);
-return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position}, smalltalk.HLHorizontalSplitter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position},smalltalk.HLHorizontalSplitter)})},
 args: ["anInteger"],
-source: "resize: anInteger\x0a\x09| container position |\x0a    \x0a    container := firstPane asJQuery parent.\x0a    position := anInteger - container offset top.\x0a    \x0a\x09firstPane asJQuery height: ((position min: container height - 100) max: 100).\x0a    secondPane asJQuery height: (((container height - position) min: container height - 100) max: 100) - 6.\x0a    \x0a    super resize",
+source: "resize: anInteger\x0a\x09| container position |\x0a    \x0a    container := firstPane asJQuery parent.\x0a    position := anInteger - container offset top.\x0a    \x0a\x09firstPane asJQuery height: ((position min: container height - 100) max: 100).\x0a    secondPane asJQuery height: (((container height - position) min: container height - 100) max: 100) - 1.\x0a    \x0a    super resize",
 messageSends: ["parent", "asJQuery", "-", "top", "offset", "height:", "max:", "min:", "height", "resize"],
 referencedClasses: []
 }),
@@ -467,14 +468,15 @@ category: 'actions',
 fn: function (anInteger){
 var self=this;
 var container,position;
-return smalltalk.withContext(function($ctx1) { 
container=_st(_st(self["@firstPane"])._asJQuery())._parent();
+return smalltalk.withContext(function($ctx1) { 
+container=_st(_st(self["@firstPane"])._asJQuery())._parent();
 position=_st(anInteger).__minus(_st(_st(container)._offset())._left());
 _st(_st(self["@firstPane"])._asJQuery())._width_(_st(_st(position)._min_(_st(_st(container)._width()).__minus((100))))._max_((100)));
-_st(_st(self["@secondPane"])._asJQuery())._width_(_st(_st(_st(_st(_st(container)._width()).__minus(position))._min_(_st(_st(container)._width()).__minus((100))))._max_((100))).__minus((6)));
+_st(_st(self["@secondPane"])._asJQuery())._width_(_st(_st(_st(_st(_st(container)._width()).__minus(position))._min_(_st(_st(container)._width()).__minus((100))))._max_((100))).__minus((1)));
 smalltalk.HLSplitter.fn.prototype._resize.apply(_st(self), []);
-return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position}, smalltalk.HLVerticalSplitter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"resize:",{anInteger:anInteger,container:container,position:position},smalltalk.HLVerticalSplitter)})},
 args: ["anInteger"],
-source: "resize: anInteger\x0a\x09| container position |\x0a    \x0a    container := firstPane asJQuery parent.\x0a    position := anInteger - container offset left.\x0a    \x0a\x09firstPane asJQuery width: ((position min: container width - 100) max: 100).\x0a    secondPane asJQuery width: (((container width - position) min: container width - 100) max: 100) - 6.\x0a    \x0a    super resize",
+source: "resize: anInteger\x0a\x09| container position |\x0a    \x0a    container := firstPane asJQuery parent.\x0a    position := anInteger - container offset left.\x0a    \x0a\x09firstPane asJQuery width: ((position min: container width - 100) max: 100).\x0a    secondPane asJQuery width: (((container width - position) min: container width - 100) max: 100) - 1.\x0a    \x0a    super resize",
 messageSends: ["parent", "asJQuery", "-", "left", "offset", "width:", "max:", "min:", "width", "resize"],
 referencedClasses: []
 }),

+ 123 - 8
js/Helios-Workspace.deploy.js

@@ -319,6 +319,27 @@ return $1;
 messageSends: ["inspect:", "new", "yourself"]}),
 smalltalk.HLCodeWidget);
 
+smalltalk.addMethod(
+"_messageHintFor_token_",
+smalltalk.method({
+selector: "messageHintFor:token:",
+fn: function (anEditor,aToken){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(_st(_st(_st(_st($Smalltalk())._current())._at_("allSelectors"))._value())._asSet())._asArray())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._includesSubString_(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._reject_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each).__eq(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget)})},
+messageSends: ["reject:", "=", "string", "select:", "includesSubString:", "asArray", "asSet", "value", "at:", "current"]}),
+smalltalk.HLCodeWidget);
+
 smalltalk.addMethod(
 "_model",
 smalltalk.method({
@@ -546,7 +567,8 @@ smalltalk.method({
 selector: "setEditorOn:",
 fn: function (aTextarea){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
+return smalltalk.withContext(function($ctx1) { 
+self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
 		theme: 'amber',
                 lineNumbers: true,
                 enterMode: 'flat',
@@ -554,12 +576,44 @@ return smalltalk.withContext(function($ctx1) { 
self['@editor'] = CodeMirror.fro
 				indentUnit: 4,
                 matchBrackets: true,
                 electricChars: false,
-				keyMap: 'Amber'
+				keyMap: 'Amber',
+				extraKeys: {"Shift-Space": "autocomplete"}
 	});
-return self}, function($ctx1) {$ctx1.fill(self,"setEditorOn:",{aTextarea:aTextarea}, smalltalk.HLCodeWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"setEditorOn:",{aTextarea:aTextarea},smalltalk.HLCodeWidget)})},
 messageSends: []}),
 smalltalk.HLCodeWidget);
 
+smalltalk.addMethod(
+"_variableHintFor_token_",
+smalltalk.method({
+selector: "variableHintFor:token:",
+fn: function (anEditor,aToken){
+var self=this;
+var variables,classNames,pseudoVariables;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+variables=_st(_st(_st(_st(window)._jQuery_(_st(_st(anEditor)._display())._wrapper()))._find_("span.cm-variable"))._get())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(window)._jQuery_(each))._html();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+classNames=_st(_st(_st($Smalltalk())._current())._classes())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._name();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+pseudoVariables=_st(_st($Smalltalk())._current())._pseudoVariableNames();
+$1=_st(_st(_st(_st(_st(_st(variables).__comma(classNames)).__comma(pseudoVariables))._asSet())._asArray())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._includesSubString_(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._reject_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each).__eq(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"variableHintFor:token:",{anEditor:anEditor,aToken:aToken,variables:variables,classNames:classNames,pseudoVariables:pseudoVariables},smalltalk.HLCodeWidget)})},
+messageSends: ["collect:", "html", "jQuery:", "get", "find:", "wrapper", "display", "name", "classes", "current", "pseudoVariableNames", "reject:", "=", "string", "select:", "includesSubString:", "asArray", "asSet", ","]}),
+smalltalk.HLCodeWidget);
+
 
 smalltalk.addMethod(
 "_canBeOpenAsTab",
@@ -572,6 +626,32 @@ return smalltalk.withContext(function($ctx1) { 
return true;
 messageSends: []}),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_hintFor_options_",
+smalltalk.method({
+selector: "hintFor:options:",
+fn: function (anEditor,options){
+var self=this;
+var cursor,token,completions;
+function $CodeMirror(){return smalltalk.CodeMirror||(typeof CodeMirror=="undefined"?nil:CodeMirror)}
+function $HLCodeWidget(){return smalltalk.HLCodeWidget||(typeof HLCodeWidget=="undefined"?nil:HLCodeWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+cursor=_st(anEditor)._getCursor();
+token=_st(anEditor)._getTokenAt_(cursor);
+_st(token)._at_put_("state",_st(_st(_st($CodeMirror())._basicAt_("innerMode"))._value_value_(_st(anEditor)._getMode(),_st(token)._at_("state")))._state());
+$1=_st(_st(token)._type()).__eq("variable");
+if(smalltalk.assert($1)){
+completions=_st($HLCodeWidget())._variableHintFor_token_(anEditor,token);
+} else {
+completions=_st($HLCodeWidget())._messageHintFor_token_(anEditor,token);
+};
+$2=smalltalk.HashedCollection._fromPairs_([_st("list").__minus_gt(completions),_st("from").__minus_gt(_st(_st($CodeMirror())._basicAt_("Pos"))._value_value_(_st(cursor)._line(),_st(token)._end())),_st("to").__minus_gt(_st(_st($CodeMirror())._basicAt_("Pos"))._value_value_(_st(cursor)._line(),_st(token)._start()))]);
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"hintFor:options:",{anEditor:anEditor,options:options,cursor:cursor,token:token,completions:completions},smalltalk.HLCodeWidget.klass)})},
+messageSends: ["getCursor", "getTokenAt:", "at:put:", "state", "value:value:", "getMode", "at:", "basicAt:", "ifTrue:ifFalse:", "variableHintFor:token:", "messageHintFor:token:", "=", "type", "->", "line", "end", "start"]}),
+smalltalk.HLCodeWidget.klass);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -612,13 +692,28 @@ smalltalk.method({
 selector: "macKeyMap",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=smalltalk.HashedCollection._fromPairs_([_st("Alt-Backspace").__minus_gt("delWordBefore"),_st("Alt-Delete").__minus_gt("delWordAfter"),_st("Alt-Left").__minus_gt("goWordBoundaryLeft"),_st("Alt-Right").__minus_gt("goWordBoundaryRight"),_st("Cmd-A").__minus_gt("selectAll"),_st("Cmd-Alt-F").__minus_gt("replace"),_st("Cmd-D").__minus_gt("doIt"),_st("Cmd-Down").__minus_gt("goDocEnd"),_st("Cmd-End").__minus_gt("goDocEnd"),_st("Cmd-F").__minus_gt("find"),_st("Cmd-G").__minus_gt("findNext"),_st("Cmd-I").__minus_gt("inspectIt"),_st("Cmd-Left").__minus_gt("goLineStart"),_st("Cmd-P").__minus_gt("printIt"),_st("Cmd-Right").__minus_gt("goLineEnd"),_st("Cmd-S").__minus_gt("saveIt"),_st("Cmd-Up").__minus_gt("goDocStart"),_st("Cmd-Y").__minus_gt("redo"),_st("Cmd-Z").__minus_gt("undo"),_st("Cmd-[").__minus_gt("indentLess"),_st("Cmd-]").__minus_gt("indentMore"),_st("Ctrl-Alt-Backspace").__minus_gt("delWordAfter"),_st("Shift-Cmd-Alt-F").__minus_gt("replaceAll"),_st("Shift-Cmd-G").__minus_gt("findPrev"),_st("Shift-Cmd-Z").__minus_gt("redo"),_st("fallthrough").__minus_gt(["basic"])]);
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=smalltalk.HashedCollection._fromPairs_([_st("Alt-Backspace").__minus_gt("delWordBefore"),_st("Alt-Delete").__minus_gt("delWordAfter"),_st("Alt-Left").__minus_gt("goWordBoundaryLeft"),_st("Alt-Right").__minus_gt("goWordBoundaryRight"),_st("Cmd-A").__minus_gt("selectAll"),_st("Cmd-Alt-F").__minus_gt("replace"),_st("Cmd-D").__minus_gt("doIt"),_st("Cmd-Down").__minus_gt("goDocEnd"),_st("Cmd-End").__minus_gt("goDocEnd"),_st("Cmd-F").__minus_gt("find"),_st("Cmd-G").__minus_gt("findNext"),_st("Cmd-I").__minus_gt("inspectIt"),_st("Cmd-Left").__minus_gt("goLineStart"),_st("Cmd-P").__minus_gt("printIt"),_st("Cmd-Right").__minus_gt("goLineEnd"),_st("Cmd-S").__minus_gt("saveIt"),_st("Cmd-Up").__minus_gt("goDocStart"),_st("Cmd-Y").__minus_gt("redo"),_st("Cmd-Z").__minus_gt("undo"),_st("Cmd-[").__minus_gt("indentLess"),_st("Cmd-]").__minus_gt("indentMore"),_st("Ctrl-Alt-Backspace").__minus_gt("delWordAfter"),_st("Shift-Cmd-Alt-F").__minus_gt("replaceAll"),_st("Shift-Cmd-G").__minus_gt("findPrev"),_st("Shift-Cmd-Z").__minus_gt("redo"),_st("fallthrough").__minus_gt(["basic","emacsy"])]);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"macKeyMap",{}, smalltalk.HLCodeWidget.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"macKeyMap",{},smalltalk.HLCodeWidget.klass)})},
 messageSends: ["->"]}),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_messageHintFor_token_",
+smalltalk.method({
+selector: "messageHintFor:token:",
+fn: function (anEditor,aToken){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(anEditor)._at_("amberCodeWidget"))._messageHintFor_token_(anEditor,aToken);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget.klass)})},
+messageSends: ["messageHintFor:token:", "at:"]}),
+smalltalk.HLCodeWidget.klass);
+
 smalltalk.addMethod(
 "_pcKeyMap",
 smalltalk.method({
@@ -638,8 +733,14 @@ smalltalk.method({
 selector: "setupCodeMirror",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
 CodeMirror.keyMap.default.fallthrough = ["basic"] ;
-return self}, function($ctx1) {$ctx1.fill(self,"setupCodeMirror",{}, smalltalk.HLCodeWidget.klass)})},
+return smalltalk.withContext(function($ctx1) { 
+ 
+		CodeMirror.keyMap.default.fallthrough = ["basic"];
+		CodeMirror.commands.autocomplete = function(cm) {
+        	CodeMirror.showHint(cm, self._hintFor_options_);
+      	}
+	;
+return self}, function($ctx1) {$ctx1.fill(self,"setupCodeMirror",{},smalltalk.HLCodeWidget.klass)})},
 messageSends: []}),
 smalltalk.HLCodeWidget.klass);
 
@@ -700,6 +801,20 @@ return smalltalk.withContext(function($ctx1) { 
return (10);
 messageSends: []}),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_variableHintFor_token_",
+smalltalk.method({
+selector: "variableHintFor:token:",
+fn: function (anEditor,aToken){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(anEditor)._at_("amberCodeWidget"))._variableHintFor_token_(anEditor,aToken);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"variableHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget.klass)})},
+messageSends: ["variableHintFor:token:", "at:"]}),
+smalltalk.HLCodeWidget.klass);
+
 
 smalltalk.addClass('HLSourceCodeWidget', smalltalk.HLCodeWidget, ['browserModel'], 'Helios-Workspace');
 smalltalk.addMethod(

+ 151 - 11
js/Helios-Workspace.js

@@ -434,6 +434,32 @@ referencedClasses: ["HLInspector"]
 }),
 smalltalk.HLCodeWidget);
 
+smalltalk.addMethod(
+"_messageHintFor_token_",
+smalltalk.method({
+selector: "messageHintFor:token:",
+category: 'hints',
+fn: function (anEditor,aToken){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(_st(_st(_st(_st($Smalltalk())._current())._at_("allSelectors"))._value())._asSet())._asArray())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._includesSubString_(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._reject_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each).__eq(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget)})},
+args: ["anEditor", "aToken"],
+source: "messageHintFor: anEditor token: aToken\x0a\x09^ ((Smalltalk current at: 'allSelectors') value asSet asArray \x0a\x09\x09select: [ :each | each includesSubString: aToken string ])\x0a\x09\x09reject: [ :each | each = aToken string ]",
+messageSends: ["reject:", "=", "string", "select:", "includesSubString:", "asArray", "asSet", "value", "at:", "current"],
+referencedClasses: ["Smalltalk"]
+}),
+smalltalk.HLCodeWidget);
+
 smalltalk.addMethod(
 "_model",
 smalltalk.method({
@@ -747,7 +773,8 @@ selector: "setEditorOn:",
 category: 'actions',
 fn: function (aTextarea){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
+return smalltalk.withContext(function($ctx1) { 
+self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
 		theme: 'amber',
                 lineNumbers: true,
                 enterMode: 'flat',
@@ -755,16 +782,53 @@ return smalltalk.withContext(function($ctx1) { 
self['@editor'] = CodeMirror.fro
 				indentUnit: 4,
                 matchBrackets: true,
                 electricChars: false,
-				keyMap: 'Amber'
+				keyMap: 'Amber',
+				extraKeys: {"Shift-Space": "autocomplete"}
 	});
-return self}, function($ctx1) {$ctx1.fill(self,"setEditorOn:",{aTextarea:aTextarea}, smalltalk.HLCodeWidget)})},
+return self}, function($ctx1) {$ctx1.fill(self,"setEditorOn:",{aTextarea:aTextarea},smalltalk.HLCodeWidget)})},
 args: ["aTextarea"],
-source: "setEditorOn: aTextarea\x0a\x09<self['@editor'] = CodeMirror.fromTextArea(aTextarea, {\x0a\x09\x09theme: 'amber',\x0a                lineNumbers: true,\x0a                enterMode: 'flat',\x0a                indentWithTabs: true,\x0a\x09\x09\x09\x09indentUnit: 4,\x0a                matchBrackets: true,\x0a                electricChars: false,\x0a\x09\x09\x09\x09keyMap: 'Amber'\x0a\x09})>",
+source: "setEditorOn: aTextarea\x0a\x09<self['@editor'] = CodeMirror.fromTextArea(aTextarea, {\x0a\x09\x09theme: 'amber',\x0a                lineNumbers: true,\x0a                enterMode: 'flat',\x0a                indentWithTabs: true,\x0a\x09\x09\x09\x09indentUnit: 4,\x0a                matchBrackets: true,\x0a                electricChars: false,\x0a\x09\x09\x09\x09keyMap: 'Amber',\x0a\x09\x09\x09\x09extraKeys: {\x22Shift-Space\x22: \x22autocomplete\x22}\x0a\x09})>",
 messageSends: [],
 referencedClasses: []
 }),
 smalltalk.HLCodeWidget);
 
+smalltalk.addMethod(
+"_variableHintFor_token_",
+smalltalk.method({
+selector: "variableHintFor:token:",
+category: 'hints',
+fn: function (anEditor,aToken){
+var self=this;
+var variables,classNames,pseudoVariables;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+variables=_st(_st(_st(_st(window)._jQuery_(_st(_st(anEditor)._display())._wrapper()))._find_("span.cm-variable"))._get())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(window)._jQuery_(each))._html();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+classNames=_st(_st(_st($Smalltalk())._current())._classes())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._name();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+pseudoVariables=_st(_st($Smalltalk())._current())._pseudoVariableNames();
+$1=_st(_st(_st(_st(_st(_st(variables).__comma(classNames)).__comma(pseudoVariables))._asSet())._asArray())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._includesSubString_(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._reject_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each).__eq(_st(aToken)._string());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"variableHintFor:token:",{anEditor:anEditor,aToken:aToken,variables:variables,classNames:classNames,pseudoVariables:pseudoVariables},smalltalk.HLCodeWidget)})},
+args: ["anEditor", "aToken"],
+source: "variableHintFor: anEditor token: aToken\x0a\x09| variables classNames pseudoVariables |\x0a\x09\x0a\x09variables := ((window jQuery: anEditor display wrapper) find: 'span.cm-variable') get\x0a\x09\x09collect: [ :each | (window jQuery: each) html ].\x0a\x09\x0a\x09classNames := Smalltalk current classes collect: [ :each | each name ].\x0a\x09pseudoVariables := Smalltalk current pseudoVariableNames.\x0a\x09\x0a\x09^ ((variables, classNames, pseudoVariables) asSet asArray \x0a\x09\x09select: [ :each | each includesSubString: aToken string ])\x0a\x09\x09reject: [ :each | each = aToken string ]",
+messageSends: ["collect:", "html", "jQuery:", "get", "find:", "wrapper", "display", "name", "classes", "current", "pseudoVariableNames", "reject:", "=", "string", "select:", "includesSubString:", "asArray", "asSet", ","],
+referencedClasses: ["Smalltalk"]
+}),
+smalltalk.HLCodeWidget);
+
 
 smalltalk.addMethod(
 "_canBeOpenAsTab",
@@ -782,6 +846,37 @@ referencedClasses: []
 }),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_hintFor_options_",
+smalltalk.method({
+selector: "hintFor:options:",
+category: 'hints',
+fn: function (anEditor,options){
+var self=this;
+var cursor,token,completions;
+function $CodeMirror(){return smalltalk.CodeMirror||(typeof CodeMirror=="undefined"?nil:CodeMirror)}
+function $HLCodeWidget(){return smalltalk.HLCodeWidget||(typeof HLCodeWidget=="undefined"?nil:HLCodeWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+cursor=_st(anEditor)._getCursor();
+token=_st(anEditor)._getTokenAt_(cursor);
+_st(token)._at_put_("state",_st(_st(_st($CodeMirror())._basicAt_("innerMode"))._value_value_(_st(anEditor)._getMode(),_st(token)._at_("state")))._state());
+$1=_st(_st(token)._type()).__eq("variable");
+if(smalltalk.assert($1)){
+completions=_st($HLCodeWidget())._variableHintFor_token_(anEditor,token);
+} else {
+completions=_st($HLCodeWidget())._messageHintFor_token_(anEditor,token);
+};
+$2=smalltalk.HashedCollection._fromPairs_([_st("list").__minus_gt(completions),_st("from").__minus_gt(_st(_st($CodeMirror())._basicAt_("Pos"))._value_value_(_st(cursor)._line(),_st(token)._end())),_st("to").__minus_gt(_st(_st($CodeMirror())._basicAt_("Pos"))._value_value_(_st(cursor)._line(),_st(token)._start()))]);
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"hintFor:options:",{anEditor:anEditor,options:options,cursor:cursor,token:token,completions:completions},smalltalk.HLCodeWidget.klass)})},
+args: ["anEditor", "options"],
+source: "hintFor: anEditor options: options\x0a\x09| cursor token completions |\x0a\x09\x0a\x09cursor := anEditor getCursor.\x0a\x09token := anEditor getTokenAt: cursor.\x0a\x09token at: 'state' put: ((CodeMirror basicAt: 'innerMode')\x0a\x09\x09value: anEditor getMode value: (token at: 'state')) state.\x0a\x09\x0a\x09completions := token type = 'variable' \x0a\x09\x09ifTrue: [ HLCodeWidget variableHintFor: anEditor token: token ]\x0a\x09\x09ifFalse: [ HLCodeWidget messageHintFor: anEditor token: token ].\x0a\x09\x0a\x09^ #{\x0a\x09\x09'list' -> completions.\x0a\x09\x09'from' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token end).\x0a\x09\x09'to' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token start)\x0a\x09}",
+messageSends: ["getCursor", "getTokenAt:", "at:put:", "state", "value:value:", "getMode", "at:", "basicAt:", "ifTrue:ifFalse:", "variableHintFor:token:", "messageHintFor:token:", "=", "type", "->", "line", "end", "start"],
+referencedClasses: ["CodeMirror", "HLCodeWidget"]
+}),
+smalltalk.HLCodeWidget.klass);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -833,17 +928,37 @@ selector: "macKeyMap",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=smalltalk.HashedCollection._fromPairs_([_st("Alt-Backspace").__minus_gt("delWordBefore"),_st("Alt-Delete").__minus_gt("delWordAfter"),_st("Alt-Left").__minus_gt("goWordBoundaryLeft"),_st("Alt-Right").__minus_gt("goWordBoundaryRight"),_st("Cmd-A").__minus_gt("selectAll"),_st("Cmd-Alt-F").__minus_gt("replace"),_st("Cmd-D").__minus_gt("doIt"),_st("Cmd-Down").__minus_gt("goDocEnd"),_st("Cmd-End").__minus_gt("goDocEnd"),_st("Cmd-F").__minus_gt("find"),_st("Cmd-G").__minus_gt("findNext"),_st("Cmd-I").__minus_gt("inspectIt"),_st("Cmd-Left").__minus_gt("goLineStart"),_st("Cmd-P").__minus_gt("printIt"),_st("Cmd-Right").__minus_gt("goLineEnd"),_st("Cmd-S").__minus_gt("saveIt"),_st("Cmd-Up").__minus_gt("goDocStart"),_st("Cmd-Y").__minus_gt("redo"),_st("Cmd-Z").__minus_gt("undo"),_st("Cmd-[").__minus_gt("indentLess"),_st("Cmd-]").__minus_gt("indentMore"),_st("Ctrl-Alt-Backspace").__minus_gt("delWordAfter"),_st("Shift-Cmd-Alt-F").__minus_gt("replaceAll"),_st("Shift-Cmd-G").__minus_gt("findPrev"),_st("Shift-Cmd-Z").__minus_gt("redo"),_st("fallthrough").__minus_gt(["basic"])]);
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=smalltalk.HashedCollection._fromPairs_([_st("Alt-Backspace").__minus_gt("delWordBefore"),_st("Alt-Delete").__minus_gt("delWordAfter"),_st("Alt-Left").__minus_gt("goWordBoundaryLeft"),_st("Alt-Right").__minus_gt("goWordBoundaryRight"),_st("Cmd-A").__minus_gt("selectAll"),_st("Cmd-Alt-F").__minus_gt("replace"),_st("Cmd-D").__minus_gt("doIt"),_st("Cmd-Down").__minus_gt("goDocEnd"),_st("Cmd-End").__minus_gt("goDocEnd"),_st("Cmd-F").__minus_gt("find"),_st("Cmd-G").__minus_gt("findNext"),_st("Cmd-I").__minus_gt("inspectIt"),_st("Cmd-Left").__minus_gt("goLineStart"),_st("Cmd-P").__minus_gt("printIt"),_st("Cmd-Right").__minus_gt("goLineEnd"),_st("Cmd-S").__minus_gt("saveIt"),_st("Cmd-Up").__minus_gt("goDocStart"),_st("Cmd-Y").__minus_gt("redo"),_st("Cmd-Z").__minus_gt("undo"),_st("Cmd-[").__minus_gt("indentLess"),_st("Cmd-]").__minus_gt("indentMore"),_st("Ctrl-Alt-Backspace").__minus_gt("delWordAfter"),_st("Shift-Cmd-Alt-F").__minus_gt("replaceAll"),_st("Shift-Cmd-G").__minus_gt("findPrev"),_st("Shift-Cmd-Z").__minus_gt("redo"),_st("fallthrough").__minus_gt(["basic","emacsy"])]);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"macKeyMap",{}, smalltalk.HLCodeWidget.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"macKeyMap",{},smalltalk.HLCodeWidget.klass)})},
 args: [],
-source: "macKeyMap\x0a\x09^ #{\x0a\x09\x09'Alt-Backspace'\x09\x09-> 'delWordBefore'.\x0a\x09\x09'Alt-Delete'\x09\x09-> 'delWordAfter'. \x0a\x09\x09'Alt-Left'\x09\x09-> 'goWordBoundaryLeft'.\x0a\x09\x09'Alt-Right'\x09\x09-> 'goWordBoundaryRight'. \x0a\x09\x09'Cmd-A'\x09\x09\x09-> 'selectAll'. \x0a\x09\x09'Cmd-Alt-F'\x09\x09-> 'replace'. \x0a\x09\x09'Cmd-D'\x09\x09\x09-> 'doIt'. \x0a\x09\x09'Cmd-Down'\x09\x09-> 'goDocEnd'. \x0a\x09\x09'Cmd-End'\x09\x09-> 'goDocEnd'. \x0a\x09\x09'Cmd-F'\x09\x09\x09-> 'find'.\x0a\x09\x09'Cmd-G'\x09\x09\x09-> 'findNext'. \x0a\x09\x09'Cmd-I'\x09\x09\x09-> 'inspectIt'. \x0a\x09\x09'Cmd-Left'\x09\x09-> 'goLineStart'. \x0a\x09\x09'Cmd-P'\x09\x09\x09-> 'printIt'. \x0a\x09\x09'Cmd-Right'\x09\x09-> 'goLineEnd'. \x0a\x09\x09'Cmd-S'\x09\x09\x09-> 'saveIt'. \x0a\x09\x09'Cmd-Up'\x09\x09-> 'goDocStart'. \x0a\x09\x09'Cmd-Y'\x09\x09\x09-> 'redo'.\x0a\x09\x09'Cmd-Z'\x09\x09\x09-> 'undo'. \x0a\x09\x09'Cmd-['\x09\x09\x09-> 'indentLess'. \x0a\x09\x09'Cmd-]'\x09\x09\x09-> 'indentMore'.\x0a\x09\x09'Ctrl-Alt-Backspace'\x09-> 'delWordAfter'. \x0a\x09\x09'Shift-Cmd-Alt-F'\x09-> 'replaceAll'.\x0a\x09\x09'Shift-Cmd-G'\x09\x09-> 'findPrev'. \x0a\x09\x09'Shift-Cmd-Z'\x09\x09-> 'redo'. \x0a    \x09'fallthrough' \x09-> { 'basic' }\x0a  }",
+source: "macKeyMap\x0a\x09^ #{\x0a\x09\x09'Alt-Backspace'\x09\x09-> 'delWordBefore'.\x0a\x09\x09'Alt-Delete'\x09\x09-> 'delWordAfter'. \x0a\x09\x09'Alt-Left'\x09\x09-> 'goWordBoundaryLeft'.\x0a\x09\x09'Alt-Right'\x09\x09-> 'goWordBoundaryRight'. \x0a\x09\x09'Cmd-A'\x09\x09\x09-> 'selectAll'. \x0a\x09\x09'Cmd-Alt-F'\x09\x09-> 'replace'. \x0a\x09\x09'Cmd-D'\x09\x09\x09-> 'doIt'. \x0a\x09\x09'Cmd-Down'\x09\x09-> 'goDocEnd'. \x0a\x09\x09'Cmd-End'\x09\x09-> 'goDocEnd'. \x0a\x09\x09'Cmd-F'\x09\x09\x09-> 'find'.\x0a\x09\x09'Cmd-G'\x09\x09\x09-> 'findNext'. \x0a\x09\x09'Cmd-I'\x09\x09\x09-> 'inspectIt'. \x0a\x09\x09'Cmd-Left'\x09\x09-> 'goLineStart'. \x0a\x09\x09'Cmd-P'\x09\x09\x09-> 'printIt'. \x0a\x09\x09'Cmd-Right'\x09\x09-> 'goLineEnd'. \x0a\x09\x09'Cmd-S'\x09\x09\x09-> 'saveIt'. \x0a\x09\x09'Cmd-Up'\x09\x09-> 'goDocStart'. \x0a\x09\x09'Cmd-Y'\x09\x09\x09-> 'redo'.\x0a\x09\x09'Cmd-Z'\x09\x09\x09-> 'undo'. \x0a\x09\x09'Cmd-['\x09\x09\x09-> 'indentLess'. \x0a\x09\x09'Cmd-]'\x09\x09\x09-> 'indentMore'.\x0a\x09\x09'Ctrl-Alt-Backspace'\x09-> 'delWordAfter'. \x0a\x09\x09'Shift-Cmd-Alt-F'\x09-> 'replaceAll'.\x0a\x09\x09'Shift-Cmd-G'\x09\x09-> 'findPrev'. \x0a\x09\x09'Shift-Cmd-Z'\x09\x09-> 'redo'. \x0a    \x09'fallthrough' \x09-> { 'basic'. 'emacsy' }\x0a  }",
 messageSends: ["->"],
 referencedClasses: []
 }),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_messageHintFor_token_",
+smalltalk.method({
+selector: "messageHintFor:token:",
+category: 'hints',
+fn: function (anEditor,aToken){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(anEditor)._at_("amberCodeWidget"))._messageHintFor_token_(anEditor,aToken);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget.klass)})},
+args: ["anEditor", "aToken"],
+source: "messageHintFor: anEditor token: aToken\x0a\x09^ (anEditor at: 'amberCodeWidget')\x0a\x09\x09messageHintFor: anEditor token: aToken",
+messageSends: ["messageHintFor:token:", "at:"],
+referencedClasses: []
+}),
+smalltalk.HLCodeWidget.klass);
+
 smalltalk.addMethod(
 "_pcKeyMap",
 smalltalk.method({
@@ -869,10 +984,16 @@ selector: "setupCodeMirror",
 category: 'initialization',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
 CodeMirror.keyMap.default.fallthrough = ["basic"] ;
-return self}, function($ctx1) {$ctx1.fill(self,"setupCodeMirror",{}, smalltalk.HLCodeWidget.klass)})},
+return smalltalk.withContext(function($ctx1) { 
+ 
+		CodeMirror.keyMap.default.fallthrough = ["basic"];
+		CodeMirror.commands.autocomplete = function(cm) {
+        	CodeMirror.showHint(cm, self._hintFor_options_);
+      	}
+	;
+return self}, function($ctx1) {$ctx1.fill(self,"setupCodeMirror",{},smalltalk.HLCodeWidget.klass)})},
 args: [],
-source: "setupCodeMirror\x0a\x09< CodeMirror.keyMap.default.fallthrough = [\x22basic\x22] >",
+source: "setupCodeMirror\x0a\x09< \x0a\x09\x09CodeMirror.keyMap.default.fallthrough = [\x22basic\x22];\x0a\x09\x09CodeMirror.commands.autocomplete = function(cm) {\x0a        \x09CodeMirror.showHint(cm, self._hintFor_options_);\x0a      \x09}\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -955,6 +1076,25 @@ referencedClasses: []
 }),
 smalltalk.HLCodeWidget.klass);
 
+smalltalk.addMethod(
+"_variableHintFor_token_",
+smalltalk.method({
+selector: "variableHintFor:token:",
+category: 'hints',
+fn: function (anEditor,aToken){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(anEditor)._at_("amberCodeWidget"))._variableHintFor_token_(anEditor,aToken);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"variableHintFor:token:",{anEditor:anEditor,aToken:aToken},smalltalk.HLCodeWidget.klass)})},
+args: ["anEditor", "aToken"],
+source: "variableHintFor: anEditor token: aToken\x0a\x09^ (anEditor at: 'amberCodeWidget')\x0a\x09\x09variableHintFor: anEditor token: aToken",
+messageSends: ["variableHintFor:token:", "at:"],
+referencedClasses: []
+}),
+smalltalk.HLCodeWidget.klass);
+
 
 smalltalk.addClass('HLSourceCodeWidget', smalltalk.HLCodeWidget, ['browserModel'], 'Helios-Workspace');
 smalltalk.addMethod(

+ 12 - 0
js/Kernel-Methods.deploy.js

@@ -422,6 +422,18 @@ return self}, function($ctx1) {$ctx1.fill(self,"fn:",{aBlock:aBlock},smalltalk.C
 messageSends: ["basicAt:put:"]}),
 smalltalk.CompiledMethod);
 
+smalltalk.addMethod(
+"_isCompiledMethod",
+smalltalk.method({
+selector: "isCompiledMethod",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isCompiledMethod",{},smalltalk.CompiledMethod)})},
+messageSends: []}),
+smalltalk.CompiledMethod);
+
 smalltalk.addMethod(
 "_messageSends",
 smalltalk.method({

+ 17 - 0
js/Kernel-Methods.js

@@ -574,6 +574,23 @@ referencedClasses: []
 }),
 smalltalk.CompiledMethod);
 
+smalltalk.addMethod(
+"_isCompiledMethod",
+smalltalk.method({
+selector: "isCompiledMethod",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isCompiledMethod",{},smalltalk.CompiledMethod)})},
+args: [],
+source: "isCompiledMethod\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.CompiledMethod);
+
 smalltalk.addMethod(
 "_messageSends",
 smalltalk.method({

+ 36 - 0
js/Kernel-Objects.deploy.js

@@ -402,6 +402,18 @@ return false;
 messageSends: []}),
 smalltalk.Object);
 
+smalltalk.addMethod(
+"_isCompiledMethod",
+smalltalk.method({
+selector: "isCompiledMethod",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isCompiledMethod",{},smalltalk.Object)})},
+messageSends: []}),
+smalltalk.Object);
+
 smalltalk.addMethod(
 "_isImmutable",
 smalltalk.method({
@@ -483,6 +495,18 @@ return false;
 messageSends: []}),
 smalltalk.Object);
 
+smalltalk.addMethod(
+"_isPackage",
+smalltalk.method({
+selector: "isPackage",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isPackage",{},smalltalk.Object)})},
+messageSends: []}),
+smalltalk.Object);
+
 smalltalk.addMethod(
 "_isParseFailure",
 smalltalk.method({
@@ -2672,6 +2696,18 @@ return self}, function($ctx1) {$ctx1.fill(self,"commitPathSt:",{aString:aString}
 messageSends: []}),
 smalltalk.Package);
 
+smalltalk.addMethod(
+"_isPackage",
+smalltalk.method({
+selector: "isPackage",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isPackage",{},smalltalk.Package)})},
+messageSends: []}),
+smalltalk.Package);
+
 smalltalk.addMethod(
 "_name",
 smalltalk.method({

+ 51 - 0
js/Kernel-Objects.js

@@ -548,6 +548,23 @@ referencedClasses: []
 }),
 smalltalk.Object);
 
+smalltalk.addMethod(
+"_isCompiledMethod",
+smalltalk.method({
+selector: "isCompiledMethod",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isCompiledMethod",{},smalltalk.Object)})},
+args: [],
+source: "isCompiledMethod\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Object);
+
 smalltalk.addMethod(
 "_isImmutable",
 smalltalk.method({
@@ -659,6 +676,23 @@ referencedClasses: []
 }),
 smalltalk.Object);
 
+smalltalk.addMethod(
+"_isPackage",
+smalltalk.method({
+selector: "isPackage",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isPackage",{},smalltalk.Object)})},
+args: [],
+source: "isPackage\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Object);
+
 smalltalk.addMethod(
 "_isParseFailure",
 smalltalk.method({
@@ -3613,6 +3647,23 @@ referencedClasses: []
 }),
 smalltalk.Package);
 
+smalltalk.addMethod(
+"_isPackage",
+smalltalk.method({
+selector: "isPackage",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isPackage",{},smalltalk.Package)})},
+args: [],
+source: "isPackage\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Package);
+
 smalltalk.addMethod(
 "_name",
 smalltalk.method({

+ 2 - 0
js/amber.js

@@ -191,7 +191,9 @@ amber = (function() {
 		addJSToLoad('js/lib/jQuery/jquery.textarea.js');
 		addJSToLoad('js/lib/CodeMirror/codemirror.js');
 		addJSToLoad('js/lib/CodeMirror/smalltalk.js');
+		addJSToLoad('js/lib/CodeMirror/addon/hint/show-hint.js');
 		loadCSS('lib/CodeMirror/codemirror.css', 'js');
+		loadCSS('lib/CodeMirror/addon/hint/show-hint.css', 'js');
 		loadCSS('lib/CodeMirror/amber.css', 'js');
 	}
 

+ 6 - 0
js/boot.js

@@ -163,6 +163,12 @@ function Smalltalk() {
 		}
 	};
 
+    /* Answer all method selectors based on dnu handlers */
+
+    st.allSelectors = function() {
+        return dnu.selectors;
+    };
+
 	/* Unique ID number generator */
 
 	var oid = 0;

+ 32 - 0
js/lib/CodeMirror/addon/dialog/dialog.css

@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+  position: absolute;
+  left: 0; right: 0;
+  background: white;
+  z-index: 15;
+  padding: .1em .8em;
+  overflow: hidden;
+  color: #333;
+}
+
+.CodeMirror-dialog-top {
+  border-bottom: 1px solid #eee;
+  top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+  border-top: 1px solid #eee;
+  bottom: 0;
+}
+
+.CodeMirror-dialog input {
+  border: none;
+  outline: none;
+  background: transparent;
+  width: 20em;
+  color: inherit;
+  font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+  font-size: 70%;
+}

+ 80 - 0
js/lib/CodeMirror/addon/dialog/dialog.js

@@ -0,0 +1,80 @@
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function() {
+  function dialogDiv(cm, template, bottom) {
+    var wrap = cm.getWrapperElement();
+    var dialog;
+    dialog = wrap.appendChild(document.createElement("div"));
+    if (bottom) {
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
+    } else {
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
+    }
+    dialog.innerHTML = template;
+    return dialog;
+  }
+
+  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var closed = false, me = this;
+    function close() {
+      if (closed) return;
+      closed = true;
+      dialog.parentNode.removeChild(dialog);
+    }
+    var inp = dialog.getElementsByTagName("input")[0], button;
+    if (inp) {
+      CodeMirror.on(inp, "keydown", function(e) {
+        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
+        if (e.keyCode == 13 || e.keyCode == 27) {
+          CodeMirror.e_stop(e);
+          close();
+          me.focus();
+          if (e.keyCode == 13) callback(inp.value);
+        }
+      });
+      if (options && options.onKeyUp) {
+        CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+      }
+      if (options && options.value) inp.value = options.value;
+      inp.focus();
+      CodeMirror.on(inp, "blur", close);
+    } else if (button = dialog.getElementsByTagName("button")[0]) {
+      CodeMirror.on(button, "click", function() {
+        close();
+        me.focus();
+      });
+      button.focus();
+      CodeMirror.on(button, "blur", close);
+    }
+    return close;
+  });
+
+  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var buttons = dialog.getElementsByTagName("button");
+    var closed = false, me = this, blurring = 1;
+    function close() {
+      if (closed) return;
+      closed = true;
+      dialog.parentNode.removeChild(dialog);
+      me.focus();
+    }
+    buttons[0].focus();
+    for (var i = 0; i < buttons.length; ++i) {
+      var b = buttons[i];
+      (function(callback) {
+        CodeMirror.on(b, "click", function(e) {
+          CodeMirror.e_preventDefault(e);
+          close();
+          if (callback) callback(me);
+        });
+      })(callbacks[i]);
+      CodeMirror.on(b, "blur", function() {
+        --blurring;
+        setTimeout(function() { if (blurring <= 0) close(); }, 200);
+      });
+      CodeMirror.on(b, "focus", function() { ++blurring; });
+    }
+  });
+})();

+ 54 - 0
js/lib/CodeMirror/addon/display/placeholder.js

@@ -0,0 +1,54 @@
+(function() {
+  CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      cm.on("focus", onFocus);
+      cm.on("blur", onBlur);
+      cm.on("change", onChange);
+      onChange(cm);
+    } else if (!val && prev) {
+      cm.off("focus", onFocus);
+      cm.off("blur", onBlur);
+      cm.off("change", onChange);
+      clearPlaceholder(cm);
+      var wrapper = cm.getWrapperElement();
+      wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
+    }
+
+    if (val && !cm.hasFocus()) onBlur(cm);
+  });
+
+  function clearPlaceholder(cm) {
+    if (cm._placeholder) {
+      cm._placeholder.parentNode.removeChild(cm._placeholder);
+      cm._placeholder = null;
+    }
+  }
+  function setPlaceholder(cm) {
+    clearPlaceholder(cm);
+    var elt = cm._placeholder = document.createElement("pre");
+    elt.style.cssText = "height: 0; overflow: visible";
+    elt.className = "CodeMirror-placeholder";
+    elt.appendChild(document.createTextNode(cm.getOption("placeholder")));
+    cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
+  }
+
+  function onFocus(cm) {
+    clearPlaceholder(cm);
+  }
+  function onBlur(cm) {
+    if (isEmpty(cm)) setPlaceholder(cm);
+  }
+  function onChange(cm) {
+    var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
+    wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
+
+    if (cm.hasFocus()) return;
+    if (empty) setPlaceholder(cm);
+    else clearPlaceholder(cm);
+  }
+
+  function isEmpty(cm) {
+    return (cm.lineCount() === 1) && (cm.getLine(0) === "");
+  }
+})();

+ 52 - 0
js/lib/CodeMirror/addon/edit/closebrackets.js

@@ -0,0 +1,52 @@
+(function() {
+  var DEFAULT_BRACKETS = "()[]{}''\"\"";
+  var SPACE_CHAR_REGEX = /\s/;
+
+  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+    var wasOn = old && old != CodeMirror.Init;
+    if (val && !wasOn)
+      cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS));
+    else if (!val && wasOn)
+      cm.removeKeyMap("autoCloseBrackets");
+  });
+
+  function buildKeymap(pairs) {
+    var map = {
+      name : "autoCloseBrackets",
+      Backspace: function(cm) {
+        if (cm.somethingSelected()) return CodeMirror.Pass;
+        var cur = cm.getCursor(), line = cm.getLine(cur.line);
+        if (cur.ch && cur.ch < line.length &&
+            pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0)
+          cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
+        else
+          return CodeMirror.Pass;
+      }
+    };
+    var closingBrackets = [];
+    for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
+      if (left != right) closingBrackets.push(right);
+      function surround(cm) {
+          var selection = cm.getSelection();
+          cm.replaceSelection(left + selection + right);
+      }
+      function maybeOverwrite(cm) {
+        var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
+        if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass;
+        else cm.execCommand("goCharRight");
+      }
+      map["'" + left + "'"] = function(cm) {
+        if (cm.somethingSelected()) return surround(cm);
+        if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
+        var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
+        var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch);
+        if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar))
+          cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
+        else
+          return CodeMirror.Pass;
+      };
+      if (left != right) map["'" + right + "'"] = maybeOverwrite;
+    })(pairs.charAt(i), pairs.charAt(i + 1));
+    return map;
+  }
+})();

+ 85 - 0
js/lib/CodeMirror/addon/edit/closetag.js

@@ -0,0 +1,85 @@
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds an "autoCloseTags" option that can be set to
+ * either true to get the default behavior, or an object to further
+ * configure its behavior.
+ *
+ * These are supported options:
+ *
+ * `whenClosing` (default true)
+ *   Whether to autoclose when the '/' of a closing tag is typed.
+ * `whenOpening` (default true)
+ *   Whether to autoclose the tag when the final '>' of an opening
+ *   tag is typed.
+ * `dontCloseTags` (default is empty tags for HTML, none for XML)
+ *   An array of tag names that should not be autoclosed.
+ * `indentTags` (default is block tags for HTML, none for XML)
+ *   An array of tag names that should, when opened, cause a
+ *   blank line to be added inside the tag, and the blank line and
+ *   closing line to be indented.
+ *
+ * See demos/closetag.html for a usage example.
+ */
+
+(function() {
+  CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
+    if (val && (old == CodeMirror.Init || !old)) {
+      var map = {name: "autoCloseTags"};
+      if (typeof val != "object" || val.whenClosing)
+        map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); };
+      if (typeof val != "object" || val.whenOpening)
+        map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); };
+      cm.addKeyMap(map);
+    } else if (!val && (old != CodeMirror.Init && old)) {
+      cm.removeKeyMap("autoCloseTags");
+    }
+  });
+
+  var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+                       "source", "track", "wbr"];
+  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
+                    "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
+
+  function autoCloseTag(cm, ch) {
+    var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
+    var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+    if (inner.mode.name != "xml") return CodeMirror.Pass;
+
+    var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
+    var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
+    var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
+
+    if (ch == ">" && state.tagName) {
+      var tagName = state.tagName;
+      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
+      var lowerTagName = tagName.toLowerCase();
+      // Don't process the '>' at the end of an end-tag or self-closing tag
+      if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 ||
+          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
+        return CodeMirror.Pass;
+
+      var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1;
+      var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1);
+      cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">",
+                          {head: curPos, anchor: curPos});
+      if (doIndent) {
+        cm.indentLine(pos.line + 1);
+        cm.indentLine(pos.line + 2);
+      }
+      return;
+    } else if (ch == "/" && tok.string == "<") {
+      var tagName = state.context && state.context.tagName;
+      if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
+      return;
+    }
+    return CodeMirror.Pass;
+  }
+
+  function indexOf(collection, elt) {
+    if (collection.indexOf) return collection.indexOf(elt);
+    for (var i = 0, e = collection.length; i < e; ++i)
+      if (collection[i] == elt) return i;
+    return -1;
+  }
+})();

+ 44 - 0
js/lib/CodeMirror/addon/edit/continuecomment.js

@@ -0,0 +1,44 @@
+(function() {
+  var modes = ["clike", "css", "javascript"];
+  for (var i = 0; i < modes.length; ++i)
+    CodeMirror.extendMode(modes[i], {blockCommentStart: "/*",
+                                     blockCommentEnd: "*/",
+                                     blockCommentContinue: " * "});
+
+  function continueComment(cm) {
+    var pos = cm.getCursor(), token = cm.getTokenAt(pos);
+    var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode;
+    var space;
+
+    if (token.type == "comment" && mode.blockCommentStart) {
+      var end = token.string.indexOf(mode.blockCommentEnd);
+      var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found;
+      if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) {
+        // Comment ended, don't continue it
+      } else if (token.string.indexOf(mode.blockCommentStart) == 0) {
+        space = full.slice(0, token.start);
+        if (!/^\s*$/.test(space)) {
+          space = "";
+          for (var i = 0; i < token.start; ++i) space += " ";
+        }
+      } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
+                 found + mode.blockCommentContinue.length > token.start &&
+                 /^\s*$/.test(full.slice(0, found))) {
+        space = full.slice(0, found);
+      }
+    }
+
+    if (space != null)
+      cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end");
+    else
+      return CodeMirror.Pass;
+  }
+
+  CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
+    if (prev && prev != CodeMirror.Init)
+      cm.removeKeyMap("continueComment");
+    var map = {name: "continueComment"};
+    map[typeof val == "string" ? val : "Enter"] = continueComment;
+    cm.addKeyMap(map);
+  });
+})();

+ 25 - 0
js/lib/CodeMirror/addon/edit/continuelist.js

@@ -0,0 +1,25 @@
+(function() {
+  'use strict';
+
+  var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/,
+      unorderedBullets = '*+-';
+
+  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
+    var pos = cm.getCursor(),
+        inList = cm.getStateAfter(pos.line).list,
+        match;
+
+    if (!inList || !(match = cm.getLine(pos.line).match(listRE))) {
+      cm.execCommand('newlineAndIndent');
+      return;
+    }
+
+    var indent = match[1], after = match[4];
+    var bullet = unorderedBullets.indexOf(match[2]) >= 0
+      ? match[2]
+      : (parseInt(match[3], 10) + 1) + '.';
+
+    cm.replaceSelection('\n' + indent + bullet + after, 'end');
+  };
+
+}());

+ 74 - 0
js/lib/CodeMirror/addon/edit/matchbrackets.js

@@ -0,0 +1,74 @@
+(function() {
+  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+    (document.documentMode == null || document.documentMode < 8);
+
+  var Pos = CodeMirror.Pos;
+  // Disable brace matching in long lines, since it'll cause hugely slow updates  
+  var maxLineLen = 1000;
+
+  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+  function findMatchingBracket(cm) {
+    var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+    var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+    if (!match) return null;
+    var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
+    var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
+
+    var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+    function scan(line, lineNo, start) {
+      if (!line.text) return;
+      var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
+      if (start != null) pos = start + d;
+      for (; pos != end; pos += d) {
+        var ch = line.text.charAt(pos);
+        if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
+          var match = matching[ch];
+          if (match.charAt(1) == ">" == forward) stack.push(ch);
+          else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+          else if (!stack.length) return {pos: pos, match: true};
+        }
+      }
+    }
+    for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+      if (i == cur.line) found = scan(line, i, pos);
+      else found = scan(cm.getLineHandle(i), i);
+      if (found) break;
+    }
+    return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
+  }
+
+  function matchBrackets(cm, autoclear) {
+    var found = findMatchingBracket(cm);
+    if (!found || cm.getLine(found.from.line).length > maxLineLen ||
+       found.to && cm.getLine(found.to.line).length > maxLineLen)
+      return;
+
+    var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+    var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
+    var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
+    // Kludge to work around the IE bug from issue #1193, where text
+    // input stops going to the textare whever this fires.
+    if (ie_lt8 && cm.state.focused) cm.display.input.focus();
+    var clear = function() {
+      cm.operation(function() { one.clear(); two && two.clear(); });
+    };
+    if (autoclear) setTimeout(clear, 800);
+    else return clear;
+  }
+
+  var currentlyHighlighted = null;
+  function doMatchBrackets(cm) {
+    cm.operation(function() {
+      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+      if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
+    });
+  }
+
+  CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
+    if (val) cm.on("cursorActivity", doMatchBrackets);
+    else cm.off("cursorActivity", doMatchBrackets);
+  });
+
+  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+  CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+})();

+ 31 - 0
js/lib/CodeMirror/addon/fold/brace-fold.js

@@ -0,0 +1,31 @@
+CodeMirror.braceRangeFinder = function(cm, start) {
+  var line = start.line, lineText = cm.getLine(line);
+  var at = lineText.length, startChar, tokenType;
+  for (;;) {
+    var found = lineText.lastIndexOf("{", at);
+    if (found < start.ch) break;
+    tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
+    if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
+    at = found - 1;
+  }
+  if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
+  var count = 1, lastLine = cm.lineCount(), end, endCh;
+  outer: for (var i = line + 1; i < lastLine; ++i) {
+    var text = cm.getLine(i), pos = 0;
+    for (;;) {
+      var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
+      if (nextOpen < 0) nextOpen = text.length;
+      if (nextClose < 0) nextClose = text.length;
+      pos = Math.min(nextOpen, nextClose);
+      if (pos == text.length) break;
+      if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) {
+        if (pos == nextOpen) ++count;
+        else if (!--count) { end = i; endCh = pos; break outer; }
+      }
+      ++pos;
+    }
+  }
+  if (end == null || end == line + 1) return;
+  return {from: CodeMirror.Pos(line, startChar + 1),
+          to: CodeMirror.Pos(end, endCh)};
+};

+ 32 - 0
js/lib/CodeMirror/addon/fold/foldcode.js

@@ -0,0 +1,32 @@
+CodeMirror.newFoldFunction = function(rangeFinder, widget) {
+  if (widget == null) widget = "\u2194";
+  if (typeof widget == "string") {
+    var text = document.createTextNode(widget);
+    widget = document.createElement("span");
+    widget.appendChild(text);
+    widget.className = "CodeMirror-foldmarker";
+  }
+
+  return function(cm, pos) {
+    if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
+    var range = rangeFinder(cm, pos);
+    if (!range) return;
+
+    var present = cm.findMarksAt(range.from), cleared = 0;
+    for (var i = 0; i < present.length; ++i) {
+      if (present[i].__isFold) {
+        ++cleared;
+        present[i].clear();
+      }
+    }
+    if (cleared) return;
+
+    var myWidget = widget.cloneNode(true);
+    CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();});
+    var myRange = cm.markText(range.from, range.to, {
+      replacedWith: myWidget,
+      clearOnEnter: true,
+      __isFold: true
+    });
+  };
+};

+ 11 - 0
js/lib/CodeMirror/addon/fold/indent-fold.js

@@ -0,0 +1,11 @@
+CodeMirror.indentRangeFinder = function(cm, start) {
+  var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
+  var myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
+  for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) {
+    var curLine = cm.getLine(i);
+    if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent &&
+        CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent)
+      return {from: CodeMirror.Pos(start.line, firstLine.length),
+              to: CodeMirror.Pos(i, curLine.length)};
+  }
+};

+ 64 - 0
js/lib/CodeMirror/addon/fold/xml-fold.js

@@ -0,0 +1,64 @@
+CodeMirror.tagRangeFinder = (function() {
+  var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
+  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
+  var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
+
+  return function(cm, start) {
+    var line = start.line, ch = start.ch, lineText = cm.getLine(line);
+
+    function nextLine() {
+      if (line >= cm.lastLine()) return;
+      ch = 0;
+      lineText = cm.getLine(++line);
+      return true;
+    }
+    function toTagEnd() {
+      for (;;) {
+        var gt = lineText.indexOf(">", ch);
+        if (gt == -1) { if (nextLine()) continue; else return; }
+        var lastSlash = lineText.lastIndexOf("/", gt);
+        var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt));
+        ch = gt + 1;
+        return selfClose ? "selfClose" : "regular";
+      }
+    }
+    function toNextTag() {
+      for (;;) {
+        xmlTagStart.lastIndex = ch;
+        var found = xmlTagStart.exec(lineText);
+        if (!found) { if (nextLine()) continue; else return; }
+        ch = found.index + found[0].length;
+        return found;
+      }
+    }
+
+    var stack = [], startCh;
+    for (;;) {
+      var openTag = toNextTag(), end;
+      if (!openTag || line != start.line || !(end = toTagEnd())) return;
+      if (!openTag[1] && end != "selfClose") {
+        stack.push(openTag[2]);
+        startCh = ch;
+        break;
+      }
+    }
+
+    for (;;) {
+      var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0);
+      if (!next || !(end = toTagEnd())) return;
+      if (end == "selfClose") continue;
+      if (next[1]) { // closing tag
+        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
+          stack.length = i;
+          break;
+        }
+        if (!stack.length) return {
+          from: CodeMirror.Pos(start.line, startCh),
+          to: CodeMirror.Pos(tagLine, tagCh)
+        };
+      } else { // opening tag
+        stack.push(next[2]);
+      }
+    }
+  };
+})();

+ 582 - 0
js/lib/CodeMirror/addon/hint/html-hint.js

@@ -0,0 +1,582 @@
+(function () {
+  function htmlHint(editor, htmlStructure, getToken) {
+    var cur = editor.getCursor();
+    var token = getToken(editor, cur);
+    var keywords = [];
+    var i = 0;
+    var j = 0;
+    var k = 0;
+    var from = {line: cur.line, ch: cur.ch};
+    var to = {line: cur.line, ch: cur.ch};
+    var flagClean = true;
+
+    var text = editor.getRange({line: 0, ch: 0}, cur);
+
+    var open = text.lastIndexOf('<');
+    var close = text.lastIndexOf('>');
+    var tokenString = token.string.replace("<","");
+
+    if(open > close) {
+      var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur);
+      if(last == "<") {
+        for(i = 0; i < htmlStructure.length; i++) {
+          keywords.push(htmlStructure[i].tag);
+        }
+        from.ch = token.start + 1;
+      } else {
+        var counter = 0;
+        var found = function(token, type, position) {
+          counter++;
+          if(counter > 50) return;
+          if(token.type == type) {
+            return token;
+          } else {
+            position.ch = token.start;
+            var newToken = editor.getTokenAt(position);
+            return found(newToken, type, position);
+          }
+        };
+
+        var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch});
+        var node = nodeToken.string.substring(1);
+
+        if(token.type === null && token.string.trim() === "") {
+          for(i = 0; i < htmlStructure.length; i++) {
+            if(htmlStructure[i].tag == node) {
+              for(j = 0; j < htmlStructure[i].attr.length; j++) {
+                keywords.push(htmlStructure[i].attr[j].key + "=\"\" ");
+              }
+
+              for(k = 0; k < globalAttributes.length; k++) {
+                keywords.push(globalAttributes[k].key + "=\"\" ");
+              }
+            }
+          }
+        } else if(token.type == "string") {
+          tokenString = tokenString.substring(1, tokenString.length - 1);
+          var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch});
+          var attribute = attributeToken.string;
+
+          for(i = 0; i < htmlStructure.length; i++) {
+            if(htmlStructure[i].tag == node) {
+              for(j = 0; j < htmlStructure[i].attr.length; j++) {
+                if(htmlStructure[i].attr[j].key == attribute) {
+                  for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) {
+                    keywords.push(htmlStructure[i].attr[j].values[k]);
+                  }
+                }
+              }
+
+              for(j = 0; j < globalAttributes.length; j++) {
+                if(globalAttributes[j].key == attribute) {
+                  for(k = 0; k < globalAttributes[j].values.length; k++) {
+                    keywords.push(globalAttributes[j].values[k]);
+                  }
+                }
+              }
+            }
+          }
+          from.ch = token.start + 1;
+        } else if(token.type == "attribute") {
+          for(i = 0; i < htmlStructure.length; i++) {
+            if(htmlStructure[i].tag == node) {
+              for(j = 0; j < htmlStructure[i].attr.length; j++) {
+                keywords.push(htmlStructure[i].attr[j].key + "=\"\" ");
+              }
+
+              for(k = 0; k < globalAttributes.length; k++) {
+                keywords.push(globalAttributes[k].key + "=\"\" ");
+              }
+            }
+          }
+          from.ch = token.start;
+        } else if(token.type == "tag") {
+          for(i = 0; i < htmlStructure.length; i++) {
+            keywords.push(htmlStructure[i].tag);
+          }
+
+          from.ch = token.start + 1;
+        }
+      }
+    } else {
+      for(i = 0; i < htmlStructure.length; i++) {
+        keywords.push("<" + htmlStructure[i].tag);
+      }
+
+      tokenString = ("<" + tokenString).trim();
+      from.ch = token.start;
+    }
+
+    if(flagClean === true && tokenString.trim() === "") {
+      flagClean = false;
+    }
+
+    if(flagClean) {
+      keywords = cleanResults(tokenString, keywords);
+    }
+
+    return {list: keywords, from: from, to: to};
+  }
+
+
+  var cleanResults = function(text, keywords) {
+    var results = [];
+    var i = 0;
+
+    for(i = 0; i < keywords.length; i++) {
+      if(keywords[i].substring(0, text.length) == text) {
+        results.push(keywords[i]);
+      }
+    }
+
+    return results;
+  };
+
+  var htmlStructure = [
+    {tag: '!DOCTYPE', attr: []},
+    {tag: 'a', attr: [
+      {key: 'href', values: ["#"]},
+      {key: 'target', values: ["_blank","_self","_top","_parent"]},
+      {key: 'ping', values: [""]},
+      {key: 'media', values: ["#"]},
+      {key: 'hreflang', values: ["en","es"]},
+      {key: 'type', values: []}
+    ]},
+    {tag: 'abbr', attr: []},
+    {tag: 'acronym', attr: []},
+    {tag: 'address', attr: []},
+    {tag: 'applet', attr: []},
+    {tag: 'area', attr: [
+      {key: 'alt', values: [""]},
+      {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]},
+      {key: 'shape', values: ["default","rect","circle","poly"]},
+      {key: 'href', values: ["#"]},
+      {key: 'target', values: ["#"]},
+      {key: 'ping', values: []},
+      {key: 'media', values: []},
+      {key: 'hreflang', values: []},
+      {key: 'type', values: []}
+
+    ]},
+    {tag: 'article', attr: []},
+    {tag: 'aside', attr: []},
+    {tag: 'audio', attr: [
+      {key: 'src', values: []},
+      {key: 'crossorigin', values: ["anonymous","use-credentials"]},
+      {key: 'preload', values: ["none","metadata","auto"]},
+      {key: 'autoplay', values: ["","autoplay"]},
+      {key: 'mediagroup', values: []},
+      {key: 'loop', values: ["","loop"]},
+      {key: 'controls', values: ["","controls"]}
+    ]},
+    {tag: 'b', attr: []},
+    {tag: 'base', attr: [
+      {key: 'href', values: ["#"]},
+      {key: 'target', values: ["_blank","_self","_top","_parent"]}
+    ]},
+    {tag: 'basefont', attr: []},
+    {tag: 'bdi', attr: []},
+    {tag: 'bdo', attr: []},
+    {tag: 'big', attr: []},
+    {tag: 'blockquote', attr: [
+      {key: 'cite', values: ["http://"]}
+    ]},
+    {tag: 'body', attr: []},
+    {tag: 'br', attr: []},
+    {tag: 'button', attr: [
+      {key: 'autofocus', values: ["","autofocus"]},
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'form', values: []},
+      {key: 'formaction', values: []},
+      {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
+      {key: 'formmethod', values: ["get","post","put","delete"]},
+      {key: 'formnovalidate', values: ["","novalidate"]},
+      {key: 'formtarget', values: ["_blank","_self","_top","_parent"]},
+      {key: 'name', values: []},
+      {key: 'type', values: ["submit","reset","button"]},
+      {key: 'value', values: []}
+    ]},
+    {tag: 'canvas', attr: [
+      {key: 'width', values: []},
+      {key: 'height', values: []}
+    ]},
+    {tag: 'caption', attr: []},
+    {tag: 'center', attr: []},
+    {tag: 'cite', attr: []},
+    {tag: 'code', attr: []},
+    {tag: 'col', attr: [
+      {key: 'span', values: []}
+    ]},
+    {tag: 'colgroup', attr: [
+      {key: 'span', values: []}
+    ]},
+    {tag: 'command', attr: [
+      {key: 'type', values: ["command","checkbox","radio"]},
+      {key: 'label', values: []},
+      {key: 'icon', values: []},
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'checked', values: ["","checked"]},
+      {key: 'radiogroup', values: []},
+      {key: 'command', values: []},
+      {key: 'title', values: []}
+    ]},
+    {tag: 'data', attr: [
+      {key: 'value', values: []}
+    ]},
+    {tag: 'datagrid', attr: [
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'multiple', values: ["","multiple"]}
+    ]},
+    {tag: 'datalist', attr: [
+      {key: 'data', values: []}
+    ]},
+    {tag: 'dd', attr: []},
+    {tag: 'del', attr: [
+      {key: 'cite', values: []},
+      {key: 'datetime', values: []}
+    ]},
+    {tag: 'details', attr: [
+      {key: 'open', values: ["","open"]}
+    ]},
+    {tag: 'dfn', attr: []},
+    {tag: 'dir', attr: []},
+    {tag: 'div', attr: [
+      {key: 'id', values: []},
+      {key: 'class', values: []},
+      {key: 'style', values: []}
+    ]},
+    {tag: 'dl', attr: []},
+    {tag: 'dt', attr: []},
+    {tag: 'em', attr: []},
+    {tag: 'embed', attr: [
+      {key: 'src', values: []},
+      {key: 'type', values: []},
+      {key: 'width', values: []},
+      {key: 'height', values: []}
+    ]},
+    {tag: 'eventsource', attr: [
+      {key: 'src', values: []}
+    ]},
+    {tag: 'fieldset', attr: [
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'form', values: []},
+      {key: 'name', values: []}
+    ]},
+    {tag: 'figcaption', attr: []},
+    {tag: 'figure', attr: []},
+    {tag: 'font', attr: []},
+    {tag: 'footer', attr: []},
+    {tag: 'form', attr: [
+      {key: 'accept-charset', values: ["UNKNOWN","utf-8"]},
+      {key: 'action', values: []},
+      {key: 'autocomplete', values: ["on","off"]},
+      {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
+      {key: 'method', values: ["get","post","put","delete","dialog"]},
+      {key: 'name', values: []},
+      {key: 'novalidate', values: ["","novalidate"]},
+      {key: 'target', values: ["_blank","_self","_top","_parent"]}
+    ]},
+    {tag: 'frame', attr: []},
+    {tag: 'frameset', attr: []},
+    {tag: 'h1', attr: []},
+    {tag: 'h2', attr: []},
+    {tag: 'h3', attr: []},
+    {tag: 'h4', attr: []},
+    {tag: 'h5', attr: []},
+    {tag: 'h6', attr: []},
+    {tag: 'head', attr: []},
+    {tag: 'header', attr: []},
+    {tag: 'hgroup', attr: []},
+    {tag: 'hr', attr: []},
+    {tag: 'html', attr: [
+      {key: 'manifest', values: []}
+    ]},
+    {tag: 'i', attr: []},
+    {tag: 'iframe', attr: [
+      {key: 'src', values: []},
+      {key: 'srcdoc', values: []},
+      {key: 'name', values: []},
+      {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]},
+      {key: 'seamless', values: ["","seamless"]},
+      {key: 'width', values: []},
+      {key: 'height', values: []}
+    ]},
+    {tag: 'img', attr: [
+      {key: 'alt', values: []},
+      {key: 'src', values: []},
+      {key: 'crossorigin', values: ["anonymous","use-credentials"]},
+      {key: 'ismap', values: []},
+      {key: 'usemap', values: []},
+      {key: 'width', values: []},
+      {key: 'height', values: []}
+    ]},
+    {tag: 'input', attr: [
+      {key: 'accept', values: ["audio/*","video/*","image/*"]},
+      {key: 'alt', values: []},
+      {key: 'autocomplete', values: ["on","off"]},
+      {key: 'autofocus', values: ["","autofocus"]},
+      {key: 'checked', values: ["","checked"]},
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'dirname', values: []},
+      {key: 'form', values: []},
+      {key: 'formaction', values: []},
+      {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
+      {key: 'formmethod', values: ["get","post","put","delete"]},
+      {key: 'formnovalidate', values: ["","novalidate"]},
+      {key: 'formtarget', values: ["_blank","_self","_top","_parent"]},
+      {key: 'height', values: []},
+      {key: 'list', values: []},
+      {key: 'max', values: []},
+      {key: 'maxlength', values: []},
+      {key: 'min', values: []},
+      {key: 'multiple', values: ["","multiple"]},
+      {key: 'name', values: []},
+      {key: 'pattern', values: []},
+      {key: 'placeholder', values: []},
+      {key: 'readonly', values: ["","readonly"]},
+      {key: 'required', values: ["","required"]},
+      {key: 'size', values: []},
+      {key: 'src', values: []},
+      {key: 'step', values: []},
+      {key: 'type', values: [
+        "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local",
+        "number","range","color","checkbox","radio","file","submit","image","reset","button"
+      ]},
+      {key: 'value', values: []},
+      {key: 'width', values: []}
+    ]},
+    {tag: 'ins', attr: [
+      {key: 'cite', values: []},
+      {key: 'datetime', values: []}
+    ]},
+    {tag: 'kbd', attr: []},
+    {tag: 'keygen', attr: [
+      {key: 'autofocus', values: ["","autofocus"]},
+      {key: 'challenge', values: []},
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'form', values: []},
+      {key: 'keytype', values: ["RSA"]},
+      {key: 'name', values: []}
+    ]},
+    {tag: 'label', attr: [
+      {key: 'for', values: []},
+      {key: 'form', values: []}
+    ]},
+    {tag: 'legend', attr: []},
+    {tag: 'li', attr: [
+      {key: 'value', values: []}
+    ]},
+    {tag: 'link', attr: [
+      {key: 'href', values: []},
+      {key: 'hreflang', values: ["en","es"]},
+      {key: 'media', values: [
+        "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses",
+        "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3",
+        "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait",
+        "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2"
+      ]},
+      {key: 'type', values: []},
+      {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]}
+    ]},
+    {tag: 'map', attr: [
+      {key: 'name', values: []}
+    ]},
+    {tag: 'mark', attr: []},
+    {tag: 'menu', attr: [
+      {key: 'type', values: ["list","context","toolbar"]},
+      {key: 'label', values: []}
+    ]},
+    {tag: 'meta', attr: [
+      {key: 'charset', attr: ["utf-8"]},
+      {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]},
+      {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]},
+      {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]}
+    ]},
+    {tag: 'meter', attr: [
+      {key: 'value', values: []},
+      {key: 'min', values: []},
+      {key: 'low', values: []},
+      {key: 'high', values: []},
+      {key: 'max', values: []},
+      {key: 'optimum', values: []}
+    ]},
+    {tag: 'nav', attr: []},
+    {tag: 'noframes', attr: []},
+    {tag: 'noscript', attr: []},
+    {tag: 'object', attr: [
+      {key: 'data', values: []},
+      {key: 'type', values: []},
+      {key: 'typemustmatch', values: ["","typemustmatch"]},
+      {key: 'name', values: []},
+      {key: 'usemap', values: []},
+      {key: 'form', values: []},
+      {key: 'width', values: []},
+      {key: 'height', values: []}
+    ]},
+    {tag: 'ol', attr: [
+      {key: 'reversed', values: ["", "reversed"]},
+      {key: 'start', values: []},
+      {key: 'type', values: ["1","a","A","i","I"]}
+    ]},
+    {tag: 'optgroup', attr: [
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'label', values: []}
+    ]},
+    {tag: 'option', attr: [
+      {key: 'disabled', values: ["", "disabled"]},
+      {key: 'label', values: []},
+      {key: 'selected', values: ["", "selected"]},
+      {key: 'value', values: []}
+    ]},
+    {tag: 'output', attr: [
+      {key: 'for', values: []},
+      {key: 'form', values: []},
+      {key: 'name', values: []}
+    ]},
+    {tag: 'p', attr: []},
+    {tag: 'param', attr: [
+      {key: 'name', values: []},
+      {key: 'value', values: []}
+    ]},
+    {tag: 'pre', attr: []},
+    {tag: 'progress', attr: [
+      {key: 'value', values: []},
+      {key: 'max', values: []}
+    ]},
+    {tag: 'q', attr: [
+      {key: 'cite', values: []}
+    ]},
+    {tag: 'rp', attr: []},
+    {tag: 'rt', attr: []},
+    {tag: 'ruby', attr: []},
+    {tag: 's', attr: []},
+    {tag: 'samp', attr: []},
+    {tag: 'script', attr: [
+      {key: 'type', values: ["text/javascript"]},
+      {key: 'src', values: []},
+      {key: 'async', values: ["","async"]},
+      {key: 'defer', values: ["","defer"]},
+      {key: 'charset', values: ["utf-8"]}
+    ]},
+    {tag: 'section', attr: []},
+    {tag: 'select', attr: [
+      {key: 'autofocus', values: ["", "autofocus"]},
+      {key: 'disabled', values: ["", "disabled"]},
+      {key: 'form', values: []},
+      {key: 'multiple', values: ["", "multiple"]},
+      {key: 'name', values: []},
+      {key: 'size', values: []}
+    ]},
+    {tag: 'small', attr: []},
+    {tag: 'source', attr: [
+      {key: 'src', values: []},
+      {key: 'type', values: []},
+      {key: 'media', values: []}
+    ]},
+    {tag: 'span', attr: []},
+    {tag: 'strike', attr: []},
+    {tag: 'strong', attr: []},
+    {tag: 'style', attr: [
+      {key: 'type', values: ["text/css"]},
+      {key: 'media', values: ["all","braille","print","projection","screen","speech"]},
+      {key: 'scoped', values: []}
+    ]},
+    {tag: 'sub', attr: []},
+    {tag: 'summary', attr: []},
+    {tag: 'sup', attr: []},
+    {tag: 'table', attr: [
+      {key: 'border', values: []}
+    ]},
+    {tag: 'tbody', attr: []},
+    {tag: 'td', attr: [
+      {key: 'colspan', values: []},
+      {key: 'rowspan', values: []},
+      {key: 'headers', values: []}
+    ]},
+    {tag: 'textarea', attr: [
+      {key: 'autofocus', values: ["","autofocus"]},
+      {key: 'disabled', values: ["","disabled"]},
+      {key: 'dirname', values: []},
+      {key: 'form', values: []},
+      {key: 'maxlength', values: []},
+      {key: 'name', values: []},
+      {key: 'placeholder', values: []},
+      {key: 'readonly', values: ["","readonly"]},
+      {key: 'required', values: ["","required"]},
+      {key: 'rows', values: []},
+      {key: 'cols', values: []},
+      {key: 'wrap', values: ["soft","hard"]}
+    ]},
+    {tag: 'tfoot', attr: []},
+    {tag: 'th', attr: [
+      {key: 'colspan', values: []},
+      {key: 'rowspan', values: []},
+      {key: 'headers', values: []},
+      {key: 'scope', values: ["row","col","rowgroup","colgroup"]}
+    ]},
+    {tag: 'thead', attr: []},
+    {tag: 'time', attr: [
+      {key: 'datetime', values: []}
+    ]},
+    {tag: 'title', attr: []},
+    {tag: 'tr', attr: []},
+    {tag: 'track', attr: [
+      {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]},
+      {key: 'src', values: []},
+      {key: 'srclang', values: ["en","es"]},
+      {key: 'label', values: []},
+      {key: 'default', values: []}
+    ]},
+    {tag: 'tt', attr: []},
+    {tag: 'u', attr: []},
+    {tag: 'ul', attr: []},
+    {tag: 'var', attr: []},
+    {tag: 'video', attr: [
+      {key: "src", values: []},
+      {key: "crossorigin", values: ["anonymous","use-credentials"]},
+      {key: "poster", values: []},
+      {key: "preload", values: ["auto","metadata","none"]},
+      {key: "autoplay", values: ["","autoplay"]},
+      {key: "mediagroup", values: ["movie"]},
+      {key: "loop", values: ["","loop"]},
+      {key: "muted", values: ["","muted"]},
+      {key: "controls", values: ["","controls"]},
+      {key: "width", values: []},
+      {key: "height", values: []}
+    ]},
+    {tag: 'wbr', attr: []}
+  ];
+
+  var globalAttributes = [
+    {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]},
+    {key: "class", values: []},
+    {key: "contenteditable", values: ["true", "false"]},
+    {key: "contextmenu", values: []},
+    {key: "dir", values: ["ltr","rtl","auto"]},
+    {key: "draggable", values: ["true","false","auto"]},
+    {key: "dropzone", values: ["copy","move","link","string:","file:"]},
+    {key: "hidden", values: ["hidden"]},
+    {key: "id", values: []},
+    {key: "inert", values: ["inert"]},
+    {key: "itemid", values: []},
+    {key: "itemprop", values: []},
+    {key: "itemref", values: []},
+    {key: "itemscope", values: ["itemscope"]},
+    {key: "itemtype", values: []},
+    {key: "lang", values: ["en","es"]},
+    {key: "spellcheck", values: ["true","false"]},
+    {key: "style", values: []},
+    {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]},
+    {key: "title", values: []},
+    {key: "translate", values: ["yes","no"]},
+    {key: "onclick", values: []},
+    {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]}
+  ];
+
+  CodeMirror.htmlHint = function(editor) {
+    if(String.prototype.trim == undefined) {
+      String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');};
+    }
+    return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); });
+  };
+})();

+ 140 - 0
js/lib/CodeMirror/addon/hint/javascript-hint.js

@@ -0,0 +1,140 @@
+(function () {
+  var Pos = CodeMirror.Pos;
+
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+  
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, keywords, getToken, options) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+		if (!/^[\w$_]*$/.test(token.string)) {
+      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+                       type: token.string == "." ? "property" : null};
+    }
+    // If it is a property, find out what it is a property of.
+    while (tprop.type == "property") {
+      tprop = getToken(editor, Pos(cur.line, tprop.start));
+      if (tprop.string != ".") return;
+      tprop = getToken(editor, Pos(cur.line, tprop.start));
+      if (tprop.string == ')') {
+        var level = 1;
+        do {
+          tprop = getToken(editor, Pos(cur.line, tprop.start));
+          switch (tprop.string) {
+          case ')': level++; break;
+          case '(': level--; break;
+          default: break;
+          }
+        } while (level > 0);
+        tprop = getToken(editor, Pos(cur.line, tprop.start));
+	if (tprop.type.indexOf("variable") === 0)
+	  tprop.type = "function";
+	else return; // no clue
+      }
+      if (!context) var context = [];
+      context.push(tprop);
+    }
+    return {list: getCompletions(token, context, keywords, options),
+            from: Pos(cur.line, token.start),
+            to: Pos(cur.line, token.end)};
+  }
+
+  CodeMirror.javascriptHint = function(editor, options) {
+    return scriptHint(editor, javascriptKeywords,
+                      function (e, cur) {return e.getTokenAt(cur);},
+                      options);
+  };
+
+  function getCoffeeScriptToken(editor, cur) {
+  // This getToken, it is for coffeescript, imitates the behavior of
+  // getTokenAt method in javascript.js, that is, returning "property"
+  // type and treat "." as indepenent token.
+    var token = editor.getTokenAt(cur);
+    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
+      token.end = token.start;
+      token.string = '.';
+      token.type = "property";
+    }
+    else if (/^\.[\w$_]*$/.test(token.string)) {
+      token.type = "property";
+      token.start++;
+      token.string = token.string.replace(/\./, '');
+    }
+    return token;
+  }
+
+  CodeMirror.coffeescriptHint = function(editor, options) {
+    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
+  };
+
+  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
+                     "toUpperCase toLowerCase split concat match replace search").split(" ");
+  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
+                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
+  var funcProps = "prototype apply call bind".split(" ");
+  var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
+                  "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
+  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
+                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
+
+  function getCompletions(token, context, keywords, options) {
+    var found = [], start = token.string;
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+    function gatherCompletions(obj) {
+      if (typeof obj == "string") forEach(stringProps, maybeAdd);
+      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
+      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
+      for (var name in obj) maybeAdd(name);
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      if (obj.type.indexOf("variable") === 0) {
+        if (options && options.additionalContext)
+          base = options.additionalContext[obj.string];
+        base = base || window[obj.string];
+      } else if (obj.type == "string") {
+        base = "";
+      } else if (obj.type == "atom") {
+        base = 1;
+      } else if (obj.type == "function") {
+        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
+            (typeof window.jQuery == 'function'))
+          base = window.jQuery();
+        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
+          base = window._();
+      }
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    else {
+      // If not, just look in the window object and any local scope
+      // (reading into JS mode internals to get at the local and global variables)
+      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
+      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
+      gatherCompletions(window);
+      forEach(keywords, maybeAdd);
+    }
+    return found;
+  }
+})();

+ 117 - 0
js/lib/CodeMirror/addon/hint/pig-hint.js

@@ -0,0 +1,117 @@
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+  
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+
+    if (!/^[\w$_]*$/.test(token.string)) {
+        token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+                         className: token.string == ":" ? "pig-type" : null};
+    }
+      
+    if (!context) var context = [];
+    context.push(tprop);
+    
+    var completionList = getCompletions(token, context); 
+    completionList = completionList.sort();
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if(completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+            from: CodeMirror.Pos(cur.line, token.start),
+            to: CodeMirror.Pos(cur.line, token.end)};
+  }
+  
+  CodeMirror.pigHint = function(editor) {
+    return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
+  };
+ 
+  var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP "
+  + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
+  + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE "
+  + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " 
+  + "NEQ MATCHES TRUE FALSE";
+  var pigKeywordsU = pigKeywords.split(" ");
+  var pigKeywordsL = pigKeywords.toLowerCase().split(" ");
+  
+  var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP";
+  var pigTypesU = pigTypes.split(" ");
+  var pigTypesL = pigTypes.toLowerCase().split(" ");
+  
+  var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " 
+  + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS "
+  + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG "
+  + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN "
+  + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER "
+  + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS "
+  + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA  "
+  + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE "
+  + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG "
+  + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER";  
+  var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" ");  
+  var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" ");   
+  var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs "
+  + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax "
+  + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum "
+  + "InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker "
+  + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize "
+  + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax "
+  + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" ");
+                    
+  function getCompletions(token, context) {
+    var found = [], start = token.string;
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+    
+    function gatherCompletions(obj) {
+      if(obj == ":") {
+        forEach(pigTypesL, maybeAdd);
+      }
+      else {
+        forEach(pigBuiltinsU, maybeAdd);
+        forEach(pigBuiltinsL, maybeAdd);
+        forEach(pigBuiltinsC, maybeAdd);
+        forEach(pigTypesU, maybeAdd);
+        forEach(pigTypesL, maybeAdd);
+        forEach(pigKeywordsU, maybeAdd);
+        forEach(pigKeywordsL, maybeAdd);
+      }
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+
+      if (obj.type == "variable") 
+          base = obj.string;
+      else if(obj.type == "variable-3")
+          base = ":" + obj.string;
+        
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return found;
+  }
+})();

+ 93 - 0
js/lib/CodeMirror/addon/hint/python-hint.js

@@ -0,0 +1,93 @@
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+
+    if (!/^[\w$_]*$/.test(token.string)) {
+        token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+                         className: token.string == ":" ? "python-type" : null};
+    }
+
+    if (!context) var context = [];
+    context.push(tprop);
+
+    var completionList = getCompletions(token, context);
+    completionList = completionList.sort();
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if(completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+            from: CodeMirror.Pos(cur.line, token.start),
+            to: CodeMirror.Pos(cur.line, token.end)};
+  }
+
+  CodeMirror.pythonHint = function(editor) {
+    return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
+  };
+
+  var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
++ "break except import print class exec in raise continue finally is return def for lambda try";
+  var pythonKeywordsL = pythonKeywords.split(" ");
+  var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
+
+  var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
++ "any eval isinstance pow sum basestring execfile issubclass print super"
++ "bin file iter property tuple bool filter len range type"
++ "bytearray float list raw_input unichr callable format locals reduce unicode"
++ "chr frozenset long reload vars classmethod getattr map repr xrange"
++ "cmp globals max reversed zip compile hasattr memoryview round __import__"
++ "complex hash min set apply delattr help next setattr buffer"
++ "dict hex object slice coerce dir id oct sorted intern ";
+  var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
+  var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
+
+  function getCompletions(token, context) {
+    var found = [], start = token.string;
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+
+    function gatherCompletions(_obj) {
+        forEach(pythonBuiltinsL, maybeAdd);
+        forEach(pythonBuiltinsU, maybeAdd);
+        forEach(pythonKeywordsL, maybeAdd);
+        forEach(pythonKeywordsU, maybeAdd);
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+
+      if (obj.type == "variable")
+          base = obj.string;
+      else if(obj.type == "variable-3")
+          base = ":" + obj.string;
+
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return found;
+  }
+})();

+ 38 - 0
js/lib/CodeMirror/addon/hint/show-hint.css

@@ -0,0 +1,38 @@
+.CodeMirror-hints {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  list-style: none;
+
+  margin: 0;
+  padding: 2px;
+
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  border-radius: 3px;
+  border: 1px solid silver;
+
+  background: white;
+  font-size: 90%;
+  font-family: monospace;
+
+  max-height: 20em;
+  overflow-y: auto;
+}
+
+.CodeMirror-hint {
+  margin: 0;
+  padding: 0 4px;
+  border-radius: 2px;
+  max-width: 19em;
+  overflow: hidden;
+  white-space: pre;
+  color: black;
+  cursor: pointer;
+}
+
+.CodeMirror-hint-active {
+  background: #08f;
+  color: white;
+}

+ 172 - 0
js/lib/CodeMirror/addon/hint/show-hint.js

@@ -0,0 +1,172 @@
+CodeMirror.showHint = function(cm, getHints, options) {
+  if (!options) options = {};
+  var startCh = cm.getCursor().ch, continued = false;
+  var closeOn = options.closeCharacters || /[\s()\[\]{};:]/;
+
+  function startHinting() {
+    // We want a single cursor position.
+    if (cm.somethingSelected()) return;
+
+    if (options.async)
+      getHints(cm, showHints, options);
+    else
+      return showHints(getHints(cm, options));
+  }
+
+  function getText(completion) {
+    if (typeof completion == "string") return completion;
+    else return completion.text;
+  }
+
+  function pickCompletion(cm, data, completion) {
+    if (completion.hint) completion.hint(cm, data, completion);
+    else cm.replaceRange(getText(completion), data.from, data.to);
+  }
+
+  function showHints(data) {
+    if (!data || !data.list.length) return;
+    var completions = data.list;
+    // When there is only one completion, use it directly.
+    if (!continued && options.completeSingle !== false && completions.length == 1) {
+      pickCompletion(cm, data, completions[0]);
+      return true;
+    }
+
+    // Build the select widget
+    var hints = document.createElement("ul"), selectedHint = 0;
+    hints.className = "CodeMirror-hints";
+    for (var i = 0; i < completions.length; ++i) {
+      var elt = hints.appendChild(document.createElement("li")), completion = completions[i];
+      var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
+      if (completion.className != null) className = completion.className + " " + className;
+      elt.className = className;
+      if (completion.render) completion.render(elt, data, completion);
+      else elt.appendChild(document.createTextNode(getText(completion)));
+      elt.hintId = i;
+    }
+    var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
+    var left = pos.left, top = pos.bottom, below = true;
+    hints.style.left = left + "px";
+    hints.style.top = top + "px";
+    document.body.appendChild(hints);
+
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+    var box = hints.getBoundingClientRect();
+    var overlapX = box.right - winW, overlapY = box.bottom - winH;
+    if (overlapX > 0) {
+      if (box.right - box.left > winW) {
+        hints.style.width = (winW - 5) + "px";
+        overlapX -= (box.right - box.left) - winW;
+      }
+      hints.style.left = (left = pos.left - overlapX) + "px";
+    }
+    if (overlapY > 0) {
+      var height = box.bottom - box.top;
+      if (box.top - (pos.bottom - pos.top) - height > 0) {
+        overlapY = height + (pos.bottom - pos.top);
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        overlapY -= height - winH;
+      }
+      hints.style.top = (top = pos.bottom - overlapY) + "px";
+    }
+
+    function changeActive(i) {
+      i = Math.max(0, Math.min(i, completions.length - 1));
+      if (selectedHint == i) return;
+      var node = hints.childNodes[selectedHint];
+      node.className = node.className.replace(" CodeMirror-hint-active", "");
+      node = hints.childNodes[selectedHint = i];
+      node.className += " CodeMirror-hint-active";
+      if (node.offsetTop < hints.scrollTop)
+        hints.scrollTop = node.offsetTop - 3;
+      else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight)
+        hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3;
+    }
+
+    function screenAmount() {
+      return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1;
+    }
+
+    var ourMap = {
+      Up: function() {changeActive(selectedHint - 1);},
+      Down: function() {changeActive(selectedHint + 1);},
+      PageUp: function() {changeActive(selectedHint - screenAmount());},
+      PageDown: function() {changeActive(selectedHint + screenAmount());},
+      Home: function() {changeActive(0);},
+      End: function() {changeActive(completions.length - 1);},
+      Enter: pick,
+      Tab: pick,
+      Esc: close
+    };
+    if (options.customKeys) for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) {
+      var val = options.customKeys[key];
+      if (/^(Up|Down|Enter|Esc)$/.test(key)) val = ourMap[val];
+      ourMap[key] = val;
+    }
+
+    cm.addKeyMap(ourMap);
+    cm.on("cursorActivity", cursorActivity);
+    var closingOnBlur;
+    function onBlur(){ closingOnBlur = setTimeout(close, 100); };
+    function onFocus(){ clearTimeout(closingOnBlur); };
+    cm.on("blur", onBlur);
+    cm.on("focus", onFocus);
+    var startScroll = cm.getScrollInfo();
+    function onScroll() {
+      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+      var newTop = top + startScroll.top - curScroll.top, point = newTop;
+      if (!below) point += hints.offsetHeight;
+      if (point <= editor.top || point >= editor.bottom) return close();
+      hints.style.top = newTop + "px";
+      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+    }
+    cm.on("scroll", onScroll);
+    CodeMirror.on(hints, "dblclick", function(e) {
+      var t = e.target || e.srcElement;
+      if (t.hintId != null) {selectedHint = t.hintId; pick();}
+    });
+    CodeMirror.on(hints, "click", function(e) {
+      var t = e.target || e.srcElement;
+      if (t.hintId != null) changeActive(t.hintId);
+    });
+    CodeMirror.on(hints, "mousedown", function() {
+      setTimeout(function(){cm.focus();}, 20);
+    });
+
+    var done = false, once;
+    function close() {
+      if (done) return;
+      done = true;
+      clearTimeout(once);
+      hints.parentNode.removeChild(hints);
+      cm.removeKeyMap(ourMap);
+      cm.off("cursorActivity", cursorActivity);
+      cm.off("blur", onBlur);
+      cm.off("focus", onFocus);
+      cm.off("scroll", onScroll);
+    }
+    function pick() {
+      pickCompletion(cm, data, completions[selectedHint]);
+      close();
+    }
+    var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length;
+    function cursorActivity() {
+      clearTimeout(once);
+
+      var pos = cm.getCursor(), line = cm.getLine(pos.line);
+      if (pos.line != lastPos.line || line.length - pos.ch != lastLen - lastPos.ch ||
+          pos.ch < startCh || cm.somethingSelected() ||
+          (pos.ch && closeOn.test(line.charAt(pos.ch - 1))))
+        close();
+      else
+        once = setTimeout(function(){close(); continued = true; startHinting();}, 70);
+    }
+    return true;
+  }
+
+  return startHinting();
+};

+ 118 - 0
js/lib/CodeMirror/addon/hint/xml-hint.js

@@ -0,0 +1,118 @@
+(function() {
+
+    CodeMirror.xmlHints = [];
+
+    CodeMirror.xmlHint = function(cm) {
+
+        var cursor = cm.getCursor();
+
+        if (cursor.ch > 0) {
+
+            var text = cm.getRange(CodeMirror.Pos(0, 0), cursor);
+            var typed = '';
+            var simbol = '';
+            for(var i = text.length - 1; i >= 0; i--) {
+                if(text[i] == ' ' || text[i] == '<') {
+                    simbol = text[i];
+                    break;
+                }
+                else {
+                    typed = text[i] + typed;
+                }
+            }
+
+            text = text.slice(0, text.length - typed.length);
+
+            var path = getActiveElement(text) + simbol;
+            var hints = CodeMirror.xmlHints[path];
+
+            if(typeof hints === 'undefined')
+                hints = [''];
+            else {
+                hints = hints.slice(0);
+                for (var i = hints.length - 1; i >= 0; i--) {
+                    if(hints[i].indexOf(typed) != 0)
+                        hints.splice(i, 1);
+                }
+            }
+
+            return {
+                list: hints,
+                from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length),
+                to: cursor
+            };
+        }
+    };
+
+    var getActiveElement = function(text) {
+
+        var element = '';
+
+        if(text.length >= 0) {
+
+            var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g');
+
+            var matches = [];
+            var match;
+            while ((match = regex.exec(text)) != null) {
+                matches.push({
+                    tag: match[1],
+                    selfclose: (match[0].slice(match[0].length - 2) === '/>')
+                });
+            }
+
+            for (var i = matches.length - 1, skip = 0; i >= 0; i--) {
+
+                var item = matches[i];
+
+                if (item.tag[0] == '/')
+                {
+                    skip++;
+                }
+                else if (item.selfclose == false)
+                {
+                    if (skip > 0)
+                    {
+                        skip--;
+                    }
+                    else
+                    {
+                        element = '<' + item.tag + '>' + element;
+                    }
+                }
+            }
+
+            element += getOpenTag(text);
+        }
+
+        return element;
+    };
+
+    var getOpenTag = function(text) {
+
+        var open = text.lastIndexOf('<');
+        var close = text.lastIndexOf('>');
+
+        if (close < open)
+        {
+            text = text.slice(open);
+
+            if(text != '<') {
+
+                var space = text.indexOf(' ');
+                if(space < 0)
+                    space = text.indexOf('\t');
+                if(space < 0)
+                    space = text.indexOf('\n');
+
+                if (space < 0)
+                    space = text.length;
+
+                return text.slice(0, space);
+            }
+        }
+
+        return '';
+    };
+
+})();

+ 127 - 0
js/lib/CodeMirror/addon/lint/javascript-lint.js

@@ -0,0 +1,127 @@
+(function() {
+
+  var bogus = [ "Dangerous comment" ];
+
+  var warnings = [ [ "Expected '{'",
+		     "Statement body should be inside '{ }' braces." ] ];
+
+  var errors = [ "Missing semicolon", "Extra comma", "Missing property name",
+	         "Unmatched ", " and instead saw", " is not defined",
+	         "Unclosed string", "Stopping, unable to continue" ];
+
+  function validator(options, text) {
+    JSHINT(text, options);
+    var errors = JSHINT.data().errors, result = [];
+    if (errors) parseErrors(errors, result);
+    return result;
+  }
+
+  CodeMirror.javascriptValidatorWithOptions = function(options) {
+    return function(text) { return validator(options, text); };
+  };
+
+  CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null);
+
+  function cleanup(error) {
+    // All problems are warnings by default
+    fixWith(error, warnings, "warning", true);
+    fixWith(error, errors, "error");
+
+    return isBogus(error) ? null : error;
+  }
+
+  function fixWith(error, fixes, severity, force) {
+    var description, fix, find, replace, found;
+
+    description = error.description;
+
+    for ( var i = 0; i < fixes.length; i++) {
+      fix = fixes[i];
+      find = (typeof fix === "string" ? fix : fix[0]);
+      replace = (typeof fix === "string" ? null : fix[1]);
+      found = description.indexOf(find) !== -1;
+
+      if (force || found) {
+	error.severity = severity;
+      }
+      if (found && replace) {
+	error.description = replace;
+      }
+    }
+  }
+
+  function isBogus(error) {
+    var description = error.description;
+    for ( var i = 0; i < bogus.length; i++) {
+      if (description.indexOf(bogus[i]) !== -1) {
+	return true;
+      }
+    }
+    return false;
+  }
+
+  function parseErrors(errors, output) {
+    for ( var i = 0; i < errors.length; i++) {
+      var error = errors[i];
+      if (error) {
+	var linetabpositions, index;
+
+	linetabpositions = [];
+
+	// This next block is to fix a problem in jshint. Jshint
+	// replaces
+	// all tabs with spaces then performs some checks. The error
+	// positions (character/space) are then reported incorrectly,
+	// not taking the replacement step into account. Here we look
+	// at the evidence line and try to adjust the character position
+	// to the correct value.
+	if (error.evidence) {
+	  // Tab positions are computed once per line and cached
+	  var tabpositions = linetabpositions[error.line];
+	  if (!tabpositions) {
+	    var evidence = error.evidence;
+	    tabpositions = [];
+	    // ugggh phantomjs does not like this
+	    // forEachChar(evidence, function(item, index) {
+	    Array.prototype.forEach.call(evidence, function(item,
+							    index) {
+	      if (item === '\t') {
+		// First col is 1 (not 0) to match error
+		// positions
+		tabpositions.push(index + 1);
+	      }
+	    });
+	    linetabpositions[error.line] = tabpositions;
+	  }
+	  if (tabpositions.length > 0) {
+	    var pos = error.character;
+	    tabpositions.forEach(function(tabposition) {
+	      if (pos > tabposition) pos -= 1;
+	    });
+	    error.character = pos;
+	  }
+	}
+
+	var start = error.character - 1, end = start + 1;
+	if (error.evidence) {
+	  index = error.evidence.substring(start).search(/.\b/);
+	  if (index > -1) {
+	    end += index;
+	  }
+	}
+
+	// Convert to format expected by validation service
+	error.description = error.reason;// + "(jshint)";
+	error.start = error.character;
+	error.end = end;
+	error = cleanup(error);
+
+	if (error)
+          output.push({message: error.description,
+                       severity: error.severity,
+                       from: CodeMirror.Pos(error.line - 1, start),
+                       to: CodeMirror.Pos(error.line - 1, end)});
+      }
+    }
+  }
+})();

+ 14 - 0
js/lib/CodeMirror/addon/lint/json-lint.js

@@ -0,0 +1,14 @@
+// Depends on jsonlint.js from https://github.com/zaach/jsonlint
+
+CodeMirror.jsonValidator = function(text) {
+  var found = [];
+  jsonlint.parseError = function(str, hash) {
+    var loc = hash.loc;
+    found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
+                to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
+                message: str});
+  };
+  try { jsonlint.parse(text); }
+  catch(e) {}
+  return found;
+};

+ 96 - 0
js/lib/CodeMirror/addon/lint/lint.css

@@ -0,0 +1,96 @@
+/* The lint marker gutter */
+.CodeMirror-lint-markers {
+  width: 16px;
+}
+
+.CodeMirror-lint-tooltip {
+  background-color: infobackground;
+  border: 1px solid black;
+  border-radius: 4px 4px 4px 4px;
+  color: infotext;
+  font-family: monospace;
+  font-size: 10pt;
+  overflow: hidden;
+  padding: 2px 5px;
+  position: fixed;
+  white-space: pre;
+  z-index: 100;
+  max-width: 600px;
+  opacity: 0;
+  transition: opacity .4s;
+  -moz-transition: opacity .4s;
+  -webkit-transition: opacity .4s;
+  -o-transition: opacity .4s;
+  -ms-transition: opacity .4s;
+}
+
+.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning {
+  background-position: left bottom;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-lint-span-error {
+  background-image:
+  url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
+  ;
+}
+
+.CodeMirror-lint-span-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
+  background-position: center center;
+  background-repeat: no-repeat;
+  cursor: pointer;
+  display: inline-block;
+  height: 16px;
+  width: 16px;
+  vertical-align: middle;
+  position: relative;
+}
+
+.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
+  padding-left: 18px;
+  background-position: top left;
+  background-repeat: no-repeat;
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
+  background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=");
+}
+
+.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
+  background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw==");
+}
+
+.CodeMirror-lint-marker-multiple {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
+  background-repeat: no-repeat;
+  background-position: right bottom;
+  width: 100%; height: 100%;
+}
+
+/* Styles for the overview ruler  
+.annotationOverview {
+  cursor: pointer;
+  border-radius: 2px;
+  left: 2px;
+  width: 8px;
+}
+.annotationOverview.error {
+  background-color: lightcoral;
+  border: 1px solid darkred;
+}
+.annotationOverview.warning {
+  background-color: Gold;
+  border: 1px solid black;
+}
+
+.annotationHTML.overlay {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
+  background-position: right bottom;
+  position: relative;
+  top: -16px;
+}
+*/

+ 183 - 0
js/lib/CodeMirror/addon/lint/lint.js

@@ -0,0 +1,183 @@
+CodeMirror.validate = (function() {
+  var GUTTER_ID = "CodeMirror-lint-markers";
+  var SEVERITIES = /^(?:error|warning)$/;
+
+  function showTooltip(e, content) {
+    var tt = document.createElement("div");
+    tt.className = "CodeMirror-lint-tooltip";
+    tt.appendChild(content.cloneNode(true));
+    document.body.appendChild(tt);
+
+    function position(e) {
+      if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
+      tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px";
+      tt.style.left = (e.clientX + 5) + "px";
+    }
+    CodeMirror.on(document, "mousemove", position);
+    position(e);
+    tt.style.opacity = 1;
+    return tt;
+  }
+  function rm(elt) {
+    if (elt.parentNode) elt.parentNode.removeChild(elt);
+  }
+  function hideTooltip(tt) {
+    if (!tt.parentNode) return;
+    if (tt.style.opacity == null) rm(tt);
+    tt.style.opacity = 0;
+    setTimeout(function() { rm(tt); }, 600);
+  }
+
+  function LintState(cm, options, hasGutter) {
+    this.marked = [];
+    this.options = options;
+    this.timeout = null;
+    this.hasGutter = hasGutter;
+    this.onMouseOver = function(e) { onMouseOver(cm, e); };
+  }
+
+  function parseOptions(options) {
+    if (options instanceof Function) return {getAnnotations: options};
+    else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)");
+    return options;
+  }
+
+  function clearMarks(cm) {
+    var state = cm._lintState;
+    if (state.hasGutter) cm.clearGutter(GUTTER_ID);
+    for (var i = 0; i < state.marked.length; ++i)
+      state.marked[i].clear();
+    state.marked.length = 0;
+  }
+
+  function makeMarker(labels, severity, multiple) {
+    var marker = document.createElement("div"), inner = marker;
+    marker.className = "CodeMirror-lint-marker-" + severity;
+    if (multiple) {
+      inner = marker.appendChild(document.createElement("div"));
+      inner.className = "CodeMirror-lint-marker-multiple";
+    }
+
+    var tooltip;
+    CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); });
+    CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); });
+
+    return marker;
+  }
+
+  function getMaxSeverity(a, b) {
+    if (a == "error") return a;
+    else return b;
+  }
+
+  function groupByLine(annotations) {
+    var lines = [];
+    for (var i = 0; i < annotations.length; ++i) {
+      var ann = annotations[i], line = ann.from.line;
+      (lines[line] || (lines[line] = [])).push(ann);
+    }
+    return lines;
+  }
+
+  function annotationTooltip(ann) {
+    var severity = ann.severity;
+    if (!SEVERITIES.test(severity)) severity = "error";
+    var tip = document.createElement("div");
+    tip.className = "CodeMirror-lint-message-" + severity;
+    tip.appendChild(document.createTextNode(ann.message));
+    return tip;
+  }
+
+  function startLinting(cm) {
+	  var state = cm._lintState, options = state.options;
+	  if (options.async)
+		  options.getAnnotations(cm, updateLinting, options);
+	  else
+		 updateLinting(cm, options.getAnnotations(cm.getValue()));
+  }
+  
+  function updateLinting(cm, annotationsNotSorted) {
+    clearMarks(cm);
+    var state = cm._lintState, options = state.options;
+
+    var annotations = groupByLine(annotationsNotSorted);
+
+    for (var line = 0; line < annotations.length; ++line) {
+      var anns = annotations[line];
+      if (!anns) continue;
+
+      var maxSeverity = null;
+      var tipLabel = state.hasGutter && document.createDocumentFragment();
+
+      for (var i = 0; i < anns.length; ++i) {
+        var ann = anns[i];
+        var severity = ann.severity;
+        if (!SEVERITIES.test(severity)) severity = "error";
+        maxSeverity = getMaxSeverity(maxSeverity, severity);
+
+	if (options.formatAnnotation) ann = options.formatAnnotation(ann);
+        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
+
+        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
+          className: "CodeMirror-lint-span-" + severity,
+          __annotation: ann
+        }));
+      }
+
+      if (state.hasGutter)
+        cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1));
+    }
+    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
+  }
+
+  function onChange(cm) {
+    var state = cm._lintState;
+    clearTimeout(state.timeout);
+    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
+  }
+
+  function popupSpanTooltip(ann, e) {
+    var tooltip = showTooltip(e, annotationTooltip(ann));
+    var target = e.target || e.srcElement;
+    CodeMirror.on(target, "mouseout", hide);
+    function hide() {
+      CodeMirror.off(target, "mouseout", hide);
+      hideTooltip(tooltip);
+    }
+  }
+
+  // When the mouseover fires, the cursor might not actually be over
+  // the character itself yet. These pairs of x,y offsets are used to
+  // probe a few nearby points when no suitable marked range is found.
+  var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
+
+  function onMouseOver(cm, e) {
+    if (!/\bCodeMirror-lint-span-/.test((e.target || e.srcElement).className)) return;
+    for (var i = 0; i < nearby.length; i += 2) {
+      var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
+                                                top: e.clientY + nearby[i + 1]}));
+      for (var j = 0; j < spans.length; ++j) {
+        var span = spans[j], ann = span.__annotation;
+        if (ann) return popupSpanTooltip(ann, e);
+      }
+    }
+  }
+
+  CodeMirror.defineOption("lintWith", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      clearMarks(cm);
+      cm.off("change", onChange);
+      CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver);
+      delete cm._lintState;
+    }
+    
+    if (val) {
+      var gutters = cm.getOption("gutters"), hasLintGutter = false;
+      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
+      var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter);
+      cm.on("change", onChange);
+      CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
+      startLinting(cm);
+    }
+  });
+})();

+ 51 - 0
js/lib/CodeMirror/addon/mode/loadmode.js

@@ -0,0 +1,51 @@
+(function() {
+  if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
+
+  var loading = {};
+  function splitCallback(cont, n) {
+    var countDown = n;
+    return function() { if (--countDown == 0) cont(); };
+  }
+  function ensureDeps(mode, cont) {
+    var deps = CodeMirror.modes[mode].dependencies;
+    if (!deps) return cont();
+    var missing = [];
+    for (var i = 0; i < deps.length; ++i) {
+      if (!CodeMirror.modes.hasOwnProperty(deps[i]))
+        missing.push(deps[i]);
+    }
+    if (!missing.length) return cont();
+    var split = splitCallback(cont, missing.length);
+    for (var i = 0; i < missing.length; ++i)
+      CodeMirror.requireMode(missing[i], split);
+  }
+
+  CodeMirror.requireMode = function(mode, cont) {
+    if (typeof mode != "string") mode = mode.name;
+    if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
+    if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
+
+    var script = document.createElement("script");
+    script.src = CodeMirror.modeURL.replace(/%N/g, mode);
+    var others = document.getElementsByTagName("script")[0];
+    others.parentNode.insertBefore(script, others);
+    var list = loading[mode] = [cont];
+    var count = 0, poll = setInterval(function() {
+      if (++count > 100) return clearInterval(poll);
+      if (CodeMirror.modes.hasOwnProperty(mode)) {
+        clearInterval(poll);
+        loading[mode] = null;
+        ensureDeps(mode, function() {
+          for (var i = 0; i < list.length; ++i) list[i]();
+        });
+      }
+    }, 200);
+  };
+
+  CodeMirror.autoLoadMode = function(instance, mode) {
+    if (!CodeMirror.modes.hasOwnProperty(mode))
+      CodeMirror.requireMode(mode, function() {
+        instance.setOption("mode", instance.getOption("mode"));
+      });
+  };
+}());

+ 95 - 0
js/lib/CodeMirror/addon/mode/multiplex.js

@@ -0,0 +1,95 @@
+CodeMirror.multiplexingMode = function(outer /*, others */) {
+  // Others should be {open, close, mode [, delimStyle]} objects
+  var others = Array.prototype.slice.call(arguments, 1);
+  var n_others = others.length;
+
+  function indexOf(string, pattern, from) {
+    if (typeof pattern == "string") return string.indexOf(pattern, from);
+    var m = pattern.exec(from ? string.slice(from) : string);
+    return m ? m.index + from : -1;
+  }
+
+  return {
+    startState: function() {
+      return {
+        outer: CodeMirror.startState(outer),
+        innerActive: null,
+        inner: null
+      };
+    },
+
+    copyState: function(state) {
+      return {
+        outer: CodeMirror.copyState(outer, state.outer),
+        innerActive: state.innerActive,
+        inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
+      };
+    },
+
+    token: function(stream, state) {
+      if (!state.innerActive) {
+        var cutOff = Infinity, oldContent = stream.string;
+        for (var i = 0; i < n_others; ++i) {
+          var other = others[i];
+          var found = indexOf(oldContent, other.open, stream.pos);
+          if (found == stream.pos) {
+            stream.match(other.open);
+            state.innerActive = other;
+            state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
+            return other.delimStyle;
+          } else if (found != -1 && found < cutOff) {
+            cutOff = found;
+          }
+        }
+        if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
+        var outerToken = outer.token(stream, state.outer);
+        if (cutOff != Infinity) stream.string = oldContent;
+        return outerToken;
+      } else {
+        var curInner = state.innerActive, oldContent = stream.string;
+        var found = indexOf(oldContent, curInner.close, stream.pos);
+        if (found == stream.pos) {
+          stream.match(curInner.close);
+          state.innerActive = state.inner = null;
+          return curInner.delimStyle;
+        }
+        if (found > -1) stream.string = oldContent.slice(0, found);
+        var innerToken = curInner.mode.token(stream, state.inner);
+        if (found > -1) stream.string = oldContent;
+        var cur = stream.current(), found = cur.indexOf(curInner.close);
+        if (found > -1) stream.backUp(cur.length - found);
+        return innerToken;
+      }
+    },
+    
+    indent: function(state, textAfter) {
+      var mode = state.innerActive ? state.innerActive.mode : outer;
+      if (!mode.indent) return CodeMirror.Pass;
+      return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
+    },
+
+    blankLine: function(state) {
+      var mode = state.innerActive ? state.innerActive.mode : outer;
+      if (mode.blankLine) {
+        mode.blankLine(state.innerActive ? state.inner : state.outer);
+      }
+      if (!state.innerActive) {
+        for (var i = 0; i < n_others; ++i) {
+          var other = others[i];
+          if (other.open === "\n") {
+            state.innerActive = other;
+            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
+          }
+        }
+      } else if (mode.close === "\n") {
+        state.innerActive = state.inner = null;
+      }
+    },
+
+    electricChars: outer.electricChars,
+
+    innerMode: function(state) {
+      return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
+    }
+  };
+};

+ 59 - 0
js/lib/CodeMirror/addon/mode/overlay.js

@@ -0,0 +1,59 @@
+// Utility function that allows modes to be combined. The mode given
+// as the base argument takes care of most of the normal mode
+// functionality, but a second (typically simple) mode is used, which
+// can override the style of text. Both modes get to parse all of the
+// text, but when both assign a non-null style to a piece of code, the
+// overlay wins, unless the combine argument was true, in which case
+// the styles are combined.
+
+// overlayParser is the old, deprecated name
+CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) {
+  return {
+    startState: function() {
+      return {
+        base: CodeMirror.startState(base),
+        overlay: CodeMirror.startState(overlay),
+        basePos: 0, baseCur: null,
+        overlayPos: 0, overlayCur: null
+      };
+    },
+    copyState: function(state) {
+      return {
+        base: CodeMirror.copyState(base, state.base),
+        overlay: CodeMirror.copyState(overlay, state.overlay),
+        basePos: state.basePos, baseCur: null,
+        overlayPos: state.overlayPos, overlayCur: null
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.start == state.basePos) {
+        state.baseCur = base.token(stream, state.base);
+        state.basePos = stream.pos;
+      }
+      if (stream.start == state.overlayPos) {
+        stream.pos = stream.start;
+        state.overlayCur = overlay.token(stream, state.overlay);
+        state.overlayPos = stream.pos;
+      }
+      stream.pos = Math.min(state.basePos, state.overlayPos);
+      if (stream.eol()) state.basePos = state.overlayPos = 0;
+
+      if (state.overlayCur == null) return state.baseCur;
+      if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
+      else return state.overlayCur;
+    },
+    
+    indent: base.indent && function(state, textAfter) {
+      return base.indent(state.base, textAfter);
+    },
+    electricChars: base.electricChars,
+
+    innerMode: function(state) { return {state: state.base, mode: base}; },
+    
+    blankLine: function(state) {
+      if (base.blankLine) base.blankLine(state.base);
+      if (overlay.blankLine) overlay.blankLine(state.overlay);
+    }
+  };
+};

+ 29 - 0
js/lib/CodeMirror/addon/runmode/colorize.js

@@ -0,0 +1,29 @@
+CodeMirror.colorize = (function() {
+
+  var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
+
+  function textContent(node, out) {
+    if (node.nodeType == 3) return out.push(node.nodeValue);
+    for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
+      textContent(ch, out);
+      if (isBlock.test(node.nodeType)) out.push("\n");
+    }
+  }
+
+  return function(collection, defaultMode) {
+    if (!collection) collection = document.body.getElementsByTagName("pre");
+
+    for (var i = 0; i < collection.length; ++i) {
+      var node = collection[i];
+      var mode = node.getAttribute("data-lang") || defaultMode;
+      if (!mode) continue;
+
+      var text = [];
+      textContent(node, text);
+      node.innerHTML = "";
+      CodeMirror.runMode(text.join(""), mode, node);
+
+      node.className += " cm-s-default";
+    }
+  };
+})();

+ 130 - 0
js/lib/CodeMirror/addon/runmode/runmode-standalone.js

@@ -0,0 +1,130 @@
+/* Just enough of CodeMirror to run runMode under node.js */
+
+window.CodeMirror = {};
+
+function splitLines(string){ return string.split(/\r?\n|\r/); };
+
+function StringStream(string) {
+  this.pos = this.start = 0;
+  this.string = string;
+}
+StringStream.prototype = {
+  eol: function() {return this.pos >= this.string.length;},
+  sol: function() {return this.pos == 0;},
+  peek: function() {return this.string.charAt(this.pos) || null;},
+  next: function() {
+    if (this.pos < this.string.length)
+      return this.string.charAt(this.pos++);
+  },
+  eat: function(match) {
+    var ch = this.string.charAt(this.pos);
+    if (typeof match == "string") var ok = ch == match;
+    else var ok = ch && (match.test ? match.test(ch) : match(ch));
+    if (ok) {++this.pos; return ch;}
+  },
+  eatWhile: function(match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start;
+  },
+  eatSpace: function() {
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+    return this.pos > start;
+  },
+  skipToEnd: function() {this.pos = this.string.length;},
+  skipTo: function(ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true;}
+  },
+  backUp: function(n) {this.pos -= n;},
+  column: function() {return this.start;},
+  indentation: function() {return 0;},
+  match: function(pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+        if (consume !== false) this.pos += pattern.length;
+        return true;
+      }
+    } else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && consume !== false) this.pos += match[0].length;
+      return match;
+    }
+  },
+  current: function(){return this.string.slice(this.start, this.pos);}
+};
+CodeMirror.StringStream = StringStream;
+
+CodeMirror.startState = function (mode, a1, a2) {
+  return mode.startState ? mode.startState(a1, a2) : true;
+};
+
+var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
+CodeMirror.defineMode = function (name, mode) { modes[name] = mode; };
+CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; };
+CodeMirror.getMode = function (options, spec) {
+  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+    spec = mimeModes[spec];
+  if (typeof spec == "string")
+    var mname = spec, config = {};
+  else if (spec != null)
+    var mname = spec.name, config = spec;
+  var mfactory = modes[mname];
+  if (!mfactory) throw new Error("Unknown mode: " + spec);
+  return mfactory(options, config || {});
+};
+
+CodeMirror.runMode = function (string, modespec, callback, options) {
+  var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec);
+
+  if (callback.nodeType == 1) {
+    var tabSize = (options && options.tabSize) || 4;
+    var node = callback, col = 0;
+    node.innerHTML = "";
+    callback = function (text, style) {
+      if (text == "\n") {
+        node.appendChild(document.createElement("br"));
+        col = 0;
+        return;
+      }
+      var content = "";
+      // replace tabs
+      for (var pos = 0; ;) {
+        var idx = text.indexOf("\t", pos);
+        if (idx == -1) {
+          content += text.slice(pos);
+          col += text.length - pos;
+          break;
+        } else {
+          col += idx - pos;
+          content += text.slice(pos, idx);
+          var size = tabSize - col % tabSize;
+          col += size;
+          for (var i = 0; i < size; ++i) content += " ";
+          pos = idx + 1;
+        }
+      }
+
+      if (style) {
+        var sp = node.appendChild(document.createElement("span"));
+        sp.className = "cm-" + style.replace(/ +/g, " cm-");
+        sp.appendChild(document.createTextNode(content));
+      } else {
+        node.appendChild(document.createTextNode(content));
+      }
+    };
+  }
+
+  var lines = splitLines(string), state = CodeMirror.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new CodeMirror.StringStream(lines[i]);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start);
+      stream.start = stream.pos;
+    }
+  }
+};

+ 52 - 0
js/lib/CodeMirror/addon/runmode/runmode.js

@@ -0,0 +1,52 @@
+CodeMirror.runMode = function(string, modespec, callback, options) {
+  var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+
+  if (callback.nodeType == 1) {
+    var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
+    var node = callback, col = 0;
+    node.innerHTML = "";
+    callback = function(text, style) {
+      if (text == "\n") {
+        node.appendChild(document.createElement("br"));
+        col = 0;
+        return;
+      }
+      var content = "";
+      // replace tabs
+      for (var pos = 0;;) {
+        var idx = text.indexOf("\t", pos);
+        if (idx == -1) {
+          content += text.slice(pos);
+          col += text.length - pos;
+          break;
+        } else {
+          col += idx - pos;
+          content += text.slice(pos, idx);
+          var size = tabSize - col % tabSize;
+          col += size;
+          for (var i = 0; i < size; ++i) content += " ";
+          pos = idx + 1;
+        }
+      }
+
+      if (style) {
+        var sp = node.appendChild(document.createElement("span"));
+        sp.className = "cm-" + style.replace(/ +/g, " cm-");
+        sp.appendChild(document.createTextNode(content));
+      } else {
+        node.appendChild(document.createTextNode(content));
+      }
+    };
+  }
+
+  var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new CodeMirror.StringStream(lines[i]);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start);
+      stream.start = stream.pos;
+    }
+  }
+};

+ 89 - 0
js/lib/CodeMirror/addon/runmode/runmode.node.js

@@ -0,0 +1,89 @@
+/* Just enough of CodeMirror to run runMode under node.js */
+
+function splitLines(string){ return string.split(/\r?\n|\r/); };
+
+function StringStream(string) {
+  this.pos = this.start = 0;
+  this.string = string;
+}
+StringStream.prototype = {
+  eol: function() {return this.pos >= this.string.length;},
+  sol: function() {return this.pos == 0;},
+  peek: function() {return this.string.charAt(this.pos) || null;},
+  next: function() {
+    if (this.pos < this.string.length)
+      return this.string.charAt(this.pos++);
+  },
+  eat: function(match) {
+    var ch = this.string.charAt(this.pos);
+    if (typeof match == "string") var ok = ch == match;
+    else var ok = ch && (match.test ? match.test(ch) : match(ch));
+    if (ok) {++this.pos; return ch;}
+  },
+  eatWhile: function(match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start;
+  },
+  eatSpace: function() {
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+    return this.pos > start;
+  },
+  skipToEnd: function() {this.pos = this.string.length;},
+  skipTo: function(ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true;}
+  },
+  backUp: function(n) {this.pos -= n;},
+  column: function() {return this.start;},
+  indentation: function() {return 0;},
+  match: function(pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+        if (consume !== false) this.pos += pattern.length;
+        return true;
+      }
+    } else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && consume !== false) this.pos += match[0].length;
+      return match;
+    }
+  },
+  current: function(){return this.string.slice(this.start, this.pos);}
+};
+exports.StringStream = StringStream;
+
+exports.startState = function(mode, a1, a2) {
+  return mode.startState ? mode.startState(a1, a2) : true;
+};
+
+var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
+exports.defineMode = function(name, mode) { modes[name] = mode; };
+exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
+exports.getMode = function(options, spec) {
+  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+    spec = mimeModes[spec];
+  if (typeof spec == "string")
+    var mname = spec, config = {};
+  else if (spec != null)
+    var mname = spec.name, config = spec;
+  var mfactory = modes[mname];
+  if (!mfactory) throw new Error("Unknown mode: " + spec);
+  return mfactory(options, config || {});
+};
+
+exports.runMode = function(string, modespec, callback) {
+  var mode = exports.getMode({indentUnit: 2}, modespec);
+  var lines = splitLines(string), state = exports.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new exports.StringStream(lines[i]);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start);
+      stream.start = stream.pos;
+    }
+  }
+};

+ 60 - 0
js/lib/CodeMirror/addon/search/match-highlighter.js

@@ -0,0 +1,60 @@
+// Highlighting text that matches the selection
+//
+// Defines an option highlightSelectionMatches, which, when enabled,
+// will style strings that match the selection throughout the
+// document.
+//
+// The option can be set to true to simply enable it, or to a
+// {minChars, style} object to explicitly configure it. minChars is
+// the minimum amount of characters that should be selected for the
+// behavior to occur, and style is the token style to apply to the
+// matches. This will be prefixed by "cm-" to create an actual CSS
+// class name.
+
+(function() {
+  var DEFAULT_MIN_CHARS = 2;
+  var DEFAULT_TOKEN_STYLE = "matchhighlight";
+  
+  function State(options) {
+    this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS;
+    this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE;
+    this.overlay = null;
+  }
+
+  CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      cm._matchHighlightState = new State(val);
+      cm.on("cursorActivity", highlightMatches);
+    } else if (!val && prev) {
+      var over = cm._matchHighlightState.overlay;
+      if (over) cm.removeOverlay(over);
+      cm._matchHighlightState = null;
+      cm.off("cursorActivity", highlightMatches);
+    }
+  });
+
+  function highlightMatches(cm) {
+    cm.operation(function() {
+      var state = cm._matchHighlightState;
+      if (state.overlay) {
+        cm.removeOverlay(state.overlay);
+        state.overlay = null;
+      }
+
+      if (!cm.somethingSelected()) return;
+      var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
+      if (selection.length < state.minChars) return;
+
+      cm.addOverlay(state.overlay = makeOverlay(selection, state.style));
+    });
+  }
+
+  function makeOverlay(query, style) {
+    return {token: function(stream) {
+      if (stream.match(query)) return style;
+      stream.next();
+      stream.skipTo(query.charAt(0)) || stream.skipToEnd();
+    }};
+  }
+})();

+ 131 - 0
js/lib/CodeMirror/addon/search/search.js

@@ -0,0 +1,131 @@
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+(function() {
+  function searchOverlay(query) {
+    if (typeof query == "string") return {token: function(stream) {
+      if (stream.match(query)) return "searching";
+      stream.next();
+      stream.skipTo(query.charAt(0)) || stream.skipToEnd();
+    }};
+    return {token: function(stream) {
+      if (stream.match(query)) return "searching";
+      while (!stream.eol()) {
+        stream.next();
+        if (stream.match(query, false)) break;
+      }
+    }};
+  }
+
+  function SearchState() {
+    this.posFrom = this.posTo = this.query = null;
+    this.overlay = null;
+  }
+  function getSearchState(cm) {
+    return cm._searchState || (cm._searchState = new SearchState());
+  }
+  function getSearchCursor(cm, query, pos) {
+    // Heuristic: if the query string is all lowercase, do a case insensitive search.
+    return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
+  }
+  function dialog(cm, text, shortText, f) {
+    if (cm.openDialog) cm.openDialog(text, f);
+    else f(prompt(shortText, ""));
+  }
+  function confirmDialog(cm, text, shortText, fs) {
+    if (cm.openConfirm) cm.openConfirm(text, fs);
+    else if (confirm(shortText)) fs[0]();
+  }
+  function parseQuery(query) {
+    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
+    return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
+  }
+  var queryDialog =
+    'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
+  function doSearch(cm, rev) {
+    var state = getSearchState(cm);
+    if (state.query) return findNext(cm, rev);
+    dialog(cm, queryDialog, "Search for:", function(query) {
+      cm.operation(function() {
+        if (!query || state.query) return;
+        state.query = parseQuery(query);
+        cm.removeOverlay(state.overlay);
+        state.overlay = searchOverlay(query);
+        cm.addOverlay(state.overlay);
+        state.posFrom = state.posTo = cm.getCursor();
+        findNext(cm, rev);
+      });
+    });
+  }
+  function findNext(cm, rev) {cm.operation(function() {
+    var state = getSearchState(cm);
+    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+    if (!cursor.find(rev)) {
+      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
+      if (!cursor.find(rev)) return;
+    }
+    cm.setSelection(cursor.from(), cursor.to());
+    state.posFrom = cursor.from(); state.posTo = cursor.to();
+  });}
+  function clearSearch(cm) {cm.operation(function() {
+    var state = getSearchState(cm);
+    if (!state.query) return;
+    state.query = null;
+    cm.removeOverlay(state.overlay);
+  });}
+
+  var replaceQueryDialog =
+    'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
+  var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
+  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
+  function replace(cm, all) {
+    dialog(cm, replaceQueryDialog, "Replace:", function(query) {
+      if (!query) return;
+      query = parseQuery(query);
+      dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
+        if (all) {
+          cm.operation(function() {
+            for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+              if (typeof query != "string") {
+                var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+                cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
+              } else cursor.replace(text);
+            }
+          });
+        } else {
+          clearSearch(cm);
+          var cursor = getSearchCursor(cm, query, cm.getCursor());
+          var advance = function() {
+            var start = cursor.from(), match;
+            if (!(match = cursor.findNext())) {
+              cursor = getSearchCursor(cm, query);
+              if (!(match = cursor.findNext()) ||
+                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+            }
+            cm.setSelection(cursor.from(), cursor.to());
+            confirmDialog(cm, doReplaceConfirm, "Replace?",
+                          [function() {doReplace(match);}, advance]);
+          };
+          var doReplace = function(match) {
+            cursor.replace(typeof query == "string" ? text :
+                           text.replace(/\$(\d)/, function(_, i) {return match[i];}));
+            advance();
+          };
+          advance();
+        }
+      });
+    });
+  }
+
+  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+  CodeMirror.commands.findNext = doSearch;
+  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
+  CodeMirror.commands.clearSearch = clearSearch;
+  CodeMirror.commands.replace = replace;
+  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
+})();

+ 130 - 0
js/lib/CodeMirror/addon/search/searchcursor.js

@@ -0,0 +1,130 @@
+(function(){
+  var Pos = CodeMirror.Pos;
+
+  function SearchCursor(cm, query, pos, caseFold) {
+    this.atOccurrence = false; this.cm = cm;
+    if (caseFold == null && typeof query == "string") caseFold = false;
+
+    pos = pos ? cm.clipPos(pos) : Pos(0, 0);
+    this.pos = {from: pos, to: pos};
+
+    // The matches method is filled in based on the type of query.
+    // It takes a position and a direction, and returns an object
+    // describing the next occurrence of the query, or null if no
+    // more matches were found.
+    if (typeof query != "string") { // Regexp match
+      if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
+      this.matches = function(reverse, pos) {
+        if (reverse) {
+          query.lastIndex = 0;
+          var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
+          for (;;) {
+            query.lastIndex = cutOff;
+            var newMatch = query.exec(line);
+            if (!newMatch) break;
+            match = newMatch;
+            start = match.index;
+            cutOff = match.index + 1;
+          }
+        } else {
+          query.lastIndex = pos.ch;
+          var line = cm.getLine(pos.line), match = query.exec(line),
+          start = match && match.index;
+        }
+        if (match && match[0])
+          return {from: Pos(pos.line, start),
+                  to: Pos(pos.line, start + match[0].length),
+                  match: match};
+      };
+    } else { // String query
+      if (caseFold) query = query.toLowerCase();
+      var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
+      var target = query.split("\n");
+      // Different methods for single-line and multi-line queries
+      if (target.length == 1) {
+        if (!query.length) {
+          // Empty string would match anything and never progress, so
+          // we define it to match nothing instead.
+          this.matches = function() {};
+        } else {
+          this.matches = function(reverse, pos) {
+            var line = fold(cm.getLine(pos.line)), len = query.length, match;
+            if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
+                        : (match = line.indexOf(query, pos.ch)) != -1)
+              return {from: Pos(pos.line, match),
+                      to: Pos(pos.line, match + len)};
+          };
+        }
+      } else {
+        this.matches = function(reverse, pos) {
+          var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
+          var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
+          if (reverse ? offsetA >= pos.ch || offsetA != match.length
+              : offsetA <= pos.ch || offsetA != line.length - match.length)
+            return;
+          for (;;) {
+            if (reverse ? !ln : ln == cm.lineCount() - 1) return;
+            line = fold(cm.getLine(ln += reverse ? -1 : 1));
+            match = target[reverse ? --idx : ++idx];
+            if (idx > 0 && idx < target.length - 1) {
+              if (line != match) return;
+              else continue;
+            }
+            var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
+            if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
+              return;
+            var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
+            return {from: reverse ? end : start, to: reverse ? start : end};
+          }
+        };
+      }
+    }
+  }
+
+  SearchCursor.prototype = {
+    findNext: function() {return this.find(false);},
+    findPrevious: function() {return this.find(true);},
+
+    find: function(reverse) {
+      var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
+      function savePosAndFail(line) {
+        var pos = Pos(line, 0);
+        self.pos = {from: pos, to: pos};
+        self.atOccurrence = false;
+        return false;
+      }
+
+      for (;;) {
+        if (this.pos = this.matches(reverse, pos)) {
+          if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
+          this.atOccurrence = true;
+          return this.pos.match || true;
+        }
+        if (reverse) {
+          if (!pos.line) return savePosAndFail(0);
+          pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
+        }
+        else {
+          var maxLine = this.cm.lineCount();
+          if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
+          pos = Pos(pos.line + 1, 0);
+        }
+      }
+    },
+
+    from: function() {if (this.atOccurrence) return this.pos.from;},
+    to: function() {if (this.atOccurrence) return this.pos.to;},
+
+    replace: function(newText) {
+      if (!this.atOccurrence) return;
+      var lines = CodeMirror.splitLines(newText);
+      this.cm.replaceRange(lines, this.pos.from, this.pos.to);
+      this.pos.to = Pos(this.pos.from.line + lines.length - 1,
+                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
+    }
+  };
+
+  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+    return new SearchCursor(this, query, pos, caseFold);
+  });
+})();

+ 39 - 0
js/lib/CodeMirror/addon/selection/active-line.js

@@ -0,0 +1,39 @@
+// Because sometimes you need to style the cursor's line.
+//
+// Adds an option 'styleActiveLine' which, when enabled, gives the
+// active line's wrapping <div> the CSS class "CodeMirror-activeline",
+// and gives its background <div> the class "CodeMirror-activeline-background".
+
+(function() {
+  "use strict";
+  var WRAP_CLASS = "CodeMirror-activeline";
+  var BACK_CLASS = "CodeMirror-activeline-background";
+
+  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      updateActiveLine(cm);
+      cm.on("cursorActivity", updateActiveLine);
+    } else if (!val && prev) {
+      cm.off("cursorActivity", updateActiveLine);
+      clearActiveLine(cm);
+      delete cm._activeLine;
+    }
+  });
+  
+  function clearActiveLine(cm) {
+    if ("_activeLine" in cm) {
+      cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS);
+      cm.removeLineClass(cm._activeLine, "background", BACK_CLASS);
+    }
+  }
+
+  function updateActiveLine(cm) {
+    var line = cm.getLineHandle(cm.getCursor().line);
+    if (cm._activeLine == line) return;
+    clearActiveLine(cm);
+    cm.addLineClass(line, "wrap", WRAP_CLASS);
+    cm.addLineClass(line, "background", BACK_CLASS);
+    cm._activeLine = line;
+  }
+})();

+ 34 - 0
js/lib/CodeMirror/addon/selection/mark-selection.js

@@ -0,0 +1,34 @@
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class "CodeMirror-selectedtext".
+
+(function() {
+  "use strict";
+
+  CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      updateSelectedText(cm);
+      cm.on("cursorActivity", updateSelectedText);
+    } else if (!val && prev) {
+      cm.off("cursorActivity", updateSelectedText);
+      clearSelectedText(cm);
+      delete cm._selectionMark;
+    }
+  });
+
+  function clearSelectedText(cm) {
+    if (cm._selectionMark) cm._selectionMark.clear();
+  }
+
+  function updateSelectedText(cm) {
+    clearSelectedText(cm);
+
+    if (cm.somethingSelected())
+      cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"),
+                                      {className: "CodeMirror-selectedtext"});
+    else
+      cm._selectionMark = null;
+  }
+})();

+ 10 - 9
js/lib/CodeMirror/codemirror.css

@@ -41,6 +41,7 @@
 
 .CodeMirror div.CodeMirror-cursor {
   border-left: 1px solid black;
+  z-index: 3;
 }
 /* Shown when moving in bi-directional text */
 .CodeMirror div.CodeMirror-secondarycursor {
@@ -49,17 +50,14 @@
 .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
   width: auto;
   border: 0;
-  background: transparent;
-  background: rgba(0, 200, 0, .4);
-  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
-}
-/* Kludge to turn off filter in ie9+, which also accepts rgba */
-.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor:not(#nonsense_id) {
-  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  background: #7e7;
+  z-index: 1;
 }
 /* Can style cursor different in overwrite (non-insert) mode */
 .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
 
+.cm-tab { display: inline-block; }
+
 /* DEFAULT THEME */
 
 .cm-s-default .cm-keyword {color: #708;}
@@ -90,7 +88,6 @@
 .cm-positive {color: #292;}
 .cm-header, .cm-strong {font-weight: bold;}
 .cm-em {font-style: italic;}
-.cm-emstrong {font-style: italic; font-weight: bold;}
 .cm-link {text-decoration: underline;}
 
 .cm-invalidchar {color: #f00;}
@@ -107,6 +104,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   line-height: 1;
   position: relative;
   overflow: hidden;
+  background: white;
+  color: black;
 }
 
 .CodeMirror-scroll {
@@ -153,6 +152,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 }
 .CodeMirror-gutter {
   height: 100%;
+  padding-bottom: 30px;
+  margin-bottom: -32px;
   display: inline-block;
   /* Hack to make IE7 behave */
   *zoom:1;
@@ -169,7 +170,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 }
 .CodeMirror pre {
   /* Reset some styles that the rest of the page might have set */
-  -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
   border-width: 0;
   background: transparent;
   font-family: inherit;

+ 264 - 132
js/lib/CodeMirror/codemirror.js

@@ -1,4 +1,4 @@
-// CodeMirror version 3.1
+// CodeMirror version 3.11
 //
 // CodeMirror is the only global var we claim
 window.CodeMirror = (function() {
@@ -41,7 +41,7 @@ window.CodeMirror = (function() {
 
   function CodeMirror(place, options) {
     if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
-    
+
     this.options = options = options || {};
     // Determine effective options based on given values and defaults.
     for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
@@ -93,10 +93,14 @@ window.CodeMirror = (function() {
 
   function makeDisplay(place, docStart) {
     var d = {};
+
     var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
     if (webkit) input.style.width = "1000px";
     else input.setAttribute("wrap", "off");
+    // if border: 0; -- iOS fails to open keyboard (issue #1287)
+    if (ios) input.style.border = "1px solid black";
     input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
+
     // Wraps and hides input textarea
     d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
     // The actual fake scrollbars.
@@ -120,7 +124,7 @@ window.CodeMirror = (function() {
     // Set to the height of the text, causes scrolling
     d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
     // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
-    d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
+    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
     // Will contain the gutters, if any
     d.gutters = elt("div", null, "CodeMirror-gutters");
     d.lineGutter = null;
@@ -181,7 +185,7 @@ window.CodeMirror = (function() {
 
     // Used for measuring wheel scrolling granularity
     d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-    
+
     return d;
   }
 
@@ -318,7 +322,7 @@ window.CodeMirror = (function() {
   // Re-synchronize the fake scrollbars with the actual size of the
   // content. Optionally force a scrollTop.
   function updateScrollbars(d /* display */, docHeight) {
-    var totalHeight = docHeight + 2 * paddingTop(d);
+    var totalHeight = docHeight + paddingVert(d);
     d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
     var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
     var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
@@ -326,7 +330,7 @@ window.CodeMirror = (function() {
     if (needsV) {
       d.scrollbarV.style.display = "block";
       d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
-      d.scrollbarV.firstChild.style.height = 
+      d.scrollbarV.firstChild.style.height =
         (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
     } else d.scrollbarV.style.display = "";
     if (needsH) {
@@ -636,7 +640,7 @@ window.CodeMirror = (function() {
     // Lines with gutter elements, widgets or a background class need
     // to be wrapped again, and have the extra elements added to the
     // wrapper div
-    
+
     if (reuse) {
       reuse.alignable = null;
       var isOk = true, widgetsSeen = 0;
@@ -669,7 +673,7 @@ window.CodeMirror = (function() {
     }
     // Kludge to make sure the styled element lies behind the selection (by z-index)
     if (line.bgClass)
-      wrap.insertBefore(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
+      wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
     if (cm.options.lineNumbers || markers) {
       var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
                                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
@@ -929,8 +933,9 @@ window.CodeMirror = (function() {
   }
 
   // POSITION MEASUREMENT
-  
+
   function paddingTop(display) {return display.lineSpace.offsetTop;}
+  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
   function paddingLeft(display) {
     var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
     return e.offsetLeft;
@@ -939,7 +944,7 @@ window.CodeMirror = (function() {
   function measureChar(cm, line, ch, data) {
     var dir = -1;
     data = data || measureLine(cm, line);
-    
+
     for (var pos = ch;; pos += dir) {
       var r = data[pos];
       if (r) break;
@@ -950,24 +955,30 @@ window.CodeMirror = (function() {
             top: r.top, bottom: r.bottom};
   }
 
-  function measureLine(cm, line) {
-    // First look in the cache
-    var display = cm.display, cache = cm.display.measureLineCache;
+  function findCachedMeasurement(cm, line) {
+    var cache = cm.display.measureLineCache;
     for (var i = 0; i < cache.length; ++i) {
       var memo = cache[i];
       if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
-          display.scroller.clientWidth == memo.width &&
+          cm.display.scroller.clientWidth == memo.width &&
           memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
         return memo.measure;
     }
-    
-    var measure = measureLineInner(cm, line);
-    // Store result in the cache
-    var memo = {text: line.text, width: display.scroller.clientWidth,
-                markedSpans: line.markedSpans, measure: measure,
-                classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
-    if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
-    else cache.push(memo);
+  }
+
+  function measureLine(cm, line) {
+    // First look in the cache
+    var measure = findCachedMeasurement(cm, line);
+    if (!measure) {
+      // Failing that, recompute and store result in cache
+      measure = measureLineInner(cm, line);
+      var cache = cm.display.measureLineCache;
+      var memo = {text: line.text, width: cm.display.scroller.clientWidth,
+                  markedSpans: line.markedSpans, measure: measure,
+                  classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
+      if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
+      else cache.push(memo);
+    }
     return measure;
   }
 
@@ -1032,15 +1043,25 @@ window.CodeMirror = (function() {
       var vr = cur.top;
       cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
     }
-    if (!cm.options.lineWrapping) {
-      var last = pre.lastChild;
-      if (last.nodeType == 3) last = pre.appendChild(elt("span", "\u200b"));
-      data.width = getRect(last).right - outer.left;
-    }
 
     return data;
   }
 
+  function measureLineWidth(cm, line) {
+    var hasBadSpan = false;
+    if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
+      var sp = line.markedSpans[i];
+      if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
+    }
+    var cached = !hasBadSpan && findCachedMeasurement(cm, line);
+    if (cached) return measureChar(cm, line, line.text.length, cached).right;
+
+    var pre = lineContent(cm, line);
+    var end = pre.appendChild(zeroWidthElement(cm.display.measure));
+    removeChildrenAndAdd(cm.display.measure, pre);
+    return getRect(end).right - getRect(cm.display.lineDiv).left;
+  }
+
   function clearCaches(cm) {
     cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
     cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
@@ -1068,6 +1089,26 @@ window.CodeMirror = (function() {
     return rect;
   }
 
+  // Context may be "window", "page", "div", or "local"/null
+  // Result is in local coords
+  function fromCoordSystem(cm, coords, context) {
+    if (context == "div") return coords;
+    var left = coords.left, top = coords.top;
+    if (context == "page") {
+      left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
+      top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
+    }
+    var lineSpaceBox = getRect(cm.display.lineSpace);
+    left -= lineSpaceBox.left;
+    top -= lineSpaceBox.top;
+    if (context == "local" || !context) {
+      var editorBox = getRect(cm.display.wrapper);
+      left -= editorBox.left;
+      top -= editorBox.top;
+    }
+    return {left: left, top: top};
+  }
+
   function charCoords(cm, pos, context, lineObj) {
     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
     return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
@@ -1139,15 +1180,15 @@ window.CodeMirror = (function() {
 
   function coordsCharInner(cm, lineObj, lineNo, x, y) {
     var innerOff = y - heightAtLine(cm, lineObj);
-    var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
+    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
     var measurement = measureLine(cm, lineObj);
 
     function getX(ch) {
       var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
                             lineObj, measurement);
       wrongLine = true;
-      if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
-      else if (innerOff < sp.top) return sp.left + cWidth;
+      if (innerOff > sp.bottom) return sp.left - adjust;
+      else if (innerOff < sp.top) return sp.left + adjust;
       else wrongLine = false;
       return sp.left;
     }
@@ -1237,7 +1278,7 @@ window.CodeMirror = (function() {
 
     if (op.updateMaxLine) computeMaxLength(cm);
     if (display.maxLineChanged && !cm.options.lineWrapping) {
-      var width = measureLine(cm, display.maxLine).width;
+      var width = measureLineWidth(cm, display.maxLine);
       display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
       display.maxLineChanged = false;
       var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
@@ -1251,8 +1292,10 @@ window.CodeMirror = (function() {
       var coords = cursorCoords(cm, doc.sel.head);
       newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
     }
-    if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
+    if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
       updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
+      if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
+    }
     if (!updated && op.selectionChanged) updateSelection(cm);
     if (op.updateScrollPos) {
       display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
@@ -1367,9 +1410,9 @@ window.CodeMirror = (function() {
     var updateInput = cm.curOp.updateInput;
     makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
                         origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
-               
+
     cm.curOp.updateInput = updateInput;
-    if (text.length > 1000) input.value = cm.display.prevInput = "";
+    if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
     else cm.display.prevInput = text;
     if (withOp) endOperation(cm);
     cm.state.pasteIncoming = false;
@@ -1413,15 +1456,17 @@ window.CodeMirror = (function() {
     if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
 
     on(d.scroller, "scroll", function() {
-      setScrollTop(cm, d.scroller.scrollTop);
-      setScrollLeft(cm, d.scroller.scrollLeft, true);
-      signal(cm, "scroll", cm);
+      if (d.scroller.clientHeight) {
+        setScrollTop(cm, d.scroller.scrollTop);
+        setScrollLeft(cm, d.scroller.scrollLeft, true);
+        signal(cm, "scroll", cm);
+      }
     });
     on(d.scrollbarV, "scroll", function() {
-      setScrollTop(cm, d.scrollbarV.scrollTop);
+      if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
     });
     on(d.scrollbarH, "scroll", function() {
-      setScrollLeft(cm, d.scrollbarH.scrollLeft);
+      if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
     });
 
     on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
@@ -1447,7 +1492,7 @@ window.CodeMirror = (function() {
       for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
       if (p) setTimeout(unregister, 5000);
       else off(window, "resize", onResize);
-    }    
+    }
     setTimeout(unregister, 5000);
 
     on(d.input, "keyup", operation(cm, function(e) {
@@ -1472,7 +1517,7 @@ window.CodeMirror = (function() {
     }
     on(d.scroller, "paste", function(e){
       if (eventInWidget(d, e)) return;
-      focusInput(cm); 
+      focusInput(cm);
       fastPoll(cm);
     });
     on(d.input, "paste", function() {
@@ -1674,7 +1719,7 @@ window.CodeMirror = (function() {
           text[i] = reader.result;
           if (++read == n) {
             pos = clipPos(cm.doc, pos);
-            replaceRange(cm.doc, text.join(""), pos, "around", "paste");
+            makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
           }
         };
         reader.readAsText(file);
@@ -1730,13 +1775,13 @@ window.CodeMirror = (function() {
 
   function onDragStart(cm, e) {
     if (eventInWidget(cm.display, e)) return;
-    
+
     var txt = cm.getSelection();
     e.dataTransfer.setData("Text", txt);
 
     // Use dummy image instead of default browsers image.
     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
+    if (e.dataTransfer.setDragImage) {
       var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
       if (opera) {
         img.width = img.height = 1;
@@ -1744,6 +1789,15 @@ window.CodeMirror = (function() {
         // Force a relayout, or Opera won't use our image for some obscure reason
         img._top = img.offsetTop;
       }
+      if (safari) {
+        if (cm.display.dragImg) {
+          img = cm.display.dragImg;
+        } else {
+          cm.display.dragImg = img;
+          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+          cm.display.wrapper.appendChild(img);
+        }
+      }
       e.dataTransfer.setDragImage(img, 0, 0);
       if (opera) img.parentNode.removeChild(img);
     }
@@ -1873,8 +1927,8 @@ window.CodeMirror = (function() {
 
   function allKeyMaps(cm) {
     var maps = cm.state.keyMaps.slice(0);
+    if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
     maps.push(cm.options.keyMap);
-    if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
     return maps;
   }
 
@@ -2005,7 +2059,7 @@ window.CodeMirror = (function() {
       if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
       slowPoll(cm);
 
-      // Try to detect the user choosing select-all 
+      // Try to detect the user choosing select-all
       if (display.input.selectionStart != null && (!ie || ie_lt9)) {
         clearTimeout(detectingSelectAll);
         var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
@@ -2063,7 +2117,7 @@ window.CodeMirror = (function() {
               head: clipPostChange(doc, change, hint.head)};
 
     if (hint == "start") return {anchor: change.from, head: change.from};
-    
+
     var end = changeEnd(change);
     if (hint == "around") return {anchor: change.from, head: end};
     if (hint == "end") return {anchor: end, head: end};
@@ -2145,6 +2199,8 @@ window.CodeMirror = (function() {
   }
 
   function makeChangeFromHistory(doc, type) {
+    if (doc.cm && doc.cm.state.suppressEdits) return;
+
     var hist = doc.history;
     var event = (type == "undo" ? hist.done : hist.undone).pop();
     if (!event) return;
@@ -2205,6 +2261,8 @@ window.CodeMirror = (function() {
                 text: [change.text[0]], origin: change.origin};
     }
 
+    change.removed = getBetween(doc, change.from, change.to);
+
     if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
     if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
     else updateDoc(doc, change, spans, selAfter);
@@ -2246,8 +2304,12 @@ window.CodeMirror = (function() {
     var lendiff = change.text.length - (to.line - from.line) - 1;
     // Remember that these lines changed, for updating the display
     regChange(cm, from.line, to.line + 1, lendiff);
+
     if (hasHandler(cm, "change")) {
-      var changeObj = {from: from, to: to, text: change.text, origin: change.origin};
+      var changeObj = {from: from, to: to,
+                       text: change.text,
+                       removed: change.removed,
+                       origin: change.origin};
       if (cm.curOp.textChanged) {
         for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
         cur.next = changeObj;
@@ -2360,16 +2422,20 @@ window.CodeMirror = (function() {
     var dir = bias || 1;
     doc.cantEdit = false;
     search: for (;;) {
-      var line = getLine(doc, curPos.line), toClear;
+      var line = getLine(doc, curPos.line);
       if (line.markedSpans) {
         for (var i = 0; i < line.markedSpans.length; ++i) {
           var sp = line.markedSpans[i], m = sp.marker;
           if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
               (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
-            if (mayClear && m.clearOnEnter) {
-              (toClear || (toClear = [])).push(m);
-              continue;
-            } else if (!m.atomic) continue;
+            if (mayClear) {
+              signal(m, "beforeCursorEnter");
+              if (m.explicitlyCleared) {
+                if (!line.markedSpans) break;
+                else {--i; continue;}
+              }
+            }
+            if (!m.atomic) continue;
             var newPos = m.find()[dir < 0 ? "from" : "to"];
             if (posEq(newPos, curPos)) {
               newPos.ch += dir;
@@ -2396,7 +2462,6 @@ window.CodeMirror = (function() {
             continue search;
           }
         }
-        if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
       }
       return curPos;
     }
@@ -2407,9 +2472,9 @@ window.CodeMirror = (function() {
   function scrollCursorIntoView(cm) {
     var coords = scrollPosIntoView(cm, cm.doc.sel.head);
     if (!cm.state.focused) return;
-    var display = cm.display, box = getRect(display.sizer), doScroll = null;
-    if (coords.top + box.top < 0) doScroll = true;
-    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+    var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
+    if (coords.top + pTop + box.top < 0) doScroll = true;
+    else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
     if (doScroll != null && !phantom) {
       var hidden = display.cursor.style.display == "none";
       if (hidden) {
@@ -2422,10 +2487,11 @@ window.CodeMirror = (function() {
     }
   }
 
-  function scrollPosIntoView(cm, pos) {
+  function scrollPosIntoView(cm, pos, margin) {
+    if (margin == null) margin = 0;
     for (;;) {
       var changed = false, coords = cursorCoords(cm, pos);
-      var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
+      var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
       var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
       if (scrollPos.scrollTop != null) {
         setScrollTop(cm, scrollPos.scrollTop);
@@ -2449,7 +2515,7 @@ window.CodeMirror = (function() {
     var display = cm.display, pt = paddingTop(display);
     y1 += pt; y2 += pt;
     var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
-    var docBottom = cm.doc.height + 2 * pt;
+    var docBottom = cm.doc.height + paddingVert(display);
     var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
     if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
     else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
@@ -2467,6 +2533,17 @@ window.CodeMirror = (function() {
     return result;
   }
 
+  function updateScrollPos(cm, left, top) {
+    cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
+  }
+
+  function addToScrollPos(cm, left, top) {
+    var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
+    var scroll = cm.display.scroller;
+    pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
+    pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
+  }
+
   // API UTILITIES
 
   function indentLine(cm, n, how, aggressive) {
@@ -2540,13 +2617,21 @@ window.CodeMirror = (function() {
 
     if (unit == "char") moveOnce();
     else if (unit == "column") moveOnce(true);
-    else if (unit == "word") {
-      var sawWord = false;
-      for (;;) {
-        if (dir < 0) if (!moveOnce()) break;
-        if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
-        else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
-        if (dir > 0) if (!moveOnce()) break;
+    else if (unit == "word" || unit == "group") {
+      var sawType = null, group = unit == "group";
+      for (var first = true;; first = false) {
+        if (dir < 0 && !moveOnce(!first)) break;
+        var cur = lineObj.text.charAt(ch) || "\n";
+        var type = isWordChar(cur) ? "w"
+          : !group ? null
+          : /\s/.test(cur) ? null
+          : "p";
+        if (sawType && sawType != type) {
+          if (dir < 0) {dir = 1; moveOnce();}
+          break;
+        }
+        if (type) sawType = type;
+        if (dir > 0 && !moveOnce(!first)) break;
       }
     }
     var result = skipAtomic(doc, Pos(line, ch), dir, true);
@@ -2558,7 +2643,7 @@ window.CodeMirror = (function() {
     var doc = cm.doc, x = pos.left, y;
     if (unit == "page") {
       var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
-      y = pos.top + dir * pageSize;
+      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
     } else if (unit == "line") {
       y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
     }
@@ -2576,9 +2661,9 @@ window.CodeMirror = (function() {
     if (line) {
       if (pos.after === false || end == line.length) --start; else ++end;
       var startChar = line.charAt(start);
-      var check = isWordChar(startChar) ? isWordChar :
-        /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
-      function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+      var check = isWordChar(startChar) ? isWordChar
+        : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
+        : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
       while (start > 0 && check(line.charAt(start - 1))) --start;
       while (end < line.length && check(line.charAt(end))) ++end;
     }
@@ -2608,8 +2693,8 @@ window.CodeMirror = (function() {
     getOption: function(option) {return this.options[option];},
     getDoc: function() {return this.doc;},
 
-    addKeyMap: function(map) {
-      this.state.keyMaps.push(map);
+    addKeyMap: function(map, bottom) {
+      this.state.keyMaps[bottom ? "push" : "unshift"](map);
     },
     removeKeyMap: function(map) {
       var maps = this.state.keyMaps;
@@ -2691,14 +2776,13 @@ window.CodeMirror = (function() {
       return charCoords(this, clipPos(this.doc, pos), mode || "page");
     },
 
-    coordsChar: function(coords) {
-      var off = getRect(this.display.lineSpace);
-      var scrollY = window.pageYOffset || (document.documentElement || document.body).scrollTop;
-      var scrollX = window.pageXOffset || (document.documentElement || document.body).scrollLeft;
-      return coordsChar(this, coords.left - off.left - scrollX, coords.top - off.top - scrollY);
+    coordsChar: function(coords, mode) {
+      coords = fromCoordSystem(this, coords, mode || "page");
+      return coordsChar(this, coords.left, coords.top);
     },
 
     defaultTextHeight: function() { return textHeight(this.display); },
+    defaultCharWidth: function() { return charWidth(this.display); },
 
     setGutterMarker: operation(null, function(line, gutterID, value) {
       return changeLine(this, line, function(line) {
@@ -2851,8 +2935,7 @@ window.CodeMirror = (function() {
       if (sel.goalColumn != null) pos.left = sel.goalColumn;
       var target = findPosV(this, pos, dir, unit);
 
-      if (unit == "page")
-        this.display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
+      if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
       extendSelection(this.doc, target, target, dir);
       sel.goalColumn = pos.left;
     }),
@@ -2863,9 +2946,10 @@ window.CodeMirror = (function() {
       else
         this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
     },
+    hasFocus: function() { return this.state.focused; },
 
     scrollTo: operation(null, function(x, y) {
-      this.curOp.updateScrollPos = {scrollLeft: x, scrollTop: y};
+      updateScrollPos(this, x, y);
     }),
     getScrollInfo: function() {
       var scroller = this.display.scroller, co = scrollerCutOff;
@@ -2874,13 +2958,13 @@ window.CodeMirror = (function() {
               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
     },
 
-    scrollIntoView: function(pos) {
+    scrollIntoView: function(pos, margin) {
       if (typeof pos == "number") pos = Pos(pos, 0);
       if (!pos || pos.line != null) {
         pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
-        scrollPosIntoView(this, pos);
+        scrollPosIntoView(this, pos, margin);
       } else {
-        scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
+        scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
       }
     },
 
@@ -2900,7 +2984,7 @@ window.CodeMirror = (function() {
 
     refresh: operation(null, function() {
       clearCaches(this);
-      this.curOp.updateScrollPos = {scrollTop: this.doc.scrollTop, scrollLeft: this.doc.scrollLeft};
+      updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
       regChange(this);
     }),
 
@@ -2909,7 +2993,7 @@ window.CodeMirror = (function() {
       old.cm = null;
       attachDoc(this, doc);
       clearCaches(this);
-      this.curOp.updateScrollPos = {scrollTop: doc.scrollTop, scrollLeft: doc.scrollLeft};
+      updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
       return old;
     }),
 
@@ -2981,7 +3065,7 @@ window.CodeMirror = (function() {
   option("firstLineNumber", 1, guttersChanged, true);
   option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
   option("showCursorWhenSelecting", false, updateSelection, true);
-  
+
   option("readOnly", false, function(cm, val) {
     if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
     else if (!val) resetInput(cm, true);
@@ -3133,6 +3217,14 @@ window.CodeMirror = (function() {
     goLineEnd: function(cm) {
       cm.extendSelection(lineEnd(cm, cm.getCursor().line));
     },
+    goLineRight: function(cm) {
+      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+      cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
+    },
+    goLineLeft: function(cm) {
+      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+      cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
+    },
     goLineUp: function(cm) {cm.moveV(-1, "line");},
     goLineDown: function(cm) {cm.moveV(1, "line");},
     goPageUp: function(cm) {cm.moveV(-1, "page");},
@@ -3142,11 +3234,15 @@ window.CodeMirror = (function() {
     goColumnLeft: function(cm) {cm.moveH(-1, "column");},
     goColumnRight: function(cm) {cm.moveH(1, "column");},
     goWordLeft: function(cm) {cm.moveH(-1, "word");},
+    goGroupRight: function(cm) {cm.moveH(1, "group");},
+    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
     goWordRight: function(cm) {cm.moveH(1, "word");},
     delCharBefore: function(cm) {cm.deleteH(-1, "char");},
     delCharAfter: function(cm) {cm.deleteH(1, "char");},
     delWordBefore: function(cm) {cm.deleteH(-1, "word");},
     delWordAfter: function(cm) {cm.deleteH(1, "word");},
+    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
+    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
     indentAuto: function(cm) {cm.indentSelection("smart");},
     indentMore: function(cm) {cm.indentSelection("add");},
     indentLess: function(cm) {cm.indentSelection("subtract");},
@@ -3184,17 +3280,17 @@ window.CodeMirror = (function() {
   keyMap.pcDefault = {
     "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
     "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
-    "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
-    "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
     "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
     "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
     fallthrough: "basic"
   };
   keyMap.macDefault = {
     "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
-    "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
-    "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
-    "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
+    "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
+    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
     "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
     "Cmd-[": "indentLess", "Cmd-]": "indentMore",
     fallthrough: ["basic", "emacsy"]
@@ -3262,6 +3358,8 @@ window.CodeMirror = (function() {
     options.value = textarea.value;
     if (!options.tabindex && textarea.tabindex)
       options.tabindex = textarea.tabindex;
+    if (!options.placeholder && textarea.placeholder)
+      options.placeholder = textarea.placeholder;
     // Set autofocus to true if this textarea is focused, or if it has
     // autofocus and no other element is focused.
     if (options.autofocus == null) {
@@ -3274,17 +3372,19 @@ window.CodeMirror = (function() {
 
     function save() {textarea.value = cm.getValue();}
     if (textarea.form) {
-      // Deplorable hack to make the submit method do the right thing.
       on(textarea.form, "submit", save);
-      var form = textarea.form, realSubmit = form.submit;
-      try {
-        var wrappedSubmit = form.submit = function() {
-          save();
-          form.submit = realSubmit;
-          form.submit();
-          form.submit = wrappedSubmit;
-        };
-      } catch(e) {}
+      // Deplorable hack to make the submit method do the right thing.
+      if (!options.leaveSubmitMethodAlone) {
+        var form = textarea.form, realSubmit = form.submit;
+        try {
+          var wrappedSubmit = form.submit = function() {
+            save();
+            form.submit = realSubmit;
+            form.submit();
+            form.submit = wrappedSubmit;
+          };
+        } catch(e) {}
+      }
     }
 
     textarea.style.display = "none";
@@ -3316,6 +3416,7 @@ window.CodeMirror = (function() {
     this.pos = this.start = 0;
     this.string = string;
     this.tabSize = tabSize || 8;
+    this.lastColumnPos = this.lastColumnValue = 0;
   }
 
   StringStream.prototype = {
@@ -3348,12 +3449,19 @@ window.CodeMirror = (function() {
       if (found > -1) {this.pos = found; return true;}
     },
     backUp: function(n) {this.pos -= n;},
-    column: function() {return countColumn(this.string, this.start, this.tabSize);},
+    column: function() {
+      if (this.lastColumnPos < this.start) {
+        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+        this.lastColumnPos = this.start;
+      }
+      return this.lastColumnValue;
+    },
     indentation: function() {return countColumn(this.string, null, this.tabSize);},
     match: function(pattern, consume, caseInsensitive) {
       if (typeof pattern == "string") {
         var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
-        if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+        var substr = this.string.substr(this.pos, pattern.length);
+        if (cased(substr) == cased(pattern)) {
           if (consume !== false) this.pos += pattern.length;
           return true;
         }
@@ -3433,7 +3541,6 @@ window.CodeMirror = (function() {
             inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
             atomic: this.atomic,
             collapsed: this.collapsed,
-            clearOnEnter: this.clearOnEnter,
             replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
             readOnly: this.readOnly,
             startStyle: this.startStyle, endStyle: this.endStyle};
@@ -3488,6 +3595,8 @@ window.CodeMirror = (function() {
       if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
     });
 
+    if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
+
     if (marker.readOnly) {
       sawReadOnlySpans = true;
       if (doc.history.done.length || doc.history.undone.length)
@@ -3540,7 +3649,9 @@ window.CodeMirror = (function() {
     options = copyObj(options);
     options.shared = false;
     var markers = [markText(doc, from, to, options, type)], primary = markers[0];
+    var widget = options.replacedWith;
     linkedDocs(doc, function(doc) {
+      if (widget) options.replacedWith = widget.cloneNode(true);
       markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
       for (var i = 0; i < doc.linked.length; ++i)
         if (doc.linked[i].isParent) return;
@@ -3814,9 +3925,7 @@ window.CodeMirror = (function() {
       if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
         var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
         updateLineHeight(line, line.height + widgetHeight(widget));
-        if (aboveVisible)
-          cm.curOp.updateScrollPos = {scrollTop: cm.doc.scrollTop + widget.height,
-                                      scrollLeft: cm.doc.scrollLeft};
+        if (aboveVisible) addToScrollPos(cm, 0, widget.height);
       }
       return true;
     });
@@ -3843,7 +3952,6 @@ window.CodeMirror = (function() {
     attachMarkedSpans(line, markedSpans);
     var estHeight = estimateHeight ? estimateHeight(line) : 1;
     if (estHeight != line.height) updateLineHeight(line, estHeight);
-    signalLater(line, "change");
   }
 
   function cleanUpLine(line) {
@@ -3855,7 +3963,8 @@ window.CodeMirror = (function() {
   // array, which contains alternating fragments of text and CSS
   // classes.
   function runMode(cm, text, mode, state, f) {
-    var flattenSpans = cm.options.flattenSpans;
+    var flattenSpans = mode.flattenSpans;
+    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
     var curText = "", curStyle = null;
     var stream = new StringStream(text, cm.options.tabSize);
     if (text == "" && mode.blankLine) mode.blankLine(state);
@@ -3957,6 +4066,8 @@ window.CodeMirror = (function() {
       builder.measure = line == realLine && measure;
       builder.pos = 0;
       builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
+      if ((ie || webkit) && cm.getOption("lineWrapping"))
+        builder.addToken = buildTokenSplitSpaces(builder.addToken);
       if (measure && sawBefore && line != realLine && !builder.addedOne) {
         measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
         builder.addedOne = true;
@@ -3989,10 +4100,11 @@ window.CodeMirror = (function() {
       }
     }
 
+    signal(cm, "renderLine", cm, realLine, builder.pre);
     return builder.pre;
   }
 
-  var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
+  var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
   function buildToken(builder, text, style, startStyle, endStyle) {
     if (!text) return;
     if (!tokenSpecialChars.test(text)) {
@@ -4032,23 +4144,42 @@ window.CodeMirror = (function() {
   }
 
   function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
+    var wrapping = builder.cm.options.lineWrapping;
     for (var i = 0; i < text.length; ++i) {
       var ch = text.charAt(i), start = i == 0;
       if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
         ch = text.slice(i, i + 2);
         ++i;
-      } else if (i && builder.cm.options.lineWrapping &&
+      } else if (i && wrapping &&
                  spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
         builder.pre.appendChild(elt("wbr"));
       }
-      builder.measure[builder.pos] =
+      var span = builder.measure[builder.pos] =
         buildToken(builder, ch, style,
                    start && startStyle, i == text.length - 1 && endStyle);
+      // In IE single-space nodes wrap differently than spaces
+      // embedded in larger text nodes, except when set to
+      // white-space: normal (issue #1268).
+      if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
+          i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
+        span.style.whiteSpace = "normal";
       builder.pos += ch.length;
     }
     if (text.length) builder.addedOne = true;
   }
 
+  function buildTokenSplitSpaces(inner) {
+    function split(old) {
+      var out = " ";
+      for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
+      out += " ";
+      return out;
+    }
+    return function(builder, text, style, startStyle, endStyle) {
+      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
+    };
+  }
+
   function buildCollapsedSpan(builder, size, widget) {
     if (widget) {
       if (!builder.display) widget = widget.cloneNode(true);
@@ -4125,6 +4256,10 @@ window.CodeMirror = (function() {
 
   function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
     function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
+    function update(line, text, spans) {
+      updateLine(line, text, spans, estimateHeight);
+      signalLater(line, "change", line, change);
+    }
 
     var from = change.from, to = change.to, text = change.text;
     var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
@@ -4136,27 +4271,25 @@ window.CodeMirror = (function() {
       // sure line objects move the way they are supposed to.
       for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
         added.push(makeLine(text[i], spansFor(i), estimateHeight));
-      updateLine(lastLine, lastLine.text, lastSpans, estimateHeight);
+      update(lastLine, lastLine.text, lastSpans);
       if (nlines) doc.remove(from.line, nlines);
       if (added.length) doc.insert(from.line, added);
     } else if (firstLine == lastLine) {
       if (text.length == 1) {
-        updateLine(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch),
-                   lastSpans, estimateHeight);
+        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
       } else {
         for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
           added.push(makeLine(text[i], spansFor(i), estimateHeight));
         added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
-        updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight);
+        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
         doc.insert(from.line + 1, added);
       }
     } else if (text.length == 1) {
-      updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch),
-                 spansFor(0), estimateHeight);
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
       doc.remove(from.line + 1, nlines);
     } else {
-      updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight);
-      updateLine(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans, estimateHeight);
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
       for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
         added.push(makeLine(text[i], spansFor(i), estimateHeight));
       if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
@@ -4300,7 +4433,7 @@ window.CodeMirror = (function() {
   var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
     if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
     if (firstLine == null) firstLine = 0;
-    
+
     BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
     this.first = firstLine;
     this.scrollTop = this.scrollLeft = 0;
@@ -4412,7 +4545,7 @@ window.CodeMirror = (function() {
       this.history.lastOp = this.history.lastOrigin = null;
     },
     isClean: function () {return this.history.dirtyCounter == 0;},
-      
+
     getHistory: function() {
       return {done: copyHistoryArray(this.history.done),
               undone: copyHistoryArray(this.history.undone)};
@@ -4707,7 +4840,7 @@ window.CodeMirror = (function() {
       while (hist.done.length > hist.undoDepth)
         hist.done.shift();
       if (hist.dirtyCounter < 0)
-        // The user has made a change after undoing past the last clean state. 
+        // The user has made a change after undoing past the last clean state.
         // We can never get back to a clean state now until markClean() is called.
         hist.dirtyCounter = NaN;
       else
@@ -4920,12 +5053,12 @@ window.CodeMirror = (function() {
 
   // Counts the column offset in a string, taking tabs into account.
   // Used mostly to find indentation.
-  function countColumn(string, end, tabSize) {
+  function countColumn(string, end, tabSize, startIndex, startValue) {
     if (end == null) {
       end = string.search(/[^\s\u00a0]/);
       if (end == -1) end = string.length;
     }
-    for (var i = 0, n = 0; i < end; ++i) {
+    for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
       if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
       else ++n;
     }
@@ -5005,9 +5138,8 @@ window.CodeMirror = (function() {
   }
 
   function removeChildren(e) {
-    // IE will break all parent-child relations in subnodes when setting innerHTML
-    if (!ie) e.innerHTML = "";
-    else while (e.firstChild) e.removeChild(e.firstChild);
+    for (var count = e.childNodes.length; count > 0; --count)
+      e.removeChild(e.firstChild);
     return e;
   }
 
@@ -5047,8 +5179,8 @@ window.CodeMirror = (function() {
   // various browsers.
   var spanAffectsWrapping = /^$/; // Won't match any two-character string
   if (gecko) spanAffectsWrapping = /$'/;
-  else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
-  else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
+  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
+  else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
 
   var knownScrollbarWidth;
   function scrollbarWidth(measure) {
@@ -5378,7 +5510,7 @@ window.CodeMirror = (function() {
 
   // THE END
 
-  CodeMirror.version = "3.1";
+  CodeMirror.version = "3.11";
 
   return CodeMirror;
 })();

+ 144 - 43
st/Helios-Browser.st

@@ -67,7 +67,8 @@ classesListWidget
 
 methodsListWidget
 	^ methodsListWidget ifNil: [
-      	methodsListWidget := HLMethodsListWidget on: self model ]
+      	methodsListWidget := HLMethodsListWidget on: self model.
+		methodsListWidget next: self sourceWidget]
 !
 
 packagesListWidget
@@ -116,6 +117,18 @@ HLNavigationListWidget subclass: #HLBrowserListWidget
 
 !HLBrowserListWidget methodsFor: 'accessing'!
 
+label
+	^ 'List'
+!
+
+menuCommands
+	"Answer a collection of commands to be put in the cog menu"
+	
+	^ (HLBrowserCommand concreteClasses 
+		select: [ :each | each isValidFor: self selectedItem ])
+		collect: [ :each | each for: self model ]
+!
+
 model
 	^ model
 !
@@ -123,7 +136,16 @@ model
 model: aBrowserModel
 	model := aBrowserModel.
     
-    self observeModel
+    self 
+		observeSystem;
+		observeModel
+!
+
+selectedItem: anItem
+	"Selection changed, update the cog menu"
+	
+	super selectedItem: anItem.
+	self updateMenu
 ! !
 
 !HLBrowserListWidget methodsFor: 'actions'!
@@ -134,12 +156,50 @@ observeModel
 observeSystem
 ! !
 
-!HLBrowserListWidget methodsFor: 'initialization'!
+!HLBrowserListWidget methodsFor: 'rendering'!
 
-initialize
-	super initialize.
-    
-    self observeSystem
+renderContentOn: html
+	self renderHeadOn: html.	
+	super renderContentOn: html
+!
+
+renderHeadOn: html
+	html div 
+		class: 'list-label';
+		with: [
+			html with: self label.
+			self renderMenuOn: html ]
+!
+
+renderMenuOn: html
+	| commands |
+	
+	commands := self menuCommands.
+	commands isEmpty ifTrue: [ ^ self ].
+	
+	html div 
+		class: 'btn-group cog';
+		with: [
+			html a
+				class: 'btn dropdown-toggle';
+				at: 'data-toggle' put: 'dropdown';
+				with: [ (html tag: 'i') class: 'icon-cog' ].
+		html ul 
+			class: 'dropdown-menu pull-right';
+			with: [ 
+				self menuCommands do: [ :each | 
+					html li with: [ html a 
+						with: each menuLabel;
+						onClick: [ self execute: each ] ] ] ] ]
+! !
+
+!HLBrowserListWidget methodsFor: 'updating'!
+
+updateMenu
+	(self wrapper asJQuery find: '.cog') remove.
+	
+	[ :html | self renderMenuOn: html ] 
+		appendToJQuery: (self wrapper asJQuery find: '.list-label')
 ! !
 
 !HLBrowserListWidget class methodsFor: 'instance creation'!
@@ -171,6 +231,10 @@ iconForItem: aClass
       	ifTrue: [ 'icon-question-sign' ]
 !
 
+label
+	^ 'Classes'
+!
+
 showInstance
 	^ self model showInstance
 ! !
@@ -194,7 +258,7 @@ observeModel
 !
 
 observeSystem
-	SystemAnnouncer current
+	self model systemAnnouncer
     	on: ClassAdded
         do: [ :ann | self onClassAdded: ann theClass ];
         on: ClassRemoved
@@ -289,12 +353,7 @@ renderButtonsOn: html
                     self model showInstance ifFalse: [ 
                     	str nextPutAll: ' active'] ]);
   				with: 'Class';
-				onClick: [ self model showInstance: false ] ].
-                 
-  	html button 
-           	class: 'btn';
-            at: 'data-toggle' put: 'button';
-  			with: 'Comment'
+				onClick: [ self model showInstance: false ] ]
 !
 
 renderItem: aClass level: anInteger on: html
@@ -367,6 +426,10 @@ iconForItem: aSelector
 			ifFalse: [ 'icon-none' ] ]
 !
 
+label
+	^ 'Methods'
+!
+
 methodForSelector: aSelector
 	^ self model selectedClass
     	methodDictionary at: aSelector
@@ -420,7 +483,7 @@ observeModel
 !
 
 observeSystem
-	SystemAnnouncer current 
+	self model systemAnnouncer 
     	on: ProtocolAdded
         do: [ :ann | self onProtocolAdded: ann theClass ];
     	on: ProtocolRemoved
@@ -557,17 +620,20 @@ HLBrowserListWidget subclass: #HLPackagesListWidget
 
 !HLPackagesListWidget methodsFor: 'accessing'!
 
-initializeItems
-	^ items := self model packages sort:[:a :b|
-						a name < b name]
-!
-
 items
 	^ items ifNil: [self initializeItems]
+!
+
+label
+	^ 'Packages'
 ! !
 
 !HLPackagesListWidget methodsFor: 'actions'!
 
+commitPackage
+	self model commitPackage
+!
+
 focusClassesListWidget
 	self model announcer announce: HLClassesListFocus new
 !
@@ -584,6 +650,13 @@ selectItem: aPackage
 	self model selectedPackage: aPackage
 ! !
 
+!HLPackagesListWidget methodsFor: 'initialization'!
+
+initializeItems
+	^ items := self model packages 
+		sort: [ :a :b | a name < b name ]
+! !
+
 !HLPackagesListWidget methodsFor: 'reactions'!
 
 onPackageSelected: aPackage
@@ -601,24 +674,17 @@ onPackagesFocusRequested
 !HLPackagesListWidget methodsFor: 'rendering'!
 
 renderButtonsOn: html
-
-	html span class: 'info'; with: 'Auto commit'.
 	html div 
-        class: 'btn-group switch';
-		at: 'data-toggle' put: 'buttons-radio';
-		with: [ 
-           	html button 
-                class: (String streamContents: [ :str |
-                	str nextPutAll: 'btn' ]);
-  				with: 'On'.
-  			html button
-  				class: (String streamContents: [ :str |
-                	str nextPutAll: 'btn active' ]);
-  				with: 'Off' ].
-                
-    html a 
-         	class: 'btn';
-			with: 'Commit'.
+		class: 'buttons';
+		with: [
+			html button 
+				class: 'btn';
+				with: 'Commit';
+				onClick: [ self commitPackage ] ]
+!
+
+renderItemLabel: aPackage on: html
+	html with: aPackage name
 ! !
 
 HLBrowserListWidget subclass: #HLProtocolsListWidget
@@ -631,6 +697,10 @@ allProtocol
 	^ self model allProtocol
 !
 
+label
+	^ 'Protocols'
+!
+
 selectedItem
 	^ super selectedItem" ifNil: [ self allProtocol ]"
 ! !
@@ -650,11 +720,11 @@ observeModel
 !
 
 observeSystem
-    SystemAnnouncer current
-    	on: ProtocolAdded 
-        do: [ :ann | self onProtocolAdded: ann protocol to: ann theClass ];
-        on: ProtocolRemoved
-        do: [ :ann | self onProtocolRemoved: ann protocol from: ann theClass ]
+	self model systemAnnouncer
+		on: ProtocolAdded 
+	    do: [ :ann | self onProtocolAdded: ann protocol to: ann theClass ];
+	    on: ProtocolRemoved
+	    do: [ :ann | self onProtocolRemoved: ann protocol from: ann theClass ]
 !
 
 selectItem: aString
@@ -851,6 +921,10 @@ showInstance: aBoolean
     	  	ifFalse: [ self selectedClass theMetaClass ]) ].
     
     self announcer announce: HLShowInstanceToggled new
+!
+
+systemAnnouncer
+	^ self environment systemAnnouncer
 ! !
 
 !HLBrowserModel methodsFor: 'actions'!
@@ -896,7 +970,9 @@ saveSourceCode
 !HLBrowserModel methodsFor: 'commands actions'!
 
 commitPackage
-	self environment commitPackage: self selectedPackage
+	self 
+		withHelperLabelled: 'Committing package ', self selectedPackage name, '...'
+		do: [ self environment commitPackage: self selectedPackage ]
 !
 
 moveMethodToClass: aClassName
@@ -1007,6 +1083,23 @@ compilationProtocol
 	^ currentProtocol = self allProtocol
 		ifTrue: [ self unclassifiedProtocol ]
 		ifFalse: [ currentProtocol ]
+!
+
+withHelperLabelled: aString do: aBlock
+	"TODO: doesn't belong here"
+
+	(window jQuery: '#helper') remove.
+
+	[ :html |
+		html div 
+			id: 'helper';
+			with: aString ] appendToJQuery: 'body' asJQuery.
+	
+	[
+		aBlock value.
+		(window jQuery: '#helper') remove
+	] 
+		valueWithTimeout: 10
 ! !
 
 !HLBrowserModel methodsFor: 'testing'!
@@ -1060,6 +1153,14 @@ model: aBrowserModel
 	model := aBrowserModel.
     
     self observeModel
+!
+
+previous
+	"for browser lists widget"
+!
+
+previous: aWidget
+	"for browser lists widget"
 ! !
 
 !HLBrowserSourceWidget methodsFor: 'actions'!

+ 38 - 2
st/Helios-Commands-Browser.st

@@ -162,6 +162,12 @@ label
 	^ 'Commit package'
 ! !
 
+!HLCommitPackageCommand class methodsFor: 'testing'!
+
+isValidFor: anObject
+	^ anObject isPackage
+! !
+
 HLBrowserCommand subclass: #HLFindCommand
 	instanceVariableNames: ''
 	package: 'Helios-Commands-Browser'!
@@ -288,6 +294,16 @@ key
 
 label	
 	^ 'to class'
+!
+
+menuLabel	
+	^ 'Move to class...'
+! !
+
+!HLMoveMethodToClassCommand class methodsFor: 'testing'!
+
+isValidFor: anObject
+	^ anObject isCompiledMethod
 ! !
 
 HLMoveMethodToCommand subclass: #HLMoveMethodToProtocolCommand
@@ -328,6 +344,16 @@ key
 
 label
 	^ 'to protocol'
+!
+
+menuLabel
+	^ 'Move to protocol...'
+! !
+
+!HLMoveMethodToProtocolCommand class methodsFor: 'testing'!
+
+isValidFor: anObject
+	^ anObject isCompiledMethod
 ! !
 
 HLBrowserCommand subclass: #HLRemoveCommand
@@ -362,12 +388,22 @@ isActive
 
 !HLRemoveMethodCommand class methodsFor: 'accessing'!
 
-key
-	^ 77
+isValidFor: anObject
+	^ anObject isCompiledMethod
 !
 
 label
 	^ 'Method'
+!
+
+menuLabel
+	^ 'Remove method'
+! !
+
+!HLRemoveMethodCommand class methodsFor: 'testing'!
+
+key
+	^ 77
 ! !
 
 HLBrowserCommand subclass: #HLToggleCommand

+ 28 - 2
st/Helios-Commands-Core.st

@@ -31,6 +31,10 @@ key
 
 label
 	^ self class label
+!
+
+menuLabel
+	^ self class menuLabel
 ! !
 
 !HLCommand methodsFor: 'converting'!
@@ -96,9 +100,11 @@ key
 
 label
 	^ ''
-! !
+!
 
-!HLCommand class methodsFor: 'registration'!
+menuLabel
+	^ self label
+!
 
 registerConcreteClassesOn: aBinding
 	| newBinding |
@@ -108,6 +114,22 @@ registerConcreteClassesOn: aBinding
 		ifFalse: [ newBinding := aBinding ].
 		
 	self subclasses do: [ :each | each registerConcreteClassesOn: newBinding ]
+! !
+
+!HLCommand class methodsFor: 'registration'!
+
+concreteClasses
+	| classes |
+	
+	classes := OrderedCollection new.
+	
+	self isConcrete
+		ifTrue: [ classes add: self ].
+		
+	self subclasses do: [ :each | 
+		classes addAll: each concreteClasses ].
+		
+	^ classes
 !
 
 registerOn: aBinding
@@ -118,6 +140,10 @@ registerOn: aBinding
 
 isConcrete
 	^ self key notNil
+!
+
+isValidFor: aModel
+	^ false
 ! !
 
 HLCommand subclass: #HLCloseTabCommand

+ 6 - 0
st/Helios-Core.st

@@ -118,6 +118,12 @@ alert: aString
 
 confirm: aString
 	^ window confirm: aString
+!
+
+execute: aCommand
+	HLManager current keyBinder
+		activate;
+		applyBinding: aCommand asBinding
 ! !
 
 !HLWidget methodsFor: 'keybindings'!

+ 8 - 0
st/Helios-Environments.st

@@ -25,6 +25,10 @@ classNamed: aString
 
 packages
 	^ self subclassResponsibility
+!
+
+systemAnnouncer
+	^ self subclassResponsibility
 ! !
 
 !HLEnvironment methodsFor: 'actions'!
@@ -104,6 +108,10 @@ classNamed: aString
 
 packages
 	^ Smalltalk current packages
+!
+
+systemAnnouncer
+	^ SystemAnnouncer current
 ! !
 
 !HLLocalEnvironment methodsFor: 'actions'!

+ 13 - 9
st/Helios-KeyBindings.st

@@ -311,8 +311,12 @@ renderOn: aBinder html: html
 				class: 'help-inline';
 				with: self message;
 				yourself) ].
-			
-	input asJQuery focus
+	
+	"Evaluate with a timeout to ensure focus.
+	Commands can be executed from a menu, clicking on the menu to
+	evaluate the command would give it the focus otherwise"
+	
+	[ input asJQuery focus ] valueWithTimeout: 10
 ! !
 
 !HLBindingInput methodsFor: 'testing'!
@@ -435,11 +439,11 @@ handleBindingFor: anEvent
 !
 
 handleInactiveKeyDown: event
-      event which = self activationKey ifTrue: [
-      		event ctrlKey  ifTrue: [
-					self activate. 
-               		 event preventDefault. 
-                	^ false ] ]
+	event which = self activationKey ifTrue: [
+    	event ctrlKey ifTrue: [
+			self activate. 
+            event preventDefault. 
+            ^ false ] ]
 !
 
 handleKeyDown: event
@@ -567,10 +571,10 @@ renderSelectionOn: html
 renderStart
 	[ :html |
 		html div 
-			id: 'keybinding-start-helper';
+			id: 'helper';
 			with: 'Press ', self keyBinder activationKeyLabel, ' to start' ] appendToJQuery: 'body' asJQuery.
 	
-	[ (window jQuery: '#keybinding-start-helper') fadeOut: 1000 ] 
+	[ (window jQuery: '#helper') fadeOut: 1000 ] 
 		valueWithTimeout: 2000
 ! !
 

+ 2 - 2
st/Helios-Layout.st

@@ -124,7 +124,7 @@ resize: anInteger
     position := anInteger - container offset top.
     
 	firstPane asJQuery height: ((position min: container height - 100) max: 100).
-    secondPane asJQuery height: (((container height - position) min: container height - 100) max: 100) - 6.
+    secondPane asJQuery height: (((container height - position) min: container height - 100) max: 100) - 1.
     
     super resize
 !
@@ -171,7 +171,7 @@ resize: anInteger
     position := anInteger - container offset left.
     
 	firstPane asJQuery width: ((position min: container width - 100) max: 100).
-    secondPane asJQuery width: (((container width - position) min: container width - 100) max: 100) - 6.
+    secondPane asJQuery width: (((container width - position) min: container width - 100) max: 100) - 1.
     
     super resize
 !

+ 68 - 9
st/Helios-Workspace.st

@@ -1,7 +1,7 @@
 Smalltalk current createPackage: 'Helios-Workspace'!
 Object subclass: #HLCodeModel
- instanceVariableNames: 'announcer environment receiver'
- package: 'Helios-Workspace'!
+	instanceVariableNames: 'announcer environment receiver'
+	package: 'Helios-Workspace'!
 
 !HLCodeModel methodsFor: 'accessing'!
 
@@ -52,8 +52,8 @@ on: anEnvironment
 ! !
 
 HLWidget subclass: #HLCodeWidget
- instanceVariableNames: 'model wrapper code editor'
- package: 'Helios-Workspace'!
+	instanceVariableNames: 'model wrapper code editor'
+	package: 'Helios-Workspace'!
 
 !HLCodeWidget methodsFor: 'accessing'!
 
@@ -204,10 +204,33 @@ setEditorOn: aTextarea
 				indentUnit: 4,
                 matchBrackets: true,
                 electricChars: false,
-				keyMap: 'Amber'
+				keyMap: 'Amber',
+				extraKeys: {"Shift-Space": "autocomplete"}
 	})>
 ! !
 
+!HLCodeWidget methodsFor: 'hints'!
+
+messageHintFor: anEditor token: aToken
+	^ ((Smalltalk current at: 'allSelectors') value asSet asArray 
+		select: [ :each | each includesSubString: aToken string ])
+		reject: [ :each | each = aToken string ]
+!
+
+variableHintFor: anEditor token: aToken
+	| variables classNames pseudoVariables |
+	
+	variables := ((window jQuery: anEditor display wrapper) find: 'span.cm-variable') get
+		collect: [ :each | (window jQuery: each) html ].
+	
+	classNames := Smalltalk current classes collect: [ :each | each name ].
+	pseudoVariables := Smalltalk current pseudoVariableNames.
+	
+	^ ((variables, classNames, pseudoVariables) asSet asArray 
+		select: [ :each | each includesSubString: aToken string ])
+		reject: [ :each | each = aToken string ]
+! !
+
 !HLCodeWidget methodsFor: 'reactions'!
 
 onDoIt
@@ -282,7 +305,7 @@ macKeyMap
 		'Shift-Cmd-Alt-F'	-> 'replaceAll'.
 		'Shift-Cmd-G'		-> 'findPrev'. 
 		'Shift-Cmd-Z'		-> 'redo'. 
-    	'fallthrough' 	-> { 'basic' }
+    	'fallthrough' 	-> { 'basic'. 'emacsy' }
   }
 !
 
@@ -325,6 +348,37 @@ tabPriority
 	^ 10
 ! !
 
+!HLCodeWidget class methodsFor: 'hints'!
+
+hintFor: anEditor options: options
+	| cursor token completions |
+	
+	cursor := anEditor getCursor.
+	token := anEditor getTokenAt: cursor.
+	token at: 'state' put: ((CodeMirror basicAt: 'innerMode')
+		value: anEditor getMode value: (token at: 'state')) state.
+	
+	completions := token type = 'variable' 
+		ifTrue: [ HLCodeWidget variableHintFor: anEditor token: token ]
+		ifFalse: [ HLCodeWidget messageHintFor: anEditor token: token ].
+	
+	^ #{
+		'list' -> completions.
+		'from' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token end).
+		'to' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token start)
+	}
+!
+
+messageHintFor: anEditor token: aToken
+	^ (anEditor at: 'amberCodeWidget')
+		messageHintFor: anEditor token: aToken
+!
+
+variableHintFor: anEditor token: aToken
+	^ (anEditor at: 'amberCodeWidget')
+		variableHintFor: anEditor token: aToken
+! !
+
 !HLCodeWidget class methodsFor: 'initialization'!
 
 initialize
@@ -336,7 +390,12 @@ initialize
 !
 
 setupCodeMirror
-	< CodeMirror.keyMap.default.fallthrough = ["basic"] >
+	< 
+		CodeMirror.keyMap.default.fallthrough = ["basic"];
+		CodeMirror.commands.autocomplete = function(cm) {
+        	CodeMirror.showHint(cm, self._hintFor_options_);
+      	}
+	>
 !
 
 setupCommands
@@ -358,8 +417,8 @@ canBeOpenAsTab
 ! !
 
 HLCodeWidget subclass: #HLSourceCodeWidget
- instanceVariableNames: 'browserModel'
- package: 'Helios-Workspace'!
+	instanceVariableNames: 'browserModel'
+	package: 'Helios-Workspace'!
 
 !HLSourceCodeWidget methodsFor: 'accessing'!
 

+ 6 - 0
st/Kernel-Methods.st

@@ -249,6 +249,12 @@ defaultCategory
 	^ 'as yet unclassified'
 ! !
 
+!CompiledMethod methodsFor: 'testing'!
+
+isCompiledMethod
+	^ true
+! !
+
 Object subclass: #ForkPool
 	instanceVariableNames: 'poolSize maxPoolSize queue worker'
 	package: 'Kernel-Methods'!

+ 14 - 0
st/Kernel-Objects.st

@@ -286,6 +286,10 @@ isClass
 	^false
 !
 
+isCompiledMethod
+	^ false
+!
+
 isImmutable
 	^ false
 !
@@ -312,6 +316,10 @@ isNumber
 	^false
 !
 
+isPackage
+	^ false
+!
+
 isParseFailure
 	^false
 !
@@ -1195,6 +1203,12 @@ printOn: aStream
 		nextPutAll: ')'
 ! !
 
+!Package methodsFor: 'testing'!
+
+isPackage
+	^ true
+! !
+
 Package class instanceVariableNames: 'defaultCommitPathJs defaultCommitPathSt'!
 
 !Package class methodsFor: 'accessing'!