Browse Source

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

Nicolas Petton 12 năm trước cách đây
mục cha
commit
7f8b8e39f8
73 tập tin đã thay đổi với 5764 bổ sung521 xóa
  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'!