Browse Source

Merge branch 'master' into ast-interpreter

Conflicts:
	js/boot.js
Nicolas Petton 12 years ago
parent
commit
8c52792c10
13 changed files with 1194 additions and 339 deletions
  1. 6 4
      bin/amberc.js
  2. 42 32
      js/IDE.deploy.js
  3. 48 38
      js/IDE.js
  4. 12 4
      js/boot.js
  5. 3 0
      js/lib/CodeMirror/codemirror.css
  6. 104 55
      js/lib/CodeMirror/codemirror.js
  7. 1 1
      repl/Makefile
  8. 12 9
      repl/REPL.js
  9. 1 1
      repl/REPL.st
  10. 297 45
      repl/amber.js
  11. 133 44
      server/FileServer.st
  12. 511 89
      server/server.js
  13. 24 17
      st/IDE.st

+ 6 - 4
bin/amberc.js

@@ -104,7 +104,7 @@ var createDefaults = function(amber_dir, finished_callback){
 		'closure': false,
 		'closure_parts': false,
 		'closure_full': false,
-		'closure_options': '',
+		'closure_options': ' --language_in=ECMASCRIPT5 ',
 		'suffix': '',
 		'loadsuffix': '',
 		'suffix_used': '',
@@ -330,9 +330,11 @@ AmberC.prototype.check_for_closure_compiler = function(callback) {
 					defaults.closure = false;
 					defaults.closure_parts = false;
 					defaults.closure_full = false;
-					callback();
-					return;
+				} else {
+					console.warn('Closure compiler found at: ' + self.closure_jar);
 				}
+				callback();
+				return;
 			});
 		});
 	} else {
@@ -679,7 +681,7 @@ AmberC.prototype.compose_js_files = function() {
 	program_files.forEach(function(file) {
 		if(path.existsSync(file)) {
 			console.log('Adding : ' + file);
-			console.log(fileStream.write(fs.readFileSync(file)));
+			fileStream.write(fs.readFileSync(file));
 		} else {
 			fileStream.end();
 			throw(new Error('Can not find file ' + file));

+ 42 - 32
js/IDE.deploy.js

@@ -536,21 +536,26 @@ smalltalk.addMethod(
 "_print_",
 smalltalk.method({
 selector: "print:",
-fn: function (aString) {
-    var self = this;
-    var start;
-    var stop;
-    start = smalltalk.send(smalltalk.HashedCollection || HashedCollection, "_new", []);
-    stop = smalltalk.send(smalltalk.HashedCollection || HashedCollection, "_new", []);
-    smalltalk.send(start, "_at_put_", ["line", smalltalk.send(smalltalk.send(self['@editor'], "_getCursor_", [false]), "_line", [])]);
-    smalltalk.send(start, "_at_put_", ["ch", smalltalk.send(smalltalk.send(self['@editor'], "_getCursor_", [false]), "_ch", [])]);
-    smalltalk.send(stop, "_at_put_", ["line", smalltalk.send(start, "_at_", ["line"])]);
-    smalltalk.send(stop, "_at_put_", ["ch", smalltalk.send(smalltalk.send(smalltalk.send(start, "_at_", ["ch"]), "__plus", [smalltalk.send(aString, "_size", [])]), "__plus", [2])]);
-    smalltalk.send(self['@editor'], "_replaceSelection_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self['@editor'], "_getSelection", []), "__comma", [" "]), "__comma", [aString]), "__comma", [" "])]);
-    smalltalk.send(self['@editor'], "_setCursor_", [smalltalk.send(self['@editor'], "_getCursor_", [true])]);
-    smalltalk.send(self['@editor'], "_setSelection_end_", [stop, start]);
-    return self;
-}
+fn: function (aString){
+var self=this;
+var start;
+var stop;
+var currentLine;
+currentLine=smalltalk.send(smalltalk.send(self["@editor"],"_getCursor_",[false]),"_line",[]);
+start=smalltalk.send((smalltalk.HashedCollection || HashedCollection),"_new",[]);
+smalltalk.send(start,"_at_put_",["line",currentLine]);
+smalltalk.send(start,"_at_put_",["ch",smalltalk.send(smalltalk.send(self["@editor"],"_getCursor_",[false]),"_ch",[])]);
+smalltalk.send(smalltalk.send(self["@editor"],"_getSelection",[]),"_ifEmpty_",[(function(){
+smalltalk.send(start,"_at_put_",["ch",smalltalk.send(smalltalk.send(self["@editor"],"_getLine_",[currentLine]),"_size",[])]);
+return smalltalk.send(self["@editor"],"_setSelection_end_",[smalltalk.HashedCollection._fromPairs_([smalltalk.send("line","__minus_gt",[currentLine]),smalltalk.send("ch","__minus_gt",[(0)])]),start]);
+})]);
+stop=smalltalk.send((smalltalk.HashedCollection || HashedCollection),"_new",[]);
+smalltalk.send(stop,"_at_put_",["line",currentLine]);
+smalltalk.send(stop,"_at_put_",["ch",smalltalk.send(smalltalk.send(smalltalk.send(start,"_at_",["ch"]),"__plus",[smalltalk.send(aString,"_size",[])]),"__plus",[(2)])]);
+smalltalk.send(self["@editor"],"_replaceSelection_",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self["@editor"],"_getSelection",[]),"__comma",[" "]),"__comma",[aString]),"__comma",[" "])]);
+smalltalk.send(self["@editor"],"_setCursor_",[smalltalk.send(self["@editor"],"_getCursor_",[true])]);
+smalltalk.send(self["@editor"],"_setSelection_end_",[stop,start]);
+return self}
 }),
 smalltalk.SourceArea);
 
@@ -1314,11 +1319,12 @@ smalltalk.addMethod(
 "_ajaxPutAt_data_",
 smalltalk.method({
 selector: "ajaxPutAt:data:",
-fn: function (anURL, aString) {
-    var self = this;
-    smalltalk.send(jQuery, "_ajax_options_", [anURL, smalltalk.HashedCollection._fromPairs_([smalltalk.send("type", "__minus_gt", ["PUT"]), smalltalk.send("data", "__minus_gt", [aString]), smalltalk.send("contentType", "__minus_gt", ["text/plain;charset=UTF-8"]), smalltalk.send("error", "__minus_gt", [function () {return smalltalk.send(window, "_alert_", [smalltalk.send("PUT request failed at:  ", "__comma", [anURL])]);}])])]);
-    return self;
-}
+fn: function (aURL,aString){
+var self=this;
+smalltalk.send(jQuery,"_ajax_options_",[aURL,smalltalk.HashedCollection._fromPairs_([smalltalk.send("type","__minus_gt",["PUT"]),smalltalk.send("data","__minus_gt",[aString]),smalltalk.send("contentType","__minus_gt",["text/plain;charset=UTF-8"]),smalltalk.send("error","__minus_gt",[(function(xhr){
+return smalltalk.send(window,"_alert_",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send("Commiting ","__comma",[aURL]),"__comma",[" failed with reason: \x22"]),"__comma",[smalltalk.send(xhr,"_responseText",[])]),"__comma",["\x22"])]);
+})])])]);
+return self}
 }),
 smalltalk.Browser);
 
@@ -1425,18 +1431,22 @@ smalltalk.addMethod(
 "_commitPackage",
 smalltalk.method({
 selector: "commitPackage",
-fn: function () {
-    var self = this;
-    if (($receiver = self['@selectedPackage']) == nil ||
-        $receiver == undefined) {
-        self['@selectedPackage'];
-    } else {
-        var package;
-        package = smalltalk.send(smalltalk.Package || Package, "_named_", [self['@selectedPackage']]);
-        smalltalk.send([smalltalk.send(smalltalk.Exporter || Exporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathJs", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".js"])]), smalltalk.send(smalltalk.StrippedExporter || StrippedExporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathJs", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".deploy.js"])]), smalltalk.send(smalltalk.ChunkExporter || ChunkExporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathSt", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".st"])])], "_do_", [function (commitStrategy) {var fileContents;fileContents = smalltalk.send(smalltalk.send(smalltalk.send(commitStrategy, "_key", []), "_new", []), "_exportPackage_", [self['@selectedPackage']]);return smalltalk.send(self, "_ajaxPutAt_data_", [smalltalk.send(commitStrategy, "_value", []), fileContents]);}]);
-    }
-    return self;
-}
+fn: function (){
+var self=this;
+if(($receiver = self["@selectedPackage"]) == nil || $receiver == undefined){
+self["@selectedPackage"];
+} else {
+var package;
+package=smalltalk.send((smalltalk.Package || Package),"_named_",[self["@selectedPackage"]]);
+package;
+smalltalk.send([smalltalk.send((smalltalk.Exporter || Exporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathJs",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".js"])]),smalltalk.send((smalltalk.StrippedExporter || StrippedExporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathJs",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".deploy.js"])]),smalltalk.send((smalltalk.ChunkExporter || ChunkExporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathSt",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".st"])])],"_do_",[(function(commitStrategy){
+var fileContents;
+fileContents=smalltalk.send(smalltalk.send(smalltalk.send(commitStrategy,"_key",[]),"_new",[]),"_exportPackage_",[self["@selectedPackage"]]);
+fileContents;
+return smalltalk.send(self,"_ajaxPutAt_data_",[smalltalk.send(commitStrategy,"_value",[]),fileContents]);
+})]);
+};
+return self}
 }),
 smalltalk.Browser);
 

+ 48 - 38
js/IDE.js

@@ -717,24 +717,29 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "print:",
 category: 'actions',
-fn: function (aString) {
-    var self = this;
-    var start;
-    var stop;
-    start = smalltalk.send(smalltalk.HashedCollection || HashedCollection, "_new", []);
-    stop = smalltalk.send(smalltalk.HashedCollection || HashedCollection, "_new", []);
-    smalltalk.send(start, "_at_put_", ["line", smalltalk.send(smalltalk.send(self['@editor'], "_getCursor_", [false]), "_line", [])]);
-    smalltalk.send(start, "_at_put_", ["ch", smalltalk.send(smalltalk.send(self['@editor'], "_getCursor_", [false]), "_ch", [])]);
-    smalltalk.send(stop, "_at_put_", ["line", smalltalk.send(start, "_at_", ["line"])]);
-    smalltalk.send(stop, "_at_put_", ["ch", smalltalk.send(smalltalk.send(smalltalk.send(start, "_at_", ["ch"]), "__plus", [smalltalk.send(aString, "_size", [])]), "__plus", [2])]);
-    smalltalk.send(self['@editor'], "_replaceSelection_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self['@editor'], "_getSelection", []), "__comma", [" "]), "__comma", [aString]), "__comma", [" "])]);
-    smalltalk.send(self['@editor'], "_setCursor_", [smalltalk.send(self['@editor'], "_getCursor_", [true])]);
-    smalltalk.send(self['@editor'], "_setSelection_end_", [stop, start]);
-    return self;
-},
+fn: function (aString){
+var self=this;
+var start;
+var stop;
+var currentLine;
+currentLine=smalltalk.send(smalltalk.send(self["@editor"],"_getCursor_",[false]),"_line",[]);
+start=smalltalk.send((smalltalk.HashedCollection || HashedCollection),"_new",[]);
+smalltalk.send(start,"_at_put_",["line",currentLine]);
+smalltalk.send(start,"_at_put_",["ch",smalltalk.send(smalltalk.send(self["@editor"],"_getCursor_",[false]),"_ch",[])]);
+smalltalk.send(smalltalk.send(self["@editor"],"_getSelection",[]),"_ifEmpty_",[(function(){
+smalltalk.send(start,"_at_put_",["ch",smalltalk.send(smalltalk.send(self["@editor"],"_getLine_",[currentLine]),"_size",[])]);
+return smalltalk.send(self["@editor"],"_setSelection_end_",[smalltalk.HashedCollection._fromPairs_([smalltalk.send("line","__minus_gt",[currentLine]),smalltalk.send("ch","__minus_gt",[(0)])]),start]);
+})]);
+stop=smalltalk.send((smalltalk.HashedCollection || HashedCollection),"_new",[]);
+smalltalk.send(stop,"_at_put_",["line",currentLine]);
+smalltalk.send(stop,"_at_put_",["ch",smalltalk.send(smalltalk.send(smalltalk.send(start,"_at_",["ch"]),"__plus",[smalltalk.send(aString,"_size",[])]),"__plus",[(2)])]);
+smalltalk.send(self["@editor"],"_replaceSelection_",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self["@editor"],"_getSelection",[]),"__comma",[" "]),"__comma",[aString]),"__comma",[" "])]);
+smalltalk.send(self["@editor"],"_setCursor_",[smalltalk.send(self["@editor"],"_getCursor_",[true])]);
+smalltalk.send(self["@editor"],"_setSelection_end_",[stop,start]);
+return self},
 args: ["aString"],
-source: "print: aString\x0a\x09| start stop |\x0a\x09start := HashedCollection new.\x0a\x09stop := HashedCollection new.\x0a\x09start at: 'line' put: (editor getCursor: false) line.\x0a\x09start at: 'ch' put: (editor getCursor: false) ch.\x0a\x09stop at: 'line' put: (start at: 'line').\x0a\x09stop at: 'ch' put: ((start at: 'ch') + aString size + 2).\x0a\x09editor replaceSelection: (editor getSelection, ' ', aString, ' ').\x0a\x09editor setCursor: (editor getCursor: true).\x0a\x09editor setSelection: stop end: start",
-messageSends: ["new", "at:put:", "line", "getCursor:", "ch", "at:", "+", "size", "replaceSelection:", ",", "getSelection", "setCursor:", "setSelection:end:"],
+source: "print: aString\x0a\x09| start stop currentLine |\x0a    currentLine := (editor getCursor: false) line.\x0a\x09start := HashedCollection new.\x0a\x09start at: 'line' put: currentLine.\x0a\x09start at: 'ch' put: (editor getCursor: false) ch.\x0a    (editor getSelection) ifEmpty: [\x0a    \x09\x22select current line if selection is empty\x22\x0a    \x09start at: 'ch' put: (editor getLine: currentLine) size.\x0a        editor setSelection: #{'line' -> currentLine. 'ch' -> 0} end: start.\x0a    ].\x0a\x09stop := HashedCollection new.\x0a\x09stop at: 'line' put: currentLine.\x0a\x09stop at: 'ch' put: ((start at: 'ch') + aString size + 2).\x0a\x0a\x09editor replaceSelection: (editor getSelection, ' ', aString, ' ').\x0a\x09editor setCursor: (editor getCursor: true).\x0a\x09editor setSelection: stop end: start",
+messageSends: ["line", "getCursor:", "new", "at:put:", "ch", "ifEmpty:", "size", "getLine:", "setSelection:end:", "->", "getSelection", "+", "at:", "replaceSelection:", ",", "setCursor:"],
 referencedClasses: ["HashedCollection"]
 }),
 smalltalk.SourceArea);
@@ -1760,14 +1765,15 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "ajaxPutAt:data:",
 category: 'network',
-fn: function (anURL, aString) {
-    var self = this;
-    smalltalk.send(jQuery, "_ajax_options_", [anURL, smalltalk.HashedCollection._fromPairs_([smalltalk.send("type", "__minus_gt", ["PUT"]), smalltalk.send("data", "__minus_gt", [aString]), smalltalk.send("contentType", "__minus_gt", ["text/plain;charset=UTF-8"]), smalltalk.send("error", "__minus_gt", [function () {return smalltalk.send(window, "_alert_", [smalltalk.send("PUT request failed at:  ", "__comma", [anURL])]);}])])]);
-    return self;
-},
-args: ["anURL", "aString"],
-source: "ajaxPutAt: anURL data: aString\x0a\x09jQuery \x0a\x09\x09ajax: anURL\x09options: #{\x09'type' -> 'PUT'.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'data' -> aString.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'contentType' -> 'text/plain;charset=UTF-8'.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'error' -> [window alert: 'PUT request failed at:  ', anURL] }",
-messageSends: ["ajax:options:", "->", "alert:", ","],
+fn: function (aURL,aString){
+var self=this;
+smalltalk.send(jQuery,"_ajax_options_",[aURL,smalltalk.HashedCollection._fromPairs_([smalltalk.send("type","__minus_gt",["PUT"]),smalltalk.send("data","__minus_gt",[aString]),smalltalk.send("contentType","__minus_gt",["text/plain;charset=UTF-8"]),smalltalk.send("error","__minus_gt",[(function(xhr){
+return smalltalk.send(window,"_alert_",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send("Commiting ","__comma",[aURL]),"__comma",[" failed with reason: \x22"]),"__comma",[smalltalk.send(xhr,"_responseText",[])]),"__comma",["\x22"])]);
+})])])]);
+return self},
+args: ["aURL", "aString"],
+source: "ajaxPutAt: aURL data: aString\x0a\x09jQuery\x0a\x09\x09ajax: aURL\x09options: #{\x09'type' -> 'PUT'.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'data' -> aString.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'contentType' -> 'text/plain;charset=UTF-8'.\x0a\x09\x09\x09\x09\x09\x09\x09\x09'error' -> [:xhr | window alert: 'Commiting ' , aURL , ' failed with reason: \x22' , (xhr responseText) , '\x22'] }",
+messageSends: ["ajax:options:", "->", "alert:", ",", "responseText"],
 referencedClasses: []
 }),
 smalltalk.Browser);
@@ -1906,20 +1912,24 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "commitPackage",
 category: 'actions',
-fn: function () {
-    var self = this;
-    if (($receiver = self['@selectedPackage']) == nil ||
-        $receiver == undefined) {
-        self['@selectedPackage'];
-    } else {
-        var package;
-        package = smalltalk.send(smalltalk.Package || Package, "_named_", [self['@selectedPackage']]);
-        smalltalk.send([smalltalk.send(smalltalk.Exporter || Exporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathJs", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".js"])]), smalltalk.send(smalltalk.StrippedExporter || StrippedExporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathJs", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".deploy.js"])]), smalltalk.send(smalltalk.ChunkExporter || ChunkExporter, "__minus_gt", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package, "_commitPathSt", []), "__comma", ["/"]), "__comma", [self['@selectedPackage']]), "__comma", [".st"])])], "_do_", [function (commitStrategy) {var fileContents;fileContents = smalltalk.send(smalltalk.send(smalltalk.send(commitStrategy, "_key", []), "_new", []), "_exportPackage_", [self['@selectedPackage']]);return smalltalk.send(self, "_ajaxPutAt_data_", [smalltalk.send(commitStrategy, "_value", []), fileContents]);}]);
-    }
-    return self;
-},
+fn: function (){
+var self=this;
+if(($receiver = self["@selectedPackage"]) == nil || $receiver == undefined){
+self["@selectedPackage"];
+} else {
+var package;
+package=smalltalk.send((smalltalk.Package || Package),"_named_",[self["@selectedPackage"]]);
+package;
+smalltalk.send([smalltalk.send((smalltalk.Exporter || Exporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathJs",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".js"])]),smalltalk.send((smalltalk.StrippedExporter || StrippedExporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathJs",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".deploy.js"])]),smalltalk.send((smalltalk.ChunkExporter || ChunkExporter),"__minus_gt",[smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(package,"_commitPathSt",[]),"__comma",["/"]),"__comma",[self["@selectedPackage"]]),"__comma",[".st"])])],"_do_",[(function(commitStrategy){
+var fileContents;
+fileContents=smalltalk.send(smalltalk.send(smalltalk.send(commitStrategy,"_key",[]),"_new",[]),"_exportPackage_",[self["@selectedPackage"]]);
+fileContents;
+return smalltalk.send(self,"_ajaxPutAt_data_",[smalltalk.send(commitStrategy,"_value",[]),fileContents]);
+})]);
+};
+return self},
 args: [],
-source: "commitPackage\x0a\x09selectedPackage ifNotNil: [ |package|\x0a               \x09\x09\x09\x09\x09\x09 package := Package named: selectedPackage.\x0a               \x09\x09\x09\x09\x09\x09 {\x09Exporter \x09\x09\x09-> (package commitPathJs, '/', selectedPackage, '.js').\x0a                        \x09\x09\x09\x09\x09StrippedExporter \x09-> (package commitPathJs, '/', selectedPackage, '.deploy.js').\x0a                       \x09\x09\x09\x09\x09\x09 ChunkExporter \x09\x09-> (package commitPathSt, '/', selectedPackage, '.st') \x09\x09\x09} \x0a                 \x0a                \x09\x09\x09\x09\x09\x09do: [:commitStrategy| |fileContents|\x0a                                                                     \x09fileContents := (commitStrategy key new exportPackage: selectedPackage).\x0a                                                                     \x09self ajaxPutAt: commitStrategy value data:  fileContents]\x0a         \x09\x09\x09\x09\x09\x09]",
+source: "commitPackage\x0a\x09selectedPackage ifNotNil: [ |package|\x0a\x09\x09package := Package named: selectedPackage.\x0a\x09\x09{  \x09Exporter              -> (package commitPathJs, '/', selectedPackage, '.js').\x0a\x09\x09\x09StrippedExporter -> (package commitPathJs, '/', selectedPackage, '.deploy.js').\x0a\x09\x09\x09ChunkExporter    -> (package commitPathSt, '/', selectedPackage, '.st')\x0a\x09\x09} do: [:commitStrategy| |fileContents|\x0a\x09\x09\x09fileContents := (commitStrategy key new exportPackage: selectedPackage).\x0a\x09\x09\x09self ajaxPutAt: commitStrategy value data:  fileContents\x0a  \x09\x09]\x0a\x09]",
 messageSends: ["ifNotNil:", "named:", "do:", "exportPackage:", "new", "key", "ajaxPutAt:data:", "value", "->", ",", "commitPathJs", "commitPathSt"],
 referencedClasses: ["Package", "Exporter", "StrippedExporter", "ChunkExporter"]
 }),

+ 12 - 4
js/boot.js

@@ -111,11 +111,19 @@ function Smalltalk() {
 	/* List of all reserved words in JavaScript. They may not be used as variables
 	   in Smalltalk. */
 
-	st.reservedWords = ['break', 'case', 'catch', 'char', 'class', 'continue', 'debugger',
+	// list of reserved JavaScript keywords as of
+	//   http://es5.github.com/#x7.6.1.1
+	// and
+	//   http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.6.1
+	st.reservedWords = ['break', 'case', 'catch', 'continue', 'debugger',
 		'default', 'delete', 'do', 'else', 'finally', 'for', 'function',
-		'if', 'in', 'instanceof', 'native', 'new', 'private', 'protected',
-		'public', 'return', 'static', 'switch', 'this', 'throw',
-		'try', 'typeof', 'var', 'void', 'while', 'with', 'yield'];
+		'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw',
+		'try', 'typeof', 'var', 'void', 'while', 'with',
+		// ES5: future use: http://es5.github.com/#x7.6.1.2
+		'class', 'const', 'enum', 'export', 'extends', 'import', 'super',
+		// ES5: future use in strict mode
+		'implements', 'interface', 'let', 'package', 'private', 'protected',
+		'public', 'static', 'yield'];
 
     var initialized = false;
 

+ 3 - 0
js/lib/CodeMirror/codemirror.css

@@ -80,6 +80,7 @@
   word-wrap: normal;
   line-height: inherit;
   color: inherit;
+  overflow: visible;
 }
 
 .CodeMirror-wrap pre {
@@ -152,6 +153,8 @@ div.CodeMirror-selected { background: #d9d9d9; }
 .cm-s-default span.cm-quote {color: #090;}
 .cm-s-default span.cm-hr {color: #999;}
 .cm-s-default span.cm-link {color: #00c;}
+span.cm-negative {color: #d44;}
+span.cm-positive {color: #292;}
 
 span.cm-header, span.cm-strong {font-weight: bold;}
 span.cm-em {font-style: italic;}

+ 104 - 55
js/lib/CodeMirror/codemirror.js

@@ -1,4 +1,4 @@
-// CodeMirror version 2.34
+// CodeMirror version 2.37
 
 // All functions that need access to the editor's state live inside
 // the CodeMirror function. Below that, at the bottom of the file,
@@ -77,7 +77,7 @@ window.CodeMirror = (function() {
     // Selection-related flags. shiftSelecting obviously tracks
     // whether the user is holding shift.
     var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
-        overwrite = false, suppressEdits = false;
+        overwrite = false, suppressEdits = false, pasteIncoming = false;
     // Variables used by startOperation/endOperation to track what
     // happened during the operation.
     var updateInput, userSelChange, changes, textChanged, selectionChanged,
@@ -130,7 +130,7 @@ window.CodeMirror = (function() {
       connect(scroller, "drop", operation(onDrop));
     }
     connect(scroller, "paste", function(){focusInput(); fastPoll();});
-    connect(input, "paste", fastPoll);
+    connect(input, "paste", function(){pasteIncoming = true; fastPoll();});
     connect(input, "cut", operation(function(){
       if (!options.readOnly) replaceSelection("");
     }));
@@ -169,6 +169,8 @@ window.CodeMirror = (function() {
         else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
         else if (option == "tabSize") updateDisplay(true);
         else if (option == "keyMap") keyMapChanged();
+        else if (option == "tabindex") input.tabIndex = value;
+        else if (option == "showCursorWhenSelecting") updateSelection();
         if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
             option == "theme" || option == "lineNumberFormatter") {
           gutterChanged();
@@ -231,6 +233,7 @@ window.CodeMirror = (function() {
         var off = eltOffset(lineSpace);
         return coordsChar(coords.x - off.left, coords.y - off.top);
       },
+      defaultTextHeight: function() { return textHeight(); },
       markText: operation(markText),
       setBookmark: setBookmark,
       findMarksAt: findMarksAt,
@@ -280,7 +283,9 @@ window.CodeMirror = (function() {
       lineCount: function() {return doc.size;},
       clipPos: clipPos,
       getCursor: function(start) {
-        if (start == null) start = sel.inverted;
+        if (start == null || start == "head") start = sel.inverted;
+        if (start == "anchor") start = !sel.inverted;
+        if (start == "end") start = false;
         return copyPos(start ? sel.from : sel.to);
       },
       somethingSelected: function() {return !posEq(sel.from, sel.to);},
@@ -345,6 +350,11 @@ window.CodeMirror = (function() {
         return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
                 height: scrollbar.scrollHeight, width: scroller.scrollWidth};
       },
+      scrollIntoView: function(pos) {
+        var coords = localCoords(pos ? clipPos(pos) : sel.inverted ? sel.from : sel.to);
+        scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
+      },
+
       setSize: function(width, height) {
         function interpret(val) {
           val = String(val);
@@ -394,7 +404,7 @@ window.CodeMirror = (function() {
     }
 
     function onScrollBar(e) {
-      if (scrollbar.scrollTop != lastScrollTop) {
+      if (Math.abs(scrollbar.scrollTop - lastScrollTop) > 1) {
         lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
         updateDisplay([]);
       }
@@ -403,7 +413,7 @@ window.CodeMirror = (function() {
     function onScrollMain(e) {
       if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
         gutter.style.left = scroller.scrollLeft + "px";
-      if (scroller.scrollTop != lastScrollTop) {
+      if (Math.abs(scroller.scrollTop - lastScrollTop) > 1) {
         lastScrollTop = scroller.scrollTop;
         if (scrollbar.scrollTop != lastScrollTop)
           scrollbar.scrollTop = lastScrollTop;
@@ -488,8 +498,12 @@ window.CodeMirror = (function() {
 
       function doSelect(cur) {
         if (type == "single") {
-          setSelectionUser(start, cur);
-        } else if (type == "double") {
+          setSelectionUser(clipPos(start), cur);
+          return;
+        }
+        startstart = clipPos(startstart);
+        startend = clipPos(startend);
+        if (type == "double") {
           var word = findWordAt(cur);
           if (posLess(cur, startstart)) setSelectionUser(word.from, startend);
           else setSelectionUser(startstart, word.to);
@@ -614,7 +628,6 @@ window.CodeMirror = (function() {
       }, 50);
 
       var name = keyNames[e_prop(e, "keyCode")], handled = false;
-      var flipCtrlCmd = opera && mac;
       if (name == null || e.altGraphKey) return false;
       if (e_prop(e, "altKey")) name = "Alt-" + name;
       if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
@@ -636,7 +649,7 @@ window.CodeMirror = (function() {
       if (handled) {
         e_preventDefault(e);
         restartBlink();
-        if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
+        if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
       }
       return handled;
     }
@@ -956,12 +969,13 @@ window.CodeMirror = (function() {
       while (same < l && prevInput[same] == text[same]) ++same;
       if (same < prevInput.length)
         sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
-      else if (overwrite && posEq(sel.from, sel.to))
+      else if (overwrite && posEq(sel.from, sel.to) && !pasteIncoming)
         sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
       replaceSelection(text.slice(same), "end");
       if (text.length > 1000) { input.value = prevInput = ""; }
       else prevInput = text;
       if (!nestedOperation) endOperation();
+      pasteIncoming = false;
       return true;
     }
     function resetInput(user) {
@@ -973,7 +987,8 @@ window.CodeMirror = (function() {
     }
 
     function focusInput() {
-      if (options.readOnly != "nocursor") input.focus();
+      if (options.readOnly != "nocursor" && (ie_lt9 || document.activeElement != input))
+        input.focus();
     }
 
     function scrollCursorIntoView() {
@@ -1238,18 +1253,20 @@ window.CodeMirror = (function() {
       var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
       inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
       inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
-      if (collapsed) {
+      if (collapsed || options.showCursorWhenSelecting) {
         cursor.style.top = headPos.y + "px";
         cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
         cursor.style.display = "";
-        selectionDiv.style.display = "none";
       } else {
+        cursor.style.display = "none";
+      }
+      if (!collapsed) {
         var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
         var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
         var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
         var add = function(left, top, right, height) {
           var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
-                                  : "right: " + right + "px";
+                                  : "right: " + (right - 1) + "px";
           fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
                                    "px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
         };
@@ -1264,8 +1281,9 @@ window.CodeMirror = (function() {
         if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
           add(0, toPos.y, clientWidth - toPos.x, th);
         removeChildrenAndAdd(selectionDiv, fragment);
-        cursor.style.display = "none";
         selectionDiv.style.display = "";
+      } else {
+        selectionDiv.style.display = "none";
       }
     }
 
@@ -1418,7 +1436,7 @@ window.CodeMirror = (function() {
         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);};
+                    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;
       }
@@ -1462,6 +1480,7 @@ window.CodeMirror = (function() {
 
       if (indentString != curSpaceString)
         replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
+      line.stateAfter = null;
     }
 
     function loadMode() {
@@ -1507,18 +1526,17 @@ window.CodeMirror = (function() {
 
     function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; }
     TextMarker.prototype.clear = operation(function() {
-      var min = Infinity, max = -Infinity;
+      var min, max;
       for (var i = 0; i < this.lines.length; ++i) {
         var line = this.lines[i];
-        var span = getMarkedSpanFor(line.markedSpans, this, true);
-        if (span.from != null || span.to != null) {
-          var lineN = lineNo(line);
-          min = Math.min(min, lineN); max = Math.max(max, lineN);
-        }
+        var span = getMarkedSpanFor(line.markedSpans, this);
+        if (span.from != null) min = lineNo(line);
+        if (span.to != null) max = lineNo(line);
+        line.markedSpans = removeMarkedSpan(line.markedSpans, span);
       }
-      if (min != Infinity)
-        changes.push({from: min, to: max + 1});
+      if (min != null) changes.push({from: min, to: max + 1});
       this.lines.length = 0;
+      this.explicitlyCleared = true;
     });
     TextMarker.prototype.find = function() {
       var from, to;
@@ -1545,7 +1563,7 @@ window.CodeMirror = (function() {
         var span = {from: curLine == from.line ? from.ch : null,
                     to: curLine == to.line ? to.ch : null,
                     marker: marker};
-        (line.markedSpans || (line.markedSpans = [])).push(span);
+        line.markedSpans = (line.markedSpans || []).concat([span]);
         marker.lines.push(line);
         ++curLine;
       });
@@ -1556,8 +1574,9 @@ window.CodeMirror = (function() {
     function setBookmark(pos) {
       pos = clipPos(pos);
       var marker = new TextMarker("bookmark"), line = getLine(pos.line);
+      history.addChange(pos.line, 1, [newHL(line.text, line.markedSpans)], true);
       var span = {from: pos.ch, to: pos.ch, marker: marker};
-      (line.markedSpans || (line.markedSpans = [])).push(span);
+      line.markedSpans = (line.markedSpans || []).concat([span]);
       marker.lines.push(line);
       return marker;
     }
@@ -1646,8 +1665,6 @@ window.CodeMirror = (function() {
 
     function measureLine(line, ch) {
       if (ch == 0) return {top: 0, left: 0};
-      var wbr = options.lineWrapping && ch < line.text.length &&
-                spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
       var pre = lineContent(line, ch);
       removeChildrenAndAdd(measure, pre);
       var anchor = pre.anchor;
@@ -1980,6 +1997,7 @@ window.CodeMirror = (function() {
       if (extensions.propertyIsEnumerable(ext) &&
           !instance.propertyIsEnumerable(ext))
         instance[ext] = extensions[ext];
+    for (var i = 0; i < initHooks.length; ++i) initHooks[i](instance);
     return instance;
   } // (end of function CodeMirror)
 
@@ -2003,6 +2021,7 @@ window.CodeMirror = (function() {
     gutter: false,
     fixedGutter: false,
     firstLineNumber: 1,
+    showCursorWhenSelecting: false,
     readOnly: false,
     dragDrop: true,
     onChange: null,
@@ -2054,7 +2073,11 @@ window.CodeMirror = (function() {
     var modeObj = mfactory(options, spec);
     if (modeExtensions.hasOwnProperty(spec.name)) {
       var exts = modeExtensions[spec.name];
-      for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop];
+      for (var prop in exts) {
+        if (!exts.hasOwnProperty(prop)) continue;
+        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
+        modeObj[prop] = exts[prop];
+      }
     }
     modeObj.name = spec.name;
     return modeObj;
@@ -2077,6 +2100,9 @@ window.CodeMirror = (function() {
     extensions[name] = func;
   };
 
+  var initHooks = [];
+  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+
   var modeExtensions = CodeMirror.modeExtensions = {};
   CodeMirror.extendMode = function(mode, properties) {
     var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
@@ -2169,7 +2195,7 @@ window.CodeMirror = (function() {
   keyMap.emacsy = {
     "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
     "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
-    "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
+    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
     "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
   };
 
@@ -2206,6 +2232,7 @@ window.CodeMirror = (function() {
     var name = keyNames[e_prop(event, "keyCode")];
     return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
   }
+  CodeMirror.isModifierKey = isModifierKey;
 
   CodeMirror.fromTextArea = function(textarea, options) {
     if (!options) options = {};
@@ -2226,15 +2253,13 @@ window.CodeMirror = (function() {
     if (textarea.form) {
       // Deplorable hack to make the submit method do the right thing.
       var rmSubmit = connect(textarea.form, "submit", save, true);
-      if (typeof textarea.form.submit == "function") {
-        var realSubmit = textarea.form.submit;
-        textarea.form.submit = function wrappedSubmit() {
-          save();
-          textarea.form.submit = realSubmit;
-          textarea.form.submit();
-          textarea.form.submit = wrappedSubmit;
-        };
-      }
+      var form = textarea.form, realSubmit = form.submit;
+      textarea.form.submit = function wrappedSubmit() {
+        save();
+        form.submit = realSubmit;
+        form.submit();
+        form.submit = wrappedSubmit;
+      };
     }
 
     textarea.style.display = "none";
@@ -2256,18 +2281,24 @@ window.CodeMirror = (function() {
     return instance;
   };
 
-  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
+  var gecko = /gecko\/\d/i.test(navigator.userAgent);
   var ie = /MSIE \d/.test(navigator.userAgent);
   var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
   var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
   var quirksMode = ie && document.documentMode == 5;
   var webkit = /WebKit\//.test(navigator.userAgent);
+  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
   var chrome = /Chrome\//.test(navigator.userAgent);
   var opera = /Opera\//.test(navigator.userAgent);
   var safari = /Apple Computer/.test(navigator.vendor);
   var khtml = /KHTML\//.test(navigator.userAgent);
   var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
 
+  var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
+  if (opera_version) opera_version = Number(opera_version[1]);
+  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
+  var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
+
   // Utility functions for working with state. Exported because modes
   // sometimes need to do this.
   function copyState(mode, state) {
@@ -2355,16 +2386,20 @@ window.CodeMirror = (function() {
     this.from = from; this.to = to; this.marker = marker;
   }
 
-  function getMarkedSpanFor(spans, marker, del) {
+  function getMarkedSpanFor(spans, marker) {
     if (spans) for (var i = 0; i < spans.length; ++i) {
       var span = spans[i];
-      if (span.marker == marker) {
-        if (del) spans.splice(i, 1);
-        return span;
-      }
+      if (span.marker == marker) return span;
     }
   }
 
+  function removeMarkedSpan(spans, span) {
+    var r;
+    for (var i = 0; i < spans.length; ++i)
+      if (spans[i] != span) (r || (r = [])).push(spans[i]);
+    return r;
+  }
+
   function markedSpansBefore(old, startCh, endCh) {
     if (old) for (var i = 0, nw; i < old.length; ++i) {
       var span = old[i], marker = span.marker;
@@ -2448,7 +2483,15 @@ window.CodeMirror = (function() {
   // hl stands for history-line, a data structure that can be either a
   // string (line without markers) or a {text, markedSpans} object.
   function hlText(val) { return typeof val == "string" ? val : val.text; }
-  function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; }
+  function hlSpans(val) {
+    if (typeof val == "string") return null;
+    var spans = val.markedSpans, out = null;
+    for (var i = 0; i < spans.length; ++i) {
+      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
+      else if (out) out.push(spans[i]);
+    }
+    return !out ? spans : out.length ? out : null;
+  }
   function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
 
   function detachMarkedSpans(line) {
@@ -2584,13 +2627,17 @@ window.CodeMirror = (function() {
         span = function(html, text, style) {
           var l = text.length;
           if (wrapAt >= outPos && wrapAt < outPos + l) {
-            if (wrapAt > outPos) {
-              span_(html, text.slice(0, wrapAt - outPos), style);
+            var cut = wrapAt - outPos;
+            if (cut) {
+              span_(html, text.slice(0, cut), style);
               // See comment at the definition of spanAffectsWrapping
-              if (compensateForWrapping) html.appendChild(elt("wbr"));
+              if (compensateForWrapping) {
+                var view = text.slice(cut - 1, cut + 1);
+                if (spanAffectsWrapping.test(view)) html.appendChild(elt("wbr"));
+                else if (!ie_lt8 && /\w\w/.test(view)) html.appendChild(document.createTextNode("\u200d"));
+              }
             }
             html.appendChild(anchor);
-            var cut = wrapAt - outPos;
             span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
             if (opera) span_(html, text.slice(cut + 1), style);
             wrapAt--;
@@ -2820,7 +2867,7 @@ window.CodeMirror = (function() {
     if (line.parent == null) return null;
     var cur = line.parent, no = indexOf(cur.lines, line);
     for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
-      for (var i = 0, e = chunk.children.length; ; ++i) {
+      for (var i = 0; ; ++i) {
         if (chunk.children[i] == cur) break;
         no += chunk.children[i].chunkSize();
       }
@@ -2874,7 +2921,7 @@ window.CodeMirror = (function() {
       var time = +new Date, cur = lst(this.done), last = cur && lst(cur);
       var dtime = time - this.time;
 
-      if (this.compound && cur && !this.closed) {
+      if (cur && !this.closed && this.compound) {
         cur.push({start: start, added: added, old: old});
       } else if (dtime > 400 || !last || this.closed ||
                  last.start > start + old.length || last.start + last.added < start) {
@@ -3080,8 +3127,10 @@ window.CodeMirror = (function() {
       if (collection[i] == elt) return i;
     return -1;
   }
+  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
   function isWordChar(ch) {
-    return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
+    return /\w/.test(ch) || ch > "\x80" &&
+      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
   }
 
   // See if "".split is the broken IE version, if so, provide an
@@ -3137,7 +3186,7 @@ window.CodeMirror = (function() {
     for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
   })();
 
-  CodeMirror.version = "2.34";
+  CodeMirror.version = "2.37";
 
   return CodeMirror;
 })();

+ 1 - 1
repl/Makefile

@@ -1,5 +1,5 @@
 repl.js: REPL.st
-	../bin/amberc -m Repl REPL.st amber
+	../bin/amberc -m Repl -l Compiler-Core,Compiler-Exceptions,Compiler-AST,Compiler-IR,Compiler-Inlining,Compiler-Semantic,parser REPL.st amber
 
 run: repl.js
 	../bin/amber

File diff suppressed because it is too large
+ 12 - 9
repl/REPL.js


+ 1 - 1
repl/REPL.st

@@ -31,7 +31,7 @@ eval: buffer
 	| result |
 	buffer isEmpty ifFalse: [
 		self try: [
-			result := Compiler new loadExpression: buffer.
+			result := Compiler new evaluateExpression: buffer.
 			Transcript show: result]
 		catch: [:e |
 			e isSmalltalkError

File diff suppressed because it is too large
+ 297 - 45
repl/amber.js


+ 133 - 44
server/FileServer.st

@@ -1,5 +1,5 @@
 Object subclass: #FileServer
-	instanceVariableNames: 'path http fs url port basePath util'
+	instanceVariableNames: 'path http fs url port basePath util username password'
 	package: 'FileServer'!
 
 !FileServer methodsFor: 'accessing'!
@@ -13,7 +13,24 @@ basePath: aString
 !
 
 port
-	^self class port
+	^port
+!
+
+port: aNumber
+	port := aNumber
+!
+
+username: aUsername
+	username := aUsername.
+!
+
+password: aPassword
+	password := aPassword.
+!
+
+username: aUsername password: aPassword
+	username := aUsername.
+	password := aPassword.
 ! !
 
 !FileServer methodsFor: 'initialization'!
@@ -24,7 +41,10 @@ initialize
 	http := self require: 'http'.
 	fs := self require: 'fs'.
 	util := self require: 'util'.
-	url := self require: 'url'
+	url := self require: 'url'.
+	port := self class defaultPort.
+	username := nil.
+	password := nil.
 !
 
 checkDirectoryLayout
@@ -33,7 +53,7 @@ checkDirectoryLayout
 	(path existsSync: self basePath, 'st') ifFalse: [
 		console warn: 'Warning: project directory is missing an "st" directory'].
 	(path existsSync: self basePath, 'js') ifFalse: [
-		console warn: 'Warning: roject directory is missing a "js" directory'].
+		console warn: 'Warning: project directory is missing a "js" directory'].
 ! !
 
 !FileServer methodsFor: 'private'!
@@ -45,26 +65,42 @@ require: aModuleString
 
 writeData: data toFileNamed: aFilename
 	console log: aFilename
+!
+
+isAuthenticated: aRequest response: aResponse
+	"Basic HTTP Auth: http://stackoverflow.com/a/5957629/293175
+	 and https://gist.github.com/1686663"
+	| header token auth parts|
+
+	(username isNil and: [password isNil]) ifTrue: [^true].
+
+	"get authentication header"
+	header := (aRequest headers at: 'authorization') ifNil:[''].
+	(header isEmpty)
+	ifTrue: [^false]
+	ifFalse: [
+		"get authentication token"
+		token := (header tokenize: ' ') ifNil:[''].
+		"convert back from base64"
+		<auth = new Buffer(token[1], 'base64').toString()>.
+		"split token at colon"
+		parts := auth tokenize: ':'.
+
+		((username = (parts at: 1)) and: [password = (parts at: 2)])
+			ifTrue: [^true]
+			ifFalse: [^false]
+	].
 ! !
 
 !FileServer methodsFor: 'request handling'!
 
-respondNotFoundTo: aResponse
-	aResponse 
-		writeHead: 404 options: #{'Content-Type' -> 'text/plain'};
-		write: '404 Not found';
-		end
-!
-
 handleRequest: aRequest respondTo: aResponse
-
 	aRequest method = 'PUT'
 		ifTrue: [self handlePUTRequest: aRequest respondTo: aResponse].
 	aRequest method = 'GET'
 		ifTrue:[self handleGETRequest: aRequest respondTo: aResponse].
 	aRequest method = 'OPTIONS'
 		ifTrue:[self handleOPTIONSRequest: aRequest respondTo: aResponse]
-
 !
 
 handleGETRequest: aRequest respondTo: aResponse
@@ -79,23 +115,27 @@ handleGETRequest: aRequest respondTo: aResponse
 
 handlePUTRequest: aRequest respondTo: aResponse
 	| file stream |
+	(self isAuthenticated: aRequest response: aResponse)
+		ifFalse: [self respondAuthenticationRequiredTo: aResponse].
+
 	file := '.', aRequest url.
 	stream := fs createWriteStream: file.
+
 	stream on: 'error' do: [:error |
-		"TODO: notify Amber about the error, otherwise the user might not notice and lose his work."
 		console warn: 'Error creating WriteStream for file ', file.
 		console warn: '    Did you forget to create the necessary js/ or st/ directory in your project?'.
-		console warn: '    The exact error is: ', error].
-	stream writable ifFalse: [
-		console log: 'Could not write to ', file.
-		^nil].
-        aRequest setEncoding: 'utf8'.
-        aRequest on: 'data' do: [:data | stream write: data].
+		console warn: '    The exact error is: ', error.
+		self respondNotCreatedTo: aResponse].
 
-        aRequest on: 'end' do: [
-                stream end.
-                self respondOKTo: aResponse]
+	stream on: 'close' do: [
+		self respondCreatedTo: aResponse].
 
+	aRequest setEncoding: 'utf8'.
+	aRequest on: 'data' do: [:data |
+		stream write: data].
+
+	aRequest on: 'end' do: [
+		stream writable ifTrue: [stream end]]
 !
 
 handleOPTIONSRequest: aRequest respondTo: aResponse
@@ -136,17 +176,43 @@ respondInternalErrorTo: aResponse
 		end
 !
 
-respondOKTo: aResponse
+respondAuthenticationRequiredTo: aResponse
+	aResponse
+		writeHead: 401 options: #{'WWW-Authenticate' -> 'Basic realm="Secured Developer Area"'};
+		write: '<html><body>Authentication needed</body></html>';
+		end.
+!
+
+respondNotFoundTo: aResponse
 	aResponse 
-		writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'}.
-	aResponse end.
-					
+		writeHead: 404 options: #{'Content-Type' -> 'text/plain'};
+		write: '404 Not found';
+		end
+!
+
+respondNotCreatedTo: aResponse
+	aResponse
+		writeHead: 400 options: #{'Content-Type' -> 'text/plain'};
+		write: 'File could not be created. Did you forget to create the st/js directories on the server?';
+		end.
+!
+
+respondCreatedTo: aResponse
+	aResponse
+		writeHead: 201 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
+		end.
+!
+
+respondOKTo: aResponse
+	aResponse
+		writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
+		end.
 ! !
 
 !FileServer methodsFor: 'starting'!
 
 startOn: aPort
-	port := aPort.
+	self port: aPort.
 	self start
 !
 
@@ -158,16 +224,12 @@ start
 	      listen: self port.
 ! !
 
-FileServer class instanceVariableNames: 'port mimeTypes'!
+FileServer class instanceVariableNames: 'mimeTypes'!
 
 !FileServer class methodsFor: 'accessing'!
 
-port
-	^port ifNil: [4000]
-!
-
-port: aNumber
-	port := aNumber
+defaultPort
+	^4000
 !
 
 defaultMimeTypes
@@ -590,21 +652,48 @@ mimeTypes
 
 mimeTypeFor: aString
 	^self mimeTypes at: (aString replace: '.*[\.]' with: '') ifAbsent: ['text/plain']
+!
+
+commandLineActions
+	^#{
+		'-p' -> [:fileServer :value | fileServer port: value].
+		'--username' -> [:fileServer :value | fileServer username: value].
+		'--password' -> [:fileServer :value | fileServer password: value]
+	}
 ! !
 
 !FileServer class methodsFor: 'initialization'!
 
-main
-	| fileServer arguments portOption portNumber|
-	fileServer := self new.
-	fileServer checkDirectoryLayout.
+createServerWithArguments: options
+	| server actions popFront front optionName optionValue |
+	actions := FileServer commandLineActions.
+
+	popFront := [:args |
+		front := args first.
+		args remove: front.
+		front].
+	server := self new.
+
+	options ifEmpty: [^server].
+	(options size even) ifFalse: [console log: 'Using default parameters. Not enough arguments: ' , options. ^server].
 
-	arguments := process argv.
-	portOption := arguments at: 3 ifAbsent: [nil].
-	portNumber := arguments at: 4 ifAbsent: [nil].
-	('-p' = portOption and: [portNumber notNil]) ifTrue: [
-		fileServer port: portNumber.
+	[options notEmpty] whileTrue: [
+		optionName  := popFront value: options.
+		optionValue := popFront value: options.
+		(actions at: optionName ifAbsent: []) value: server value: optionValue.
 	].
+
+	^server.
+!
+
+main
+	| fileServer args |
+	args := process argv.
+	args removeFrom: 1 to: 3.
+
+	fileServer := FileServer createServerWithArguments: args.
+
+	fileServer checkDirectoryLayout.
 	^fileServer start
 ! !
 

+ 511 - 89
server/server.js

@@ -97,11 +97,19 @@ function Smalltalk(){
 	/* List of all reserved words in JavaScript. They may not be used as variables
 	   in Smalltalk. */
 
-	st.reservedWords = ['break', 'case', 'catch', 'char', 'class', 'continue', 'debugger', 
-		'default', 'delete', 'do', 'else', 'finally', 'for', 'function', 
-		'if', 'in', 'instanceof', 'new', 'private', 'protected', 
-		'public', 'return', 'static', 'switch', 'this', 'throw',
-		'try', 'typeof', 'var', 'void', 'while', 'with', 'yield'];
+	// list of reserved JavaScript keywords as of
+	//   http://es5.github.com/#x7.6.1.1
+	// and
+	//   http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.6.1
+	st.reservedWords = ['break', 'case', 'catch', 'continue', 'debugger',
+		'default', 'delete', 'do', 'else', 'finally', 'for', 'function',
+		'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw',
+		'try', 'typeof', 'var', 'void', 'while', 'with',
+		// ES5: future use: http://es5.github.com/#x7.6.1.2
+		'class', 'const', 'enum', 'export', 'extends', 'import', 'super',
+		// ES5: future use in strict mode
+		'implements', 'interface', 'let', 'package', 'private', 'protected',
+		'public', 'static', 'yield'];
 
 	/* The symbol table ensures symbol unicity */
 
@@ -568,11 +576,11 @@ function Smalltalk(){
 	};
 
     /* Boolean assertion */
-    st.assert = function(boolean) {
-        if ((undefined !== boolean) && (boolean.klass === smalltalk.Boolean)) {
-            return boolean;
+    st.assert = function(shouldBeBoolean) {
+        if ((undefined !== shouldBeBoolean) && (shouldBeBoolean.klass === smalltalk.Boolean)) {
+            return shouldBeBoolean;
         } else {
-            smalltalk.NonBooleanReceiver._new()._object_(boolean)._signal();
+            smalltalk.NonBooleanReceiver._new()._object_(shouldBeBoolean)._signal();
         }
     }
 };
@@ -6840,18 +6848,31 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "ensure:",
 category: 'evaluating',
-fn: function (aBlock) {
-    var self = this;
-    var $1;
-    var success;
-    success = false;
-    $1 = smalltalk.send(function () {smalltalk.send(self, "_value", []);success = true;success;return smalltalk.send(aBlock, "_value", []);}, "_on_do_", [smalltalk.Error || Error, function (ex) {if (!smalltalk.assert(success)) {smalltalk.send(aBlock, "_value", []);}return smalltalk.send(ex, "_signal", []);}]);
-    return $1;
-},
+fn: function (aBlock){
+var self=this;
+try{return self()}finally{aBlock._value()};
+;
+return self},
 args: ["aBlock"],
-source: "ensure: aBlock\x0a\x09| success |\x0a\x09success := false.\x0a\x09^[self value. success := true. aBlock value]\x0a\x09\x09on: Error\x0a\x09\x09do: [:ex |\x0a\x09\x09\x09success ifFalse: [aBlock value].\x0a\x09\x09\x09ex signal]",
-messageSends: ["on:do:", "ifFalse:", "value", "signal"],
-referencedClasses: ["Error"]
+source: "ensure: aBlock\x0a\x09<try{return self()}finally{aBlock._value()}>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.BlockClosure);
+
+smalltalk.addMethod(
+"_fork",
+smalltalk.method({
+selector: "fork",
+category: 'timeout/interval',
+fn: function (){
+var self=this;
+smalltalk.send(smalltalk.send((smalltalk.ForkPool || ForkPool),"_default",[]),"_fork_",[self]);
+return self},
+args: [],
+source: "fork\x0a\x09ForkPool default fork: self",
+messageSends: ["fork:", "default"],
+referencedClasses: ["ForkPool"]
 }),
 smalltalk.BlockClosure);
 
@@ -7425,6 +7446,141 @@ smalltalk.CompiledMethod);
 
 
 
+smalltalk.addClass('ForkPool', smalltalk.Object, ['poolSize', 'maxPoolSize', 'queue', 'worker'], 'Kernel-Methods');
+smalltalk.addMethod(
+"_addWorker",
+smalltalk.method({
+selector: "addWorker",
+category: 'action',
+fn: function (){
+var self=this;
+smalltalk.send(self["@worker"],"_valueWithTimeout_",[(0)]);
+self["@poolSize"]=smalltalk.send(self["@poolSize"],"__plus",[(1)]);
+return self},
+args: [],
+source: "addWorker\x0a\x09worker valueWithTimeout: 0.\x0a    poolSize := poolSize + 1",
+messageSends: ["valueWithTimeout:", "+"],
+referencedClasses: []
+}),
+smalltalk.ForkPool);
+
+smalltalk.addMethod(
+"_fork_",
+smalltalk.method({
+selector: "fork:",
+category: 'action',
+fn: function (aBlock){
+var self=this;
+var $1;
+$1=smalltalk.send(self["@poolSize"],"__lt",[self["@maxPoolSize"]]);
+if(smalltalk.assert($1)){
+smalltalk.send(self,"_addWorker",[]);
+};
+smalltalk.send(self["@queue"],"_back_",[aBlock]);
+return self},
+args: ["aBlock"],
+source: "fork: aBlock\x0a\x09poolSize < maxPoolSize ifTrue: [ self addWorker ].\x0a\x09queue back: aBlock",
+messageSends: ["ifTrue:", "addWorker", "<", "back:"],
+referencedClasses: []
+}),
+smalltalk.ForkPool);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+category: 'initialization',
+fn: function (){
+var self=this;
+var $1;
+var sentinel;
+self["@poolSize"]=(0);
+self["@maxPoolSize"]=smalltalk.send(smalltalk.send(self,"_class",[]),"_defaultMaxPoolSize",[]);
+self["@queue"]=smalltalk.send((smalltalk.Queue || Queue),"_new",[]);
+sentinel=smalltalk.send((smalltalk.Object || Object),"_new",[]);
+self["@worker"]=(function(){
+var block;
+self["@poolSize"]=smalltalk.send(self["@poolSize"],"__minus",[(1)]);
+self["@poolSize"];
+block=smalltalk.send(self["@queue"],"_frontIfAbsent_",[(function(){
+return sentinel;
+})]);
+block;
+$1=smalltalk.send(block,"__eq_eq",[sentinel]);
+if(! smalltalk.assert($1)){
+return smalltalk.send((function(){
+return smalltalk.send(block,"_value",[]);
+}),"_ensure_",[(function(){
+return smalltalk.send(self,"_addWorker",[]);
+})]);
+};
+});
+return self},
+args: [],
+source: "initialize\x0a\x09| sentinel |\x0a\x09poolSize := 0.\x0a    maxPoolSize := self class defaultMaxPoolSize.\x0a    queue := Queue new.\x0a    sentinel := Object new.\x0a    worker := [\x0a\x09\x09| block |\x0a        poolSize := poolSize - 1.\x0a\x09\x09block := queue frontIfAbsent: [ sentinel ].\x0a        block == sentinel ifFalse: [\x0a        \x09[ block value ] ensure: [ self addWorker ]]].",
+messageSends: ["defaultMaxPoolSize", "class", "new", "-", "frontIfAbsent:", "ifFalse:", "ensure:", "addWorker", "value", "=="],
+referencedClasses: ["Queue", "Object"]
+}),
+smalltalk.ForkPool);
+
+
+smalltalk.ForkPool.klass.iVarNames = ['default'];
+smalltalk.addMethod(
+"_default",
+smalltalk.method({
+selector: "default",
+category: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+if(($receiver = self["@default"]) == nil || $receiver == undefined){
+self["@default"]=smalltalk.send(self,"_new",[]);
+$1=self["@default"];
+} else {
+$1=self["@default"];
+};
+return $1;
+},
+args: [],
+source: "default\x0a\x09^default ifNil: [ default := self new ]",
+messageSends: ["ifNil:", "new"],
+referencedClasses: []
+}),
+smalltalk.ForkPool.klass);
+
+smalltalk.addMethod(
+"_defaultMaxPoolSize",
+smalltalk.method({
+selector: "defaultMaxPoolSize",
+category: 'accessing',
+fn: function (){
+var self=this;
+return (100);
+},
+args: [],
+source: "defaultMaxPoolSize\x0a\x09^100",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ForkPool.klass);
+
+smalltalk.addMethod(
+"_resetDefault",
+smalltalk.method({
+selector: "resetDefault",
+category: 'accessing',
+fn: function (){
+var self=this;
+self["@default"]=nil;
+return self},
+args: [],
+source: "resetDefault\x0a\x09default := nil",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ForkPool.klass);
+
+
 smalltalk.addClass('Message', smalltalk.Object, ['selector', 'arguments'], 'Kernel-Methods');
 smalltalk.Message.comment="Generally, the system does not use instances of Message for efficiency reasons.\x0aHowever, when a message is not understood by its receiver, the interpreter will make up an instance of it in order to capture the information involved in an actual message transmission. \x0aThis instance is sent it as an argument with the message `doesNotUnderstand:` to the receiver.\x0a\x0aSee boot.js, `messageNotUnderstood`  and its counterpart `Object>>doesNotUnderstand:`"
 smalltalk.addMethod(
@@ -12302,6 +12458,108 @@ smalltalk.Set);
 
 
 
+smalltalk.addClass('Queue', smalltalk.Object, ['read', 'readIndex', 'write'], 'Kernel-Collections');
+smalltalk.addMethod(
+"_back_",
+smalltalk.method({
+selector: "back:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+smalltalk.send(self["@write"],"_add_",[anObject]);
+return self},
+args: ["anObject"],
+source: "back: anObject\x0a\x09write add: anObject\x0a",
+messageSends: ["add:"],
+referencedClasses: []
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_front",
+smalltalk.method({
+selector: "front",
+category: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=smalltalk.send(self,"_frontIfAbsent_",[(function(){
+return smalltalk.send(self,"_error_",["Cannot read from empty Queue."]);
+})]);
+return $1;
+},
+args: [],
+source: "front\x0a    ^self frontIfAbsent: [ self error: 'Cannot read from empty Queue.']\x0a",
+messageSends: ["frontIfAbsent:", "error:"],
+referencedClasses: []
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_frontIfAbsent_",
+smalltalk.method({
+selector: "frontIfAbsent:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+var $1,$2,$3;
+var $early={};
+try {
+var result;
+result=smalltalk.send(self["@read"],"_at_ifAbsent_",[self["@readIndex"],(function(){
+$1=smalltalk.send(self["@write"],"_isEmpty",[]);
+if(smalltalk.assert($1)){
+$2=smalltalk.send(self["@readIndex"],"__gt",[(1)]);
+if(smalltalk.assert($2)){
+self["@read"]=[];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+};
+$3=smalltalk.send(aBlock,"_value",[]);
+throw $early=[$3];
+};
+self["@read"]=self["@write"];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+self["@write"];
+return smalltalk.send(self["@read"],"_first",[]);
+})]);
+smalltalk.send(self["@read"],"_at_put_",[self["@readIndex"],nil]);
+self["@readIndex"]=smalltalk.send(self["@readIndex"],"__plus",[(1)]);
+return result;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+},
+args: ["aBlock"],
+source: "frontIfAbsent: aBlock\x0a\x09| result |\x0a\x09result := read at: readIndex ifAbsent: [\x0a\x09\x09write isEmpty ifTrue: [\x0a\x09\x09\x09readIndex > 1 ifTrue: [ read := #(). readIndex := 1 ].\x0a\x09\x09\x09^aBlock value ].\x0a    \x09read := write.\x0a    \x09readIndex := 1.\x0a    \x09write := OrderedCollection new.\x0a    \x09read first ].\x0a    read at: readIndex put: nil.\x0a    readIndex := readIndex + 1.\x0a    ^result\x0a",
+messageSends: ["at:ifAbsent:", "ifTrue:", ">", "value", "isEmpty", "new", "first", "at:put:", "+"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+category: 'initialization',
+fn: function (){
+var self=this;
+self["@read"]=[];
+self["@readIndex"]=(1);
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+return self},
+args: [],
+source: "initialize\x0a\x09read := #().\x0a    readIndex := 1.\x0a    write := OrderedCollection new",
+messageSends: ["new"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.Queue);
+
+
+
 smalltalk.addClass('RegularExpression', smalltalk.Object, [], 'Kernel-Collections');
 smalltalk.addMethod(
 "_compile_",
@@ -13926,7 +14184,7 @@ smalltalk.addClass('MethodRemoved', smalltalk.MethodAnnouncement, [], 'Kernel-An
 
 
 smalltalk.addPackage('FileServer', {});
-smalltalk.addClass('FileServer', smalltalk.Object, ['path', 'http', 'fs', 'url', 'port', 'basePath', 'util'], 'FileServer');
+smalltalk.addClass('FileServer', smalltalk.Object, ['path', 'http', 'fs', 'url', 'port', 'basePath', 'util', 'username', 'password'], 'FileServer');
 smalltalk.addMethod(
 "_basePath",
 smalltalk.method({
@@ -13968,10 +14226,10 @@ fn: function (){
 var self=this;
 ((($receiver = smalltalk.send(self['@path'], "_existsSync_", [smalltalk.send(smalltalk.send(self, "_basePath", []), "__comma", ["index.html"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory does not contain index.html"]);})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory does not contain index.html"]);})]));
 ((($receiver = smalltalk.send(self['@path'], "_existsSync_", [smalltalk.send(smalltalk.send(self, "_basePath", []), "__comma", ["st"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory is missing an \x22st\x22 directory"]);})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory is missing an \x22st\x22 directory"]);})]));
-((($receiver = smalltalk.send(self['@path'], "_existsSync_", [smalltalk.send(smalltalk.send(self, "_basePath", []), "__comma", ["js"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: roject directory is missing a \x22js\x22 directory"]);})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: roject directory is missing a \x22js\x22 directory"]);})]));
+((($receiver = smalltalk.send(self['@path'], "_existsSync_", [smalltalk.send(smalltalk.send(self, "_basePath", []), "__comma", ["js"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory is missing a \x22js\x22 directory"]);})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["Warning: project directory is missing a \x22js\x22 directory"]);})]));
 return self;},
 args: [],
-source: "checkDirectoryLayout\x0a\x09(path existsSync: self basePath, 'index.html') ifFalse: [\x0a\x09\x09console warn: 'Warning: project directory does not contain index.html'].\x0a\x09(path existsSync: self basePath, 'st') ifFalse: [\x0a\x09\x09console warn: 'Warning: project directory is missing an \x22st\x22 directory'].\x0a\x09(path existsSync: self basePath, 'js') ifFalse: [\x0a\x09\x09console warn: 'Warning: roject directory is missing a \x22js\x22 directory'].",
+source: "checkDirectoryLayout\x0a\x09(path existsSync: self basePath, 'index.html') ifFalse: [\x0a\x09\x09console warn: 'Warning: project directory does not contain index.html'].\x0a\x09(path existsSync: self basePath, 'st') ifFalse: [\x0a\x09\x09console warn: 'Warning: project directory is missing an \x22st\x22 directory'].\x0a\x09(path existsSync: self basePath, 'js') ifFalse: [\x0a\x09\x09console warn: 'Warning: project directory is missing a \x22js\x22 directory'].",
 messageSends: ["ifFalse:", "existsSync:", ",", "basePath", "warn:"],
 referencedClasses: []
 }),
@@ -14021,21 +14279,20 @@ selector: "handlePUTRequest:respondTo:",
 category: 'request handling',
 fn: function (aRequest, aResponse){
 var self=this;
-var $early={};
-try{var file=nil;
+var file=nil;
 var stream=nil;
+((($receiver = smalltalk.send(self, "_isAuthenticated_response_", [aRequest, aResponse])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send(self, "_respondAuthenticationRequiredTo_", [aResponse]);})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){return smalltalk.send(self, "_respondAuthenticationRequiredTo_", [aResponse]);})]));
 (file=smalltalk.send(".", "__comma", [smalltalk.send(aRequest, "_url", [])]));
 (stream=smalltalk.send(self['@fs'], "_createWriteStream_", [file]));
-smalltalk.send(stream, "_on_do_", ["error", (function(error){smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", [smalltalk.send("Error creating WriteStream for file ", "__comma", [file])]);smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["    Did you forget to create the necessary js/ or st/ directory in your project?"]);return smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", [smalltalk.send("    The exact error is: ", "__comma", [error])]);})]);
-((($receiver = smalltalk.send(stream, "_writable", [])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send("Could not write to ", "__comma", [file])]);return (function(){throw $early=[nil]})();})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send("Could not write to ", "__comma", [file])]);return (function(){throw $early=[nil]})();})]));
+smalltalk.send(stream, "_on_do_", ["error", (function(error){smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", [smalltalk.send("Error creating WriteStream for file ", "__comma", [file])]);smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", ["    Did you forget to create the necessary js/ or st/ directory in your project?"]);smalltalk.send((typeof console == 'undefined' ? nil : console), "_warn_", [smalltalk.send("    The exact error is: ", "__comma", [error])]);return smalltalk.send(self, "_respondNotCreatedTo_", [aResponse]);})]);
+smalltalk.send(stream, "_on_do_", ["close", (function(){return smalltalk.send(self, "_respondCreatedTo_", [aResponse]);})]);
 smalltalk.send(aRequest, "_setEncoding_", ["utf8"]);
 smalltalk.send(aRequest, "_on_do_", ["data", (function(data){return smalltalk.send(stream, "_write_", [data]);})]);
-smalltalk.send(aRequest, "_on_do_", ["end", (function(){smalltalk.send(stream, "_end", []);return smalltalk.send(self, "_respondOKTo_", [aResponse]);})]);
-return self;
-} catch(e) {if(e===$early)return e[0]; throw e}},
+smalltalk.send(aRequest, "_on_do_", ["end", (function(){return ((($receiver = smalltalk.send(stream, "_writable", [])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return smalltalk.send(stream, "_end", []);})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return smalltalk.send(stream, "_end", []);})]));})]);
+return self;},
 args: ["aRequest", "aResponse"],
-source: "handlePUTRequest: aRequest respondTo: aResponse\x0a\x09| file stream |\x0a\x09file := '.', aRequest url.\x0a\x09stream := fs createWriteStream: file.\x0a\x09stream on: 'error' do: [:error |\x0a\x09\x09\x22TODO: notify Amber about the error, otherwise the user might not notice and lose his work.\x22\x0a\x09\x09console warn: 'Error creating WriteStream for file ', file.\x0a\x09\x09console warn: '    Did you forget to create the necessary js/ or st/ directory in your project?'.\x0a\x09\x09console warn: '    The exact error is: ', error].\x0a\x09stream writable ifFalse: [\x0a\x09\x09console log: 'Could not write to ', file.\x0a\x09\x09^nil].\x0a        aRequest setEncoding: 'utf8'.\x0a        aRequest on: 'data' do: [:data | stream write: data].\x0a\x0a        aRequest on: 'end' do: [\x0a                stream end.\x0a                self respondOKTo: aResponse]",
-messageSends: [",", "url", "createWriteStream:", "on:do:", "warn:", "ifFalse:", "writable", "log:", "setEncoding:", "write:", "end", "respondOKTo:"],
+source: "handlePUTRequest: aRequest respondTo: aResponse\x0a\x09| file stream |\x0a\x09(self isAuthenticated: aRequest response: aResponse)\x0a\x09\x09ifFalse: [self respondAuthenticationRequiredTo: aResponse].\x0a\x0a\x09file := '.', aRequest url.\x0a\x09stream := fs createWriteStream: file.\x0a\x0a\x09stream on: 'error' do: [:error |\x0a\x09\x09console warn: 'Error creating WriteStream for file ', file.\x0a\x09\x09console warn: '    Did you forget to create the necessary js/ or st/ directory in your project?'.\x0a\x09\x09console warn: '    The exact error is: ', error.\x0a\x09\x09self respondNotCreatedTo: aResponse].\x0a\x0a\x09stream on: 'close' do: [\x0a\x09\x09self respondCreatedTo: aResponse].\x0a\x0a\x09aRequest setEncoding: 'utf8'.\x0a\x09aRequest on: 'data' do: [:data |\x0a\x09\x09stream write: data].\x0a\x0a\x09aRequest on: 'end' do: [\x0a\x09\x09stream writable ifTrue: [stream end]]",
+messageSends: ["ifFalse:", "isAuthenticated:response:", "respondAuthenticationRequiredTo:", ",", "url", "createWriteStream:", "on:do:", "warn:", "respondNotCreatedTo:", "respondCreatedTo:", "setEncoding:", "write:", "ifTrue:", "writable", "end"],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -14052,7 +14309,7 @@ var self=this;
 ((($receiver = smalltalk.send(smalltalk.send(aRequest, "_method", []), "__eq", ["OPTIONS"])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return smalltalk.send(self, "_handleOPTIONSRequest_respondTo_", [aRequest, aResponse]);})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return smalltalk.send(self, "_handleOPTIONSRequest_respondTo_", [aRequest, aResponse]);})]));
 return self;},
 args: ["aRequest", "aResponse"],
-source: "handleRequest: aRequest respondTo: aResponse\x0a\x0a\x09aRequest method = 'PUT'\x0a\x09\x09ifTrue: [self handlePUTRequest: aRequest respondTo: aResponse].\x0a\x09aRequest method = 'GET'\x0a\x09\x09ifTrue:[self handleGETRequest: aRequest respondTo: aResponse].\x0a\x09aRequest method = 'OPTIONS'\x0a\x09\x09ifTrue:[self handleOPTIONSRequest: aRequest respondTo: aResponse]",
+source: "handleRequest: aRequest respondTo: aResponse\x0a\x09aRequest method = 'PUT'\x0a\x09\x09ifTrue: [self handlePUTRequest: aRequest respondTo: aResponse].\x0a\x09aRequest method = 'GET'\x0a\x09\x09ifTrue:[self handleGETRequest: aRequest respondTo: aResponse].\x0a\x09aRequest method = 'OPTIONS'\x0a\x09\x09ifTrue:[self handleOPTIONSRequest: aRequest respondTo: aResponse]",
 messageSends: ["ifTrue:", "=", "method", "handlePUTRequest:respondTo:", "handleGETRequest:respondTo:", "handleOPTIONSRequest:respondTo:"],
 referencedClasses: []
 }),
@@ -14065,16 +14322,59 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-smalltalk.send(self, "_initialize", [], smalltalk.FileServer.superclass || nil);
+smalltalk.send((typeof super_ == 'undefined' ? nil : super_), "_initialize", []);
 (self['@path']=smalltalk.send(self, "_require_", ["path"]));
 (self['@http']=smalltalk.send(self, "_require_", ["http"]));
 (self['@fs']=smalltalk.send(self, "_require_", ["fs"]));
 (self['@util']=smalltalk.send(self, "_require_", ["util"]));
 (self['@url']=smalltalk.send(self, "_require_", ["url"]));
+(self['@port']=smalltalk.send(smalltalk.send(self, "_class", []), "_defaultPort", []));
+(self['@username']=nil);
+(self['@password']=nil);
 return self;},
 args: [],
-source: "initialize\x0a\x09super initialize.\x0a\x09path := self require: 'path'.\x0a\x09http := self require: 'http'.\x0a\x09fs := self require: 'fs'.\x0a\x09util := self require: 'util'.\x0a\x09url := self require: 'url'",
-messageSends: ["initialize", "require:"],
+source: "initialize\x0a\x09super initialize.\x0a\x09path := self require: 'path'.\x0a\x09http := self require: 'http'.\x0a\x09fs := self require: 'fs'.\x0a\x09util := self require: 'util'.\x0a\x09url := self require: 'url'.\x0a\x09port := self class defaultPort.\x0a\x09username := nil.\x0a\x09password := nil.",
+messageSends: ["initialize", "require:", "defaultPort", "class"],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_isAuthenticated_response_",
+smalltalk.method({
+selector: "isAuthenticated:response:",
+category: 'private',
+fn: function (aRequest, aResponse){
+var self=this;
+var $early={};
+try{var header=nil;
+var token=nil;
+var auth=nil;
+var parts=nil;
+((($receiver = smalltalk.send(smalltalk.send(self['@username'], "_isNil", []), "_and_", [(function(){return smalltalk.send(self['@password'], "_isNil", []);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw $early=[true]})();})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (function(){throw $early=[true]})();})]));
+(header=(($receiver = smalltalk.send(smalltalk.send(aRequest, "_headers", []), "_at_", ["authorization"])) == nil || $receiver == undefined) ? (function(){return "";})() : $receiver);
+((($receiver = smalltalk.send(header, "_isEmpty", [])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw $early=[false]})();})() : (function(){(token=(($receiver = smalltalk.send(header, "_tokenize_", [" "])) == nil || $receiver == undefined) ? (function(){return "";})() : $receiver);auth = new Buffer(token[1], 'base64').toString();(parts=smalltalk.send(auth, "_tokenize_", [":"]));return ((($receiver = smalltalk.send(smalltalk.send(self['@username'], "__eq", [smalltalk.send(parts, "_at_", [(1)])]), "_and_", [(function(){return smalltalk.send(self['@password'], "__eq", [smalltalk.send(parts, "_at_", [(2)])]);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw $early=[true]})();})() : (function(){return (function(){throw $early=[false]})();})()) : smalltalk.send($receiver, "_ifTrue_ifFalse_", [(function(){return (function(){throw $early=[true]})();}), (function(){return (function(){throw $early=[false]})();})]));})()) : smalltalk.send($receiver, "_ifTrue_ifFalse_", [(function(){return (function(){throw $early=[false]})();}), (function(){(token=(($receiver = smalltalk.send(header, "_tokenize_", [" "])) == nil || $receiver == undefined) ? (function(){return "";})() : $receiver);auth = new Buffer(token[1], 'base64').toString();(parts=smalltalk.send(auth, "_tokenize_", [":"]));return ((($receiver = smalltalk.send(smalltalk.send(self['@username'], "__eq", [smalltalk.send(parts, "_at_", [(1)])]), "_and_", [(function(){return smalltalk.send(self['@password'], "__eq", [smalltalk.send(parts, "_at_", [(2)])]);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw $early=[true]})();})() : (function(){return (function(){throw $early=[false]})();})()) : smalltalk.send($receiver, "_ifTrue_ifFalse_", [(function(){return (function(){throw $early=[true]})();}), (function(){return (function(){throw $early=[false]})();})]));})]));
+return self;
+} catch(e) {if(e===$early)return e[0]; throw e}},
+args: ["aRequest", "aResponse"],
+source: "isAuthenticated: aRequest response: aResponse\x0a\x09\x22Basic HTTP Auth: http://stackoverflow.com/a/5957629/293175\x0a\x09 and https://gist.github.com/1686663\x22\x0a\x09| header token auth parts|\x0a\x0a\x09(username isNil and: [password isNil]) ifTrue: [^true].\x0a\x0a\x09\x22get authentication header\x22\x0a\x09header := (aRequest headers at: 'authorization') ifNil:[''].\x0a\x09(header isEmpty)\x0a\x09ifTrue: [^false]\x0a\x09ifFalse: [\x0a\x09\x09\x22get authentication token\x22\x0a\x09\x09token := (header tokenize: ' ') ifNil:[''].\x0a\x09\x09\x22convert back from base64\x22\x0a\x09\x09<auth = new Buffer(token[1], 'base64').toString()>.\x0a\x09\x09\x22split token at colon\x22\x0a\x09\x09parts := auth tokenize: ':'.\x0a\x0a\x09\x09((username = (parts at: 1)) and: [password = (parts at: 2)])\x0a\x09\x09\x09ifTrue: [^true]\x0a\x09\x09\x09ifFalse: [^false]\x0a\x09].",
+messageSends: ["ifTrue:", "and:", "isNil", "ifNil:", "at:", "headers", "ifTrue:ifFalse:", "isEmpty", "tokenize:", "="],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_password_",
+smalltalk.method({
+selector: "password:",
+category: 'accessing',
+fn: function (aPassword){
+var self=this;
+(self['@password']=aPassword);
+return self;},
+args: ["aPassword"],
+source: "password: aPassword\x0a\x09password := aPassword.",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -14086,11 +14386,27 @@ selector: "port",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.send(smalltalk.send(self, "_class", []), "_port", []);
+return self['@port'];
 return self;},
 args: [],
-source: "port\x0a\x09^self class port",
-messageSends: ["port", "class"],
+source: "port\x0a\x09^port",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_port_",
+smalltalk.method({
+selector: "port:",
+category: 'accessing',
+fn: function (aNumber){
+var self=this;
+(self['@port']=aNumber);
+return self;},
+args: ["aNumber"],
+source: "port: aNumber\x0a\x09port := aNumber",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -14111,6 +14427,38 @@ referencedClasses: []
 }),
 smalltalk.FileServer);
 
+smalltalk.addMethod(
+"_respondAuthenticationRequiredTo_",
+smalltalk.method({
+selector: "respondAuthenticationRequiredTo:",
+category: 'request handling',
+fn: function (aResponse){
+var self=this;
+(function($rec){smalltalk.send($rec, "_writeHead_options_", [(401), smalltalk.HashedCollection._fromPairs_([smalltalk.send("WWW-Authenticate", "__minus_gt", ["Basic realm=\x22Secured Developer Area\x22"])])]);smalltalk.send($rec, "_write_", ["<html><body>Authentication needed</body></html>"]);return smalltalk.send($rec, "_end", []);})(aResponse);
+return self;},
+args: ["aResponse"],
+source: "respondAuthenticationRequiredTo: aResponse\x0a\x09aResponse\x0a\x09\x09writeHead: 401 options: #{'WWW-Authenticate' -> 'Basic realm=\x22Secured Developer Area\x22'};\x0a\x09\x09write: '<html><body>Authentication needed</body></html>';\x0a\x09\x09end.",
+messageSends: ["writeHead:options:", "->", "write:", "end"],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_respondCreatedTo_",
+smalltalk.method({
+selector: "respondCreatedTo:",
+category: 'request handling',
+fn: function (aResponse){
+var self=this;
+(function($rec){smalltalk.send($rec, "_writeHead_options_", [(201), smalltalk.HashedCollection._fromPairs_([smalltalk.send("Content-Type", "__minus_gt", ["text/plain"]),smalltalk.send("Access-Control-Allow-Origin", "__minus_gt", ["*"])])]);return smalltalk.send($rec, "_end", []);})(aResponse);
+return self;},
+args: ["aResponse"],
+source: "respondCreatedTo: aResponse\x0a\x09aResponse\x0a\x09\x09writeHead: 201 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};\x0a\x09\x09end.",
+messageSends: ["writeHead:options:", "->", "end"],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
 smalltalk.addMethod(
 "_respondFileNamed_to_",
 smalltalk.method({
@@ -14147,6 +14495,22 @@ referencedClasses: []
 }),
 smalltalk.FileServer);
 
+smalltalk.addMethod(
+"_respondNotCreatedTo_",
+smalltalk.method({
+selector: "respondNotCreatedTo:",
+category: 'request handling',
+fn: function (aResponse){
+var self=this;
+(function($rec){smalltalk.send($rec, "_writeHead_options_", [(400), smalltalk.HashedCollection._fromPairs_([smalltalk.send("Content-Type", "__minus_gt", ["text/plain"])])]);smalltalk.send($rec, "_write_", ["File could not be created. Did you forget to create the st/js directories on the server?"]);return smalltalk.send($rec, "_end", []);})(aResponse);
+return self;},
+args: ["aResponse"],
+source: "respondNotCreatedTo: aResponse\x0a\x09aResponse\x0a\x09\x09writeHead: 400 options: #{'Content-Type' -> 'text/plain'};\x0a\x09\x09write: 'File could not be created. Did you forget to create the st/js directories on the server?';\x0a\x09\x09end.",
+messageSends: ["writeHead:options:", "->", "write:", "end"],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
 smalltalk.addMethod(
 "_respondNotFoundTo_",
 smalltalk.method({
@@ -14170,11 +14534,10 @@ selector: "respondOKTo:",
 category: 'request handling',
 fn: function (aResponse){
 var self=this;
-smalltalk.send(aResponse, "_writeHead_options_", [(200), smalltalk.HashedCollection._fromPairs_([smalltalk.send("Content-Type", "__minus_gt", ["text/plain"]),smalltalk.send("Access-Control-Allow-Origin", "__minus_gt", ["*"])])]);
-smalltalk.send(aResponse, "_end", []);
+(function($rec){smalltalk.send($rec, "_writeHead_options_", [(200), smalltalk.HashedCollection._fromPairs_([smalltalk.send("Content-Type", "__minus_gt", ["text/plain"]),smalltalk.send("Access-Control-Allow-Origin", "__minus_gt", ["*"])])]);return smalltalk.send($rec, "_end", []);})(aResponse);
 return self;},
 args: ["aResponse"],
-source: "respondOKTo: aResponse\x0a\x09aResponse \x0a\x09\x09writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'}.\x0a\x09aResponse end.",
+source: "respondOKTo: aResponse\x0a\x09aResponse\x0a\x09\x09writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};\x0a\x09\x09end.",
 messageSends: ["writeHead:options:", "->", "end"],
 referencedClasses: []
 }),
@@ -14203,12 +14566,45 @@ selector: "startOn:",
 category: 'starting',
 fn: function (aPort){
 var self=this;
-(self['@port']=aPort);
+smalltalk.send(self, "_port_", [aPort]);
 smalltalk.send(self, "_start", []);
 return self;},
 args: ["aPort"],
-source: "startOn: aPort\x0a\x09port := aPort.\x0a\x09self start",
-messageSends: ["start"],
+source: "startOn: aPort\x0a\x09self port: aPort.\x0a\x09self start",
+messageSends: ["port:", "start"],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_username_",
+smalltalk.method({
+selector: "username:",
+category: 'accessing',
+fn: function (aUsername){
+var self=this;
+(self['@username']=aUsername);
+return self;},
+args: ["aUsername"],
+source: "username: aUsername\x0a\x09username := aUsername.",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
+smalltalk.addMethod(
+"_username_password_",
+smalltalk.method({
+selector: "username:password:",
+category: 'accessing',
+fn: function (aUsername, aPassword){
+var self=this;
+(self['@username']=aUsername);
+(self['@password']=aPassword);
+return self;},
+args: ["aUsername", "aPassword"],
+source: "username: aUsername password: aPassword\x0a\x09username := aUsername.\x0a\x09password := aPassword.",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -14230,7 +14626,53 @@ referencedClasses: []
 smalltalk.FileServer);
 
 
-smalltalk.FileServer.klass.iVarNames = ['port','mimeTypes'];
+smalltalk.FileServer.klass.iVarNames = ['mimeTypes'];
+smalltalk.addMethod(
+"_commandLineActions",
+smalltalk.method({
+selector: "commandLineActions",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.HashedCollection._fromPairs_([smalltalk.send("-p", "__minus_gt", [(function(fileServer, value){return smalltalk.send(fileServer, "_port_", [value]);})]),smalltalk.send("--username", "__minus_gt", [(function(fileServer, value){return smalltalk.send(fileServer, "_username_", [value]);})]),smalltalk.send("--password", "__minus_gt", [(function(fileServer, value){return smalltalk.send(fileServer, "_password_", [value]);})])]);
+return self;},
+args: [],
+source: "commandLineActions\x0a\x09^#{\x0a\x09\x09'-p' -> [:fileServer :value | fileServer port: value].\x0a\x09\x09'--username' -> [:fileServer :value | fileServer username: value].\x0a\x09\x09'--password' -> [:fileServer :value | fileServer password: value]\x0a\x09}",
+messageSends: ["->", "port:", "username:", "password:"],
+referencedClasses: []
+}),
+smalltalk.FileServer.klass);
+
+smalltalk.addMethod(
+"_createServerWithArguments_",
+smalltalk.method({
+selector: "createServerWithArguments:",
+category: 'initialization',
+fn: function (options){
+var self=this;
+var $early={};
+try{var server=nil;
+var actions=nil;
+var popFront=nil;
+var front=nil;
+var optionName=nil;
+var optionValue=nil;
+(actions=smalltalk.send((smalltalk.FileServer || FileServer), "_commandLineActions", []));
+(popFront=(function(args){(front=smalltalk.send(args, "_first", []));smalltalk.send(args, "_remove_", [front]);return front;}));
+(server=smalltalk.send(self, "_new", []));
+smalltalk.send(options, "_ifEmpty_", [(function(){return (function(){throw $early=[server]})();})]);
+((($receiver = smalltalk.send(smalltalk.send(options, "_size", []), "_even", [])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send("Using default parameters. Not enough arguments: ", "__comma", [options])]);return (function(){throw $early=[server]})();})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send("Using default parameters. Not enough arguments: ", "__comma", [options])]);return (function(){throw $early=[server]})();})]));
+(function(){while((function(){return smalltalk.send(options, "_notEmpty", []);})()) {(function(){(optionName=smalltalk.send(popFront, "_value_", [options]));(optionValue=smalltalk.send(popFront, "_value_", [options]));return smalltalk.send(smalltalk.send(actions, "_at_ifAbsent_", [optionName, (function(){return nil;})]), "_value_value_", [server, optionValue]);})()}})();
+return server;
+return self;
+} catch(e) {if(e===$early)return e[0]; throw e}},
+args: ["options"],
+source: "createServerWithArguments: options\x0a\x09| server actions popFront front optionName optionValue |\x0a\x09actions := FileServer commandLineActions.\x0a\x0a\x09popFront := [:args |\x0a\x09\x09front := args first.\x0a\x09\x09args remove: front.\x0a\x09\x09front].\x0a\x09server := self new.\x0a\x0a\x09options ifEmpty: [^server].\x0a\x09(options size even) ifFalse: [console log: 'Using default parameters. Not enough arguments: ' , options. ^server].\x0a\x0a\x09[options notEmpty] whileTrue: [\x0a\x09\x09optionName  := popFront value: options.\x0a\x09\x09optionValue := popFront value: options.\x0a\x09\x09(actions at: optionName ifAbsent: []) value: server value: optionValue.\x0a\x09].\x0a\x0a\x09^server.",
+messageSends: ["commandLineActions", "first", "remove:", "new", "ifEmpty:", "ifFalse:", "even", "size", "log:", ",", "whileTrue:", "notEmpty", "value:", "value:value:", "at:ifAbsent:"],
+referencedClasses: ["FileServer"]
+}),
+smalltalk.FileServer.klass);
+
 smalltalk.addMethod(
 "_defaultMimeTypes",
 smalltalk.method({
@@ -14247,6 +14689,22 @@ referencedClasses: []
 }),
 smalltalk.FileServer.klass);
 
+smalltalk.addMethod(
+"_defaultPort",
+smalltalk.method({
+selector: "defaultPort",
+category: 'accessing',
+fn: function (){
+var self=this;
+return (4000);
+return self;},
+args: [],
+source: "defaultPort\x0a\x09^4000",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.FileServer.klass);
+
 smalltalk.addMethod(
 "_main",
 smalltalk.method({
@@ -14255,21 +14713,17 @@ category: 'initialization',
 fn: function (){
 var self=this;
 var fileServer=nil;
-var arguments=nil;
-var portOption=nil;
-var portNumber=nil;
-(fileServer=smalltalk.send(self, "_new", []));
+var args=nil;
+(args=smalltalk.send((typeof process == 'undefined' ? nil : process), "_argv", []));
+smalltalk.send(args, "_removeFrom_to_", [(1), (3)]);
+(fileServer=smalltalk.send((smalltalk.FileServer || FileServer), "_createServerWithArguments_", [args]));
 smalltalk.send(fileServer, "_checkDirectoryLayout", []);
-(arguments=smalltalk.send((typeof process == 'undefined' ? nil : process), "_argv", []));
-(portOption=smalltalk.send(arguments, "_at_ifAbsent_", [(3), (function(){return nil;})]));
-(portNumber=smalltalk.send(arguments, "_at_ifAbsent_", [(4), (function(){return nil;})]));
-((($receiver = smalltalk.send(smalltalk.send("-p", "__eq", [portOption]), "_and_", [(function(){return smalltalk.send(portNumber, "_notNil", []);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return smalltalk.send(fileServer, "_port_", [portNumber]);})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return smalltalk.send(fileServer, "_port_", [portNumber]);})]));
 return smalltalk.send(fileServer, "_start", []);
 return self;},
 args: [],
-source: "main\x0a\x09| fileServer arguments portOption portNumber|\x0a\x09fileServer := self new.\x0a\x09fileServer checkDirectoryLayout.\x0a\x0a\x09arguments := process argv.\x0a\x09portOption := arguments at: 3 ifAbsent: [nil].\x0a\x09portNumber := arguments at: 4 ifAbsent: [nil].\x0a\x09('-p' = portOption and: [portNumber notNil]) ifTrue: [\x0a\x09\x09fileServer port: portNumber.\x0a\x09].\x0a\x09^fileServer start",
-messageSends: ["new", "checkDirectoryLayout", "argv", "at:ifAbsent:", "ifTrue:", "and:", "=", "notNil", "port:", "start"],
-referencedClasses: []
+source: "main\x0a\x09| fileServer args |\x0a\x09args := process argv.\x0a\x09args removeFrom: 1 to: 3.\x0a\x0a\x09fileServer := FileServer createServerWithArguments: args.\x0a\x0a\x09fileServer checkDirectoryLayout.\x0a\x09^fileServer start",
+messageSends: ["argv", "removeFrom:to:", "createServerWithArguments:", "checkDirectoryLayout", "start"],
+referencedClasses: ["FileServer"]
 }),
 smalltalk.FileServer.klass);
 
@@ -14305,38 +14759,6 @@ referencedClasses: []
 }),
 smalltalk.FileServer.klass);
 
-smalltalk.addMethod(
-"_port",
-smalltalk.method({
-selector: "port",
-category: 'accessing',
-fn: function (){
-var self=this;
-return (($receiver = self['@port']) == nil || $receiver == undefined) ? (function(){return (4000);})() : $receiver;
-return self;},
-args: [],
-source: "port\x0a\x09^port ifNil: [4000]",
-messageSends: ["ifNil:"],
-referencedClasses: []
-}),
-smalltalk.FileServer.klass);
-
-smalltalk.addMethod(
-"_port_",
-smalltalk.method({
-selector: "port:",
-category: 'accessing',
-fn: function (aNumber){
-var self=this;
-(self['@port']=aNumber);
-return self;},
-args: ["aNumber"],
-source: "port: aNumber\x0a\x09port := aNumber",
-messageSends: [],
-referencedClasses: []
-}),
-smalltalk.FileServer.klass);
-
 
 smalltalk.init(smalltalk.Object); //metaclasses are in through Class
 smalltalk.classes()._do_(function(each) {

+ 24 - 17
st/IDE.st

@@ -296,13 +296,20 @@ inspectIt
 !
 
 print: aString
-	| start stop |
+	| start stop currentLine |
+    currentLine := (editor getCursor: false) line.
 	start := HashedCollection new.
-	stop := HashedCollection new.
-	start at: 'line' put: (editor getCursor: false) line.
+	start at: 'line' put: currentLine.
 	start at: 'ch' put: (editor getCursor: false) ch.
-	stop at: 'line' put: (start at: 'line').
+    (editor getSelection) ifEmpty: [
+    	"select current line if selection is empty"
+    	start at: 'ch' put: (editor getLine: currentLine) size.
+        editor setSelection: #{'line' -> currentLine. 'ch' -> 0} end: start.
+    ].
+	stop := HashedCollection new.
+	stop at: 'line' put: currentLine.
 	stop at: 'ch' put: ((start at: 'ch') + aString size + 2).
+
 	editor replaceSelection: (editor getSelection, ' ', aString, ' ').
 	editor setCursor: (editor getCursor: true).
 	editor setSelection: stop end: start
@@ -790,15 +797,15 @@ cancelChanges
 
 commitPackage
 	selectedPackage ifNotNil: [ |package|
-               						 package := Package named: selectedPackage.
-               						 {	Exporter 			-> (package commitPathJs, '/', selectedPackage, '.js').
-                        					StrippedExporter 	-> (package commitPathJs, '/', selectedPackage, '.deploy.js').
-                       						 ChunkExporter 		-> (package commitPathSt, '/', selectedPackage, '.st') 			} 
-                 
-                						do: [:commitStrategy| |fileContents|
-                                                                     	fileContents := (commitStrategy key new exportPackage: selectedPackage).
-                                                                     	self ajaxPutAt: commitStrategy value data:  fileContents]
-         						]
+		package := Package named: selectedPackage.
+		{  	Exporter              -> (package commitPathJs, '/', selectedPackage, '.js').
+			StrippedExporter -> (package commitPathJs, '/', selectedPackage, '.deploy.js').
+			ChunkExporter    -> (package commitPathSt, '/', selectedPackage, '.st')
+		} do: [:commitStrategy| |fileContents|
+			fileContents := (commitStrategy key new exportPackage: selectedPackage).
+			self ajaxPutAt: commitStrategy value data:  fileContents
+  		]
+	]
 !
 
 compile
@@ -1041,12 +1048,12 @@ initialize
 
 !Browser methodsFor: 'network'!
 
-ajaxPutAt: anURL data: aString
-	jQuery 
-		ajax: anURL	options: #{	'type' -> 'PUT'.
+ajaxPutAt: aURL data: aString
+	jQuery
+		ajax: aURL	options: #{	'type' -> 'PUT'.
 								'data' -> aString.
 								'contentType' -> 'text/plain;charset=UTF-8'.
-								'error' -> [window alert: 'PUT request failed at:  ', anURL] }
+								'error' -> [:xhr | window alert: 'Commiting ' , aURL , ' failed with reason: "' , (xhr responseText) , '"'] }
 ! !
 
 !Browser methodsFor: 'rendering'!

Some files were not shown because too many files changed in this diff