Przeglądaj źródła

Merge remote-tracking branch 'gokr/master'

Nicolas Petton 13 lat temu
rodzic
commit
9398d20880

+ 90 - 0
ide.html

@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<!-- saved from url=(0025)http://jtalk-project.org/ -->
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Jtalk Smalltalk</title>
+    
+    <meta name="author" content="Nicolas Petton">
+    <link rel="stylesheet" type="text/css" href="./ide/style.css">
+    <link rel="stylesheet" type="text/css" href="./ide/syntax.css">
+    <script type="text/javascript" src="./js/jquery-1.4.4.min.js"></script>
+    <script type="text/javascript" src="./js/jquery-ui-1.8.9.custom.min.js"></script>
+    <script type="text/javascript" src="./js/jquery.textarea.js"></script>
+    <script type="text/javascript" src="./js/boot.js"></script>
+    <script type="text/javascript" src="./js/Kernel.js"></script>
+    <script type="text/javascript" src="./js/Canvas.js"></script>
+    <script type="text/javascript" src="./js/JQuery.js"></script>
+    <script type="text/javascript" src="./js/Parser.js"></script>
+    <script type="text/javascript" src="./js/Compiler.js"></script>
+    <script type="text/javascript" src="./js/IDE.js"></script>
+    <script type="text/javascript" src="./js/SUnit.js"></script>
+    <script type="text/javascript" src="./js/Examples.js"></script>
+    <script type="text/javascript" src="./js/init.js"></script>
+    <link rel="stylesheet" type="text/css" href="./css/jtalk.css">
+    <link rel="stylesheet" type="text/css" href="./css/sunit.css">
+  </head>
+  <body>
+    <script type="text/javascript">
+      jQuery(window).scroll(function() {
+      	if(jQuery(window).scrollTop() > 210) {
+		jQuery('#menu')
+      			.css('position', 'fixed')
+			.css('top', '0px')
+	} else {
+      		jQuery('#menu')
+			.css('position', 'absolute')
+      			.css('top', '210px')
+      	}
+      });
+    </script>
+
+    <a href="http://github.com/NicolasPetton/jtalk"><img style="position: absolute; top: 0; lef
+t: 0; border: 0;" src="./ide/fork_me.png" alt="Fork me on GitHub"></a>
+
+    <div id="wrapper">
+      <div id="header">
+	<div class="main">
+	  <img alt="Jtalk, the Smalltalk for web developers" src="./ide/text_header.png">
+	</div>
+      </div>
+      
+      <div id="menu">
+	<div class="main">
+	  <ul>
+	    <li><a href="http://jtalk-project.org/index.html">Overview</a></li> · 
+	    <li><a href="http://jtalk-project.org/index.html#download">Download</a></li> · 
+	    <li><a id="doc_link" href="http://jtalk-project.org/documentation.html">Documentation</a></li> ·
+	    <li><a target="_blank" href="https://github.com/NicolasPetton/jtalk">Source</a></li> 
+	  </ul>
+	</div>
+      </div>
+
+      <div id="content">
+	<div class="main">
+	  <div class="box first">
+  <div class="content">
+    <h1><img alt="Jtalk is an implementation of the Smalltalk language that runs on the JavaScript runtime." src="./ide/title_container1.png"></h1>
+    <div class="left">
+      <p>Jtalk is an implementation of the  <a href="http://en.wikipedia.org/wiki/Smalltalk">Smalltalk</a> language that runs on top of the <a href="http://en.wikipedia.org/wiki/Javascript">JavaScript</a> runtime. It is designed to make client-side development faster and easier.</p>
+      <p>Jtalk is written in itself, including the parser and compiler. Jtalk compiles into efficient JavaScript, mapping one-to-one with the equivalent JavaScript. There is no interpretation at runtime.</p>
+      <p>Try a <button onclick="smalltalk.Browser._open()"> Class browser</button> right now!</p>
+    </div>
+    <div class="right"><img src="./ide/screen2.png"></div>
+    <div class="clear"></div>
+  </div>
+</div>
+
+	</div>
+      </div>
+    
+    <div id="footer">
+      <div class="main">
+	<p>Copyright © 2011 <a href="http://www.nicolas-petton.fr/">Nicolas Petton</a>. The content of this website in licensed under <a href="http://creativecommons.org/licenses/by-nc/3.0/">CC-BY-NC 3.0</a>.</p>
+	<p>Jtalk is an opensource project sponsored by <a href="http://www.objectfusion.fr/">objectfusion</a>.</p>
+	<p>The design of this website was provided by <a href="mailto:neomie.thirion@gmail.com">Noémie Thirion</a>.</p>
+      </div> 
+    </div>
+    
+  
+
+
+</div></body></html>

BIN
ide/fork_me.png


BIN
ide/screen2.png


+ 456 - 0
ide/style.css

@@ -0,0 +1,456 @@
+/* CSS Reset */
+
+html,body {
+	margin: 0;
+	padding: 0;
+	font: 16px/1.6em Georgia,Times,Serif;
+}
+
+/* Layout */
+
+.clear {clear:both}
+
+.main {
+    margin: 0 auto;
+    width: 960px;
+}
+
+#header {
+	background: transparent url("../images/background_header.png") top center repeat-x;
+	height: 210px;
+}
+
+#header .main {
+    background: url("../images/balloon_header.png") no-repeat scroll right bottom;
+    height: 210px;
+    width: 973px;
+}
+
+#menu {
+    background: #d0def1;
+    border-top: 1px solid #aaa;
+    border-bottom: 1px solid #aaa;
+    position: absolute;
+    width: 100%;
+    height: 36px;
+    top: 210px;
+    font-family: arial,helvetica,sans;
+    z-index: 1;
+}
+
+#menu ul {
+    padding: 5px 0;
+    margin: 0;
+}
+
+#menu li {
+	display: inline;
+	padding: 1px;
+}
+
+#menu a {
+	color: #515a6a;
+	text-decoration: none;
+	padding: 10px 15px;
+}
+
+#menu a:hover {
+	color: #072d5a;
+	text-decoration: underline;
+}
+
+#menu .selected a,
+#menu a:hover {
+	color: #072d5a;
+	text-decoration: none;
+}
+
+#menu #dropdown {
+    float: left;
+    margin-left: 220px;
+    background: #d0def1;
+    border: 1px solid #aaa;
+    border-top: 0 none;
+    width: auto; 
+    display: none;
+}
+
+#menu #dropdown li {
+    display: block;
+    padding: 5px 0;
+}
+	
+#content .main {
+	width: 980px;
+	margin: 0 auto;
+}
+
+.box {
+    padding: 30px;
+    margin: 40px 0;
+    color: #777;
+    box-shadow: 0 0 8px #aaa;
+    -moz-box-shadow: 0 0 8px #aaa;
+    -webkit-box-shadow: 0 0 8px #aaa;
+    border-radius: 10px;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    background: #fff url('../images/background_box.png') bottom left repeat-x;
+}
+
+.box a {
+    color: #5b9ff2;
+    text-decoration: none; 
+}
+
+.box a:hover {
+    text-decoration: underline;
+}
+
+.box h2 {
+	font: 26px Georgia, Times, serif;
+	color: #242424;
+	margin: -30px;
+	margin-bottom: 0px;
+	padding: 15px;
+	background: #eee;
+	border-bottom: 1px solid #ccc;
+	border-top-left-radius: 10px;
+	border-top-right-radius: 10px;
+	-webkit-border-top-left-radius: 10px;
+	-webkit-border-top-right-radius: 10px;
+	-moz-border-radius-topleft: 10px;
+	-moz-border-radius-topright: 10px;
+}
+
+.box.first {
+    padding-top: 0;
+    margin-top: 70px;
+}
+
+.box.first, .box.last {
+    background-image: none;
+}
+
+.box.first h1 {
+	padding-bottom: 15px;
+}
+
+.box.first .content .right img {
+	margin-right: -30px
+}
+
+.box .content .right {
+	float: right;
+}
+
+.box .content .left {
+	float: left;
+	width: 420px;
+}
+
+.doc .content {
+	width: 620px;
+}
+
+.box h1 {
+    text-align: center;
+    margin-top: 0;
+    padding-top: 0;
+}
+
+.doc h3 a {
+    color: #ccc;
+}
+
+.doc h3 a:hover {
+    color: #0088CC;
+}
+
+.box .content p, h3 {
+	font: 16px Georgia, Times, Serif;
+	line-height: 1.6em;
+	padding: 10px 0;
+	margin: 0;
+}
+
+.box .content h3 {
+	font-size: 20px;
+	padding-bottom: 2px;
+	padding-top: 30px;
+	color: #272727;
+}
+
+.box .content h4 {
+	font: 18px Georgia, Times, serif;
+	color: #272727;
+	padding-top: 1em;
+	margin-top: 10px;
+	margin-bottom: 5px;
+}
+
+.box .content pre {
+	border: 1px solid #ccc;
+	background-color: #f5f5f5;
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	padding: 1em;
+	line-height: 1.5em;
+	font-size: 14px;
+	color: #333;
+}
+
+.box .content code {
+	font-size: 14px;
+	color: #333;
+	margin: 0;
+	padding: 0 3px;
+	border: 1px solid #bbb;
+	background: #f1f1f1;
+}
+
+.box .content pre code {
+    background: transparent;
+    border: 0 none;
+    color: #333;
+}
+
+.box .content pre .prompt {
+    color: #aaa;
+}
+
+.box .content pre .kbd {
+    font-weight: bold;
+}
+
+.box .content pre .kbd.var {
+    font-weight: normal;
+    font-style: italic;
+}
+
+.box .content .tip {
+	border: 1px solid #565656;
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	padding: 1em;
+}
+
+.box .top img {
+	margin: 30px 0 0 70px;
+}
+
+.box.last .content .left {
+    width: 560px;
+}
+
+.box.last .content .left {
+    margin-left: -30px;
+    margin-bottom: -32px
+}
+
+.box.doc {
+    margin-top: 70px;
+    position: relative;
+    width: 630px;
+}
+
+.box .content .warning,
+.box .content .information {
+    position: absolute;
+    right: -250px;
+    margin-top: 20px;
+    width: 200px;
+    font-size: 14px;
+    color: #222;
+    font-family: arial,helevetica,sans;
+    padding: 10px;
+    opacity: 0.7;
+    -moz-transition: opacity 0.3s;
+    -webkit-transition: opacity 0.3s;
+    -o-transition: opacity 0.3s;
+}
+
+.box .content .warning:hover,
+.box .content .information:hover {
+    opacity: 1;
+}
+
+.box .content .information:before,
+.box .content .warning:before {
+    font-family: Georgia, sans-serif;
+    font-size: 28px;
+    font-style: italic;
+    position: absolute;
+    right: 0;
+    top: -21px;
+    opacity: 0.5;
+}
+
+.box .content .information:before {
+    content: "Info";
+    color: #a6b1f3;
+}
+
+.box .content .warning:before {
+    content: "Warning";
+    color: #e869e7;
+}
+
+.box .content .information {
+    background-color: #e4e8ff;
+}    
+
+.box .content .warning {
+    background-color: #f8ccf8;
+}
+
+.box .content .code {
+	background: #565656;
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	padding: 7px;
+}
+
+.box .content .code p {
+	color: white;
+}
+
+.box.first .content .left h2 {
+	font: 30px regular Georgia, Times, serif;
+	color: #272727;
+	margin-top: 10px;
+}
+
+.box.first .content .left ul {
+	padding-left: 10px;
+}
+
+.box.last .content .right {
+    width: 300px;
+}
+
+.doc .box.first .content .right {
+	float: right;
+	width: 230px;
+	margin-top: 20px;
+	padding: 10px;
+	background-color: #ececec;
+	border: 1px solid #e1e1e1;
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+.doc .box.first .content .right ol, 
+.doc .box.first .content .right ol a {
+	font: 18px Georgia, Time, serif;
+	color: #03388a;
+}
+
+.doc .box.first .content .right ol {
+	padding: 0 0 7px 35px;
+}
+
+.doc .box.first .content .right ol li{
+	padding: 7px 0 7px 0; 
+}
+
+.doc .box.first .content .right ul a {
+	font: 15px Helvetica, Arial, sans;
+	color: black;
+}
+
+.doc .box.first .content .right ul li {
+	list-style-type: none;
+	padding: 2px 0 2px 0;
+}
+
+.trysmalltalk {
+    text-align: right;
+    padding: 10px;
+}
+
+.trysmalltalk textarea {
+    width: 850px;
+    height: 300px;
+    padding: 20px;
+    background-color: #eaeaea;
+    border: 1px solid #d3d3d3;
+    border-radius: 10px;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    font: 16px monaco, courier, monospace;
+    color: 434343;
+    line-height: 1.6em;
+}
+
+.trysmalltalk button {
+    margin: 20px 0 0 5px;
+    padding: 4px;
+    background: #75aef5;
+    border: 1px solid #7caeed;
+    border-radius: 8px;
+    -moz-border-radius: 8px;
+    -webkit-border-radius: 8px;
+    color: white;
+    cursor: pointer;
+}
+
+.trysmalltalk .print-it {
+	background: #bebebe;
+	border: 1px solid #b2b2b2;
+}
+
+.trysmalltalk button:hover {
+	background: #3180e1;
+	border: 1px solid #518cd6; 
+}
+
+#counters {
+    border: 2px dashed;
+    text-align: center;
+    padding: 10px;
+    margin: 10px;
+    background: #ffffcc;
+    font-family: arial,helvetica,sans;
+}
+
+#counters h1 {
+    margin: 10px;
+    color: #333;
+}
+
+
+.trysmalltalk .print-it:hover {
+	background: #898989;
+	border: 1px solid #919293; 
+}
+
+#footer {
+	height: 160px;
+	margin: 0 auto;
+}
+
+#footer .main {
+    	border-top: 1px dashed #bbb;
+	padding-top: 10px;
+}
+
+#footer p {
+	text-align: center;
+	line-height: 1em;
+	color: #aaa;
+}
+
+#footer a {
+	color: #3faae1;
+	text-decoration: none;
+}
+
+#footer a:hover {
+	text-decoration: underline;
+}
+

+ 61 - 0
ide/syntax.css

@@ -0,0 +1,61 @@
+.highlight { background: #ffffff; }
+.highlight .c { color: #999988; font-style: italic } /* Comment */
+.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
+.highlight .k { font-weight: bold } /* Keyword */
+.highlight .o { font-weight: bold } /* Operator */
+.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
+.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #aa0000 } /* Generic.Error */
+.highlight .gh { color: #999999 } /* Generic.Heading */
+.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #555555 } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
+.highlight .gt { color: #aa0000 } /* Generic.Traceback */
+.highlight .kc { font-weight: bold } /* Keyword.Constant */
+.highlight .kd { font-weight: bold } /* Keyword.Declaration */
+.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
+.highlight .kr { font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #009999 } /* Literal.Number */
+.highlight .s { color: #d14 } /* Literal.String */
+.highlight .na { color: #008080 } /* Name.Attribute */
+.highlight .nb { color: #0086B3 } /* Name.Builtin */
+.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
+.highlight .no { color: #008080 } /* Name.Constant */
+.highlight .ni { color: #800080 } /* Name.Entity */
+.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
+.highlight .nn { color: #555555 } /* Name.Namespace */
+.highlight .nt { color: #000080 } /* Name.Tag */
+.highlight .nv { color: #008080 } /* Name.Variable */
+.highlight .ow { font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #009999 } /* Literal.Number.Float */
+.highlight .mh { color: #009999 } /* Literal.Number.Hex */
+.highlight .mi { color: #009999 } /* Literal.Number.Integer */
+.highlight .mo { color: #009999 } /* Literal.Number.Oct */
+.highlight .sb { color: #d14 } /* Literal.String.Backtick */
+.highlight .sc { color: #d14 } /* Literal.String.Char */
+.highlight .sd { color: #d14 } /* Literal.String.Doc */
+.highlight .s2 { color: #d14 } /* Literal.String.Double */
+.highlight .se { color: #d14 } /* Literal.String.Escape */
+.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
+.highlight .si { color: #d14 } /* Literal.String.Interpol */
+.highlight .sx { color: #d14 } /* Literal.String.Other */
+.highlight .sr { color: #009926 } /* Literal.String.Regex */
+.highlight .s1 { color: #d14 } /* Literal.String.Single */
+.highlight .ss { color: #990073 } /* Literal.String.Symbol */
+.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #008080 } /* Name.Variable.Class */
+.highlight .vg { color: #008080 } /* Name.Variable.Global */
+.highlight .vi { color: #008080 } /* Name.Variable.Instance */
+.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
+

BIN
ide/text_header.png


BIN
ide/title_container1.png


BIN
images/background_box.png


BIN
images/background_header.png


BIN
images/balloon_header.png


+ 2 - 2
js/Canvas.js

@@ -269,7 +269,7 @@ self['@root']=smalltalk.send(smalltalk.TagBrush, "_fromString_canvas_", ["div",
 return self;},
 source: unescape('initialize%0A%20%20%20%20super%20initialize.%0A%20%20%20%20root%20%3A%3D%20TagBrush%20fromString%3A%20%27div%27%20canvas%3A%20self%0A'),
 messageSends: ["initialize", "fromString:canvas:"],
-referencedClasses: [smalltalk.TagBrush]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.HTMLCanvas);
 
@@ -299,7 +299,7 @@ return smalltalk.send(smalltalk.TagBrush, "_fromString_canvas_", [aString, self]
 return self;},
 source: unescape('newTag%3A%20aString%0A%20%20%20%20%5ETagBrush%20fromString%3A%20aString%20canvas%3A%20self%0A'),
 messageSends: ["fromString:canvas:"],
-referencedClasses: [smalltalk.TagBrush]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.HTMLCanvas);
 

Plik diff jest za duży
+ 10 - 10
js/Compiler.js


+ 1 - 1
js/Examples.js

@@ -277,7 +277,7 @@ self['@movingPiece']=smalltalk.send(smalltalk.TetrisPiece, "_atRandom", []);
 return self;},
 source: unescape('newPiece%0A%09movingPiece%20%3A%3D%20TetrisPiece%20atRandom'),
 messageSends: ["atRandom"],
-referencedClasses: [smalltalk.TetrisPiece]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Tetris);
 

+ 33 - 20
js/IDE.js

@@ -518,13 +518,13 @@ var self=this;
 try{var lines=nil;
 var startLine=nil;
 var endLine=nil;
-lines=smalltalk.send(smalltalk.send(smalltalk.send(self['@textarea'], "_asJQuery", []), "_val", []), "_tokenize_", [smalltalk.send(smalltalk.String, "_cr", [])]);
+lines=smalltalk.send(smalltalk.send(smalltalk.send(self['@textarea'], "_asJQuery", []), "_val", []), "_tokenize_", [smalltalk.send(smalltalk.String, "_lf", [])]);
 startLine=endLine=(0);
 smalltalk.send(lines, "_do_", [(function(each){endLine=smalltalk.send(startLine, "__plus", [smalltalk.send(each, "_size", [])]);startLine=smalltalk.send(endLine, "__plus", [(1)]);return smalltalk.send(smalltalk.send(endLine, "__gt_eq", [smalltalk.send(self, "_selectionStart", [])]), "_ifTrue_", [(function(){smalltalk.send(self, "_selectionEnd_", [endLine]);return (function(){throw({name: 'stReturn', selector: '_currentLine', fn: function(){return each}})})();})]);})]);
 return self;
 } catch(e) {if(e.name === 'stReturn' && e.selector === '_currentLine'){return e.fn()} throw(e)}},
-source: unescape('currentLine%0A%20%20%20%20%7C%20lines%20startLine%20endLine%7C%0A%20%20%20%20lines%20%3A%3D%20textarea%20asJQuery%20val%20tokenize%3A%20String%20cr.%0A%20%20%20%20startLine%20%3A%3D%20endLine%20%3A%3D%200.%0A%20%20%20%20lines%20do%3A%20%5B%3Aeach%20%7C%0A%09endLine%20%3A%3D%20startLine%20+%20each%20size.%0A%09startLine%20%3A%3D%20endLine%20+%201.%0A%09endLine%20%3E%3D%20self%20selectionStart%20ifTrue%3A%20%5B%0A%09%20%20%20%20self%20selectionEnd%3A%20endLine.%0A%09%20%20%20%20%5Eeach%5D%5D%0A'),
-messageSends: ["tokenize:", "val", "asJQuery", "cr", "do:", unescape("+"), "size", "ifTrue:", unescape("%3E%3D"), "selectionStart", "selectionEnd:"],
+source: unescape('currentLine%0A%20%20%20%20%7C%20lines%20startLine%20endLine%7C%0A%20%20%20%20lines%20%3A%3D%20textarea%20asJQuery%20val%20tokenize%3A%20String%20lf.%0A%20%20%20%20startLine%20%3A%3D%20endLine%20%3A%3D%200.%0A%20%20%20%20lines%20do%3A%20%5B%3Aeach%20%7C%0A%09endLine%20%3A%3D%20startLine%20+%20each%20size.%0A%09startLine%20%3A%3D%20endLine%20+%201.%0A%09endLine%20%3E%3D%20self%20selectionStart%20ifTrue%3A%20%5B%0A%09%20%20%20%20self%20selectionEnd%3A%20endLine.%0A%09%20%20%20%20%5Eeach%5D%5D%0A'),
+messageSends: ["tokenize:", "val", "asJQuery", "lf", "do:", unescape("+"), "size", "ifTrue:", unescape("%3E%3D"), "selectionStart", "selectionEnd:"],
 referencedClasses: [smalltalk.String]
 }),
 smalltalk.Workspace);
@@ -961,14 +961,12 @@ category: 'accessing',
 fn: function (){
 var self=this;
 try{var klass=nil;
-var protocols=nil;
-protocols=smalltalk.send(smalltalk.Array, "_new", []);
-smalltalk.send(self['@selectedClass'], "_ifNotNil_", [(function(){smalltalk.send(smalltalk.send(self['@selectedTab'], "__eq", ["comment"]), "_ifTrue_", [(function(){return (function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return []}})})();})]);klass=smalltalk.send(smalltalk.send(self['@selectedTab'], "__eq", ["instance"]), "_ifTrue_ifFalse_", [(function(){return self['@selectedClass'];}), (function(){return smalltalk.send(self['@selectedClass'], "_class", []);})]);smalltalk.send(smalltalk.send(smalltalk.send(klass, "_methodDictionary", []), "_isEmpty", []), "_ifTrue_", [(function(){return smalltalk.send(protocols, "_add_", ["not yet classified"]);})]);return smalltalk.send(smalltalk.send(klass, "_methodDictionary", []), "_do_", [(function(each){return smalltalk.send(smalltalk.send(protocols, "_includes_", [smalltalk.send(each, "_category", [])]), "_ifFalse_", [(function(){return smalltalk.send(protocols, "_add_", [smalltalk.send(each, "_category", [])]);})]);})]);})]);
-(function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return smalltalk.send(protocols, "_sort", [])}})})();
+smalltalk.send(self['@selectedClass'], "_ifNotNil_", [(function(){smalltalk.send(smalltalk.send(self['@selectedTab'], "__eq", ["comment"]), "_ifTrue_", [(function(){return (function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return []}})})();})]);klass=smalltalk.send(smalltalk.send(self['@selectedTab'], "__eq", ["instance"]), "_ifTrue_ifFalse_", [(function(){return self['@selectedClass'];}), (function(){return smalltalk.send(self['@selectedClass'], "_class", []);})]);smalltalk.send(smalltalk.send(smalltalk.send(klass, "_methodDictionary", []), "_isEmpty", []), "_ifTrue_", [(function(){return (function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return smalltalk.send(smalltalk.Array, "_with_", ["not yet classified"])}})})();})]);return (function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return smalltalk.send(klass, "_protocols", [])}})})();})]);
+(function(){throw({name: 'stReturn', selector: '_protocols', fn: function(){return smalltalk.send(smalltalk.Array, "_new", [])}})})();
 return self;
 } catch(e) {if(e.name === 'stReturn' && e.selector === '_protocols'){return e.fn()} throw(e)}},
-source: unescape('protocols%0A%20%20%20%20%7C%20klass%20protocols%20%7C%0A%20%20%20%20protocols%20%3A%3D%20Array%20new.%0A%20%20%20%20selectedClass%20ifNotNil%3A%20%5B%0A%09selectedTab%20%3D%20%23comment%20ifTrue%3A%20%5B%5E%23%28%29%5D.%0A%09klass%20%3A%3D%20selectedTab%20%3D%20%23instance%0A%09%20%20%20%20ifTrue%3A%20%5BselectedClass%5D%0A%09%20%20%20%20ifFalse%3A%20%5BselectedClass%20class%5D.%0A%09klass%20methodDictionary%20isEmpty%20ifTrue%3A%20%5B%0A%09%20%20%20%20protocols%20add%3A%20%27not%20yet%20classified%27%5D.%0A%09klass%20methodDictionary%20do%3A%20%5B%3Aeach%20%7C%0A%09%20%20%20%20%28protocols%20includes%3A%20each%20category%29%20ifFalse%3A%20%5B%0A%09%09protocols%20add%3A%20each%20category%5D%5D%5D.%0A%20%20%20%20%5Eprotocols%20sort%0A'),
-messageSends: ["new", "ifNotNil:", "ifTrue:", unescape("%3D"), "ifTrue:ifFalse:", "class", "isEmpty", "methodDictionary", "add:", "do:", "ifFalse:", "includes:", "category", "sort"],
+source: unescape('protocols%0A%20%20%20%20%7C%20klass%20%7C%0A%20%20%20%20selectedClass%20ifNotNil%3A%20%5B%0A%09selectedTab%20%3D%20%23comment%20ifTrue%3A%20%5B%5E%23%28%29%5D.%0A%09klass%20%3A%3D%20selectedTab%20%3D%20%23instance%0A%09%20%20%20%20ifTrue%3A%20%5BselectedClass%5D%0A%09%20%20%20%20ifFalse%3A%20%5BselectedClass%20class%5D.%0A%09klass%20methodDictionary%20isEmpty%20ifTrue%3A%20%5B%0A%09%20%20%20%20%5EArray%20with%3A%20%27not%20yet%20classified%27%5D.%0A%09%5Eklass%20protocols%5D.%0A%20%20%20%20%5EArray%20new'),
+messageSends: ["ifNotNil:", "ifTrue:", unescape("%3D"), "ifTrue:ifFalse:", "class", "isEmpty", "methodDictionary", "with:", "protocols", "new"],
 referencedClasses: [smalltalk.Array]
 }),
 smalltalk.Browser);
@@ -1063,11 +1061,11 @@ fn: function (){
 var self=this;
 var stream=nil;
 stream=smalltalk.send("", "_writeStream", []);
-smalltalk.send(self['@selectedClass'], "_ifNotNil_", [(function(){(function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(self['@selectedClass'], "_superclass", []), "_asString", [])]);smalltalk.send($rec, "_nextPutAll_", [unescape("%20subclass%3A%20%23")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self['@selectedClass'], "_name", [])]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(smalltalk.String, "_cr", []), "__comma", [smalltalk.send(smalltalk.String, "_tab", [])])]);return smalltalk.send($rec, "_nextPutAll_", [unescape("instanceVariableNames%3A%20%27")]);})(stream);smalltalk.send(smalltalk.send(self['@selectedClass'], "_instanceVariableNames", []), "_do_separatedBy_", [(function(each){return smalltalk.send(stream, "_nextPutAll_", [each]);}), (function(){return smalltalk.send(stream, "_nextPutAll_", [" "]);})]);return (function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(unescape("%27"), "__comma", [smalltalk.send(smalltalk.String, "_cr", [])]), "__comma", [smalltalk.send(smalltalk.String, "_tab", [])])]);smalltalk.send($rec, "_nextPutAll_", [unescape("category%3A%20%27")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self['@selectedClass'], "_category", [])]);return smalltalk.send($rec, "_nextPutAll_", [unescape("%27")]);})(stream);})]);
+smalltalk.send(self['@selectedClass'], "_ifNotNil_", [(function(){(function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(self['@selectedClass'], "_superclass", []), "_asString", [])]);smalltalk.send($rec, "_nextPutAll_", [unescape("%20subclass%3A%20%23")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self['@selectedClass'], "_name", [])]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(smalltalk.String, "_lf", []), "__comma", [smalltalk.send(smalltalk.String, "_tab", [])])]);return smalltalk.send($rec, "_nextPutAll_", [unescape("instanceVariableNames%3A%20%27")]);})(stream);smalltalk.send(smalltalk.send(self['@selectedClass'], "_instanceVariableNames", []), "_do_separatedBy_", [(function(each){return smalltalk.send(stream, "_nextPutAll_", [each]);}), (function(){return smalltalk.send(stream, "_nextPutAll_", [" "]);})]);return (function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(smalltalk.send(unescape("%27"), "__comma", [smalltalk.send(smalltalk.String, "_lf", [])]), "__comma", [smalltalk.send(smalltalk.String, "_tab", [])])]);smalltalk.send($rec, "_nextPutAll_", [unescape("category%3A%20%27")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self['@selectedClass'], "_category", [])]);return smalltalk.send($rec, "_nextPutAll_", [unescape("%27")]);})(stream);})]);
 return smalltalk.send(stream, "_contents", []);
 return self;},
-source: unescape('classDeclarationSource%0A%20%20%20%20%7C%20stream%20%7C%0A%20%20%20%20stream%20%3A%3D%20%27%27%20writeStream.%0A%20%20%20%20selectedClass%20ifNotNil%3A%20%5B%0A%09stream%20%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20superclass%20asString%3B%0A%09%20%20%20%20nextPutAll%3A%20%27%20subclass%3A%20%23%27%3B%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20name%3B%0A%09%20%20%20%20nextPutAll%3A%20String%20cr%2C%20String%20tab%3B%0A%09%20%20%20%20nextPutAll%3A%20%27instanceVariableNames%3A%20%27%27%27.%0A%09selectedClass%20instanceVariableNames%20%0A%09%20%20%20%20do%3A%20%5B%3Aeach%20%7C%20stream%20nextPutAll%3A%20each%5D%20%0A%09%20%20%20%20separatedBy%3A%20%5Bstream%20nextPutAll%3A%20%27%20%27%5D.%0A%09stream%0A%09%20%20%20%20nextPutAll%3A%20%27%27%27%27%2C%20String%20cr%2C%20String%20tab%3B%0A%09%20%20%20%20nextPutAll%3A%20%27category%3A%20%27%27%27%3B%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20category%3B%0A%09%20%20%20%20nextPutAll%3A%20%27%27%27%27%5D.%0A%20%20%20%20%5Estream%20contents%0A'),
-messageSends: ["writeStream", "ifNotNil:", "nextPutAll:", "asString", "superclass", "name", unescape("%2C"), "cr", "tab", "do:separatedBy:", "instanceVariableNames", "category", "contents"],
+source: unescape('classDeclarationSource%0A%20%20%20%20%7C%20stream%20%7C%0A%20%20%20%20stream%20%3A%3D%20%27%27%20writeStream.%0A%20%20%20%20selectedClass%20ifNotNil%3A%20%5B%0A%09stream%20%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20superclass%20asString%3B%0A%09%20%20%20%20nextPutAll%3A%20%27%20subclass%3A%20%23%27%3B%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20name%3B%0A%09%20%20%20%20nextPutAll%3A%20String%20lf%2C%20String%20tab%3B%0A%09%20%20%20%20nextPutAll%3A%20%27instanceVariableNames%3A%20%27%27%27.%0A%09selectedClass%20instanceVariableNames%20%0A%09%20%20%20%20do%3A%20%5B%3Aeach%20%7C%20stream%20nextPutAll%3A%20each%5D%20%0A%09%20%20%20%20separatedBy%3A%20%5Bstream%20nextPutAll%3A%20%27%20%27%5D.%0A%09stream%0A%09%20%20%20%20nextPutAll%3A%20%27%27%27%27%2C%20String%20lf%2C%20String%20tab%3B%0A%09%20%20%20%20nextPutAll%3A%20%27category%3A%20%27%27%27%3B%0A%09%20%20%20%20nextPutAll%3A%20selectedClass%20category%3B%0A%09%20%20%20%20nextPutAll%3A%20%27%27%27%27%5D.%0A%20%20%20%20%5Estream%20contents%0A'),
+messageSends: ["writeStream", "ifNotNil:", "nextPutAll:", "asString", "superclass", "name", unescape("%2C"), "lf", "tab", "do:separatedBy:", "instanceVariableNames", "category", "contents"],
 referencedClasses: [smalltalk.String]
 }),
 smalltalk.Browser);
@@ -1299,11 +1297,11 @@ selector: 'commitCategory',
 category: 'actions',
 fn: function (){
 var self=this;
-smalltalk.send(self['@selectedCategory'], "_ifNotNil_", [(function(){return (function($rec){smalltalk.send($rec, "_at_put_", ["type", "PUT"]);smalltalk.send($rec, "_at_put_", ["data", smalltalk.send(smalltalk.send(smalltalk.Exporter, "_new", []), "_exportCategory_", [self['@selectedCategory']])]);smalltalk.send($rec, "_at_put_", ["error", (function(){return smalltalk.send(self, "_alert_", [unescape("Commit%20failed%21")]);})]);return smalltalk.send($rec, "_send", []);})(smalltalk.send(smalltalk.Ajax, "_url_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self, "_class", []), "_commitPath", []), "__comma", [unescape("/")]), "__comma", [self['@selectedCategory']]), "__comma", [".js"])]));})]);
+smalltalk.send(self['@selectedCategory'], "_ifNotNil_", [(function(){(function($rec){smalltalk.send($rec, "_at_put_", ["type", "PUT"]);smalltalk.send($rec, "_at_put_", ["data", smalltalk.send(smalltalk.send(smalltalk.Exporter, "_new", []), "_exportCategory_", [self['@selectedCategory']])]);smalltalk.send($rec, "_at_put_", ["error", (function(){return smalltalk.send(self, "_alert_", [unescape("Commit%20failed%21")]);})]);return smalltalk.send($rec, "_send", []);})(smalltalk.send(smalltalk.Ajax, "_url_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self, "_class", []), "_commitPathJs", []), "__comma", [unescape("/")]), "__comma", [self['@selectedCategory']]), "__comma", [".js"])]));return (function($rec){smalltalk.send($rec, "_at_put_", ["type", "PUT"]);smalltalk.send($rec, "_at_put_", ["data", smalltalk.send(smalltalk.send(smalltalk.ChunkExporter, "_new", []), "_exportCategory_", [self['@selectedCategory']])]);smalltalk.send($rec, "_at_put_", ["error", (function(){return smalltalk.send(self, "_alert_", [unescape("Commit%20failed%21")]);})]);return smalltalk.send($rec, "_send", []);})(smalltalk.send(smalltalk.Ajax, "_url_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(self, "_class", []), "_commitPathSt", []), "__comma", [unescape("/")]), "__comma", [self['@selectedCategory']]), "__comma", [".st"])]));})]);
 return self;},
-source: unescape('commitCategory%0A%20%20%20%20selectedCategory%20ifNotNil%3A%20%5B%0A%09%28Ajax%20url%3A%20self%20class%20commitPath%2C%20%27/%27%2C%20selectedCategory%2C%20%27.js%27%29%0A%09%20%20%20%20at%3A%20%27type%27%20put%3A%20%27PUT%27%3B%0A%09%20%20%20%20at%3A%20%27data%27%20put%3A%20%28Exporter%20new%20exportCategory%3A%20selectedCategory%29%3B%0A%09%20%20%20%20at%3A%20%27error%27%20put%3A%20%5Bself%20alert%3A%20%27Commit%20failed%21%27%5D%3B%0A%09%20%20%20%20send%5D%0A'),
-messageSends: ["ifNotNil:", "at:put:", "exportCategory:", "new", "alert:", "send", "url:", unescape("%2C"), "commitPath", "class"],
-referencedClasses: [smalltalk.Exporter,smalltalk.Ajax]
+source: unescape('commitCategory%0A%20%20%20%20selectedCategory%20ifNotNil%3A%20%5B%0A%09%28Ajax%20url%3A%20self%20class%20commitPathJs%2C%20%27/%27%2C%20selectedCategory%2C%20%27.js%27%29%0A%09%20%20%20%20at%3A%20%27type%27%20put%3A%20%27PUT%27%3B%0A%09%20%20%20%20at%3A%20%27data%27%20put%3A%20%28Exporter%20new%20exportCategory%3A%20selectedCategory%29%3B%0A%09%20%20%20%20at%3A%20%27error%27%20put%3A%20%5Bself%20alert%3A%20%27Commit%20failed%21%27%5D%3B%0A%09%20%20%20%20send.%0A%09%28Ajax%20url%3A%20self%20class%20commitPathSt%2C%20%27/%27%2C%20selectedCategory%2C%20%27.st%27%29%0A%09%20%20%20%20at%3A%20%27type%27%20put%3A%20%27PUT%27%3B%0A%09%20%20%20%20at%3A%20%27data%27%20put%3A%20%28ChunkExporter%20new%20exportCategory%3A%20selectedCategory%29%3B%0A%09%20%20%20%20at%3A%20%27error%27%20put%3A%20%5Bself%20alert%3A%20%27Commit%20failed%21%27%5D%3B%0A%09%20%20%20%20send%5D%0A'),
+messageSends: ["ifNotNil:", "at:put:", "exportCategory:", "new", "alert:", "send", "url:", unescape("%2C"), "commitPathJs", "class", "commitPathSt"],
+referencedClasses: [smalltalk.Exporter,smalltalk.Ajax,smalltalk.ChunkExporter]
 }),
 smalltalk.Browser);
 
@@ -1713,7 +1711,7 @@ smalltalk.send(smalltalk.ReferencesBrowser, "_search_", [smalltalk.send(self['@s
 return self;},
 source: unescape('searchClassReferences%0A%09ReferencesBrowser%20search%3A%20selectedClass%20name'),
 messageSends: ["search:", "name"],
-referencedClasses: [smalltalk.ReferencesBrowser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Browser);
 
@@ -1749,15 +1747,30 @@ referencedClasses: []
 smalltalk.Browser.klass);
 
 smalltalk.addMethod(
-'_commitPath',
+'_commitPathJs',
 smalltalk.method({
-selector: 'commitPath',
+selector: 'commitPathJs',
 category: 'accessing',
 fn: function (){
 var self=this;
 return "js";
 return self;},
-source: unescape('commitPath%0A%09%5E%27js%27'),
+source: unescape('commitPathJs%0A%09%5E%27js%27'),
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Browser.klass);
+
+smalltalk.addMethod(
+'_commitPathSt',
+smalltalk.method({
+selector: 'commitPathSt',
+category: 'accessing',
+fn: function (){
+var self=this;
+return "st";
+return self;},
+source: unescape('commitPathSt%0A%09%5E%27st%27'),
 messageSends: [],
 referencedClasses: []
 }),

+ 0 - 0
js/JQuery.js


+ 100 - 22
js/Kernel.js

@@ -176,7 +176,7 @@ return smalltalk.send(smalltalk.Association, "_key_value_", [self, anObject]);
 return self;},
 source: unescape('-%3E%20anObject%0A%09%5EAssociation%20key%3A%20self%20value%3A%20anObject%0A'),
 messageSends: ["key:value:"],
-referencedClasses: [smalltalk.Association]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Object);
 
@@ -655,7 +655,7 @@ var self=this;
 return self;},
 source: unescape('doesNotUnderstand%3A%20aMessage%0A%09MessageNotUnderstood%20new%0A%09%09receiver%3A%20self%3B%0A%09%09message%3A%20aMessage%3B%0A%09%09signal'),
 messageSends: ["receiver:", "message:", "signal", "new"],
-referencedClasses: [smalltalk.MessageNotUnderstood]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Object);
 
@@ -960,7 +960,7 @@ return (function($rec){smalltalk.send($rec, "_class_category_", [self, aString])
 return self;},
 source: unescape('methodsFor%3A%20aString%0A%09%5EClassCategoryReader%20new%0A%09%20%20%20%20class%3A%20self%20category%3A%20aString%3B%0A%09%20%20%20%20yourself%0A'),
 messageSends: ["class:category:", "yourself", "new"],
-referencedClasses: [smalltalk.ClassCategoryReader]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Behavior);
 
@@ -1037,7 +1037,7 @@ return (function($rec){smalltalk.send($rec, "_class_", [self]);return smalltalk.
 return self;},
 source: unescape('commentStamp%0A%20%20%20%20%5EClassCommentReader%20new%0A%09class%3A%20self%3B%0A%09yourself%0A'),
 messageSends: ["class:", "yourself", "new"],
-referencedClasses: [smalltalk.ClassCommentReader]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Behavior);
 
@@ -1072,6 +1072,24 @@ referencedClasses: []
 }),
 smalltalk.Behavior);
 
+smalltalk.addMethod(
+'_protocols',
+smalltalk.method({
+selector: 'protocols',
+category: 'accessing',
+fn: function (){
+var self=this;
+var protocols=nil;
+protocols=smalltalk.send(smalltalk.Array, "_new", []);
+smalltalk.send(smalltalk.send(self, "_methodDictionary", []), "_do_", [(function(each){return smalltalk.send(smalltalk.send(protocols, "_includes_", [smalltalk.send(each, "_category", [])]), "_ifFalse_", [(function(){return smalltalk.send(protocols, "_add_", [smalltalk.send(each, "_category", [])]);})]);})]);
+return smalltalk.send(protocols, "_sort", []);
+return self;},
+source: unescape('protocols%0A%20%20%20%20%7C%20protocols%20%7C%0A%20%20%20%20protocols%20%3A%3D%20Array%20new.%0A%20%20%20%20self%20methodDictionary%20do%3A%20%5B%3Aeach%20%7C%0A%09%20%20%20%20%28protocols%20includes%3A%20each%20category%29%20ifFalse%3A%20%5B%0A%09%09protocols%20add%3A%20each%20category%5D%5D.%0A%20%20%20%20%5Eprotocols%20sort'),
+messageSends: ["new", "do:", "methodDictionary", "ifFalse:", "includes:", "category", "add:", "sort"],
+referencedClasses: [smalltalk.Array]
+}),
+smalltalk.Behavior);
+
 
 
 smalltalk.addClass('Class', smalltalk.Behavior, [], 'Kernel');
@@ -1131,7 +1149,7 @@ return smalltalk.send(smalltalk.send(smalltalk.ClassBuilder, "_new", []), "_supe
 return self;},
 source: unescape('subclass%3A%20aString%20instanceVariableNames%3A%20aString2%20category%3A%20aString3%0A%09%5EClassBuilder%20new%0A%09%20%20%20%20superclass%3A%20self%20subclass%3A%20aString%20instanceVariableNames%3A%20aString2%20category%3A%20aString3%0A'),
 messageSends: ["superclass:subclass:instanceVariableNames:category:", "new"],
-referencedClasses: [smalltalk.ClassBuilder]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Class);
 
@@ -1213,7 +1231,7 @@ smalltalk.send(smalltalk.send(smalltalk.ClassBuilder, "_new", []), "_class_insta
 return self;},
 source: unescape('instanceVariableNames%3A%20aCollection%0A%09ClassBuilder%20new%0A%09%20%20%20%20class%3A%20self%20instanceVariableNames%3A%20aCollection%0A'),
 messageSends: ["class:instanceVariableNames:", "new"],
-referencedClasses: [smalltalk.ClassBuilder]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Metaclass);
 
@@ -1743,7 +1761,7 @@ return smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.sen
 return self;},
 source: unescape('atRandom%0A%20%20%20%20%5E%28Random%20new%20next%20*%20self%29%20truncated%20+%201%0A'),
 messageSends: [unescape("+"), "truncated", unescape("*"), "next", "new"],
-referencedClasses: [smalltalk.Random]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Number);
 
@@ -1758,7 +1776,7 @@ return smalltalk.send(smalltalk.Point, "_x_y_", [self, aNumber]);
 return self;},
 source: unescape('@%20aNumber%0A%09%5EPoint%20x%3A%20self%20y%3A%20aNumber'),
 messageSends: ["x:y:"],
-referencedClasses: [smalltalk.Point]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Number);
 
@@ -1773,7 +1791,7 @@ return smalltalk.send(smalltalk.Point, "_x_y_", [self, self]);
 return self;},
 source: unescape('asPoint%0A%09%5EPoint%20x%3A%20self%20y%3A%20self'),
 messageSends: ["x:y:"],
-referencedClasses: [smalltalk.Point]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Number);
 
@@ -2845,7 +2863,7 @@ return smalltalk.send(smalltalk.send(smalltalk.ClassBuilder, "_new", []), "_supe
 return self;},
 source: unescape('subclass%3A%20aString%20instanceVariableNames%3A%20aString2%20category%3A%20aString3%0A%09%5EClassBuilder%20new%0A%09%20%20%20%20superclass%3A%20self%20subclass%3A%20aString%20instanceVariableNames%3A%20aString2%20category%3A%20aString3%0A'),
 messageSends: ["superclass:subclass:instanceVariableNames:category:", "new"],
-referencedClasses: [smalltalk.ClassBuilder]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.UndefinedObject);
 
@@ -3402,7 +3420,7 @@ return smalltalk.Stream;
 return self;},
 source: unescape('streamClass%0A%09%20%20%20%20%5EStream%0A'),
 messageSends: [],
-referencedClasses: [smalltalk.Stream]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.Collection.klass);
 
@@ -4043,7 +4061,7 @@ return smalltalk.send(smalltalk.send(smalltalk.PPStringParser, "_new", []), "_st
 return self;},
 source: unescape('asParser%0A%20%20%20%20%09%5EPPStringParser%20new%20string%3A%20self%0A'),
 messageSends: ["string:", "new"],
-referencedClasses: [smalltalk.PPStringParser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.String);
 
@@ -4058,7 +4076,7 @@ return smalltalk.send(smalltalk.PPChoiceParser, "_withAll_", [smalltalk.send(sma
 return self;},
 source: unescape('asChoiceParser%0A%20%20%20%20%09%5EPPChoiceParser%20withAll%3A%20%28self%20asArray%20collect%3A%20%5B%3Aeach%20%7C%20each%20asParser%5D%29%0A'),
 messageSends: ["withAll:", "collect:", "asArray", "asParser"],
-referencedClasses: [smalltalk.PPChoiceParser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.String);
 
@@ -4073,7 +4091,7 @@ return smalltalk.send(smalltalk.send(smalltalk.PPCharacterParser, "_new", []), "
 return self;},
 source: unescape('asCharacterParser%0A%20%20%20%20%09%5EPPCharacterParser%20new%20string%3A%20self%0A'),
 messageSends: ["string:", "new"],
-referencedClasses: [smalltalk.PPCharacterParser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.String);
 
@@ -4239,7 +4257,7 @@ return smalltalk.StringStream;
 return self;},
 source: unescape('streamClass%0A%09%20%20%20%20%5EStringStream%0A'),
 messageSends: [],
-referencedClasses: [smalltalk.StringStream]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.String.klass);
 
@@ -4265,9 +4283,9 @@ selector: 'cr',
 category: 'accessing',
 fn: function (){
 var self=this;
-return '\n';;
+return '\r';;
 return self;},
-source: unescape('cr%0A%09%7B%27return%20%27%27%5Cn%27%27%3B%27%7D'),
+source: unescape('cr%0A%09%7B%27return%20%27%27%5Cr%27%27%3B%27%7D'),
 messageSends: [],
 referencedClasses: []
 }),
@@ -4280,9 +4298,9 @@ selector: 'lf',
 category: 'accessing',
 fn: function (){
 var self=this;
-return '\r';;
+return '\n';;
 return self;},
-source: unescape('lf%0A%09%7B%27return%20%27%27%5Cr%27%27%3B%27%7D%0A'),
+source: unescape('lf%0A%09%7B%27return%20%27%27%5Cn%27%27%3B%27%7D%0A'),
 messageSends: [],
 referencedClasses: []
 }),
@@ -4318,6 +4336,21 @@ referencedClasses: []
 }),
 smalltalk.String.klass);
 
+smalltalk.addMethod(
+'_crlf',
+smalltalk.method({
+selector: 'crlf',
+category: 'accessing',
+fn: function (){
+var self=this;
+return '\r\n';;
+return self;},
+source: unescape('crlf%0A%09%7B%27return%20%27%27%5Cr%5Cn%27%27%3B%27%7D%0A'),
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.String.klass);
+
 
 smalltalk.addClass('Array', smalltalk.SequenceableCollection, [], 'Kernel');
 smalltalk.addMethod(
@@ -5364,7 +5397,7 @@ self['@chunkParser']=smalltalk.send(smalltalk.ChunkParser, "_new", []);
 return self;},
 source: unescape('initialize%0A%09super%20initialize.%0A%09chunkParser%20%3A%3D%20ChunkParser%20new.%0A'),
 messageSends: ["initialize", "new"],
-referencedClasses: [smalltalk.ChunkParser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.ClassCategoryReader);
 
@@ -5415,7 +5448,7 @@ smalltalk.send(self['@class'], "_addCompiledMethod_", [method]);
 return self;},
 source: unescape('compileMethod%3A%20aString%0A%09%7C%20method%20%7C%0A%09method%20%3A%3D%20Compiler%20new%20load%3A%20aString%20forClass%3A%20class.%0A%09method%20category%3A%20category.%0A%09class%20addCompiledMethod%3A%20method%0A'),
 messageSends: ["load:forClass:", "new", "category:", "addCompiledMethod:"],
-referencedClasses: [smalltalk.Compiler]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.ClassCategoryReader);
 
@@ -5842,6 +5875,51 @@ referencedClasses: []
 }),
 smalltalk.StringStream);
 
+smalltalk.addMethod(
+'_cr',
+smalltalk.method({
+selector: 'cr',
+category: 'writing',
+fn: function (){
+var self=this;
+return smalltalk.send(self, "_nextPutAll_", [smalltalk.send(smalltalk.String, "_cr", [])]);
+return self;},
+source: unescape('cr%0A%09%5Eself%20nextPutAll%3A%20String%20cr'),
+messageSends: ["nextPutAll:", "cr"],
+referencedClasses: [smalltalk.String]
+}),
+smalltalk.StringStream);
+
+smalltalk.addMethod(
+'_crlf',
+smalltalk.method({
+selector: 'crlf',
+category: 'writing',
+fn: function (){
+var self=this;
+return smalltalk.send(self, "_nextPutAll_", [smalltalk.send(smalltalk.String, "_crlf", [])]);
+return self;},
+source: unescape('crlf%0A%09%5Eself%20nextPutAll%3A%20String%20crlf'),
+messageSends: ["nextPutAll:", "crlf"],
+referencedClasses: [smalltalk.String]
+}),
+smalltalk.StringStream);
+
+smalltalk.addMethod(
+'_lf',
+smalltalk.method({
+selector: 'lf',
+category: 'writing',
+fn: function (){
+var self=this;
+return smalltalk.send(self, "_nextPutAll_", [smalltalk.send(smalltalk.String, "_lf", [])]);
+return self;},
+source: unescape('lf%0A%09%5Eself%20nextPutAll%3A%20String%20lf'),
+messageSends: ["nextPutAll:", "lf"],
+referencedClasses: [smalltalk.String]
+}),
+smalltalk.StringStream);
+
 
 
 smalltalk.addClass('ClassCommentReader', smalltalk.Object, ['class', 'chunkParser'], 'Kernel');
@@ -5889,7 +5967,7 @@ self['@chunkParser']=smalltalk.send(smalltalk.ChunkParser, "_new", []);
 return self;},
 source: unescape('initialize%0A%09super%20initialize.%0A%09chunkParser%20%3A%3D%20ChunkParser%20new.%0A'),
 messageSends: ["initialize", "new"],
-referencedClasses: [smalltalk.ChunkParser]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.ClassCommentReader);
 

Plik diff jest za duży
+ 15 - 15
js/Parser.js


+ 4 - 4
js/SUnit.js

@@ -130,7 +130,7 @@ var self=this;
 return self;},
 source: unescape('signalFailure%3A%20aString%0A%09TestFailure%20new%0A%09%09messageText%3A%20aString%3B%0A%09%09signal'),
 messageSends: ["messageText:", "signal", "new"],
-referencedClasses: [smalltalk.TestFailure]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.TestCase);
 
@@ -162,7 +162,7 @@ smalltalk.send(smalltalk.send(self, "_methods", []), "_do_", [(function(each){sm
 return self;},
 source: unescape('performTestFor%3A%20aResult%0A%09self%20methods%20do%3A%20%5B%3Aeach%20%7C%20%0A%09%09%5B%5Bself%20perform%3A%20each%5D%0A%09%09%09on%3A%20TestFailure%20do%3A%20%5B%3Aex%20%7C%20aResult%20addFailure%3A%20self%20class%20name%2C%20%27%3E%3E%27%2C%20each%5D%5D%0A%09%09%09on%3A%20Error%20do%3A%20%5B%3Aex%20%7C%20aResult%20addError%3A%20self%20class%20name%2C%20%27%3E%3E%27%2C%20each%5D.%0A%09%09aResult%20increaseRuns%5D'),
 messageSends: ["do:", "methods", "on:do:", "perform:", "addFailure:", unescape("%2C"), "name", "class", "addError:", "increaseRuns"],
-referencedClasses: [smalltalk.TestFailure,smalltalk.Error]
+referencedClasses: [smalltalk.nil,smalltalk.Error]
 }),
 smalltalk.TestCase);
 
@@ -595,7 +595,7 @@ smalltalk.send(aCollection, "_do_", [(function(each){return smalltalk.send((func
 return self;},
 source: unescape('run%3A%20aCollection%0A%09result%20%3A%3D%20TestResult%20new.%0A%09self%20%0A%09%09updateStatusDiv%3B%0A%09%09updateMethodsList.%0A%09self%20progressBar%20updatePercent%3A%200.%0A%09result%20total%3A%20%28aCollection%20inject%3A%200%20into%3A%20%5B%3Aacc%20%3Aeach%20%7C%20acc%20+%20each%20methods%20size%5D%29.%0A%09aCollection%20do%3A%20%5B%3Aeach%20%7C%20%0A%09%09%5Beach%20runCaseFor%3A%20result.%0A%09%09self%20progressBar%20updatePercent%3A%20result%20runs%20/%20result%20total%20*%20100.%0A%09%09self%20updateStatusDiv.%0A%09%09self%20updateMethodsList%5D%20valueWithTimeout%3A%20100%5D.'),
 messageSends: ["new", "updateStatusDiv", "updateMethodsList", "updatePercent:", "progressBar", "total:", "inject:into:", unescape("+"), "size", "methods", "do:", "valueWithTimeout:", "runCaseFor:", unescape("*"), unescape("/"), "runs", "total"],
-referencedClasses: [smalltalk.TestResult]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.TestRunner);
 
@@ -690,7 +690,7 @@ self['@result']=smalltalk.send(smalltalk.TestResult, "_new", []);
 return self;},
 source: unescape('initialize%0A%09super%20initialize.%0A%09result%20%3A%3D%20TestResult%20new'),
 messageSends: ["initialize", "new"],
-referencedClasses: [smalltalk.TestResult]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.TestRunner);
 

+ 0 - 0
js/boot.js


+ 0 - 0
js/init.js


+ 0 - 0
js/jquery-1.4.4.min.js


+ 0 - 0
js/jquery-ui-1.8.9.custom.min.js


+ 0 - 0
js/jquery.textarea.js


+ 0 - 0
js/jtalk.deploy.js


+ 0 - 0
js/jtalk.js


+ 215 - 66
st/canvas.st → st/Canvas.st

@@ -1,15 +1,103 @@
+Object subclass: #CanvasRenderingContext
+	instanceVariableNames: ''
+	category: 'Canvas'!
+
+!CanvasRenderingContext methodsFor: 'drawing arcs'!
+
+arcTo: aPoint radius: aNumber startAngle: aNumber2 endAngle: aNumber3 anticlockwise: aBoolean
+	{'self.arc(aPoint._x(), aPoint._y(), aNumber, aNumber2, aNumber3, aBoolean)'} 
+!
+
+arcTo: aPoint radius: aNumber
+	self arcTo: aPoint radius: aNumber startAngle: 0 endAngle: Number pi * 2 anticlockwise: false
+! !
+
+!CanvasRenderingContext methodsFor: 'drawing paths'!
+
+fillStyle: aString
+	{'self.fillStyle = String(aString)'}
+!
+
+beginPath
+	{'self.beginPath()'}
+!
+
+closePath
+	{'self.closePath()'}
+!
+
+fill
+	{'self.fill()'}
+!
+
+stroke
+	{'self.stroke()'}
+!
+
+moveTo: aPoint
+	{'self.moveTo(aPoint._x(), aPoint._y())'}
+!
+
+lineTo: aPoint
+	{'self.lineTo(aPoint._x(), aPoint._y())'}
+!
+
+strokeStyle: aString
+	{'self.strokeStyle = String(aString)'}
+!
+
+lineWidth: aNumber
+	{'self.lineWidth = aNumber'}
+! !
+
+!CanvasRenderingContext methodsFor: 'drawing rectangles'!
+
+fillRectFrom: aPoint to: anotherPoint
+	{'self.fillRect(aPoint._x(), aPoint._y(), anotherPoint._x(), anotherPoint._y())'}
+!
+
+strokeRectFrom: aPoint to: anotherPoint
+	{'self.strokeRect(aPoint._x(), aPoint._y(), anotherPoint._x(), anotherPoint._y())'}
+!
+
+clearRectFrom: aPoint to: anotherPoint
+	{'self.fillRect(aPoint._x(), aPoint._y(), anotherPoint._x(), anotherPoint._y())'}
+! !
+
+!CanvasRenderingContext class methodsFor: 'instance creation'!
+
+tagBrush: aTagBrush
+	{'return aTagBrush._element().getContext(''2d'')'}
+! !
+
 Object subclass: #HTMLCanvas
 	instanceVariableNames: 'root'
 	category: 'Canvas'!
 
+!HTMLCanvas methodsFor: '*JQuery'!
+
+appendToJQuery: aJQuery
+    aJQuery appendElement: root element
+
+! !
+
 !HTMLCanvas methodsFor: 'accessing'!
 
 root: aTagBrush
     root := aTagBrush
+
 !
 
 root
     ^root
+
+! !
+
+!HTMLCanvas methodsFor: 'adding'!
+
+with: anObject
+    ^self root with: anObject
+
 ! !
 
 !HTMLCanvas methodsFor: 'initialization'!
@@ -17,138 +105,155 @@ root
 initialize
     super initialize.
     root := TagBrush fromString: 'div' canvas: self
-! !
 
-!HTMLCanvas methodsFor: 'adding'!
-
-with: anObject
-    ^self root with: anObject
 ! !
 
 !HTMLCanvas methodsFor: 'tags'!
 
 newTag: aString
     ^TagBrush fromString: aString canvas: self
+
 !
 
 tag: aString
     ^root addBrush: (self newTag: aString)
+
 !
 
 h1
     ^self tag: 'h1'
+
 !
 
 h2
     ^self tag: 'h2'
+
 !
 
 h3
     ^self tag: 'h3'
+
 !
 
 h4
     ^self tag: 'h4'
+
 !
 
 h5
     ^self tag: 'h5'
+
 !
 
 h6
     ^self tag: 'h6'
+
 !
 
 p
     ^self tag: 'p'
+
 !
 
 div
     ^self tag: 'div'
+
 !
 
 span
     ^self tag: 'span'
+
 !
 
 img
     ^self tag: 'img'
+
 !
 
 ul
     ^self tag: 'ul'
+
 !
 
 ol
     ^self tag: 'ol'
+
 !
 
 li
     ^self tag: 'li'
+
 !
 
 table
     ^self tag: 'table'
+
 !
 
 tr
     ^self tag: 'tr'
+
 !
 
 td 
     ^self tag: 'td'
+
 !
 
 th
     ^self tag: 'th'
+
 !
 
 form
     ^self tag: 'form'
+
 !
 
 input
     ^self tag: 'input'
+
 !
 
 button
     ^self tag: 'button'
+
 !
 
 select
     ^self tag: 'select'
+
 !
 
 option
     ^self tag: 'option'
+
 !
 
 textarea
     ^self tag: 'textarea'
+
 !
 
 a
     ^self tag: 'a'
-! !
 
+!
 
-!HTMLCanvas methodsFor: '*JQuery'!
+canvas
+	^self tag: 'canvas'
 
-appendToJQuery: aJQuery
-    aJQuery appendElement: root element
 ! !
 
-
 Object subclass: #TagBrush
-	instanceVariableNames: 'element'
+	instanceVariableNames: 'canvas, element'
 	category: 'Canvas'!
 
-!TagBrush class methodsFor: 'instance creation'!
+!TagBrush methodsFor: 'accessing'!
+
+element
+    ^element
 
-fromString: aString canvas: aCanvas
-    ^self new
-	initializeFromString: aString canvas: aCanvas;
-	yourself
 ! !
 
 !TagBrush methodsFor: 'adding'!
@@ -156,23 +261,28 @@ fromString: aString canvas: aCanvas
 contents: anObject
     self asJQuery empty.
     self append: anObject
+
 !
 
 addBrush: aTagBrush
     self appendChild: aTagBrush element.
     ^aTagBrush
+
 !
 
 with: anObject
     self append: anObject
+
 !
 
 append: anObject
     anObject appendToBrush: self
+
 !
 
 appendToBrush: aTagBrush
     aTagBrush addBrush: self
+
 !
 
 appendBlock: aBlock
@@ -181,114 +291,138 @@ appendBlock: aBlock
     canvas root: self.
     aBlock value: canvas.
     canvas root: root
+
 !
 
 appendChild: anElement
     {'self[''@element''].appendChild(anElement)'}
+
 !
 
 appendString: aString
     self appendChild: (self createTextNodeFor: aString)
+
 ! !
 
 !TagBrush methodsFor: 'attributes'!
 
 at: aString put: aValue
     {'self[''@element''].setAttribute(aString, aValue)'}
+
 !
 
 removeAt: aString
     {'self[''@element''].removeAttribute(aString)'}
+
 !
 
 class: aString
     self at: 'class' put: aString
+
 !
 
 id: aString
     self at: 'id' put: aString
+
 !
 
 src: aString
     self  at: 'src' put: aString
+
 !
 
 href: aString
     self at: 'href' put: aString
+
 !
 
 title: aString
     self at: 'title' put: aString
+
 !
 
 style: aString
     self at: 'style' put: aString
-! !
-
-!TagBrush methodsFor: 'initialization'!
 
-initializeFromString: aString canvas: aCanvas
-    element := self createElementFor: aString.
-    canvas := aCanvas
-! !
-
-!TagBrush methodsFor: 'accessing'!
-
-element
-    ^element
 ! !
 
 !TagBrush methodsFor: 'converting'!
 
 asJQuery
-    ^{'return smalltalk.JQuery._from_(jQuery(self[''@element'']))'}
+	{'return smalltalk.JQuery._from_(jQuery(self[''@element'']))'}
+
 !
 
 asJQueryDo: aBlock
     aBlock value: self asJQuery
+
 ! !
 
 !TagBrush methodsFor: 'events'!
 
 onKeyDown: aBlock
     self asJQuery on: 'keydown' do: aBlock
+
 !
 
 onKeyPress: aBlock
     self asJQuery on: 'keypress' do: aBlock
+
 !
 
 onKeyUp: aBlock
     self asJQuery on: 'keyup' do: aBlock
+
 !
 
 onFocus: aBlock
     self asJQuery on: 'focus' do: aBlock
+
 !
 
 onBlur: aBlock
     self asJQuery on: 'blur' do: aBlock
+
 !
 
 onChange: aBlock
     self asJQuery on: 'change' do: aBlock
+
 !
 
 onClick: aBlock
     self asJQuery on: 'click' do: aBlock
+
+! !
+
+!TagBrush methodsFor: 'initialization'!
+
+initializeFromString: aString canvas: aCanvas
+    element := self createElementFor: aString.
+    canvas := aCanvas
+
 ! !
 
 !TagBrush methodsFor: 'private'!
 
 createElementFor: aString
-    ^{'return document.createElement(String(aString))'}
+	{'return document.createElement(String(aString))'}
+
 !
 
 createTextNodeFor: aString
-    ^{'return document.createTextNode(String(aString))'}
+	{'return document.createTextNode(String(aString))'}
+
 ! !
 
+!TagBrush class methodsFor: 'instance creation'!
 
+fromString: aString canvas: aCanvas
+    ^self new
+	initializeFromString: aString canvas: aCanvas;
+	yourself
+
+! !
 
 Object subclass: #Widget
 	instanceVariableNames: 'root'
@@ -298,35 +432,29 @@ Object subclass: #Widget
 
 root
     ^root
-! !
-
-!Widget methodsFor: 'adding'!
 
-appendToBrush: aTagBrush
-    self appendToJQuery: aTagBrush asJQuery
-!
-
-appendToJQuery: aJQuery
-    self render.
-    aJQuery append: self root asJQuery
 ! !
 
 !Widget methodsFor: 'actions'!
 
 alert: aString
     {'alert(aString)'}
+
 !
 
 confirm: aString
-    ^{'return window.confirm(aString)'}
+    {'return window.confirm(aString)'}
+
 !
 
 prompt: aString
     ^self prompt: aString default: ''
+
 !
 
 prompt: aString default: anotherString
-    ^{'return window.prompt(aString, anotherString)'}
+    {'return window.prompt(aString, anotherString)'}
+
 !
 
 update
@@ -335,6 +463,20 @@ update
     canvas root: self root.
     self root asJQuery empty.
     self renderOn: canvas
+
+! !
+
+!Widget methodsFor: 'adding'!
+
+appendToBrush: aTagBrush
+    self appendToJQuery: aTagBrush asJQuery
+
+!
+
+appendToJQuery: aJQuery
+    self render.
+    aJQuery append: self root asJQuery
+
 ! !
 
 !Widget methodsFor: 'rendering'!
@@ -344,43 +486,50 @@ render
     canvas := HTMLCanvas new.
     root := canvas root.
     self renderOn: canvas
+
 !
 
 renderOn: html
     self
+
 ! !
 
-Widget subclass: #Counter
-	instanceVariableNames: 'count'
+TagBrush subclass: #CanvasBrush
+	instanceVariableNames: ''
 	category: 'Canvas'!
 
-!Counter methodsFor: 'initialization'!
+!CanvasBrush methodsFor: 'initialization'!
 
-initialize
-    super initialize.
-    count := 0
+initializeWithCanvas: aCanvas
+	canvas := aCanvas
 ! !
 
-!Counter methodsFor: 'rendering'!
+!CanvasBrush methodsFor: 'private'!
 
-renderOn: html
-    html h1 with: count asString.
-    html button
-	with: '++';
-	onClick: [self increase].
-    html button
-	with: '--';
-	onClick: [self decrease]
+createElement
+	{'return document.createElement(''canvas'')'}
 ! !
 
-!Counter methodsFor: 'actions'!
+!CanvasBrush class methodsFor: 'instance creation'!
+
+canvas: aCanvas
+	^self new
+		initializeWithCanvas: aCanvas;
+		yourself
+! !
+
+
+
+appendToBrush: aTagBrush
+    aTagBrush append: self asString
 
-increase
-    count := count + 1.
-    self update
 !
 
-decrease
-    count := count - 1.
-    self update
-! !
+appendToBrush: aTagBrush
+    aTagBrush appendBlock: self
+!
+
+appendToBrush: aTagBrush
+    aTagBrush appendString: self
+
+!

+ 325 - 195
st/compiler.st → st/Compiler.st

@@ -2,109 +2,109 @@ Object subclass: #Node
 	instanceVariableNames: 'nodes'
 	category: 'Compiler'!
 
-
 !Node methodsFor: 'accessing'!
 
+nodes
+	^nodes ifNil: [nodes := Array new]
+
+!
+
 addNode: aNode
 	self nodes add: aNode
-!
 
-nodes
-	^nodes ifNil: [nodes := Array new]
 ! !
 
 !Node methodsFor: 'building'!
 
 nodes: aCollection
 	nodes := aCollection
+
 ! !
 
 !Node methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitNode: self
-! !
 
+! !
 
 Node subclass: #MethodNode
-	instanceVariableNames: 'selector arguments source'
+	instanceVariableNames: 'selector, arguments, source'
 	category: 'Compiler'!
 
-
 !MethodNode methodsFor: 'accessing'!
 
+selector
+	^selector
+
+!
+
+selector: aString
+	selector := aString
+
+!
+
 arguments
 	^arguments ifNil: [#()]
+
 !
 
 arguments: aCollection
 	arguments := aCollection
-!
-
-selector
-	^selector
-!
 
-selector: aString
-	selector := aString
 !
 
 source
 	^source
+
 !
 
 source: aString
 	source := aString
+
 ! !
 
 !MethodNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitMethodNode: self
-! !
 
+! !
 
 Node subclass: #SendNode
-	instanceVariableNames: 'selector arguments receiver'
+	instanceVariableNames: 'selector, arguments, receiver'
 	category: 'Compiler'!
 
-
 !SendNode methodsFor: 'accessing'!
 
+selector
+	^selector
+
+!
+
+selector: aString
+	selector := aString
+
+!
+
 arguments
 	^arguments ifNil: [arguments := #()]
+
 !
 
 arguments: aCollection
 	arguments := aCollection
-!
 
-cascadeNodeWithMessages: aCollection
-	| first |
-	first := SendNode new
-	    selector: self selector;
-	    arguments: self arguments;
-	    yourself.
-	^CascadeNode new
-	    receiver: self receiver;
-	    nodes: (Array with: first), aCollection;
-	    yourself
 !
 
 receiver
 	^receiver
+
 !
 
 receiver: aNode
 	receiver := aNode
-!
 
-selector
-	^selector
-!
-
-selector: aString
-	selector := aString
 !
 
 valueForReceiver: anObject
@@ -115,102 +115,122 @@ valueForReceiver: anObject
 	    selector: self selector;
 	    arguments: self arguments;
 	    yourself
+
+!
+
+cascadeNodeWithMessages: aCollection
+	| first |
+	first := SendNode new
+	    selector: self selector;
+	    arguments: self arguments;
+	    yourself.
+	^CascadeNode new
+	    receiver: self receiver;
+	    nodes: (Array with: first), aCollection;
+	    yourself
+
 ! !
 
 !SendNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitSendNode: self
-! !
 
+! !
 
 Node subclass: #CascadeNode
 	instanceVariableNames: 'receiver'
 	category: 'Compiler'!
 
-
 !CascadeNode methodsFor: 'accessing'!
 
 receiver
 	^receiver
+
 !
 
 receiver: aNode
 	receiver := aNode
+
 ! !
 
 !CascadeNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitCascadeNode: self
-! !
 
+! !
 
 Node subclass: #AssignmentNode
-	instanceVariableNames: 'left right'
+	instanceVariableNames: 'left, right'
 	category: 'Compiler'!
 
-
 !AssignmentNode methodsFor: 'accessing'!
 
 left
 	^left
+
 !
 
 left: aNode
 	left := aNode
+
 !
 
 right
 	^right
+
 !
 
 right: aNode
 	right := aNode
+
 ! !
 
 !AssignmentNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitAssignmentNode: self
-! !
 
+! !
 
 Node subclass: #BlockNode
 	instanceVariableNames: 'parameters'
 	category: 'Compiler'!
 
-
 !BlockNode methodsFor: 'accessing'!
 
 parameters
 	^parameters ifNil: [parameters := Array new]
+
 !
 
 parameters: aCollection
 	parameters := aCollection
+
 ! !
 
 !BlockNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitBlockNode: self
-! !
 
+! !
 
 Node subclass: #SequenceNode
 	instanceVariableNames: 'temps'
 	category: 'Compiler'!
 
-
 !SequenceNode methodsFor: 'accessing'!
 
 temps
 	^temps ifNil: [#()]
+
 !
 
 temps: aCollection
 	temps := aCollection
+
 ! !
 
 !SequenceNode methodsFor: 'testing'!
@@ -220,205 +240,244 @@ asBlockSequenceNode
 	    nodes: self nodes;
 	    temps: self temps;
 	    yourself
+
 ! !
 
 !SequenceNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitSequenceNode: self
-! !
 
+! !
 
 SequenceNode subclass: #BlockSequenceNode
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
-
 !BlockSequenceNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitBlockSequenceNode: self
-! !
 
+! !
 
 Node subclass: #ReturnNode
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
-
 !ReturnNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitReturnNode: self
-! !
 
+! !
 
 Node subclass: #ValueNode
 	instanceVariableNames: 'value'
 	category: 'Compiler'!
 
-
 !ValueNode methodsFor: 'accessing'!
 
 value
 	^value
+
 !
 
 value: anObject
 	value := anObject
+
 ! !
 
 !ValueNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitValueNode: self
-! !
 
+! !
 
 ValueNode subclass: #VariableNode
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
-
 !VariableNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitVariableNode: self
-! !
 
+! !
 
 VariableNode subclass: #ClassReferenceNode
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
-
 !ClassReferenceNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitClassReferenceNode: self
-! !
 
+! !
 
 Node subclass: #JSStatementNode
 	instanceVariableNames: 'source'
 	category: 'Compiler'!
 
-
 !JSStatementNode methodsFor: 'accessing'!
 
 source
 	^source ifNil: ['']
+
 !
 
 source: aString
 	source := aString
+
 ! !
 
 !JSStatementNode methodsFor: 'visiting'!
 
 accept: aVisitor
 	aVisitor visitJSStatementNode: self
-! !
 
+! !
 
 Object subclass: #NodeVisitor
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
-
 !NodeVisitor methodsFor: 'visiting'!
 
 visit: aNode
 	aNode accept: self
+
 !
 
-visitAssignmentNode: aNode
-	self visitNode: aNode
+visitNode: aNode
+
 !
 
-visitBlockNode: aNode
+visitMethodNode: aNode
 	self visitNode: aNode
-!
 
-visitBlockSequenceNode: aNode
-	self visitSequenceNode: aNode
 !
 
-visitCascadeNode: aNode
+visitSequenceNode: aNode
 	self visitNode: aNode
-!
 
-visitClassReferenceNode: aNode
-	self 
-	    nextPutAll: 'smalltalk.';
-	    nextPutAll: aNode value
 !
 
-visitJSStatementNode: aNode
-	self 
-	    nextPutAll: 'function(){';
-	    nextPutAll: aNode source;
-	    nextPutAll: '})()'
+visitBlockSequenceNode: aNode
+	self visitSequenceNode: aNode
+
 !
 
-visitMethodNode: aNode
+visitBlockNode: aNode
 	self visitNode: aNode
-!
 
-visitNode: aNode
 !
 
 visitReturnNode: aNode
 	self visitNode: aNode
+
 !
 
 visitSendNode: aNode
 	self visitNode: aNode
+
 !
 
-visitSequenceNode: aNode
+visitCascadeNode: aNode
 	self visitNode: aNode
+
 !
 
 visitValueNode: aNode
 	self visitNode: aNode
+
 !
 
 visitVariableNode: aNode
-! !
 
+!
+
+visitAssignmentNode: aNode
+	self visitNode: aNode
+
+!
+
+visitClassReferenceNode: aNode
+	self 
+	    nextPutAll: 'smalltalk.';
+	    nextPutAll: aNode value
+
+!
+
+visitJSStatementNode: aNode
+	self 
+	    nextPutAll: 'function(){';
+	    nextPutAll: aNode source;
+	    nextPutAll: '})()'
+
+! !
 
 NodeVisitor subclass: #Compiler
-	instanceVariableNames: 'stream nestedBlocks earlyReturn currentClass currentSelector'
+	instanceVariableNames: 'stream, nestedBlocks, earlyReturn, currentClass, currentSelector, unknownVariables, tempVariables, messageSends, referencedClasses'
 	category: 'Compiler'!
 
-
 !Compiler methodsFor: 'accessing'!
 
+parser
+	^SmalltalkParser new
+
+!
+
 currentClass
 	^currentClass
+
 !
 
 currentClass: aClass
 	currentClass := aClass
+
 !
 
-parser
-	^SmalltalkParser new
+unknownVariables
+	^unknownVariables copy
+!
+
+pseudoVariables
+	^#('self' 'super' 'true' 'false' 'nil' 'thisContext')
+!
+
+tempVariables
+	^tempVariables copy
+!
+
+knownVariables
+	^self pseudoVariables 
+		addAll: self tempVariables;
+		yourself
+!
+
+classNameFor: aClass
+	^aClass isMetaclass
+	    ifTrue: [aClass instanceClass name, '.klass']
+	    ifFalse: [
+		aClass isNil
+		    ifTrue: ['nil']
+		    ifFalse: [aClass name]]
 ! !
 
 !Compiler methodsFor: 'compiling'!
 
-parse: aString
-    ^self parser parse: aString readStream
-!
+loadExpression: aString
+	DoIt addCompiledMethod: (self eval: (self compileExpression: aString)).
+	^DoIt new doIt
 
-parseExpression: aString
-    ^self parse: 'doIt ^[', aString, '] value'
 !
 
-compile: aString
-	^self compileNode: (self parse: aString)
+load: aString forClass: aClass
+	^self eval: (self compile: aString forClass: aClass)
+
 !
 
 compile: aString forClass: aClass
@@ -429,55 +488,138 @@ compile: aString forClass: aClass
 compileExpression: aString
 	self currentClass: DoIt.
 	^self compileNode: (self parseExpression: aString)
+
+!
+
+eval: aString
+	{'return eval(aString)'}
+!
+
+compile: aString
+	^self compileNode: (self parse: aString)
+
 !
 
 compileNode: aNode
 	stream := '' writeStream.
 	self visit: aNode.
 	^stream contents
+
 !
 
-eval: aString
-	^{'return eval(aString);'}
+parse: aString
+    ^self parser parse: aString readStream
+
 !
 
-load: aString forClass: aClass
-	^self eval: (self compile: aString forClass: aClass)
+parseExpression: aString
+    ^self parse: 'doIt ^[', aString, '] value'
+
 !
 
-loadExpression: aString
-	DoIt addCompiledMethod: (self eval: (self compileExpression: aString)).
-	^DoIt new doIt
+recompile: aClass
+	aClass methodDictionary do: [:each || method |
+		method := self load: each source forClass: aClass.
+		method category: each category.
+		aClass addCompiledMethod: method].
+	aClass isMetaclass ifFalse: [self recompile: aClass class]
+!
+
+recompileAll
+	Smalltalk current classes do: [:each |
+		self recompile: each]
 ! !
 
 !Compiler methodsFor: 'initialization'!
 
 initialize
 	super initialize.
-	stream := '' writeStream
+	stream := '' writeStream.
+	unknownVariables := #().
+	tempVariables := #().
+	messageSends := #().
+	classReferenced := #()
+
 ! !
 
 !Compiler methodsFor: 'visiting'!
 
 visit: aNode
 	aNode accept: self
+
 !
 
-visitAssignmentNode: aNode
-	self visit: aNode left.
-	stream nextPutAll: '='.
-	self visit: aNode right
+visitMethodNode: aNode
+	| str currentSelector |
+	currentSelector := aNode selector asSelector.
+	nestedBlocks := 0.
+	earlyReturn := false.
+	messageSends := #().
+	referencedClasses := #().
+	unknownVariables := #().
+	tempVariables := #().
+	stream 
+	    nextPutAll: 'smalltalk.method({'; lf;
+	    nextPutAll: 'selector: "', aNode selector, '",'; lf.
+	Smalltalk current debugMode ifTrue: [
+	    stream nextPutAll: 'source: unescape("', aNode source escaped, '"),';lf].
+	stream nextPutAll: 'fn: function('.
+	aNode arguments 
+	    do: [:each | 
+		tempVariables add: each.
+		stream nextPutAll: each]
+	    separatedBy: [stream nextPutAll: ', '].
+	stream 
+	    nextPutAll: '){'; lf;
+	    nextPutAll: 'var self=this;'; lf.
+	str := stream.
+	stream := '' writeStream.
+	aNode nodes do: [:each |
+	    self visit: each].
+	earlyReturn ifTrue: [
+	    str nextPutAll: 'try{'].
+	str nextPutAll: stream contents.
+	stream := str.
+	stream 
+	    lf; 
+	    nextPutAll: 'return self;'.
+	earlyReturn ifTrue: [
+	    stream lf; nextPutAll: '} catch(e) {if(e.name === ''stReturn'' && e.selector === ', currentSelector printString, '){return e.fn()} throw(e)}'].
+	stream nextPutAll: '}'.
+	Smalltalk current debugMode ifTrue: [
+		stream 
+			nextPutAll: ',', String lf, 'messageSends: ';
+			nextPutAll: messageSends asJavascript, ','; lf;
+			nextPutAll: 'referencedClasses: ['.
+		referencedClasses 
+			do: [:each | stream nextPutAll: each]
+			separatedBy: [stream nextPutAll: ','].
+		stream nextPutAll: ']'].
+	stream nextPutAll: '})'
 !
 
 visitBlockNode: aNode
 	stream nextPutAll: '(function('.
 	aNode parameters 
 	    do: [:each |
+		tempVariables add: each.
 		stream nextPutAll: each]
 	    separatedBy: [stream nextPutAll: ', '].
 	stream nextPutAll: '){'.
 	aNode nodes do: [:each | self visit: each].
 	stream nextPutAll: '})'
+
+!
+
+visitSequenceNode: aNode
+	aNode temps do: [:each |
+	    tempVariables add: each.
+	    stream nextPutAll: 'var ', each, '=nil;'; lf].
+	aNode nodes do: [:each |
+	    self visit: each.
+	    stream nextPutAll: ';']
+	    separatedBy: [stream lf]
+
 !
 
 visitBlockSequenceNode: aNode
@@ -488,8 +630,8 @@ visitBlockSequenceNode: aNode
 		stream nextPutAll: 'return nil;']
 	    ifFalse: [
 		aNode temps do: [:each |
-		    stream nextPutAll: 'var ', each, '=nil;'.
-		    stream nextPutAll: String cr].
+		    tempVariables add: each.
+		    stream nextPutAll: 'var ', each, '=nil;'; lf].
 		index := 0.
 		aNode nodes do: [:each |
 		    index := index + 1.
@@ -498,68 +640,7 @@ visitBlockSequenceNode: aNode
 		    self visit: each.
 		    stream nextPutAll: ';']].
 	nestedBlocks := nestedBlocks - 1
-!
-
-visitCascadeNode: aNode
-	| index |
-	index := 0.
-	stream nextPutAll: '(function($rec){'.
-	aNode nodes do: [:each |
-	    index := index + 1.
-	    index = aNode nodes size ifTrue: [
-		stream nextPutAll: 'return '].
-	    each receiver: (VariableNode new value: '$rec').
-	    self visit: each.
-	    stream nextPutAll: ';'].
-	stream nextPutAll: '})('.
-	self visit: aNode receiver.
-	stream nextPutAll: ')'
-!
-
-visitClassReferenceNode: aNode
-	stream
-	    nextPutAll: 'smalltalk.';
-	    nextPutAll: aNode value
-!
 
-visitJSStatementNode: aNode
-	stream nextPutAll: '(function(){'.
-	stream nextPutAll: (aNode source value replace: '''''' with: '''').
-	stream nextPutAll: '})()'
-!
-
-visitMethodNode: aNode
-	| str currentSelector |
-	currentSelector := aNode selector asSelector.
-	nestedBlocks := 0.
-	earlyReturn := false.
-	stream 
-	    nextPutAll: 'smalltalk.method({', String cr;
-	    nextPutAll: 'selector: "', aNode selector, '",', String cr;
-	    nextPutAll: 'source: unescape("', aNode source escaped, '"),', String cr;
-	    nextPutAll: 'fn: function('.
-	aNode arguments 
-	    do: [:each | stream nextPutAll: each]
-	    separatedBy: [stream nextPutAll: ', '].
-	stream 
-	    nextPutAll: '){', String cr;
-	    nextPutAll: 'var self=this;', String cr.
-	str := stream.
-	stream := '' writeStream.
-	aNode nodes do: [:each |
-	    self visit: each].
-	earlyReturn ifTrue: [
-	    str nextPutAll: 'try{'].
-	str nextPutAll: stream contents.
-	stream := str.
-	stream 
-	    nextPutAll: String cr; 
-	    nextPutAll: 'return self;'.
-	earlyReturn ifTrue: [
-	    stream nextPutAll: String cr, '} catch(e) {if(e.name === ''stReturn'' && e.selector === ', currentSelector printString, '){return e.fn()} throw(e)}'].
-	stream 
-	    nextPutAll: '}', String cr;
-	    nextPutAll: '})'
 !
 
 visitReturnNode: aNode
@@ -580,53 +661,102 @@ visitReturnNode: aNode
 !
 
 visitSendNode: aNode
-	| str |
+	| str receiver superSend |
 	str := stream.
+	(messageSends includes: aNode selector) ifFalse: [
+		messageSends add: aNode selector].
 	stream := '' writeStream.
 	self visit: aNode receiver.
-	stream contents = 'super' 
-	    ifTrue: [
-		stream := str.
-		stream 
-		    nextPutAll: 'self.klass.superclass.fn.prototype[''';
-		    nextPutAll: aNode selector asSelector;
-		    nextPutAll: '''].apply(self, ['.
-		aNode arguments 
-		    do: [:each | self visit: each]
-		    separatedBy: [stream nextPutAll: ','].
-		stream nextPutAll: '])']
-	    ifFalse: [
-		str nextPutAll: stream contents.
-		stream := str.
-		stream nextPutAll: '.', aNode selector asSelector, '('.
-		aNode arguments 
-		    do: [:each | self visit: each]
-		    separatedBy: [stream nextPutAll: ','].
-		stream nextPutAll: ')']
+	superSend := stream contents = 'super'.
+	receiver := superSend ifTrue: ['self'] ifFalse: [stream contents].
+	str nextPutAll: 'smalltalk.send('.
+	str nextPutAll: receiver.
+	stream := str.
+	stream nextPutAll: ', "', aNode selector asSelector, '", ['.
+	aNode arguments 
+	    do: [:each | self visit: each]
+	    separatedBy: [stream nextPutAll: ', '].
+	stream nextPutAll: ']'.
+	superSend ifTrue: [
+		stream nextPutAll: ', smalltalk.', (self classNameFor: self currentClass superclass)].
+	stream nextPutAll: ')'
 !
 
-visitSequenceNode: aNode
-	aNode temps do: [:each |
-	    stream nextPutAll: 'var ', each, '=nil;'.
-	    stream nextPutAll: String cr].
+visitCascadeNode: aNode
+	| index |
+	index := 0.
+	(tempVariables includes: '$rec') ifFalse: [
+		tempVariables add: '$rec'].
+	stream nextPutAll: '(function($rec){'.
 	aNode nodes do: [:each |
+	    index := index + 1.
+	    index = aNode nodes size ifTrue: [
+		stream nextPutAll: 'return '].
+	    each receiver: (VariableNode new value: '$rec').
 	    self visit: each.
-	    stream nextPutAll: ';']
-	    separatedBy: [stream nextPutAll: String cr]
+	    stream nextPutAll: ';'].
+	stream nextPutAll: '})('.
+	self visit: aNode receiver.
+	stream nextPutAll: ')'
+
 !
 
 visitValueNode: aNode
 	stream nextPutAll: aNode value asJavascript
+
+!
+
+visitAssignmentNode: aNode
+	self visit: aNode left.
+	stream nextPutAll: '='.
+	self visit: aNode right
+
+!
+
+visitClassReferenceNode: aNode
+	| klass |
+	klass := 'smalltalk.', aNode value.
+	(Smalltalk current at: aNode value) isClass ifTrue: [
+		(referencedClasses includes: klass)
+			ifFalse: [referencedClasses add: klass]].
+	stream nextPutAll: klass
 !
 
 visitVariableNode: aNode
-	(self currentClass instVarNames includes: aNode value) 
-	    ifTrue: [stream nextPutAll: 'self[''@', aNode value, ''']']
-	    ifFalse: [stream nextPutAll: aNode value]
+	(self currentClass instanceVariableNames includes: aNode value) 
+		ifTrue: [stream nextPutAll: 'self[''@', aNode value, ''']']
+		ifFalse: [
+			(self knownVariables includes: aNode value) ifFalse: [
+				unknownVariables add: aNode value].
+			stream nextPutAll: aNode value]
+
+!
+
+visitJSStatementNode: aNode
+	stream nextPutAll: (aNode source value replace: '''''' with: '''')
 ! !
 
+!Compiler class methodsFor: 'compiling'!
+
+recompile: aClass
+	aClass methodDictionary do: [:each || method |
+		method := self new load: each source forClass: aClass.
+		method category: each category.
+		aClass addCompiledMethod: method].
+	aClass isMetaclass ifFalse: [self recompile: aClass class]
+!
+
+recompileAll
+	Smalltalk current classes do: [:each |
+		self recompile: each]
+! !
 
 Object subclass: #DoIt
 	instanceVariableNames: ''
 	category: 'Compiler'!
 
+!DoIt methodsFor: ''!
+
+doIt ^[ChunkExporter new exportCategory: 'Parser' ] value
+! !
+

+ 361 - 0
st/Examples.st

@@ -0,0 +1,361 @@
+Widget subclass: #Counter
+	instanceVariableNames: 'count, header'
+	category: 'Examples'!
+
+!Counter methodsFor: 'actions'!
+
+increase
+    count := count + 1.
+    header contents: [:html | html with: count asString]
+!
+
+decrease
+    count := count - 1.
+    header contents: [:html | html with: count asString]
+! !
+
+!Counter methodsFor: 'initialization'!
+
+initialize
+    super initialize.
+    count := 0
+
+! !
+
+!Counter methodsFor: 'rendering'!
+
+renderOn: html
+    header := html h1 
+	with: count asString;
+	yourself.
+    html button
+	with: '++';
+	onClick: [self increase].
+    html button
+	with: '--';
+	onClick: [self decrease]
+
+! !
+
+Widget subclass: #Tetris
+	instanceVariableNames: 'renderingContext, timer, speed, score, rows, movingPiece'
+	category: 'Examples'!
+
+!Tetris methodsFor: 'accessing'!
+
+width
+	^self class width
+!
+
+height
+	^self class height
+!
+
+squares
+	^self class squares
+!
+
+gluePiece: aPiece
+	aPiece glueOn: self
+	
+!
+
+rows
+	"An array of rows. Each row is a collection of points."
+	^rows
+!
+
+addRow: aCollection
+	self rows add: aCollection
+! !
+
+!Tetris methodsFor: 'actions'!
+
+startNewGame
+	self newGame.
+	timer ifNotNil: [timer clearInterval].
+	timer := [self nextStep] valueWithInterval: speed
+!
+
+nextStep
+	movingPiece ifNil: [self newPiece].
+	(movingPiece canMoveIn: self)
+		ifTrue: [movingPiece position: movingPiece position + (0@1)]
+		ifFalse: [self newPiece].
+	self redraw
+	
+!
+
+redraw
+	renderingContext clearRectFrom: 0@ self width to: 0@ self height.
+	self 
+		drawMap;
+		drawPiece
+!
+
+drawMap
+	renderingContext 
+		fillStyle: '#fafafa';
+		fillRectFrom: 0@0 to: self width@self height.
+	renderingContext 
+		lineWidth: 0.5;
+		strokeStyle: '#999'.
+	0 to: self class squares x do: [:each | | x |
+		x := each * self class squareSize.
+		self drawLineFrom: x@0 to: x@self height].
+	0 to: self class squares y do: [:each | | y |
+		y := each * self class squareSize.
+		self drawLineFrom: 0@y to: self width@y].
+!
+
+drawLineFrom: aPoint to: anotherPoint
+	renderingContext 
+		beginPath;
+		moveTo: aPoint;
+		lineTo: anotherPoint;
+		stroke
+!
+
+newGame
+	rows := #().
+	movingPiece := nil.
+	speed := 200.
+	score := 0
+!
+
+newPiece
+	movingPiece := TetrisPiece atRandom
+!
+
+drawRows
+	self rows do: [:each |].
+	movingPiece ifNotNil: [movingPiece drawOn: renderingContext]
+!
+
+drawPiece
+	movingPiece ifNotNil: [
+		movingPiece drawOn: renderingContext]
+! !
+
+!Tetris methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	self newGame
+! !
+
+!Tetris methodsFor: 'rendering'!
+
+renderOn: html
+	html div
+		class: 'tetris';
+		with: [
+			html h3 with: 'Tetris'.
+			self renderCanvasOn: html.
+			self renderButtonsOn: html]
+!
+
+renderCanvasOn: html
+	| canvas |
+	canvas := html canvas.
+	canvas at: 'width' put: self width asString.
+	canvas at: 'height' put: self height asString.
+	renderingContext := CanvasRenderingContext tagBrush: canvas.
+	self redraw
+!
+
+renderButtonsOn: html
+	html div 
+		class: 'tetris_buttons';
+		with: [
+			html button
+				with: 'New game';
+				onClick: [self startNewGame].
+			html button
+				with: 'play/pause';
+				onClick: [self update]]
+! !
+
+!Tetris class methodsFor: 'accessing'!
+
+squareSize
+	^22
+!
+
+width
+	^self squareSize * (self squares x)
+!
+
+height
+	^self squareSize * (self squares y)
+!
+
+squares
+	^10@15
+! !
+
+Widget subclass: #TetrisPiece
+	instanceVariableNames: 'rotation, position'
+	category: 'Examples'!
+
+!TetrisPiece methodsFor: 'accessing'!
+
+rotation
+	^rotation ifNil: [rotation := 1]
+!
+
+rotation: aNumber
+	rotation := aNumber
+!
+
+position
+	^position ifNil: [(Tetris squares x / 2) -1 @ 0]
+!
+
+position: aPoint
+	^position := aPoint
+!
+
+bounds
+	self subclassResponsibility
+!
+
+color
+	^'#afa'
+!
+
+height
+	^2
+! !
+
+!TetrisPiece methodsFor: 'drawing'!
+
+drawOn: aRenderingContext
+	aRenderingContext fillStyle: self color.
+	self bounds do: [:each |
+		aRenderingContext 
+			fillRectFrom: each + self position* Tetris squareSize to: 1@1 * Tetris squareSize;
+			strokeStyle: '#999';
+			lineWidth: 2;
+			strokeRectFrom: each + self position* Tetris squareSize to: 1@1 * Tetris squareSize]
+! !
+
+!TetrisPiece methodsFor: 'testing'!
+
+canMove
+	^self position y < (Tetris squares y - self height)
+!
+
+canMoveIn: aTetris
+	^self position y < (aTetris squares y - self height)
+! !
+
+!TetrisPiece class methodsFor: 'instance creation'!
+
+atRandom
+	^(self subclasses at: self subclasses size atRandom) new
+! !
+
+TetrisPiece subclass: #TetrisPieceO
+	instanceVariableNames: ''
+	category: 'Examples'!
+
+!TetrisPieceO methodsFor: 'accessing'!
+
+bounds
+	^Array new
+		add: 0@0;
+		add: 0@1;
+		add: 1@0;
+		add: 1@1;
+		yourself
+! !
+
+TetrisPiece subclass: #TetrisPieceL
+	instanceVariableNames: ''
+	category: 'Examples'!
+
+!TetrisPieceL methodsFor: 'accessing'!
+
+bounds
+	^Array new
+		add: 0@0;
+		add: 0@1;
+		add: 0@2;
+		add: 1@2;
+		yourself
+!
+
+color
+	^'#ffa'
+!
+
+height
+	^3
+! !
+
+TetrisPiece subclass: #TetrisPieceJ
+	instanceVariableNames: ''
+	category: 'Examples'!
+
+!TetrisPieceJ methodsFor: 'accessing'!
+
+color
+	^'#aaf'
+!
+
+bounds
+	^Array new
+		add: 1@0;
+		add: 1@1;
+		add: 1@2;
+		add: 0@2;
+		yourself
+!
+
+height
+	^3
+! !
+
+TetrisPiece subclass: #TetrisPieceI
+	instanceVariableNames: ''
+	category: 'Examples'!
+
+!TetrisPieceI methodsFor: 'accessing'!
+
+color
+	^'#faa'
+!
+
+bounds
+	^Array new
+		add: 0@0;
+		add: 0@1;
+		add: 0@2;
+		add: 0@3;
+		yourself
+!
+
+height
+	^4
+! !
+
+TetrisPiece subclass: #TetrisPieceT
+	instanceVariableNames: ''
+	category: 'Examples'!
+
+!TetrisPieceT methodsFor: 'accessing'!
+
+bounds
+	^Array new
+		add: 0@0;
+		add: 1@0;
+		add: 2@0;
+		add: 1@1;
+		yourself
+!
+
+color
+	^'#aaf'
+! !
+

+ 713 - 162
st/ide.st → st/IDE.st

@@ -1,72 +1,34 @@
 Widget subclass: #TabManager
-	instanceVariableNames: 'selectedTab tabs opened'
+	instanceVariableNames: 'selectedTab, tabs, opened'
 	category: 'IDE'!
 
-TabManager class instanceVariableNames: 'current'!
-
-!TabManager class methodsFor: 'instance creation'!
-
-current
-    ^current ifNil: [current := super new]
-!
-
-new
-    self shouldNotImplement
-! !
-
-!TabManager methodsFor: 'initialization'!
-
-initialize
-    super initialize.
-    opened := true.
-    'body' asJQuery 
-	append: self;
-	append: [:html | html div id: 'jtalk'];
-	addClass: 'jtalkBody'.
-    self 
-	addTab: Transcript current;
-	addTab: Workspace new.
-    self selectTab: self tabs last.
-    self 
-	onResize: [self updateBodyMargin; updatePosition];
-	onWindowResize: [self updatePosition]
-! !
-
 !TabManager methodsFor: 'accessing'!
 
 tabs
     ^tabs ifNil: [tabs := Array new]
-! !
-
-!TabManager methodsFor: 'adding/Removing'!
 
-addTab: aWidget
-    self tabs add: aWidget.
-    '#jtalk' asJQuery append: aWidget.
-    aWidget root asJQuery hide
-!
-
-removeTab: aWidget
-    self tabs remove: aWidget.
-    self update
 ! !
 
 !TabManager methodsFor: 'actions'!
 
 updateBodyMargin
     self setBodyMargin: '#jtalk' asJQuery height + 27
+
 !
 
 updatePosition
     {'jQuery(''#jtalk'').css(''top'', '''''').css(''bottom'', ''27px'');'}
+
 !
 
 removeBodyMargin
     self setBodyMargin: 0
+
 !
 
 setBodyMargin: anInteger
     '.jtalkBody' asJQuery cssAt: 'margin-bottom' put: anInteger asString, 'px'
+
 !
 
 onResize: aBlock
@@ -75,10 +37,12 @@ onResize: aBlock
 	resize: aBlock,
 	minHeight: 230
 });'}
+
 !
 
 onWindowResize: aBlock
     {'jQuery(window).resize(aBlock)'}
+
 !
 
 open
@@ -89,6 +53,7 @@ open
 	self updateBodyMargin.
 	selectedTab root asJQuery show.
 	opened := true]
+
 !
 
 close
@@ -98,10 +63,12 @@ close
 	self removeBodyMargin.
 	'body' asJQuery removeClass: 'jtalkBody'.
 	opened := false]
+
 !
 
 newBrowserTab
     Browser open
+
 !
 
 selectTab: aWidget
@@ -111,6 +78,7 @@ selectTab: aWidget
 	each root asJQuery hide].
     aWidget root asJQuery show.
     self update
+
 !
 
 closeTab: aWidget
@@ -118,6 +86,41 @@ closeTab: aWidget
     self selectTab: self tabs last.
     aWidget root asJQuery remove.
     self update
+
+! !
+
+!TabManager methodsFor: 'adding/Removing'!
+
+addTab: aWidget
+    self tabs add: aWidget.
+    '#jtalk' asJQuery append: aWidget.
+    aWidget root asJQuery hide
+
+!
+
+removeTab: aWidget
+    self tabs remove: aWidget.
+    self update
+
+! !
+
+!TabManager methodsFor: 'initialization'!
+
+initialize
+    super initialize.
+    opened := true.
+    'body' asJQuery 
+	append: self;
+	append: [:html | html div id: 'jtalk'];
+	addClass: 'jtalkBody'.
+    self 
+	addTab: Transcript current;
+	addTab: Workspace new.
+    self selectTab: self tabs last.
+    self 
+	onResize: [self updateBodyMargin; updatePosition];
+	onWindowResize: [self updatePosition]
+
 ! !
 
 !TabManager methodsFor: 'rendering'!
@@ -136,6 +139,7 @@ renderOn: html
 		class: 'newtab';
 		with: ' + ';
 		onClick: [self newBrowserTab]]
+
 !
 
 renderTabFor: aWidget on: html
@@ -152,23 +156,32 @@ renderTabFor: aWidget on: html
 		class: 'close';
 		with: 'x';
 		onClick: [self closeTab: aWidget]]]
+
 ! !
 
+TabManager class instanceVariableNames: 'current'!
 
-Widget subclass: #TabWidget
-	instanceVariableNames: ''
-	category: 'IDE'!
+!TabManager class methodsFor: 'instance creation'!
 
-!TabWidget class methodsFor: 'instance creation'!
+current
+    ^current ifNil: [current := super new]
+
+!
+
+new
+    self shouldNotImplement
 
-open
-    ^self new open
 ! !
 
+Widget subclass: #TabWidget
+	instanceVariableNames: ''
+	category: 'IDE'!
+
 !TabWidget methodsFor: 'accessing'!
 
 label
     self subclassResponsibility
+
 ! !
 
 !TabWidget methodsFor: 'actions'!
@@ -177,12 +190,7 @@ open
     TabManager current
 	addTab: self;
 	selectTab: self
-! !
 
-!TabWidget methodsFor: 'testing'!
-
-canBeClosed
-    ^false
 ! !
 
 !TabWidget methodsFor: 'rendering'!
@@ -197,15 +205,30 @@ renderOn: html
 	    html div
 		class: 'jt_buttons';
 		with: [self renderButtonsOn: html]]
+
 !
 
 renderBoxOn: html
+
 !
 
 renderButtonsOn: html
+
 ! !
 
+!TabWidget methodsFor: 'testing'!
+
+canBeClosed
+    ^false
 
+! !
+
+!TabWidget class methodsFor: 'instance creation'!
+
+open
+    ^self new open
+
+! !
 
 TabWidget subclass: #Workspace
 	instanceVariableNames: 'textarea'
@@ -215,31 +238,37 @@ TabWidget subclass: #Workspace
 
 label
     ^'[Workspace]'
+
 !
 
 selection
-    ^{'return document.selection'}
+    {'return document.selection'}
+
 !
 
 selectionStart
-    ^{'return jQuery(''.jt_workspace'')[0].selectionStart'}
+    {'return jQuery(''.jt_workspace'')[0].selectionStart'}
+
 !
 
 selectionEnd
-    ^{'return jQuery(''.jt_workspace'')[0].selectionEnd'}
+    {'return jQuery(''.jt_workspace'')[0].selectionEnd'}
+
 !
 
 selectionStart: anInteger
     {'jQuery(''.jt_workspace'')[0].selectionStart = anInteger'}
+
 !
 
 selectionEnd: anInteger
     {'jQuery(''.jt_workspace'')[0].selectionEnd = anInteger'}
+
 !
 
 currentLine
     | lines startLine endLine|
-    lines := textarea asJQuery val tokenize: String cr.
+    lines := textarea asJQuery val tokenize: String lf.
     startLine := endLine := 0.
     lines do: [:each |
 	endLine := startLine + each size.
@@ -247,39 +276,50 @@ currentLine
 	endLine >= self selectionStart ifTrue: [
 	    self selectionEnd: endLine.
 	    ^each]]
+
 ! !
 
 !Workspace methodsFor: 'actions'!
 
 handleKeyDown: anEvent
-    ^{'if(anEvent.ctrlKey) {
-		if(anEvent.keyCode === 68) { //ctrl+p
+    {'if(anEvent.ctrlKey) {
+		if(anEvent.keyCode === 80) { //ctrl+p
 			self._printIt();
+			anEvent.preventDefault();
 			return false;
 		}
-		if(anEvent.keyCode === 80) { //ctrl+d
+		if(anEvent.keyCode === 68) { //ctrl+d
 			self._doIt();
+			anEvent.preventDefault();
+			return false;
+		}
+		if(anEvent.keyCode === 73) { //ctrl+i
+			self._inspectIt();
+			anEvent.preventDefault();
 			return false;
 		}
 	}'}
+
 !
 
 clearWorkspace
     textarea asJQuery val: ''
-!
 
-doIt
-    self printIt
 !
 
-printIt
+doIt
     | selection |
     textarea asJQuery focus.
     self selectionStart = self selectionEnd
 	ifTrue: [selection := self currentLine]
 	ifFalse: [
 	    selection := textarea asJQuery val copyFrom: self selectionStart + 1 to: self selectionEnd + 1].
-    self print: (self eval: selection) printString
+    ^self eval: selection
+!
+
+printIt
+    self print: self doIt printString
+
 !
 
 print: aString
@@ -291,6 +331,7 @@ print: aString
 	(textarea asJQuery val copyFrom: start + 1 to: textarea asJQuery val size)).
     self selectionStart: start.
     self selectionEnd: start + aString size + 2
+
 !
 
 eval: aString
@@ -300,6 +341,12 @@ eval: aString
     node isParseFailure ifTrue: [
 	^self alert: node reason, ', position: ', node position].
     ^compiler loadExpression: aString
+
+!
+
+inspectIt
+    self doIt inspect
+
 ! !
 
 !Workspace methodsFor: 'rendering'!
@@ -311,6 +358,7 @@ renderBoxOn: html
     textarea 
 	class: 'jt_workspace';
 	at: 'spellcheck' put: 'false'
+
 !
 
 renderButtonsOn: html
@@ -322,52 +370,25 @@ renderButtonsOn: html
 	with: 'PrintIt';
 	title: 'ctrl+p';
 	onClick: [self printIt].
+    html button
+	with: 'InspectIt';
+	title: 'ctrl+i';
+	onClick: [self inspectIt].
     html button
 	with: 'Clear workspace';
 	onClick: [self clearWorkspace]
-! !
-
 
+! !
 
 TabWidget subclass: #Transcript
 	instanceVariableNames: 'textarea'
 	category: 'IDE'!
 
-Transcript class instanceVariableNames: 'current'!
-
-!Transcript class methodsFor: 'instance creation'!
-
-open
-    self current open
-!
-
-new
-    self shouldNotImplement
-!
-
-current
-    ^current ifNil: [current := super new]
-! !
-
-!Transcript class methodsFor: 'printing'!
-
-show: anObject
-    self current show: anObject
-!
-
-cr
-    self current show: String cr
-!
-
-clear
-    self current clear
-! !
-
-
 !Transcript methodsFor: 'accessing'!
 
 label
     ^'[Transcript]'
+
 ! !
 
 !Transcript methodsFor: 'actions'!
@@ -375,14 +396,17 @@ label
 show: anObject
     textarea asJQuery val: textarea asJQuery val, anObject asString.
 
+
 !
 
 cr
     textarea asJQuery val: textarea asJQuery val, String cr.
+
 !
 
 clear
     textarea asJQuery val: ''
+
 ! !
 
 !Transcript methodsFor: 'rendering'!
@@ -393,45 +417,63 @@ renderBoxOn: html
     textarea 
 	class: 'jt_transcript';
 	at: 'spellcheck' put: 'false'
+
 !
 
 renderButtonsOn: html
     html button
 	with: 'Clear transcript';
 	onClick: [self clear]
+
 ! !
 
-TabWidget subclass: #Browser
-	instanceVariableNames: 'selectedCategory selectedClass selectedProtocol selectedMethod commitButton categoriesList classesList protocolsList methodsList sourceTextarea tabsList selectedTab saveButton classButtons methodButtons unsavedChanges'
-	category: 'IDE'!
+Transcript class instanceVariableNames: 'current'!
 
-!Browser class methodsFor: 'convenience'!
+!Transcript class methodsFor: 'instance creation'!
+
+open
+    self current open
 
-openOn: aClass
-    self new
-	open;
-	selectCategory: aClass category;
-	selectClass: aClass
 !
 
-open
-    self new open
+new
+    self shouldNotImplement
+
+!
+
+current
+    ^current ifNil: [current := super new]
+
 ! !
 
-!Browser methodsFor: 'initialization'!
+!Transcript class methodsFor: 'printing'!
+
+show: anObject
+    self current show: anObject
+
+!
+
+cr
+    self current show: String cr
+
+!
+
+clear
+    self current clear
 
-initialize
-    super initialize.
-    selectedTab := #instance.
-    unsavedChanges := false
 ! !
 
+TabWidget subclass: #Browser
+	instanceVariableNames: 'selectedCategory, selectedClass, selectedProtocol, selectedMethod, commitButton, categoriesList, classesList, protocolsList, methodsList, sourceTextarea, tabsList, selectedTab, saveButton, classButtons, methodButtons, unsavedChanges'
+	category: 'IDE'!
+
 !Browser methodsFor: 'accessing'!
 
 label
     ^selectedClass 
 	ifNil: ['Browser (nil)']
 	ifNotNil: [selectedClass name]
+
 !
 
 categories
@@ -441,28 +483,27 @@ categories
 	(categories includes: each category) ifFalse: [
 	    categories add: each category]].
     ^categories sort
+
 !
 
 classes
     ^(Smalltalk current classes 
 	select: [:each | each category = selectedCategory])
 	sort: [:a :b | a name > b name]
+
 !
 
 protocols
-    | klass protocols |
-    protocols := Array new.
+    | klass |
     selectedClass ifNotNil: [
 	selectedTab = #comment ifTrue: [^#()].
 	klass := selectedTab = #instance
 	    ifTrue: [selectedClass]
 	    ifFalse: [selectedClass class].
 	klass methodDictionary isEmpty ifTrue: [
-	    protocols add: 'not yet classified'].
-	klass methodDictionary do: [:each |
-	    (protocols includes: each category) ifFalse: [
-		protocols add: each category]]].
-    ^protocols sort
+	    ^Array with: 'not yet classified'].
+	^klass protocols].
+    ^Array new
 !
 
 methods
@@ -480,6 +521,7 @@ methods
 	ifNotNil: [
 	    klass methodDictionary values select: [:each |
 		each category = selectedProtocol]]) sort: [:a :b | a selector > b selector]
+
 !
 
 source
@@ -490,12 +532,14 @@ source
     ^selectedClass
 	ifNil: ['']
 	ifNotNil: [self classCommentSource]
+
 !
 
 methodSource
     ^selectedMethod
 	ifNil: [self dummyMethodSource]
 	ifNotNil: [selectedMethod source]
+
 !
 
 dummyMethodSource
@@ -504,12 +548,14 @@ dummyMethodSource
 
 	| temporary variable names |
 	statements'
+
 !
 
 declarationSource
     ^selectedTab = #instance
 	ifTrue: [self classDeclarationSource]
 	ifFalse: [self metaclassDeclarationSource]
+
 !
 
 classDeclarationSource
@@ -520,17 +566,18 @@ classDeclarationSource
 	    nextPutAll: selectedClass superclass asString;
 	    nextPutAll: ' subclass: #';
 	    nextPutAll: selectedClass name;
-	    nextPutAll: String cr, String tab;
+	    nextPutAll: String lf, String tab;
 	    nextPutAll: 'instanceVariableNames: '''.
 	selectedClass instanceVariableNames 
 	    do: [:each | stream nextPutAll: each] 
 	    separatedBy: [stream nextPutAll: ' '].
 	stream
-	    nextPutAll: '''', String cr, String tab;
+	    nextPutAll: '''', String lf, String tab;
 	    nextPutAll: 'category: ''';
 	    nextPutAll: selectedClass category;
 	    nextPutAll: ''''].
     ^stream contents
+
 !
 
 metaclassDeclarationSource
@@ -546,10 +593,12 @@ metaclassDeclarationSource
 	    separatedBy: [stream nextPutAll: ' '].
 	stream nextPutAll: ''''].
     ^stream contents
+
 !
 
 classCommentSource
     ^selectedClass comment
+
 ! !
 
 !Browser methodsFor: 'actions'!
@@ -557,28 +606,34 @@ classCommentSource
 enableSaveButton
     saveButton removeAt: 'disabled'.
     unsavedChanges := true
+
 !
 
 disableSaveButton
     saveButton ifNotNil: [
 	saveButton at: 'disabled' put: true].
     unsavedChanges := false
+
 !
 
 hideClassButtons
     classButtons asJQuery hide
+
 !
 
 showClassButtons
     classButtons asJQuery show
+
 !
 
 hideMethodButtons
     methodButtons asJQuery hide
+
 !
 
 showMethodButtons
     methodButtons asJQuery show
+
 !
 
 compile
@@ -589,16 +644,19 @@ compile
     (selectedProtocol notNil or: [selectedMethod notNil])
 	ifFalse: [self compileDefinition]
 	ifTrue: [self compileMethodDefinition]
+
 !
 
 compileClassComment
     selectedClass comment: sourceTextarea asJQuery val
+
 !
 
 compileMethodDefinition
     selectedTab = #instance
 	ifTrue: [self compileMethodDefinitionFor: selectedClass]
 	ifFalse: [self compileMethodDefinitionFor: selectedClass class]
+
 !
 
 compileMethodDefinitionFor: aClass
@@ -612,9 +670,14 @@ compileMethodDefinitionFor: aClass
     compiler currentClass: selectedClass.
     method := compiler eval: (compiler compileNode: node).
     method category: selectedProtocol.
+    compiler unknownVariables do: [:each |
+	(self confirm: 'Declare ''', each, ''' as instance variable?') ifTrue: [
+		self addInstanceVariableNamed: each toClass: aClass.
+		^self compileMethodDefinitionFor: aClass]].
     aClass addCompiledMethod: method.
     self updateMethodsList.
     self selectMethod: method
+
 !
 
 compileDefinition
@@ -623,23 +686,29 @@ compileDefinition
     self 
 	updateCategoriesList;
 	updateClassesList
+
 !
 
 commitCategory
     selectedCategory ifNotNil: [
-	(Ajax url: 'js/', selectedCategory, '.js')
+	(Ajax url: self class commitPathJs, '/', selectedCategory, '.js')
 	    at: 'type' put: 'PUT';
 	    at: 'data' put: (Exporter new exportCategory: selectedCategory);
-	    at: 'error' put: [self alert: 'Commit failed!!'];
+	    at: 'error' put: [self alert: 'Commit failed!'];
+	    send.
+	(Ajax url: self class commitPathSt, '/', selectedCategory, '.st')
+	    at: 'type' put: 'PUT';
+	    at: 'data' put: (ChunkExporter new exportCategory: selectedCategory);
+	    at: 'error' put: [self alert: 'Commit failed!'];
 	    send]
-!
-
 
+!
 
 cancelChanges
     ^unsavedChanges 
 	ifTrue: [self confirm: 'Cancel changes?']
 	ifFalse: [true]
+
 !
 
 removeClass
@@ -647,14 +716,18 @@ removeClass
 	ifTrue: [
 	    Smalltalk current basicDelete: selectedClass name.
 	    self selectClass: nil]
+
 !
 
 removeMethod
     self cancelChanges ifTrue: [
 	(self confirm: 'Do you really want to remove #', selectedMethod selector, '?')
 	    ifTrue: [
-		selectedClass removeCompiledMethod: selectedMethod.
+		selectedTab = #instance 
+			ifTrue: [selectedClass removeCompiledMethod: selectedMethod]
+			ifFalse: [selectedClass class removeCompiledMethod: selectedMethod].
 		self selectMethod: nil]]
+
 !
 
 setMethodProtocol: aString
@@ -669,6 +742,7 @@ setMethodProtocol: aString
 		    updateProtocolsList;
 		    updateMethodsList;
 		    updateSourceAndButtons]]
+
 !
 
 addNewProtocol
@@ -677,6 +751,7 @@ addNewProtocol
     newProtocol notEmpty ifTrue: [
 	selectedMethod category: newProtocol.
 	self setMethodProtocol: newProtocol]
+
 !
 
 selectCategory: aCategory
@@ -689,6 +764,7 @@ selectCategory: aCategory
 	    updateProtocolsList;
 	    updateMethodsList;
 	    updateSourceAndButtons]
+
 !
 
 selectClass: aClass
@@ -700,6 +776,7 @@ selectClass: aClass
 	    updateProtocolsList;
 	    updateMethodsList;
 	    updateSourceAndButtons]
+
 !
 
 selectProtocol: aString
@@ -710,6 +787,7 @@ selectProtocol: aString
 	    updateProtocolsList;
 	    updateMethodsList;
 	    updateSourceAndButtons]
+
 !
 
 selectMethod: aMethod
@@ -719,6 +797,7 @@ selectMethod: aMethod
 	    updateProtocolsList;
 	    updateMethodsList;
 	    updateSourceAndButtons]
+
 !
 
 selectTab: aString
@@ -726,8 +805,41 @@ selectTab: aString
 	selectedTab := aString.
 	self selectProtocol: nil.
 	self updateTabsList]
+
+!
+
+renameClass
+    | newName |
+    newName := self prompt: 'Rename class ', selectedClass name.
+    newName notEmpty ifTrue: [
+	selectedClass rename: newName.
+	self 
+		updateClassesList;
+		updateSourceAndButtons]
+
+!
+
+addInstanceVariableNamed: aString toClass: aClass
+	ClassBuilder new
+		addSubclassOf: aClass superclass named: aClass name instanceVariableNames: (aClass instanceVariableNames copy add: aString; yourself)
+!
+
+searchReferencesOf: aString
+	ReferencesBrowser search: aString
+!
+
+searchClassReferences
+	ReferencesBrowser search: selectedClass name
 ! !
 
+!Browser methodsFor: 'initialization'!
+
+initialize
+    super initialize.
+    selectedTab := #instance.
+    unsavedChanges := false
+
+! !
 
 !Browser methodsFor: 'rendering'!
 
@@ -736,6 +848,7 @@ renderBoxOn: html
 	renderTopPanelOn: html;
 	renderTabsOn: html;
 	renderBottomPanelOn: html
+
 !
 
 renderTopPanelOn: html
@@ -757,11 +870,13 @@ renderTopPanelOn: html
 		updateProtocolsList;
 		updateMethodsList.
 	    html div class: 'jt_clear']
+
 !
 
 renderTabsOn: html
     tabsList := html ul class: 'jt_tabs'.
     self updateTabsList.
+
 !
 
 renderBottomPanelOn: html
@@ -773,6 +888,7 @@ renderBottomPanelOn: html
 		class: 'source';
 		at: 'spellcheck' put: 'false'.
 	    sourceTextarea asJQuery call: 'tabby']
+
 !
 
 renderButtonsOn: html
@@ -783,19 +899,31 @@ renderButtonsOn: html
     methodButtons := html span.
     classButtons := html span.
     self updateSourceAndButtons
+
+! !
+
+!Browser methodsFor: 'testing'!
+
+canBeClosed
+    ^true
+
 ! !
 
 !Browser methodsFor: 'updating'!
 
 updateCategoriesList
     categoriesList contents: [:html |
-	self categories do: [:each || li |
+	self categories do: [:each || li label |
+	    each isEmpty 
+		ifTrue: [label := 'Unclassified']
+		ifFalse: [label := each].
 	    li := html li.
 	    selectedCategory = each ifTrue: [
 		li class: 'selected'].
 	    li
-		with: each;
+		with: label;
 		onClick: [self selectCategory: each]]]
+
 !
 
 updateClassesList
@@ -808,6 +936,7 @@ updateClassesList
 	    li
 		with: each name;
 		onClick: [self selectClass: each]]]
+
 !
 
 updateProtocolsList
@@ -819,6 +948,7 @@ updateProtocolsList
 	    li 
 		with: each;
 		onClick: [self selectProtocol: each]]]
+
 !
 
 updateMethodsList
@@ -830,6 +960,7 @@ updateMethodsList
 	    li
 		with: each selector;
 		onClick: [self selectMethod: each]]]
+
 !
 
 updateTabsList
@@ -849,50 +980,470 @@ updateTabsList
 	li
 	    with: 'Comment';
 	    onClick: [self selectTab: #comment]]
+
 !
 
 updateSourceAndButtons
-    self disableSaveButton.
-    classButtons contents: [:html |
-	html button
-	    with: 'Remove class';
-	    onClick: [self removeClass]].
-    methodButtons contents: [:html |
+	self disableSaveButton.
+	classButtons contents: [:html |
+		html button
+			with: 'Rename class';
+			onClick: [self renameClass].
+		html button
+			with: 'Remove class';
+			onClick: [self removeClass].
+		html button
+			with: 'References';
+			onClick: [self searchClassReferences]].
+	methodButtons contents: [:html |
+		html button
+			with: 'Remove method';
+			onClick: [self removeMethod].
+		html select 
+	    		onChange: [:e :select | self setMethodProtocol: select val];
+	    		with: [
+				html option
+		    			with: 'Method protocol';
+					at: 'disabled' put: 'disabled'.
+				html option
+		    			class: 'important';
+		    			with: 'New...'.
+				self protocols do: [:each |
+		    			html option with: each]].
+		selectedMethod isNil ifFalse: [
+			html select 
+	    			onChange: [:e :select | self searchReferencesOf: select val];
+	    			with: [
+					html option
+		    				with: 'References';
+						at: 'disabled' put: 'disabled'.
+					html option
+		    				class: 'important';
+		    				with: selectedMethod selector.
+					selectedMethod messageSends sorted do: [:each |
+		    				html option with: each]]]].
+    	selectedMethod isNil
+		ifTrue: [
+	    		self hideMethodButtons.
+	    			(selectedClass isNil or: [selectedProtocol notNil])
+					ifTrue: [self hideClassButtons]
+	    				ifFalse: [self showClassButtons]]
+		ifFalse: [
+	    		self hideClassButtons.
+	    		self showMethodButtons].
+    	sourceTextarea asJQuery val: self source
+
+! !
+
+!Browser class methodsFor: 'accessing'!
+
+commitPathJs
+	^'js'
+!
+
+commitPathSt
+	^'st'
+! !
+
+!Browser class methodsFor: 'convenience'!
+
+openOn: aClass
+    ^self new
+	open;
+	selectCategory: aClass category;
+	selectClass: aClass
+
+!
+
+open
+    self new open
+
+! !
+
+TabWidget subclass: #Inspector
+	instanceVariableNames: 'label, variables, object, selectedVariable, variablesList, valueTextarea, workspaceTextarea, diveButton'
+	category: 'IDE'!
+
+!Inspector methodsFor: 'accessing'!
+
+label
+	^label ifNil: ['Inspector (nil)']
+!
+
+variables
+	^variables
+!
+
+setVariables: aCollection
+	variables := aCollection
+!
+
+setLabel: aString
+	label := aString
+!
+
+selectedVariable
+	^selectedVariable
+!
+
+selectedVariable: aString
+	selectedVariable := aString
+! !
+
+!Inspector methodsFor: 'actions'!
+
+inspect: anObject
+	object := anObject.
+	variables := #().
+	object inspectOn: self
+!
+
+dive
+	(self variables at: self selectedVariable) inspect
+!
+
+refresh
+	self 
+		inspect: object; 
+		updateVariablesList;
+		updateValueTextarea
+! !
+
+!Inspector methodsFor: 'rendering'!
+
+renderBoxOn: html
+	self 
+		renderTopPanelOn: html;
+		renderBottomPanelOn: html
+!
+
+renderTopPanelOn: html
+    html div 
+	class: 'top'; 
+	with: [
+	    variablesList := html ul class: 'jt_column variables'.
+	    valueTextarea := html textarea class: 'jt_column value'; at: 'readonly' put: 'readonly'.
+	    self
+		updateVariablesList;
+		updateValueTextarea.
+	    html div class: 'jt_clear']
+
+!
+
+renderBottomPanelOn: html
+    html div
+	class: 'jt_sourceCode';
+	with: [
+	    workspaceTextarea := html textarea 
+		class: 'source';
+		at: 'spellcheck' put: 'false'.
+	    workspaceTextarea asJQuery call: 'tabby']
+
+!
+
+renderButtonsOn: html
 	html button
-	    with: 'Remove method';
-	    onClick: [self removeMethod].
-	html select 
-	    onChange: [:s | self setMethodProtocol: s val];
-	    with: [
-		html option
-		    with: 'Method protocol';
-		    at: 'disabled' put: 'disabled'.
-		html option
-		    class: 'important';
-		    with: 'New...'.
-		self protocols do: [:each |
-		    html option with: each]]].
-    selectedMethod 
-	ifNil: [
-	    self hideMethodButtons.
-	    selectedClass 
-		ifNil: [self hideClassButtons]
-	    ifNotNil: [self showClassButtons]]
-	ifNotNil: [
-	    self hideClassButtons.
-	    self showMethodButtons].
-    sourceTextarea asJQuery val: self source
+		with: 'Refresh';
+		onClick: [self refresh].
+	diveButton := html button 
+		with: 'Dive'; 
+		onClick: [self dive].
+	self updateButtons
+	
 ! !
 
-!Browser methodsFor: 'testing'!
+!Inspector methodsFor: 'testing'!
 
 canBeClosed
-    ^true
+	^true
 ! !
+
+!Inspector methodsFor: 'updating'!
+
+updateVariablesList
+	variablesList contents: [:html |
+		self variables keys do: [:each || li |
+			li := html li.
+			li
+				with: each;
+				onClick: [self selectVariable: each].
+			self selectedVariable = each ifTrue: [
+				li class: 'selected']]]
+!
+
+selectVariable: aString
+	self selectedVariable: aString.
+	self 
+		updateVariablesList;
+		updateValueTextarea;
+		updateButtons
+!
+
+updateValueTextarea
+	valueTextarea asJQuery val: (self selectedVariable isNil
+		ifTrue: ['']
+		ifFalse: [(self variables at: self selectedVariable) printString])
+!
+
+updateButtons
+	(self selectedVariable notNil and: [(self variables at: self selectedVariable) notNil])
+		ifFalse: [diveButton at: 'disabled' put: true] 
+		ifTrue: [diveButton removeAt: 'disabled']
 		
+! !
+
+!Inspector class methodsFor: 'instance creation'!
+
+on: anObject
+	^self new
+		inspect: anObject;
+		yourself
+! !
+
+TabWidget subclass: #ReferencesBrowser
+	instanceVariableNames: 'implementors, senders, implementorsList, input, timer, selector, sendersList, referencedClasses, referencedClassesList'
+	category: 'IDE'!
+
+!ReferencesBrowser methodsFor: 'accessing'!
+
+implementors
+	^implementors ifNil: [implementors := Array new]
+!
 
+label
+	^'[ReferencesBrowser]'
+!
+
+selector
+	^selector
+!
+
+senders
+	^senders ifNil: [senders := Array new]
+!
+
+classesAndMetaclasses
+	^Smalltalk current classes, (Smalltalk current classes collect: [:each | each class])
+!
+
+referencedClasses
+	^referencedClasses ifNil: [referencedClasses := Array new]
+! !
+
+!ReferencesBrowser methodsFor: 'actions'!
+
+openBrowserOn: aMethod
+       | browser |
+       browser := Browser openOn: (aMethod class isMetaclass 
+		ifTrue: [aMethod methodClass instanceClass] ifFalse: [aMethod methodClass]).
+       aMethod methodClass isMetaclass ifTrue: [browser selectTab: #class].
+       browser
+               selectProtocol: aMethod category;
+               selectMethod: aMethod
+!
+
+searchReferencesFor: aString
+	selector := aString.
+	implementors := Array new.
+	senders := Array new.
+	referencedClasses := Array new.
+	(selector match: '^[A-Z]') 
+		ifFalse: [self searchSelectorReferencesFor: selector]
+		ifTrue: [self searchReferencedClassesFor: selector]
+!
+
+search: aString
+	self 
+		searchReferencesFor: aString;
+		updateImplementorsList;
+		updateSendersList;
+		updateReferencedClassesList
+!
+
+searchReferencedClassesFor: aString
+	self classesAndMetaclasses do: [:each |
+		each methodDictionary values do: [:value |
+			(((value referencedClasses select: [:each | each notNil])collect: [:each | each name]) includes: selector) ifTrue: [
+				self referencedClasses add: value]]]
+!
+
+searchSelectorReferencesFor: aString
+	self classesAndMetaclasses do: [:each | 
+		each methodDictionary keysAndValuesDo: [:key :value | 
+			key = selector ifTrue: [self implementors add: value]].
+		each methodDictionary keysAndValuesDo: [:key :value | 
+			(value messageSends includes: selector) ifTrue: [
+				self senders add: value]]]
+! !
+
+!ReferencesBrowser methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	selector := ''
+! !
+
+!ReferencesBrowser methodsFor: 'private'!
+
+setInputEvents
+	input
+		onKeyUp: [timer := [self search: input asJQuery val] valueWithTimeout: 100];
+		onKeyDown: [timer ifNotNil: [timer clearTimeout]]
+! !
+
+!ReferencesBrowser methodsFor: 'rendering'!
+
+renderBoxOn: html
+	self 
+		renderInputOn: html;
+		renderImplementorsOn: html;
+		renderSendersOn: html;
+		renderReferencedClassesOn: html
+!
+
+renderInputOn: html
+	input := html input 
+		class: 'implementors';
+		yourself.
+	input asJQuery val: selector.
+	self setInputEvents
+!
+
+renderImplementorsOn: html
+    	implementorsList := html ul class: 'jt_column implementors'.
+	self updateImplementorsList
+!
+
+renderSendersOn: html
+    	sendersList := html ul class: 'jt_column senders'.
+	self updateSendersList
+!
 
+renderReferencedClassesOn: html
+    	referencedClassesList := html ul class: 'jt_column referenced_classes'.
+	self updateReferencedClassesList
+! !
 
+!ReferencesBrowser methodsFor: 'testing'!
 
+canBeClosed
+	^true
+! !
+
+!ReferencesBrowser methodsFor: 'updating'!
+
+updateImplementorsList
+    implementorsList contents: [:html |
+	html li
+		class: 'column_label'; 
+		with: 'Implementors';
+		style: 'font-weight: bold'.
+	self implementors do: [:each || li |
+	    li := html li.
+	    li
+		with: (each methodClass asString, ' >> ', self selector);
+		onClick: [self openBrowserOn: each]]]
+!
+
+updateSendersList
+    	sendersList contents: [:html |
+	html li
+		class: 'column_label'; 
+		with: 'Senders';
+		style: 'font-weight: bold'.
+	self senders do: [:each |
+		html li
+	    		with: (each methodClass asString, ' >> ', each selector);
+			onClick: [self openBrowserOn: each]]]
+!
+
+updateReferencedClassesList
+    	referencedClassesList contents: [:html |
+	html li
+		class: 'column_label'; 
+		with: 'Class references';
+		style: 'font-weight: bold'.
+	self referencedClasses do: [:each |
+		html li
+	    		with: (each methodClass asString, ' >> ', each selector);
+			onClick: [self openBrowserOn: each]]]
+! !
 
+!ReferencesBrowser class methodsFor: 'instance creation'!
+
+search: aString
+	^self new
+		searchReferencesFor: aString;
+		open
+! !
+
+
+
+inspect
+	Inspector new 
+		inspect: self;
+		open
+!
+
+inspectOn: anInspector
+	| variables |
+	variables := Dictionary new.
+	variables at: '#self' put: self.
+	self class instanceVariableNames do: [:each |
+		variables at: each put: (self instVarAt: each)].
+	anInspector 
+		setLabel: self printString;
+		setVariables: variables
 	
+	
+!
+
+inspectOn: anInspector
+	| variables |
+	variables := Dictionary new.
+	variables at: '#self' put: self.
+	variables at: '#year' put: self year.
+	variables at: '#month' put: self month.
+	variables at: '#day' put: self day.
+	variables at: '#hours' put: self hours.
+	variables at: '#minutes' put: self minutes.
+	variables at: '#seconds' put: self seconds.
+	variables at: '#milliseconds' put: self milliseconds.
+	anInspector 
+		setLabel: self printString;
+		setVariables: variables
+	
+	
+!
+
+inspectOn: anInspector
+	| variables |
+	variables := Dictionary new.
+	variables at: '#self' put: self.
+	self withIndexDo: [:each :i |
+		variables at: i put: each].
+	anInspector 
+		setLabel: self printString;
+		setVariables: variables
+!
+
+inspectOn: anInspector
+	| label |
+	super inspectOn: anInspector.
+	self printString size > 30 
+		ifTrue: [label := (self printString copyFrom: 1 to: 30), '...''']
+		ifFalse: [label := self printString]. 
+	anInspector setLabel: label
+!
+
+inspectOn: anInspector
+	| variables |
+	variables := Dictionary new.
+	variables at: '#self' put: self.
+	variables at: '#keys' put: self keys.
+	self keysAndValuesDo: [:key :value |
+		variables at: key put: value].
+	anInspector 
+		setLabel: self printString;
+		setVariables: variables
+!

+ 131 - 61
st/jQuery.st → st/JQuery.st

@@ -2,18 +2,34 @@ Object subclass: #JQuery
 	instanceVariableNames: 'jquery'
 	category: 'JQuery'!
 
-!JQuery class methodsFor: 'instance creation'!
+!JQuery methodsFor: 'DOM insertion'!
+
+append: anObject
+    "Append anObject at the end of the element."
+    anObject appendToJQuery: self
 
-fromString: aString
-    | newJQuery |
-    {'newJQuery = jQuery(String(aString))'}.
-    ^self from: newJQuery
 !
 
-from: anObject
-    ^self new
-	initializeWithJQueryObject: anObject;
-	yourself
+appendElement: anElement
+    "Append anElement at the end of the element.
+     Dont't call this method directly, use #append: instead"
+    self call: 'append' withArgument: anElement
+
+!
+
+appendToJQuery: aJQuery
+    aJQuery appendElement: jquery
+
+!
+
+contents: anObject
+    self empty.
+    self append: anObject
+
+!
+
+empty
+    ^self call: 'empty'
 ! !
 
 !JQuery methodsFor: 'attributes'!
@@ -21,253 +37,307 @@ from: anObject
 removeAttribute: aString
     "Remove an attribute from each element in the set of matched elements."
     ^self call: 'removeAttribute' withArgument: aString
+
 !
 
 attr: aString
     "Get the value of an attribute for the first element in the set of matched elements."
     ^self call: 'attr' withArgument: aString
+
 !
 
 val
     "Get the current value of the first element in the set of matched elements."
     ^self call: 'val'
+
 !
 
 val: aString
     self call: 'val' withArgument: aString
+
 ! !
 
 !JQuery methodsFor: 'css'!
 
 cssAt: aString
-    ^{'return self[''@jquery''].css(aString)'}
+	{'return self[''@jquery''].css(aString)'}
 !
 
 cssAt: aString put: anotherString
     {'self[''@jquery''].css(aString, anotherString)'}
+
 !
- 
+
 addClass: aString
     "Adds the specified class(es) to each of the set of matched elements."
     self call: 'addClass' withArgument: aString
+
 !
 
 removeClass: aString
     "Remove a single class, multiple classes, or all classes from each element in the set of matched elements."
     self call: 'removeClass' withArgument: aString
+
 !
 
 toggleClass: aString
     "Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument."
     self call: 'toggleClass' withArgument: aString
+
 !
 
 height 
     "Get the current computed height for the first element in the set of matched elements."
     ^self call: 'height'
+
 !
 
 height: anInteger
     self call: 'height' withArgument: anInteger
+
 !
 
 width: anInteger
     self call: 'width' withArgument: anInteger
+
 !
- 
+
 width
     "Get the current computed width for the first element in the set of matched elements."
     ^self call: 'width'
+
 !
 
 innerHeight
     "Get the current computed height for the first element in the set of matched elements, including padding but not border."
     ^self call: 'innerHeight'
+
 !
 
 innerWidth
     "Get the current computed width for the first element in the set of matched elements, including padding but not border."
     ^self call: 'innerWidth'
+
 !
 
 outerHeight
     "Get the current computed height for the first element in the set of matched elements, including padding, border, and optionally margin."
     ^self call: 'outerHeight'
+
 !
 
 outerWidth
     "Get the current computed width for the first element in the set of matched elements, including padding and border."
     ^self call: 'outerWidth'
+
 !
 
 top
     "Get the current y coordinate of the first element in the set of matched elements, relative to the offset parent."
     ^(self call: 'position') basicAt: 'top'
+
 !
 
 left
     "Get the current x coordinate of the first element in the set of matched elements, relative to the offset parent."
     ^(self call: 'position') basicAt: 'left'
+
 !
 
 offsetLeft
     "Get the current coordinates of the first element in the set of matched elements, relative to the document."
     ^(self call: 'offset') basicAt: 'left'
+
 !
 
 offsetTop
     "Get the current coordinates of the first element in the set of matched elements, relative to the document."
     ^(self call: 'offset') basicAt: 'top'
+
 !
 
 scrollLeft
     "Get the current horizontal position of the scroll bar for the first element in the set of matched elements."
     ^self call: 'scrollLeft'
+
 !
 
 scrollTop
     "Get the current vertical position of the scroll bar for the first element in the set of matched elements."
     ^self call: 'scrollTop'
+
 !
 
 scrollLeft: anInteger
     self call: 'scrollLeft' withArgument: anInteger
+
 !
 
 scrollTop: anInteger
     self call: 'scrollTop' withArgument: anInteger
+
 ! !
 
 !JQuery methodsFor: 'events'!
 
 focus
     self call: 'focus'
+
 !
 
 show
     self call: 'show'
+
 !
 
 hide
     self call: 'hide'
+
 !
 
 remove
     self call: 'remove'
+
 !
 
 on: anEventString do: aBlock
     "Attach aBlock for anEventString on the element"
-    {'self[''@jquery''].bind(anEventString, function(e){aBlock(self)})'}
+    {'self[''@jquery''].bind(anEventString, function(e){aBlock(e, self)})'}
+
 !
 
 removeEvents: aString
     "Unbind all handlers attached to the event aString"
     self call: 'unbind' withArgument: aString
-! !
-
-!JQuery methodsFor: 'DOM insertion'!
-
-append: anObject
-    "Append anObject at the end of the element."
-    anObject appendToJQuery: self
-!
-
-appendElement: anElement
-    "Append anElement at the end of the element.
-     Dont't call this method directly, use #append: instead"
-    self call: 'append' withArgument: anElement
-!
-
-appendToJQuery: aJQuery
-    aJQuery appendElement: jquery
-!
-
-contents: anObject
-    self empty.
-    self append: anObject
-!
 
-empty
-    self call: 'empty'
 ! !
 
 !JQuery methodsFor: 'initialization'!
 
 initializeWithJQueryObject: anObject
     jquery := anObject
+
 ! !
 
 !JQuery methodsFor: 'private'!
 
 call: aString
-    ^{'return self[''@jquery''][aString]()'}
+	{'return self[''@jquery''][aString]()'}
 !
 
 call: aString withArgument: anObject
-    ^{'return self[''@jquery''][aString](anObject)'}
+    {'return self[''@jquery''][aString](anObject)'}
 ! !
 
-
 !JQuery methodsFor: 'testing'!
 
 hasClass: aString
     "Determine whether any of the matched elements are assigned the given class."
     ^self call: 'hasClass' withArgument: aString
-! !
-
 
-Object subclass: #Ajax
-	instanceVariableNames: 'settings'
-	category: 'JQuery'!
+! !
 
-!Ajax commentStamp!
+!JQuery class methodsFor: 'instance creation'!
 
-instance variable names:
-- settings  A set of key/value pairs that configure the Ajax request. All settings are optional.
+fromString: aString
+    | newJQuery |
+    {'newJQuery = jQuery(String(aString))'}.
+    ^self from: newJQuery
 
-Full list of settings options at http://api.jquery.com/jQuery.ajax/
 !
 
-!Ajax class methodsFor: 'instance creation'!
-
-url: aString
+from: anObject
     ^self new
-	url: aString;
+	initializeWithJQueryObject: anObject;
 	yourself
-! !
 
-!Ajax methodsFor: 'initialization'!
+!
 
-initialize
-    super initialize.
-    settings := Dictionary new
+window
+	{'return self._from_(jQuery(window))'}
+!
+
+body
+	{'return self._from_(jQuery(body))'}
+!
+
+document
+	{'return self._from_(jQuery(document))'}
 ! !
 
+Object subclass: #Ajax
+	instanceVariableNames: 'settings'
+	category: 'JQuery'!
+!Ajax commentStamp!
+instance%20variable%20names%3A%0A-%20settings%20%20A%20set%20of%20key/value%20pairs%20that%20configure%20the%20Ajax%20request.%20All%20settings%20are%20optional.%0A%0AFull%20list%20of%20settings%20options%20at%20http%3A//api.jquery.com/jQuery.ajax/%0A!
+
 !Ajax methodsFor: 'accessing'!
 
 at: aKey
     ^settings at: aKey ifAbsent: [nil]
+
 !
 
 at: aKey put: aValue
     settings at: aKey put: aValue
+
 !
 
 url
     ^self at: 'url'
+
 !
 
 url: aString
     self at: 'url' put: aString
-! !
 
+! !
 
 !Ajax methodsFor: 'actions'!
 
 send
     {'jQuery.ajax(self[''@settings''])'}
+
+! !
+
+!Ajax methodsFor: 'initialization'!
+
+initialize
+    super initialize.
+    settings := Dictionary new
+
+! !
+
+!Ajax class methodsFor: 'instance creation'!
+
+url: aString
+    ^self new
+	url: aString;
+	yourself
+
 ! !
 
 
-    
+
+appendToJQuery: aJQuery
+	| canvas |
+	canvas := HTMLCanvas new.
+	self value: canvas.
+	aJQuery append: canvas
+
+!
+
+asJQuery
+    ^JQuery fromString: self
+
+!
+
+appendToJQuery: aJQuery
+    {'aJQuery._appendElement_(String(self))'}
+
+!
+
+appendToJQuery: aJQuery
+    aJQuery appendElement: root element
+
+!

Plik diff jest za duży
+ 373 - 209
st/Kernel.st


+ 286 - 173
st/parser.st → st/Parser.st

@@ -2,41 +2,65 @@ Object subclass: #PPParser
 	instanceVariableNames: 'memo'
 	category: 'Parser'!
 
-
 !PPParser methodsFor: 'accessing'!
 
 memo
 	^memo
-! !
-
-!PPParser methodsFor: 'error handling'!
 
-onFailure: aBlock
-	^PPFailureActionParser on: self block: aBlock
 ! !
 
 !PPParser methodsFor: 'initialization'!
 
 initialize
 	memo := Dictionary new
+
 ! !
 
 !PPParser methodsFor: 'operations'!
 
+flatten
+	^PPFlattenParser on: self
+
+!
+
+withSource
+	^PPSourceParser on: self
+
+!
+
+==> aBlock
+	^PPActionParser on: self block: aBlock
+
+!
+
 , aParser
 	^PPSequenceParser with: self with: aParser
+
 !
 
 / aParser
 	^PPChoiceParser with: self with: aParser
+
 !
 
-==> aBlock
-	^PPActionParser on: self block: aBlock
+plus
+	^PPRepeatingParser on: self min: 1
+
 !
 
-flatten
-	^PPFlattenParser on: self
+star
+	^PPRepeatingParser on: self min: 0
+
+!
+
+not
+	^PPNotParser on: self
+
+!
+
+optional
+	^self / PPEpsilonParser new
+
 !
 
 memoizedParse: aStream
@@ -51,32 +75,14 @@ memoizedParse: aStream
 		end := aStream position.
 		self memo at: start put: (Array with: node with: end).
 		node]
-!
-
-not
-	^PPNotParser on: self
-!
-
-optional
-	^self / PPEpsilonParser new
-!
-
-plus
-	^PPRepeatingParser on: self min: 1
-!
-
-star
-	^PPRepeatingParser on: self min: 0
-!
 
-withSource
-	^PPSourceParser on: self
 ! !
 
 !PPParser methodsFor: 'parsing'!
 
 parse: aStream
 	self subclassResponsibility
+
 !
 
 parseAll: aStream
@@ -85,14 +91,13 @@ parseAll: aStream
 	^result isParseFailure 
 	    ifTrue: [self error: (result messageFor: aStream contents)]
 	    ifFalse: [result first]
-! !
 
+! !
 
 PPParser subclass: #PPEOFParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPEOFParser methodsFor: 'parsing'!
 
 parse: aStream
@@ -100,14 +105,13 @@ parse: aStream
 	    ifFalse: [
 		PPFailure new reason: 'EOF expected' at: aStream position]
 	    ifTrue: [nil]
-! !
 
+! !
 
 PPParser subclass: #PPAnyParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPAnyParser methodsFor: 'parsing'!
 
 parse: aStream
@@ -115,34 +119,34 @@ parse: aStream
 	    ifTrue: [PPFailure new
 			 reason: 'did not expect EOF' at: aStream position]
 	    ifFalse: [aStream next]
-! !
 
+! !
 
 PPParser subclass: #PPEpsilonParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPEpsilonParser methodsFor: 'parsing'!
 
 parse: aStream
 	^nil
-! !
 
+! !
 
 PPParser subclass: #PPStringParser
 	instanceVariableNames: 'string'
 	category: 'Parser'!
 
-
 !PPStringParser methodsFor: 'accessing'!
 
 string
 	^string
+
 !
 
 string: aString
 	string := aString
+
 ! !
 
 !PPStringParser methodsFor: 'parsing'!
@@ -156,18 +160,18 @@ parse: aStream
 	    ifFalse: [
 		aStream position: position.
 		PPFailure new reason: 'Expected ', self string, ' but got ', (result at: position) printString; yourself]
-! !
 
+! !
 
 PPParser subclass: #PPCharacterParser
 	instanceVariableNames: 'regexp'
 	category: 'Parser'!
 
-
 !PPCharacterParser methodsFor: 'accessing'!
 
 string: aString
 	regexp := RegularExpression fromString: '[', aString, ']'
+
 ! !
 
 !PPCharacterParser methodsFor: 'parsing'!
@@ -176,58 +180,62 @@ parse: aStream
 	^(aStream peek notNil and: [self match: aStream peek])
 	    ifTrue: [aStream next]
 	    ifFalse: [PPFailure new reason: 'Could not match' at: aStream position]
+
 ! !
 
 !PPCharacterParser methodsFor: 'private'!
 
 match: aString
 	^aString match: regexp
-! !
 
+! !
 
 PPParser subclass: #PPListParser
 	instanceVariableNames: 'parsers'
 	category: 'Parser'!
 
-
-!PPListParser class methodsFor: 'instance creation'!
-
-with: aParser with: anotherParser
-	    ^self withAll: (Array with: aParser with: anotherParser)
-!
-
-withAll: aCollection
-	    ^self new
-		parsers: aCollection;
-		yourself
-! !
-
 !PPListParser methodsFor: 'accessing'!
 
 parsers
 	^parsers ifNil: [#()]
+
 !
 
 parsers: aCollection
 	parsers := aCollection
+
 ! !
 
 !PPListParser methodsFor: 'copying'!
 
 copyWith: aParser
 	^self class withAll: (self parsers copyWith: aParser)
+
 ! !
 
+!PPListParser class methodsFor: 'instance creation'!
+
+withAll: aCollection
+	    ^self new
+		parsers: aCollection;
+		yourself
+
+!
+
+with: aParser with: anotherParser
+	    ^self withAll: (Array with: aParser with: anotherParser)
+
+! !
 
 PPListParser subclass: #PPSequenceParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPSequenceParser methodsFor: 'copying'!
 
 , aRule
 	^self copyWith: aRule
+
 ! !
 
 !PPSequenceParser methodsFor: 'parsing'!
@@ -245,18 +253,18 @@ parse: aStream
 	^element isParseFailure
 	    ifFalse: [elements]
 	    ifTrue: [aStream position: start. element]
-! !
 
+! !
 
 PPListParser subclass: #PPChoiceParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPChoiceParser methodsFor: 'copying'!
 
 / aRule
 	^self copyWith: aRule
+
 ! !
 
 !PPChoiceParser methodsFor: 'parsing'!
@@ -269,64 +277,65 @@ parse: aStream
 		result isParseFailure not]
 	    ifNone: [].
 	^result
-! !
 
+! !
 
 PPParser subclass: #PPDelegateParser
 	instanceVariableNames: 'parser'
 	category: 'Parser'!
 
-
-!PPDelegateParser class methodsFor: 'instance creation'!
-
-on: aParser
-	    ^self new
-		parser: aParser;
-		yourself
-! !
-
 !PPDelegateParser methodsFor: 'accessing'!
 
 parser
 	^parser
+
 !
 
 parser: aParser
 	parser := aParser
+
 ! !
 
 !PPDelegateParser methodsFor: 'parsing'!
 
 parse: aStream
 	^self parser memoizedParse: aStream
+
 ! !
 
+!PPDelegateParser class methodsFor: 'instance creation'!
+
+on: aParser
+	    ^self new
+		parser: aParser;
+		yourself
+
+! !
 
 PPDelegateParser subclass: #PPAndParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPAndParser methodsFor: 'parsing'!
 
+parse: aStream
+	^self basicParse: aStream
+
+!
+
 basicParse: aStream
 	| element position |
 	position := aStream position.
 	element := self parser memoizedParse: aStream.
 	aStream position: position.
 	^element
-!
 
-parse: aStream
-	^self basicParse: aStream
 ! !
 
-
 PPAndParser subclass: #PPNotParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPNotParser methodsFor: 'parsing'!
 
 parse: aStream
@@ -335,31 +344,23 @@ parse: aStream
 	^element isParseFailure 
 	    ifTrue: [nil]
 	    ifFalse: [PPFailure reason: element at: aStream position]
-! !
 
+! !
 
 PPDelegateParser subclass: #PPActionParser
 	instanceVariableNames: 'block'
 	category: 'Parser'!
 
-
-!PPActionParser class methodsFor: 'instance creation'!
-
-on: aParser block: aBlock
-	    ^self new
-		parser: aParser;
-		block: aBlock;
-		yourself
-! !
-
 !PPActionParser methodsFor: 'accessing'!
 
 block
 	^block
+
 !
 
 block: aBlock
 	block := aBlock
+
 ! !
 
 !PPActionParser methodsFor: 'parsing'!
@@ -370,14 +371,23 @@ parse: aStream
 	^element isParseFailure
 	    ifFalse: [self block value: element]
 	    ifTrue: [element]
+
 ! !
 
+!PPActionParser class methodsFor: 'instance creation'!
+
+on: aParser block: aBlock
+	    ^self new
+		parser: aParser;
+		block: aBlock;
+		yourself
+
+! !
 
 PPDelegateParser subclass: #PPFlattenParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPFlattenParser methodsFor: 'parsing'!
 
 parse: aStream
@@ -389,14 +399,13 @@ parse: aStream
 	    ifFalse: [aStream collection 
 		copyFrom: start + 1 
 		to: aStream position]
-! !
 
+! !
 
 PPDelegateParser subclass: #PPSourceParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !PPSourceParser methodsFor: 'parsing'!
 
 parse: aStream
@@ -407,31 +416,23 @@ parse: aStream
 		ifTrue: [element]
 		ifFalse: [result := aStream collection copyFrom: start + 1 to: aStream position.
 			Array with: element with: result].
-! !
 
+! !
 
 PPDelegateParser subclass: #PPRepeatingParser
 	instanceVariableNames: 'min'
 	category: 'Parser'!
 
-
-!PPRepeatingParser class methodsFor: 'instance creation'!
-
-on: aParser min: aNumber
-	    ^self new
-		parser: aParser;
-		min: aNumber;
-		yourself
-! !
-
 !PPRepeatingParser methodsFor: 'accessing'!
 
 min
 	^min
+
 !
 
 min: aNumber
 	min := aNumber
+
 ! !
 
 !PPRepeatingParser methodsFor: 'parsing'!
@@ -454,65 +455,72 @@ parse: aStream
 				ifFalse: [elements addLast: element]].
 				elements]
 		ifNotNil: [failure].
-! !
-
-
-Object subclass: #PPFailure
-	instanceVariableNames: 'position reason'
-	category: 'Parser'!
 
+! !
 
-!PPFailure class methodsFor: 'instance creation'!
+!PPRepeatingParser class methodsFor: 'instance creation'!
 
-reason: aString at: anInteger
+on: aParser min: aNumber
 	    ^self new
-		reason: aString at: anInteger;
+		parser: aParser;
+		min: aNumber;
 		yourself
+
 ! !
 
+Object subclass: #PPFailure
+	instanceVariableNames: 'position, reason'
+	category: 'Parser'!
+
 !PPFailure methodsFor: 'accessing'!
 
 position
 	^position ifNil: [0]
+
 !
 
 position: aNumber
 	position := aNumber
+
 !
 
 reason
 	^reason ifNil: ['']
+
 !
 
 reason: aString
 	reason := aString
+
 !
 
 reason: aString at: anInteger
 	self 
 	    reason: aString; 
 	    position: anInteger
+
 ! !
 
 !PPFailure methodsFor: 'testing'!
 
 isParseFailure
 	^true
+
 ! !
 
+!PPFailure class methodsFor: 'instance creation'!
+
+reason: aString at: anInteger
+	    ^self new
+		reason: aString at: anInteger;
+		yourself
+
+! !
 
 Object subclass: #SmalltalkParser
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
-!SmalltalkParser class methodsFor: 'instance creation'!
-
-parse: aStream
-	    ^self new
-		parse: aStream
-! !
-
 !SmalltalkParser methodsFor: 'grammar'!
 
 parser
@@ -678,115 +686,129 @@ parser
 		    yourself].
 	
 	^method, PPEOFParser new ==> [:node | node first]
+
 ! !
 
 !SmalltalkParser methodsFor: 'parsing'!
 
 parse: aStream
 	^self parser parse: aStream
+
 ! !
 
+!SmalltalkParser class methodsFor: 'instance creation'!
+
+parse: aStream
+	    ^self new
+		parse: aStream
+
+! !
 
 Object subclass: #Chunk
 	instanceVariableNames: 'contents'
 	category: 'Parser'!
 
-
 !Chunk methodsFor: 'accessing'!
 
 contents
 	^contents ifNil: ['']
+
 !
 
 contents: aString
 	contents := aString
+
 ! !
 
 !Chunk methodsFor: 'testing'!
 
 isEmptyChunk
 	^false
+
 !
 
 isInstructionChunk
 	^false
-! !
 
+! !
 
 Chunk subclass: #InstructionChunk
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !InstructionChunk methodsFor: 'testing'!
 
 isInstructionChunk
 	^true
-! !
 
+! !
 
 Chunk subclass: #EmptyChunk
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
 !EmptyChunk methodsFor: 'testing'!
 
 isEmptyChunk
 	^true
-! !
 
+! !
 
 Object subclass: #ChunkParser
-	instanceVariableNames: 'parser separator eof ws chunk emptyChunk instructionChunk'
+	instanceVariableNames: 'parser, separator, eof, ws, chunk, emptyChunk, instructionChunk'
 	category: 'Parser'!
 
+!ChunkParser methodsFor: ''!
+
+instructionChunk
+	^instructionChunk ifNil: [
+	    instructionChunk := self ws, '!' asParser, self chunk
+	    ==> [:node | InstructionChunk new contents: node last contents]]
+
+! !
 
 !ChunkParser methodsFor: 'accessing'!
 
-chunk
-	^chunk ifNil: [chunk := self ws, ('!!!!' asParser / ('!!' asParser not, PPAnyParser new)) plus flatten, '!!' asParser ==> [:node | Chunk new contents: (node second replace: '!!!!' with: '!!')]]
-!
+parser
+	^parser ifNil: [
+	    parser := self instructionChunk / self emptyChunk / self chunk / self eof]
 
-emptyChunk
-	^emptyChunk ifNil: [emptyChunk := self separator plus, '!!' asParser, self ws ==> [:node | EmptyChunk new]]
 !
 
 eof
 	^eof ifNil: [eof := self ws, PPEOFParser new ==> [:node | nil]]
-!
 
-parser
-	^parser ifNil: [
-	    parser := self instructionChunk / self emptyChunk / self chunk / self eof]
 !
 
 separator
 	^separator ifNil: [separator := (String cr, String space, String lf, String tab) asChoiceParser]
+
 !
 
 ws
 	^ws ifNil: [ws := self separator star]
-! !
 
-!ChunkParser methodsFor: nil!
+!
 
-instructionChunk
-	^instructionChunk ifNil: [
-	    instructionChunk := self ws, '!!' asParser, self chunk
-	    ==> [:node | InstructionChunk new contents: node last contents]]
-! !
+chunk
+	^chunk ifNil: [chunk := self ws, ('!!' asParser / ('!' asParser not, PPAnyParser new)) plus flatten, '!' asParser ==> [:node | Chunk new contents: (node second replace: '!!' with: '!')]]
+
+!
 
+emptyChunk
+	^emptyChunk ifNil: [emptyChunk := self separator plus, '!' asParser, self ws ==> [:node | EmptyChunk new]]
+
+! !
 
 Object subclass: #Importer
 	instanceVariableNames: 'chunkParser'
 	category: 'Parser'!
 
-
 !Importer methodsFor: 'accessing'!
 
 chunkParser
 	^chunkParser ifNil: [chunkParser := ChunkParser new parser]
+
 ! !
 
 !Importer methodsFor: 'fileIn'!
@@ -801,15 +823,14 @@ import: aStream
 					 scanFrom: aStream]
 		    ifFalse: [Compiler new loadExpression: nextChunk contents].
 		self import: aStream]]
-! !
 
+! !
 
 Object subclass: #Exporter
 	instanceVariableNames: ''
 	category: 'Parser'!
 
-
-!Exporter methodsFor: 'fileout'!
+!Exporter methodsFor: 'fileOut'!
 
 exportCategory: aString
 	| stream |
@@ -817,42 +838,30 @@ exportCategory: aString
 	(Smalltalk current classes 
 	    select: [:each | each category = aString])
 	    do: [:each | stream nextPutAll: (self export: each)].
+	self exportCategoryExtensions: aString on: stream.
 	^stream contents
-! !
-
-!Exporter methodsFor: 'fileOut'!
+!
 
 export: aClass
 	| stream |
 	stream := '' writeStream.
 	self exportDefinitionOf: aClass on: stream.
-	stream nextPutAll: String cr.
 	self exportMethodsOf: aClass on: stream.
-	stream nextPutAll: String cr.
 	self exportMetaDefinitionOf: aClass on: stream.
 	self exportMethodsOf: aClass class on: stream.
-	stream nextPutAll: String cr.
 	^stream contents
+
 ! !
 
 !Exporter methodsFor: 'private'!
 
-classNameFor: aClass
-	^aClass isMetaclass
-	    ifTrue: [aClass instanceClass name, '.klass']
-	    ifFalse: [
-		aClass isNil
-		    ifTrue: ['nil']
-		    ifFalse: [aClass name]]
-!
-
 exportDefinitionOf: aClass on: aStream
 	aStream 
 	    nextPutAll: 'smalltalk.addClass(';
 	    nextPutAll: '''', (self classNameFor: aClass), ''', ';
 	    nextPutAll: 'smalltalk.', (self classNameFor: aClass superclass);
 	    nextPutAll: ', ['.
-	aClass instVarNames 
+	aClass instanceVariableNames 
 	    do: [:each | aStream nextPutAll: '''', each, '''']
 	    separatedBy: [aStream nextPutAll: ', '].
 	aStream	
@@ -861,37 +870,141 @@ exportDefinitionOf: aClass on: aStream
 	    nextPutAll: ');'.
 	aClass comment notEmpty ifTrue: [
 	    aStream 
-	    	nextPutAll: String cr;
+	    	lf;
 		nextPutAll: 'smalltalk.';
 		nextPutAll: (self classNameFor: aClass);
 		nextPutAll: '.comment=';
-		nextPutAll: 'unescape(''', aClass comment escaped, ''')']
+		nextPutAll: 'unescape(''', aClass comment escaped, ''')'].
+	aStream lf
+
 !
 
 exportMetaDefinitionOf: aClass on: aStream
-	aClass class instVarNames isEmpty ifFalse: [
+	aClass class instanceVariableNames isEmpty ifFalse: [
 	    aStream 
 		nextPutAll: 'smalltalk.', (self classNameFor: aClass class);
 		nextPutAll: '.iVarNames = ['.
-	    aClass class instVarNames
+	    aClass class instanceVariableNames
 		do: [:each | aStream nextPutAll: '''', each, '''']
 		separatedBy: [aStream nextPutAll: ','].
-	    aStream nextPutAll: '];', String cr]
+	    aStream nextPutAll: '];', String lf]
+
 !
 
 exportMethodsOf: aClass on: aStream
-	aClass methodDictionary keysAndValuesDo: [:key :value |
-	    aStream 
-		nextPutAll: 'smalltalk.addMethod(', String cr;
-		nextPutAll: '''', value selector asSelector, ''',', String cr;
-		nextPutAll: 'smalltalk.method({', String cr;
-		nextPutAll: 'selector: ''', value selector, ''',', String cr;
-		nextPutAll: 'category: ''', value category, ''',', String cr;
-		nextPutAll: 'fn: ', value fn compiledSource, ',', String cr;
-		nextPutAll: 'source: unescape(''', value source escaped, ''')';
-		nextPutAll: '}),', String cr;
+	aClass methodDictionary values do: [:each |
+		(each category match: '^\*') ifFalse: [
+			self exportMethod: each of: aClass on: aStream]].
+	aStream lf
+!
+
+classNameFor: aClass
+	^aClass isMetaclass
+	    ifTrue: [aClass instanceClass name, '.klass']
+	    ifFalse: [
+		aClass isNil
+		    ifTrue: ['nil']
+		    ifFalse: [aClass name]]
+
+!
+
+exportMethod: aMethod of: aClass on: aStream
+	aStream 
+		nextPutAll: 'smalltalk.addMethod(';lf;
+		nextPutAll: '''', aMethod selector asSelector, ''',';lf;
+		nextPutAll: 'smalltalk.method({';lf;
+		nextPutAll: 'selector: ''', aMethod selector, ''',';lf;
+		nextPutAll: 'category: ''', aMethod category, ''',';lf;
+		nextPutAll: 'fn: ', aMethod fn compiledSource, ',';lf;
+		nextPutAll: 'source: unescape(''', aMethod source escaped, '''),';lf;
+		nextPutAll: 'messageSends: ', aMethod messageSends asJavascript, ',';lf;
+		nextPutAll: 'referencedClasses: ['.
+	    		aMethod referencedClasses 
+				do: [:each | aStream nextPutAll: 'smalltalk.', (self classNameFor: each)]
+				separatedBy: [aStream nextPutAll: ','].
+	aStream
+		nextPutAll: ']';lf;
+		nextPutAll: '}),';lf;
 		nextPutAll: 'smalltalk.', (self classNameFor: aClass);
-		nextPutAll: ');', String cr, String cr]
+		nextPutAll: ');';lf;lf
+!
+
+
+exportCategoryExtensions: aString on: aStream
+	Smalltalk current classes, (Smalltalk current classes collect: [:each | each class]) do: [:each |
+		each methodDictionary values do: [:method |
+			method category = ('*', aString) ifTrue: [
+				self exportMethod: method of: each on: aStream]]]
 ! !
 
+Exporter subclass: #ChunkExporter
+	instanceVariableNames: ''
+	category: 'Parser'!
+
+!ChunkExporter methodsFor: 'not yet classified'!
+
+exportDefinitionOf: aClass on: aStream
+	"Chunk format."
+
+	aStream 
+	    nextPutAll: (self classNameFor: aClass superclass);
+	    nextPutAll: ' subclass: #', (self classNameFor: aClass); lf;
+	    nextPutAll: '	instanceVariableNames: '''.
+	aClass instanceVariableNames 
+	    do: [:each | aStream nextPutAll: each]
+	    separatedBy: [aStream nextPutAll: ', '].
+	aStream	
+	    nextPutAll: ''''; lf;
+	    nextPutAll: '	category: ''', aClass category, '''!'; lf.
+ 	aClass comment notEmpty ifTrue: [
+	    aStream 
+		nextPutAll: '!', (self classNameFor: aClass), ' commentStamp!';lf;
+		nextPutAll: aClass comment escaped, '!';lf].
+	aStream lf
+
+!
+
+exportMethod: aMethod of: aClass on: aStream
+	aStream 
+		lf; lf; nextPutAll: aMethod source; lf;
+		nextPutAll: '!'
+!
+
+exportMethodsOf: aClass on: aStream
+
+    | methodsByCategory |
+    methodsByCategory := Dictionary new.
+    aClass methodDictionary values do: [:m |
+	(methodsByCategory at: m category ifAbsentPut: [Array new])
+ 		add: m]. 
+    aClass protocols do: [:category |       
+	aStream
+		nextPutAll: '!', (self classNameFor: aClass);
+		nextPutAll: ' methodsFor: ''', category, '''!'.
+    	(methodsByCategory at: category) do: [:each |
+		self exportMethod: each of: aClass on: aStream].
+	aStream nextPutAll: ' !'; lf; lf]
+!
+
+exportMetaDefinitionOf: aClass on: aStream
+
+	aClass class instanceVariableNames isEmpty ifFalse: [
+		aStream 
+		    nextPutAll: (self classNameFor: aClass class);
+		    nextPutAll: ' instanceVariableNames: '''.
+		aClass class instanceVariableNames 
+		    do: [:each | aStream nextPutAll: each]
+		    separatedBy: [aStream nextPutAll: ', '].
+		aStream	
+		    nextPutAll: '''!'; lf; lf]
+!
+
+classNameFor: aClass
+	^aClass isMetaclass
+	    ifTrue: [aClass instanceClass name, ' class']
+	    ifFalse: [
+		aClass isNil
+		    ifTrue: ['nil']
+		    ifFalse: [aClass name]]
+! !
 

+ 453 - 0
st/SUnit.st

@@ -0,0 +1,453 @@
+Object subclass: #TestCase
+	instanceVariableNames: 'testedClass'
+	category: 'SUnit'!
+
+!TestCase methodsFor: 'accessing'!
+
+testedClass
+	^testedClass
+!
+
+testedClass: aClass
+	testedClass := aClass
+! !
+
+!TestCase methodsFor: 'private'!
+
+cleanUpInstanceVariables
+	self class instanceVariableNames do: [ :name |
+		name = 'testSelector' ifFalse: [
+			self instVarAt: name put: nil ]]
+!
+
+signalFailure: aString
+	TestFailure new
+		messageText: aString;
+		signal
+! !
+
+!TestCase methodsFor: 'running'!
+
+setUp
+!
+
+tearDown
+!
+
+methods
+	^self class methodDictionary keys select: [:each | each match: '^test']
+!
+
+runCaseFor: aTestResult
+	[self setUp.
+	self performTestFor: aTestResult]
+		on: Error
+		do: [:ex |
+			self tearDown.
+			self cleanUpInstanceVariables.
+			ex signal].
+	self tearDown.
+	self cleanUpInstanceVariables
+!
+
+performTestFor: aResult
+	self methods do: [:each | 
+		[[self perform: each]
+			on: TestFailure do: [:ex | aResult addFailure: self class name, '>>', each]]
+			on: Error do: [:ex | aResult addError: self class name, '>>', each].
+		aResult increaseRuns]
+! !
+
+!TestCase methodsFor: 'testing'!
+
+assert: aBoolean
+	aBoolean ifFalse: [self signalFailure: 'Assertion failed']
+!
+
+deny: aBoolean
+	self assert: aBoolean not
+! !
+
+TestCase subclass: #ExampleTest
+	instanceVariableNames: 'test'
+	category: 'SUnit'!
+
+!ExampleTest methodsFor: 'not yet classified'!
+
+testFailure
+	self deny: true
+	
+!
+
+testPasses
+	100000 timesRepeat: [self assert: 1 + 1 = 2]
+!
+
+testError
+	self assert: 1 foo
+! !
+
+TabWidget subclass: #ProgressBar
+	instanceVariableNames: 'percent, progressDiv'
+	category: 'SUnit'!
+
+!ProgressBar methodsFor: 'accessing'!
+
+percent
+	^percent ifNil: [0]
+!
+
+percent: aNumber
+	percent := aNumber
+! !
+
+!ProgressBar methodsFor: 'rendering'!
+
+renderOn: html 
+	html div 
+		class: 'progress_bar';
+		with: [
+			html div 
+				class: 'progress';
+				style: 'width:', self percent asString, '%']
+! !
+
+!ProgressBar methodsFor: 'updating'!
+
+updatePercent: aNumber
+	self percent: aNumber.
+	self update
+! !
+
+Error subclass: #TestFailure
+	instanceVariableNames: ''
+	category: 'SUnit'!
+
+TabWidget subclass: #TestRunner
+	instanceVariableNames: 'selectedCategories, categoriesList, selectedClasses, classesList, selectedMethods, progressBar, methodsList, result, statusDiv'
+	category: 'SUnit'!
+
+!TestRunner methodsFor: 'accessing'!
+
+label
+    ^'[Test runner]'
+
+!
+
+categories
+    | categories |
+    categories := Array new.
+    self allClasses do: [:each |
+	(categories includes: each category) ifFalse: [
+	    categories add: each category]].
+    ^categories sort
+!
+
+classes
+    ^(self allClasses 
+	select: [:each | self selectedCategories includes: each category])
+	sort: [:a :b | a name > b name]
+!
+
+selectedCategories
+	^selectedCategories ifNil: [selectedCategories := Array new]
+!
+
+allClasses
+	^TestCase allSubclasses
+!
+
+selectedClasses
+	^selectedClasses  ifNil: [selectedClasses := Array new]
+!
+
+progressBar
+	^progressBar ifNil: [progressBar := ProgressBar new]
+!
+
+selectedMethods
+	^selectedMethods ifNil: [self selectedClasses collect: [:each |
+		each methodDictionary keys select: [:key |  key beginsWith: 'test' ]]]
+!
+
+statusInfo
+	^self printTotal, self printPasses, self printErrors, self printFailures
+!
+
+result
+	^result
+!
+
+failedMethods
+	self result failures collect: [:each |
+		html li 
+			class: 'failures';
+			with: each]
+! !
+
+!TestRunner methodsFor: 'actions'!
+
+selectAllCategories
+	self categories do: [:each | 
+		(selectedCategories includes: each) ifFalse: [
+			self selectedCategories add: each]].
+	self 
+	    updateCategoriesList;
+	    updateClassesList
+!
+
+toggleCategory: aCategory
+	(self isSelectedCategory: aCategory) 
+		ifFalse: [selectedCategories add: aCategory]
+		ifTrue: [selectedCategories remove: aCategory].
+	self 
+	    updateCategoriesList;
+	    updateClassesList
+!
+
+toggleClass: aClass
+	(self isSelectedClass: aClass) 
+		ifFalse: [selectedClasses add: aClass]
+		ifTrue: [selectedClasses remove: aClass].
+	self 
+	    updateClassesList
+!
+
+selectAllClasses
+	self classes do: [:each | 
+		(selectedClasses includes: each) ifFalse: [
+			self selectedClasses add: each]].
+	self 
+	    updateCategoriesList;
+	    updateClassesList
+!
+
+run: aCollection
+	result := TestResult new.
+	self 
+		updateStatusDiv;
+		updateMethodsList.
+	self progressBar updatePercent: 0.
+	result total: (aCollection inject: 0 into: [:acc :each | acc + each methods size]).
+	aCollection do: [:each | 
+		[each runCaseFor: result.
+		self progressBar updatePercent: result runs / result total * 100.
+		self updateStatusDiv.
+		self updateMethodsList] valueWithTimeout: 100].
+! !
+
+!TestRunner methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	result := TestResult new
+! !
+
+!TestRunner methodsFor: 'printing'!
+
+printErrors
+	^self result errors size asString , ' errors, '
+!
+
+printFailures
+	^self result failures size asString, ' failures'
+!
+
+printPasses
+	^(((self result total) - (self result errors size + (self result failures size))) asString) , ' passes, '
+!
+
+printTotal
+	^self result total asString, ' runs, '
+! !
+
+!TestRunner methodsFor: 'rendering'!
+
+renderBoxOn: html
+    self 
+	renderCategoriesOn: html;
+	renderClassesOn: html;
+	renderResultsOn: html
+!
+
+renderButtonsOn: html
+    html button
+	with: 'Run selected';
+	onClick: [self run: (self selectedClasses collect: [:each | each new])]
+
+!
+
+renderCategoriesOn: html
+    	categoriesList := html ul class: 'jt_column sunit categories'.
+	self updateCategoriesList
+!
+
+renderClassesOn: html
+    	classesList := html ul class: 'jt_column sunit classes'.
+	self updateClassesList
+!
+
+renderResultsOn: html
+    	statusDiv := html div.
+	html with: self progressBar.
+   	methodsList := html ul class: 'jt_column sunit methods'.
+	self updateMethodsList.
+	self updateStatusDiv
+!
+
+renderFailuresOn: html
+	self result failures do: [:each |
+		html li 
+			class: 'failures';
+			with: each]
+!
+
+renderErrorsOn: html
+	self result errors do: [:each |
+		html li 
+			class: 'errors';
+			with: each]
+! !
+
+!TestRunner methodsFor: 'testing'!
+
+canBeClosed
+    ^true
+
+!
+
+isSelectedClass: aClass
+	^(self selectedClasses includes: aClass)
+!
+
+isSelectedCategory: aCategory
+	^(self selectedCategories includes: aCategory)
+! !
+
+!TestRunner methodsFor: 'updating'!
+
+updateCategoriesList
+    categoriesList contents: [:html |
+	    html li 
+		class: 'all';
+		with: 'All';
+		onClick: [self selectAllCategories].
+	self categories do: [:each || li |
+	    li := html li.
+	    (self selectedCategories includes: each) ifTrue: [
+		li class: 'selected'].
+	    li
+		with: each;
+		onClick: [self toggleCategory: each]]]
+!
+
+updateClassesList
+    classesList contents: [:html |
+	(self selectedCategories isEmpty) ifFalse: [
+		html li
+			class: 'all';
+			with: 'All';
+			onClick: [self selectAllClasses]].
+	self classes do: [:each || li |
+		li := html li.
+		(self selectedClasses includes: each) ifTrue: [
+			li class: 'selected'].
+		li
+			with: each name;
+			onClick: [self toggleClass: each]]]
+!
+
+updateMethodsList
+	methodsList contents: [:html |
+		self renderFailuresOn: html.
+                self renderErrorsOn: html]
+!
+
+updateStatusDiv
+	statusDiv class: 'sunit status ', result status.
+	statusDiv contents: [:html |
+		html span with: self statusInfo]
+! !
+
+Object subclass: #TestResult
+	instanceVariableNames: 'timestamp, runs, errors, failures, total'
+	category: 'SUnit'!
+
+!TestResult methodsFor: 'accessing'!
+
+timestamp
+	^timestamp
+!
+
+errors
+	^errors
+!
+
+failures
+	^failures
+!
+
+total
+	^total
+!
+
+total: aNumber
+	total := aNumber
+!
+
+addError: anError
+	self errors add: anError
+!
+
+addFailure: aFailure
+	self failures add: aFailure
+!
+
+runs
+	^runs
+!
+
+increaseRuns
+	runs := runs + 1
+!
+
+status
+	^self errors isEmpty 
+		ifTrue: [
+			self failures isEmpty 
+				ifTrue: ['success']
+				ifFalse: ['failure']]
+		ifFalse: ['error']
+! !
+
+!TestResult methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	timestamp := Date now.
+	runs := 0.
+	errors := Array new.
+	failures := Array new.
+	total := 0
+! !
+
+TestCase subclass: #ExampleTest2
+	instanceVariableNames: ''
+	category: 'SUnit'!
+
+!ExampleTest2 methodsFor: 'not yet classified'!
+
+testPasses
+	100000 timesRepeat: [self assert: 1 + 1 = 2]
+! !
+
+TestCase subclass: #ExampleTest3
+	instanceVariableNames: ''
+	category: 'SUnit'!
+
+!ExampleTest3 methodsFor: 'not yet classified'!
+
+testPasses
+	100000 timesRepeat: [self assert: 1 + 1 = 2]
+! !
+

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików