diff --git a/tracks/README b/tracks/README
new file mode 100644
index 00000000..ca5f3737
--- /dev/null
+++ b/tracks/README
@@ -0,0 +1,189 @@
+== Welcome to Rails
+
+Rails is a web-application and persistance framework that includes everything
+needed to create database-backed web-applications according to the
+Model-View-Control pattern of separation. This pattern splits the view (also
+called the presentation) into "dumb" templates that are primarily responsible
+for inserting pre-build data in between HTML tags. The model contains the
+"smart" domain objects (such as Account, Product, Person, Post) that holds all
+the business logic and knows how to persist themselves to a database. The
+controller handles the incoming requests (such as Save New Account, Update
+Product, Show Post) by manipulating the model and directing data to the view.
+
+In Rails, the model is handled by what's called a object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/activerecord/README.html.
+
+The controller and view is handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/actionpack/README.html.
+
+
+== Requirements
+
+* Database and driver (MySQL, PostgreSQL, or SQLite)
+* Rake[http://rake.rubyforge.org] for running tests and the generating documentation
+
+== Optionals
+
+* Apache 1.3.x or 2.x or lighttpd 1.3.11+ (or any FastCGI-capable webserver with a
+ mod_rewrite-like module)
+* FastCGI (or mod_ruby) for better performance on Apache
+
+== Getting started
+
+1. Run the WEBrick servlet: ruby script/server
+ (run with --help for options)
+2. Go to http://localhost:3000/ and get "Congratulations, you've put Ruby on Rails!"
+3. Follow the guidelines on the "Congratulations, you've put Ruby on Rails!" screen
+
+
+== Example for Apache conf
+
+
+ ServerName rails
+ DocumentRoot /path/application/public/
+ ErrorLog /path/application/log/server.log
+
+
+ Options ExecCGI FollowSymLinks
+ AllowOverride all
+ Allow from all
+ Order allow,deny
+
+
+
+NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
+should be on and ".cgi" should respond. All requests from 127.0.0.1 goes
+through CGI, so no Apache restart is necessary for changes. All other requests
+goes through FCGI (or mod_ruby) that requires restart to show changes.
+
+
+== Example for lighttpd conf (with FastCGI)
+
+server.port = 8080
+server.bind = "127.0.0.1"
+# server.event-handler = "freebsd-kqueue" # needed on OS X
+
+server.modules = ( "mod_rewrite", "mod_fastcgi" )
+
+url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
+server.error-handler-404 = "/dispatch.fcgi"
+
+server.document-root = "/path/application/public"
+server.errorlog = "/path/application/log/server.log"
+
+fastcgi.server = ( ".fcgi" =>
+ ( "localhost" =>
+ (
+ "min-procs" => 1,
+ "max-procs" => 5,
+ "socket" => "/tmp/application.fcgi.socket",
+ "bin-path" => "/path/application/public/dispatch.fcgi",
+ "bin-environment" => ( "RAILS_ENV" => "development" )
+ )
+ )
+)
+
+
+== Debugging Rails
+
+Have "tail -f" commands running on both the server.log, production.log, and
+test.log files. Rails will automatically display debugging and runtime
+information to these files. Debugging info will also be shown in the browser
+on requests from 127.0.0.1.
+
+
+== Breakpoints
+
+Breakpoint support is available through the script/breakpointer client. This
+means that you can break out of execution at any point in the code, investigate
+and change the model, AND then resume execution! Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find_all
+ breakpoint "Breaking out from the list"
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the breakpointer window. Here you can do things like:
+
+Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+ >> @posts.inspect
+ => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+ #\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+ >> @posts.first.title = "hello from a breakpoint"
+ => "hello from a breakpoint"
+
+...and even better is that you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you press CTRL-D
+
+
+== Console
+
+You can interact with the domain model by starting the console through script/console.
+Here you'll have all parts of the application configured, just like it is when the
+application is running. You can inspect domain models, change values, and save to the
+database. Start the script without arguments to see the options.
+
+
+== Description of contents
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/controllers
+ Holds controllers that should be named like weblog_controller.rb for
+ automated URL mapping. All controllers should descend from
+ ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb.
+ Most models will descent from ActiveRecord::Base.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblog/index.rhtml for the WeblogController#index action. All views uses eRuby
+ syntax. This directory can also be used to keep stylesheets, images, and so on
+ that can be symlinked to public.
+
+app/helpers
+ Holds view helpers that should be named like weblog_helper.rb.
+
+config
+ Configuration files for the Rails environment, the routing map, the database, and other dependencies.
+
+components
+ Self-contained mini-applications that can bundle controllers, models, and views together.
+
+lib
+ Application specific libraries. Basically, any kind of custom code that doesn't
+ belong controllers, models, or helpers. This directory is in the load path.
+
+public
+ The directory available for the web server. Contains sub-directories for images, stylesheets,
+ and javascripts. Also contains the dispatchers and the default HTML files.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures.
+
+vendor
+ External libraries that the application depend on. This directory is in the load path.
diff --git a/tracks/public/javascripts/prototype.js b/tracks/public/javascripts/prototype.js
new file mode 100644
index 00000000..5f2f9edb
--- /dev/null
+++ b/tracks/public/javascripts/prototype.js
@@ -0,0 +1,336 @@
+/* Prototype: an object-oriented Javascript library, version 1.0.1
+ * (c) 2005 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see http://prototype.conio.net/
+ */
+
+Prototype = {
+ Version: '1.0.1'
+}
+
+Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+Abstract = new Object();
+
+Object.prototype.extend = function(object) {
+ for (property in object) {
+ this[property] = object[property];
+ }
+ return this;
+}
+
+Function.prototype.bind = function(object) {
+ var method = this;
+ return function() {
+ method.apply(object, arguments);
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var method = this;
+ return function(event) {
+ method.call(object, event || window.event);
+ }
+}
+
+Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+Toggle = {
+ display: function() {
+ for (var i = 0; i < elements.length; i++) {
+ var element = $(elements[i]);
+ element.style.display =
+ (element.style.display == 'none' ? '' : 'none');
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+
+function getElementsByClassName(className, element) {
+ var children = (element || document).getElementsByTagName('*');
+ var elements = new Array();
+
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ var classNames = child.className.split(' ');
+ for (var j = 0; j < classNames.length; j++) {
+ if (classNames[j] == className) {
+ elements.push(child);
+ break;
+ }
+ }
+ }
+
+ return elements;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ emptyFunction: function() {}
+}
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }.extend(options || {});
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = (new Ajax.Base()).extend({
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ try {
+ if (this.options.method == 'get')
+ url += '?' + this.options.parameters + '&_=';
+
+ this.transport.open(this.options.method, url, true);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ if (this.options.method == 'post') {
+ this.transport.setRequestHeader('Connection', 'close');
+ this.transport.setRequestHeader('Content-type',
+ 'application/x-www-form-urlencoded');
+ }
+
+ this.transport.send(this.options.method == 'post' ?
+ this.options.parameters + '&_=' : null);
+
+ } catch (e) {
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
+ }
+});
+
+Ajax.Updater = Class.create();
+Ajax.Updater.prototype = (new Ajax.Base()).extend({
+ initialize: function(container, url, options) {
+ this.container = $(container);
+ this.setOptions(options);
+
+ if (this.options.asynchronous) {
+ this.onComplete = this.options.onComplete;
+ this.options.onComplete = this.updateContent.bind(this);
+ }
+
+ this.request = new Ajax.Request(url, this.options);
+
+ if (!this.options.asynchronous)
+ this.updateContent();
+ },
+
+ updateContent: function() {
+ this.container.innerHTML = this.request.transport.responseText;
+ if (this.onComplete) this.onComplete(this.request);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return encodeURIComponent(parameter[0]) + '=' +
+ encodeURIComponent(parameter[1]);
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'hidden':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ var index = element.selectedIndex;
+ return [element.name, (index >= 0) ? element.options[index].value : ''];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+
+ this.registerCallback();
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
diff --git a/tracks/script/runner b/tracks/script/runner
new file mode 100755
index 00000000..acb18f00
--- /dev/null
+++ b/tracks/script/runner
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ruby
+
+require File.dirname(__FILE__) + '/../config/environment'
+eval(ARGV.first)
\ No newline at end of file