function joo$prepareWindow(window) { with(window) {
if (typeof document.getElementsByTagNameNS!="function") {
  document.getElementsByTagNameNS = function(namespaceURI, localName) {
    var elements = this.getElementsByTagName(localName);
    var i=0;
    while (i<elements.length) {
      if (elements[i].scopeName=="HTML") { // TODO: mapping from namespaceURI to scopeName!
        elements.splice(i,1);
      } else {
        ++i;
      }
    }
    return elements;
  }
}

if (typeof Node=="undefined") {
  window.Node = {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3
    // TODO
  };
}

Function.prototype.bind = function(object) {
  var fn = this;
  return function() {
    return fn.apply(object,arguments);
  };
}

RegExp.compile = typeof RegExp.prototype.compile=="function" ?
 (function(regExpStr, options) {
    var r = new RegExp();
    if (!regExpStr) {
      return this;
    }
    r.compile(regExpStr, options);
    return r;
  }) :
 (function(regExpStr, options) {
   return new RegExp(regExpStr, options);
 });
if (typeof Array.prototype.forEach!="function") {
  Array.prototype.forEach = function(fn, bind){
    for (var i = 0, j = this.length; i < j; i++) {
      if (i in this) {
        fn.call(bind, this[i], i, this);
      }
    }
  };
}

if (typeof Array.prototype.filter!="function") {
  Array.prototype.filter = function(fn, bind) {
    var len = this.length;
    var res = [];
    for (var i = 0; i < len; i++) {
      if (i in this) {
        var val = this[i];
        if (fn.call(bind, this[i], i, this)) {
          res.push(val);
        }
      }
    }

    return res;
  };
}

if (typeof window.addEventListener!="function") {
  // we are in IE:
  window.addEventListener = function(eventName, listener, capture) {
    var attachee = this;
    //noinspection FallthroughInSwitchStatementJS
    if (eventName=="focus" || eventName=="blur") {
      eventName = eventName=="focus" ? "focusin" : "focusout";
      attachee = this.document;
    }
    attachee.attachEvent("on"+eventName, function() {
      window.event.preventDefault = function() { this.returnValue = false; };
      return listener(window.event);
    });
  };
}

Array.prototype.broadcast = function(methodName, parameter1, parameter2) {
  for (var i=0; i < this.length; ++i) {
    this[i][methodName](parameter1, parameter2);
  }
}

Array.prototype.remove=function(s){
  for(i=0; i < this.length; i++){
    if (s==this[i]) {
      this.splice(i, 1);
      return true;
    }
  }
  return false;
}

if (typeof Array.prototype.indexOf!="function") {
  Array.prototype.indexOf = function(item, from){
    var len = this.length;
    for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
      if (this[i] === item) return i;
    }
    return -1;
  }

}

Array.prototype.contains = function(element) {
  return this.indexOf(element)!=-1;
}

Array.prototype.binarySearch = function(element) {
  var left = -1,
      right = this.length;
  while (right - left > 1) {
    var mid = (left + right) >>> 1;
    var comp = this[mid].compareTo(element);
    if (comp==0) {
      return mid;
    } else if(comp < 0) {
      left = mid;
    } else {
      right = mid;
    }
  }
  return -right-1;
}

Math.sgn = function(number) {
  return number<0 ? -1 : number>0 ? 1 : 0;
}

Number.prototype.format = (function() {
  var ZEROS = "0000000000";
  return function format(digits) {
    var result = this.toString();
    var zeros = digits-result.length;
    if (zeros<=0)
      return result;
    return ZEROS.substring(0,zeros)+result;
  }
})();


if (typeof MouseEvent=="function") {
    HTMLTableCellElement.prototype.super$setAttribute = HTMLTableCellElement.prototype.setAttribute;
    HTMLTableCellElement.prototype.setAttribute = function(name, value) {
      return this.super$setAttribute(name.toLowerCase(), value);
    };
    window.attachEvent = Element.prototype.attachEvent = function(eventName, handler) {
	  var self = this;
	  this.addEventListener(eventName.substring(2), function(event) {
	    self.ownerDocument.defaultView.event = event;
		handler();
	  }, false);
	};
  Event.prototype.__defineSetter__("returnValue", function(val) { if (val==false) this.preventDefault(); });
	Event.prototype.__defineGetter__("srcElement", function() { return this.target; });
	MouseEvent.prototype.__defineGetter__("fromElement", function() {
	  return this.type=="mouseover" ? this.relatedTarget : this.target;
	});
	MouseEvent.prototype.__defineGetter__("toElement", function() {
	  return this.type=="mouseover" ? this.target : this.relatedTarget;
	});

  HTMLBodyElement.prototype.createTextRange = function() {
    return this.ownerDocument.createRange();
  }
	Range.prototype.createTextRange = function() {
        return this.ownerDocument.createRange();
    };
	Range.prototype.parentElement = function() {
      var parentElement = this.commonAncestorContainer;
      if (parentElement.nodeType==Node.TEXT_NODE) {
        parentElement = parentElement.parentNode;
      }
      return parentElement;
    };
    Range.prototype.duplicate = function () {
      return this.cloneRange();
    };
    Range.prototype.move = function(str, units) {
      if(str == "character") {
        var elem = this.startContainer;
        return this.setStart(elem, elem.nodeType==Node.TEXT_NODE ? this.startOffset+units : 0);
      }
      return false;
    };
    Range.prototype.moveToElementText = function(element) {
      this.selectNodeContents(element);
    };
    Range.prototype.pasteHTML = function(html) {
      var span = this.startContainer.ownerDocument.createElement("div");
      span.innerHTML = html;
      span=span.firstChild;
      this.deleteContents();
      this.insertNode(span);
    };
    Range.prototype.compareEndPoints = function(how, otherRange) {
      return -this.compareBoundaryPoints(
            how=="StartToStart" ? Range.START_TO_START
	  : how=="StartToEnd" ? Range.END_TO_START
	  : how=="EndToStart" ? Range.START_TO_END
	                      : Range.END_TO_END,
	    otherRange);
    };
    Range.prototype.setEndPoint = function(startOrEndToStartOrEnd, range) {
      var parts = startOrEndToStartOrEnd.split("To");
      if (parts.length!=2) {
        throw new Error(startOrEndToStartOrEnd+" does not match (Start|End)To(Start|End).");
      }
      var toNode = parts[1]=="Start" ? range.startContainer : range.endContainer;
      var toOffset = parts[1]=="Start" ? range.startOffset : range.endOffset;
      this["set"+parts[0]](toNode, toOffset);
    };
    Range.prototype.select = function() {
      var selection = this.startContainer.ownerDocument.defaultView.getSelection();
      selection.removeAllRanges();
      selection.addRange(this);
    };
    Range.prototype.scrollIntoView = function() {
      return this.startContainer.scrollIntoView();
    };
    Range.prototype.__defineGetter__("text", function() {
	  if (this.startContainer.nodeType==Node.TEXT_NODE && this.startContainer==this.endContainer) {
        return this.startContainer.data.substring(this.startOffset,this.endOffset);
      }
	  return null;
    });

    Selection.prototype.type = "Text";
    Selection.prototype.createRange = function() {
        return this.rangeCount==1 ? this.getRangeAt(0) : {text: ""};
    };
    HTMLDocument.prototype.__defineGetter__("selection", function() {
        return this.defaultView.getSelection();
    });
    window.clipboardData = {
      _prepare: function() {
        if (!this._clipboardDocument) {
          var clipboardFrame = document.createElement("iframe");
          clipboardFrame.style.width = "1px";
          clipboardFrame.style.height = "1px";
          clipboardFrame.style.visibility = "hidden";
          document.body.appendChild(clipboardFrame);
          this._clipboardDocument = clipboardFrame.contentWindow.document;
          this._clipboardDocument.designMode = "on";
        }
      },
      _selectBody: function() {
        var selection = this._clipboardDocument.defaultView.getSelection();
        selection.selectAllChildren(this._clipboardDocument.body);
        return selection;
      },
      dispose: function() {
        if (this._clipboardDocument) {
          document.body.removeChild(this._clipboardDocument.defaultView.frameElement);
        }
      },
      getData: function(flavor) {
        var data;
        if (flavor=="Text") {
          this._prepare();
          this._selectBody();
          this._clipboardDocument.execCommand("Paste", undefined, undefined);
          data = this._clipboardDocument.body.innerHTML;
          this._clipboardDocument.body.innerHTML = "";
        }
        return data;
      },
      setData: function(flavor, data) {
        if (flavor=="Text") {
          this._prepare();
          this._clipboardDocument.body.innerHTML = data;
          this._selectBody();
          this._clipboardDocument.execCommand("Copy", undefined, undefined);
          this._clipboardDocument.body.innerHTML = "";
        }
      },
      clearData: function() {
        this.setData("Text", "");
      }
    };
}

if (typeof KeyEvent=="undefined") {
  KeyEvent = {
    DOM_VK_LEFT: 37,
    DOM_VK_UP: 38,
    DOM_VK_RIGHT: 39,
    DOM_VK_DOWN: 40
  };
  for (var i=65; i<65+26; ++i) {
    KeyEvent["DOM_VK_"+String.fromCharCode(i)] = i;
  };
}
}}

joo$prepareWindow(window);Function.prototype.getName = typeof Function.prototype.name=="string"
? (function getName() { return this.name; })
: (function() {
  var nameRE = /function +([a-zA-Z\$_][a-zA-Z\$_0-9]*) *\(/;
  return (function getName() {
    if (!("name" in this)) {
      var matches = nameRE.exec(this.toString());
      var name = matches ? matches[1] : "";
      if (name=="anonymous")
        name = "";
      this.name = name;
    }
    return this.name;
  });
}());

(function() {
  function initFields(privateBean, publicBean, fieldNames) {
    for (var i=0; i<fieldNames.length; ++i) {
      var fieldName = fieldNames[i];
      //alert("init field: "+fieldName);
      var bean = publicBean[fieldName] ? publicBean : privateBean;
      bean[fieldName] = bean[fieldName]();
    }
  }
  function createPackage(packageName) {
    var $package = window;
    if (packageName) {
      var packageParts = packageName.split(".");
      for (var i=0; i<packageParts.length; ++i) {
        var subpackage = $package[packageParts[i]];
        if (!subpackage) {
          subpackage = new Package();
          $package[packageParts[i]] = subpackage;
        }
        $package = subpackage;
      }
    }
    return $package;
  }
  function isFunction(object) {
    return typeof object=="function" && object.constructor!==RegExp;
  }
  function createMethod(object) {
    for (var name in object) {
      return setFunctionName(object[name], name);
    }
  }
  function createEmptyConstructor($prototype) {
    var $constructor = new Function();
    $constructor.prototype =  $prototype;
    return $constructor;
  };
  function createDefaultConstructor(superName) {
    return (function $DefaultConstructor() {
      this[superName].apply(this,arguments);
    });
  }
  function setFunctionName(theFunction, name) {
    theFunction.getName = function() { return name; };
    return theFunction;
  }
  function registerPrivateMember(privateStatic, classPrefix, memberName) {
    var privateMemberName = classPrefix+memberName;
    privateStatic["_"+memberName] = privateMemberName;
    return privateMemberName;
  }
  function createGetClass($constructor) {
    return (function Object$getClass() { return $constructor; });
  }
  function Object$hashCode() { return this["0hashCode"]; }
  function Object$toString() { return this.getClass().getName()+"@"+this.hashCode(); }
  var ClassDescription = (function() {
    var ClassDescription$static = {
      // static members:
      PENDING: 0,
      PREPARING: 1,
      PREPARED: 2,
      INITIALIZING: 3,
      INITIALIZED: 4,
      classDescriptions: {},
      pendingClassDescriptions: {},
      getClassDescription: function(fullClassName) {
          return this.classDescriptions[fullClassName];
      },
      waitForSuper: function(classDef) {
        var pendingCDs = this.pendingClassDescriptions[classDef.$extends];
        if (!pendingCDs) {
          pendingCDS = this.pendingClassDescriptions[classDef.$extends] = [];
        }
        pendingCDS.push(classDef);
      },
      prepareSubclasses: function(classDef) {
        var pendingCDS = this.pendingClassDescriptions[classDef.fullClassName];
        if (pendingCDS) {
          delete this.pendingClassDescriptions[classDef.fullClassName];
          for (var c=0; c<pendingCDS.length; ++c) {
            pendingCDS[c].prepare();
          }
        }
      }
    };
    // constructor:
    function ClassDescription(classDef) {
      for (var m in classDef) {
        this[m] = classDef[m];
      }
      this.fullClassName = this.$package + "." + this.$class;
      ClassDescription$static.classDescriptions[this.fullClassName] = this;
      this.prepare();
    }
    with(ClassDescription$static) {
      // instance members:
      ClassDescription.prototype = {
        fullClassName: undefined,
        $extends: "joo.lang.Object",
        level: undefined,
        state: PENDING,
        superClassDescription: undefined,
        $constructor: undefined,
        Public: undefined,
        Static: undefined,
        getStatic: undefined,
        /**
         * Prepares this class to be used by constructor, by accessing a static member, or as a super class.
         * The actual class loading is done when any of this three methods is called.
         */
        prepare: function() {
          if (this.state===PREPARING)
            throw new Error("cyclic usages between classes "+this.fullClassName+" and "+this.superClassDescription.fullClassName+".");
          if (this.state!==PENDING)
            return;
          this.superClassDescription = getClassDescription(this.$extends);
          if (!this.superClassDescription || this.superClassDescription.state==PENDING) {
            // super class not yet loaded, stay pending and wait for super class:
            waitForSuper(this);
            return;
          }
          this.state = PREPARING;
          // Only do the minimal setup to allow a preliminary, initializing public constructor and static getter,
          // and to allow subclasses to plug their constructor into this class.
          // create preliminary constructor and static getter that initialize before delegating to the real ones:
          this.level = this.superClassDescription.level + 1;
          var classDescription = this;
          this.$constructor = function() {
            classDescription.initialize();
            classDescription.$constructor.apply(this,arguments);
          };
          setFunctionName(this.$constructor, this.fullClassName);
          this.$constructor.prototype = new (this.superClassDescription.Public)();
          // TODO: only if not final:
          this.Public = createEmptyConstructor(this.$constructor.prototype);
          // static part:
          this.Static = createEmptyConstructor(new (this.superClassDescription.Static)());
          this.getStatic = function() {
            classDescription.initialize();
            return classDescription.getStatic();
          }
          // TODO: only if public:
          this.$package = createPackage(this.$package);
          this.$package[this.$class] = this.$constructor;
          this.$package[this.$class+"_"] = this.getStatic;

          this.state = PREPARED;
          prepareSubclasses(this);
        },
        /**
         * Initializes this class by finishing the class setup and then invoking all static initializers.
         */
        initialize: function() {
          if (this.state!==PREPARED)
            return;
          this.state = INITIALIZING;
          // finish object structure setup of this class:
          // public part: avoid recursion!
          this.$package[this.$class+"_"] = this.getStatic = undefined;
          this.$package[this.$class] = this.$constructor = undefined;

          // private part of the object structure:
          var classPrefix = this.level; // + "$";
          var superName = classPrefix+"super";
          var fieldsWithInitializer = [];
          var classDescription = this;
          this.Public.prototype[superName] = function $super() {
            delete this[superName]; // only allow to call $super once!
            classDescription.superClassDescription.$constructor.apply(this,arguments);
            initFields(null, this, fieldsWithInitializer);
          };
          // static part:
          this.$package[this.$class+"_"] = this.getStatic = function() {
            // overwrite, this time without initializing:
            return classDescription.Static.prototype;
          };
          var privateStatic = new (this.Static)();
          privateStatic._super = superName;

          // init super class:
          this.superClassDescription.initialize();

          // evaluate $members, transfer members into the prepared objects:

          // Define a mapping to efficiently find the right prototype object to store a member,
          // depending on its modifiers.
          // Note: As long as "protected" is not implemented, treat it like "public".
          var targetMap = {
            $this: {
              fieldsWithInitializer: fieldsWithInitializer,
              $public: this.Public.prototype,
              $protected: this.Public.prototype,
              $private: this.Public.prototype
            },
            $static: {
              fieldsWithInitializer: [],
              $public: this.Static.prototype,
              $protected: this.Static.prototype,
              $private: privateStatic
            }
          };
          if (isFunction(this.$members)) {
            var memberDeclarations = this.$members(privateStatic);
            var i=0;
            while (i<memberDeclarations.length) {
              var memberKey = "$this"; // default: not static
              var visibility = "$public"; // default: public visibility
              var members = memberDeclarations[i++];
              if (members===undefined) {
                continue;
              }
              var memberType = "function";
              var modifiers;
              if (typeof members=="string") {
                modifiers = members.split(" ");
                for (var j=0; j<modifiers.length; ++j) {
                  var modifier = modifiers[j];
                  if (modifier=="static") {
                    memberKey = "$static";
                  } else if (modifier=="private" || modifier=="public" || modifier=="protected") {
                    visibility = "$"+modifier;
                  } else if (modifier=="var" || modifier=="const") {
                    memberType = modifier;
                  } else {
                    throw new Error("Unknown modifier '"+modifier+"'.");
                  }
                }
                if (i>=memberDeclarations.length) {
                  throw new Error("Member expected after modifiers "+modifiers.join(" "));
                }
                members = memberDeclarations[i++];
              } else {
                modifiers = [];
              }
              var target = targetMap[memberKey][visibility];
              //document.writeln("defining "+modifiers.join(" ")+" member(s):");
	      var memberName;
	      if (memberType=="function") {
                if (typeof members=="object") {
                  members = createMethod(members);
                }
                memberName = members.getName();
                if (memberName==this.$class) {
                  this.$constructor = members;
                } else if (memberKey=="$this") {
                  if (visibility=="$private") {
                    memberName = registerPrivateMember(privateStatic, classPrefix, memberName);
                    setFunctionName(members, memberName);
                  } else if (isFunction(target[memberName])) {
                    // Found overriding! Store super method as private method delegate for super access:
                    this.Public.prototype[registerPrivateMember(privateStatic, classPrefix, memberName)] = target[memberName];
                  }
                }
                target[memberName] = members;
              } else {
                var targetFieldsWithInitializer = targetMap[memberKey].fieldsWithInitializer;
                for (memberName in members) {
                  var member = members[memberName];
                  if (memberKey=="$this" && visibility=="$private") {
                    memberName = registerPrivateMember(privateStatic, classPrefix, memberName);
                  }
                  target[memberName] = member;
                  if (isFunction(member)) {
                    targetFieldsWithInitializer.push(memberName);
                  }
                }
              }
            }
          }
          if (!this.$constructor) {
            this.$constructor = createDefaultConstructor(superName);
          }
          setFunctionName(this.$constructor, this.fullClassName);
          this.$constructor.prototype = this.Public.prototype;
          this.Public.prototype.getClass = createGetClass(this.$constructor);
          // TODO: constructor visibility!
          this.$package[this.$class] = this.$constructor;
          // init static fields with initializer:
          initFields(privateStatic, this.Static.prototype, targetMap.$static.fieldsWithInitializer);
        }
      };
      classDescriptions["joo.lang.Object"] = {
        $package: createPackage("joo.lang"),
        $class: "Object",
        fullClassName: "joo.lang.Object",
        $extends: undefined,
        level: 0,
        state: INITIALIZED,
        superClassDescription: undefined,
        $constructor: (function() {
          return (function Object$constructor() {
            this["0hashCode"] = "_"+(hashCodeCnt++);
          });
        })(),
        Public: new Function(),
        Static: new Function(),
        getStatic: new Function(),
        prepare: new Function(),
        initialize: new Function()
      };
      classDescriptions["joo.lang.Object"].Public.prototype = {
         hashCode: Object$hashCode,
         toString: Object$toString
       };
    }
    ClassDescription.$static = ClassDescription$static;
    return ClassDescription;
  })();
  function Package() { }
  window.joo = new Package();
  var hashCodeCnt = 0;
  window.joo.Array = function $Array(array) {
    if (!array)
      array = [];
    array["0hashCode"] = "_"+(hashCodeCnt++);
    array.hashCode = Object$hashCode;
    return array;
  }
  window.joo.Class = {};
  var loadedClasses = {};
  window.joo.Class.load = function(fullClassName) {
    if (!loadedClasses[fullClassName]) {
      loadedClasses[fullClassName] = true;
      var uri = document.location.href;
      uri = uri.substring(0, uri.lastIndexOf("/")+1);
      uri += fullClassName.replace(/\./g,"/")+".js";
      var script = document.createElement("script");
      script.src = uri;
      document.body.appendChild(script);
    }
  };
  window.joo.Class.run = function(fullClassName, args) {
    window.joo.Class.load(fullClassName);
    window.onload = function() {
      eval(fullClassName+"_()").main(args);
    }
  }
  window.joo.Class.prepare = function(packageDef, classDef, members) {
    var classDesc = { $members: members };
    if (typeof packageDef!="string")
      throw new Error("package declaration must be a string.");
    var packageParts = packageDef.split(/\s+/);
    if (packageParts[0]!="package")
      throw new Error("package declaration must start with 'package'.");
    if (packageParts.length!=2) {
      throw new Error("package declaration must be followed by a package name.");
    }
    classDesc.$package = packageParts[1];

    if (typeof classDef!="string")
      throw new Error("class declaration must be a string.");
    var classParts = classDef.split(/\s+/);
    var i=0;
    if (classParts[i]=="public") {
      classDesc.visibility = classParts[i++];
    }
    if (classParts[i]=="abstract") {
      classDesc.$abstract = true;
      ++i;
    }
    if (classParts[i++]!="class")
      throw new Error("expected 'class' after class modifiers.");
    if (i==classParts.length) {
      throw new Error("expected class name after keyword 'class'.");
    }
    classDesc.$class = classParts[i++];
    if (i<classParts.length) {
      if (classParts[i++]!="extends")
        throw new Error("expected EOL or 'extends' after class name.");
      if (i==classParts.length)
        throw new Error("expected class name after 'extends'.");
      classDesc.$extends = classParts[i++];
    }
    if (i<classParts.length)
      throw new Error("unexpected token '"+classParts[i]+" after class declaration.");
    new ClassDescription(classDesc);
  };
})();
//  alert("runtime loaded!");
joo.typeOf = function typeOf(obj){
  if (obj==undefined) return false;
  var type = typeof obj;
  if (type == 'object' || type == 'function'){
    switch(obj.constructor){
      case Array: return 'array';
      case RegExp: return 'regexp';
    }
    if (typeof obj.length == 'number' && obj.callee) {
      return 'arguments';
    }
  }
  return type;
};
joo.Class.prepare("package joo.util",

"public class TimedExecutor",function($jscContext){with(joo.util)with($jscContext)return[

"public",function TimedExecutor(handler,timeout){
this[_handler]=handler;
this[_timeout]=timeout;
this[_trigger]=this[_trigger].bind(this);
},

"private",function setTimeout(delay){
this[_timer]=window.setTimeout(this[_trigger],Math.max(this[_timeout]-delay,0));
},

"private",function trigger(){
var triggerStartTime=new Date().getTime();
var realTimeout=triggerStartTime-this[_lastTriggerStartTime];

this[_lastTriggerStartTime]=triggerStartTime;
var result=this[_handler](realTimeout);
if(typeof result=="number"){
this[_timeout]=result;
}
if(result===false||result<0){
this[_timer]=undefined;
}else{
var duration=new Date().getTime()-triggerStartTime;
this[_setTimeout](duration);
}
},

"public",function isRunning(){
return! !this[_timer];
},

"public",function start(){
if(!this[_timer]){
this[_lastTriggerStartTime]=new Date().getTime();
this[_setTimeout](0);
return true;
}
return false;
},

"public",function stop(){
if(this[_timer]){
window.clearTimeout(this[_timer]);
this[_timer]=undefined;
return true;
}
return false;
},

"public",function restart(){
var result=this.stop();
this.start();
return result;
},

"private var",{handler: undefined},
"private var",{timeout:0},
"private var",{timer: undefined},
"private var",{lastTriggerStartTime: undefined},
]});joo.Class.prepare("package joo.util",

"public class Offset",function($jscContext){with(joo.util)with($jscContext)return[

"public static var",{HOME:function(){return new Offset(0,0);}},

"public",function Offset(left,top){
this.left=left;
this.top=top;
},

"public static",function getOffset(offset,top){
if(top!==undefined){
return new Offset(offset,top);
}
return offset;
},

"public",function clone(){
return new Offset(this.left,this.top);
},

"public",function plus(offset,top){
offset=getOffset(offset,top);
return new Offset(this.left+offset.left,this.top+offset.top);
},

"public",function minus(offset,top){
offset=getOffset(offset,top);
return new Offset(this.left-offset.left,this.top-offset.top);
},

"public",function getDistance(){
return Math.sqrt(this.left*this.left+this.top*this.top);
},

"public",function scale(factor){
return new Offset(this.left*factor,this.top*factor);
},

"public",function isHome(){
return this.left==0&&this.top==0;
},

"public var",{left: undefined},
"public var",{top: undefined},
]});joo.Class.prepare(
"package joo.util",

"public class HashSet",function($jscContext){with(joo.util)with($jscContext)return[

"public",function HashSet(element){
this[_super]();
this[_reset]();
if(element){
if(!this.addAll(element)){
this.add(element);
}
}
},
"public",function size(){
return this[_size];
},
"public",function isEmpty(){
return this[_size]==0;
},
"public",function iterator(){
return new GapArrayIterator(this[_elements]);
},
"public",function add(element){
if(element===undefined){
throw"IllegalArgument: may not add undefined to HashSet.";
}
var hashCode=getHashCode(element);
var indexByHashCode=this[_indexByHashCode];
var index=indexByHashCode[hashCode];
var elements=this[_elements];
if(index!==undefined){
return false;
}
indexByHashCode[hashCode]=elements.length;
elements.push(element);
++this[_size];
return true;
},
"public",function addAll(array){
var result=false;
if(array&&array.constructor===Array){
for(var i=0;i<array.length;++i){
if(array[i]!==undefined){
result|=this.add(array[i]);
}
}
}
return result;
},
"public",function pop(){
var elements=this[_elements];
for(var i=0;i<elements.length;++i){
if(elements[i]!==undefined){
var result=elements[i];
this[_elements][i]=undefined;
--this[_size];
delete this[_indexByHashCode][getHashCode(result)];
return result;
}
}
return undefined;
},
"public",function remove(element){
var hashCode=getHashCode(element);
var index=this[_indexByHashCode][hashCode];
if(index===undefined){
return false;
}
this[_elements][index]=undefined;
--this[_size];
delete this[_indexByHashCode][hashCode];


return true;
},
"public static",function getHashCode(element){
















return element&&element.hashCode?element.hashCode():"_"+element;
},
"private",function reset(){
this[_indexByHashCode]={};
this[_elements]=[];
this[_size]=0;
},
"public",function clear(){
this[_reset]();
},
"public",function contains(element){
return this[_indexByHashCode][getHashCode(element)]!==undefined;
},
"private var",{indexByHashCode: undefined},
"private var",{elements: undefined},
"private var",{size: undefined},
"public",function toString(){
var elements=this[_elements];
var size=this[_size];
var definedElements;
if(size==elements.length){
definedElements=elements;
}else{
definedElements=[];
for(var i=this.iterator();i.hasNext();){
definedElements.push(i.next());
}
}
return"{"+definedElements.join(", ")+"}";
},
]});joo.Class.prepare("package joo.util",

"public class Interval",function($jscContext){with(joo.util)with($jscContext)return[

"public static",function create(numberOrArrayOrInterval){
switch(numberOrArrayOrInterval){
case"number":return new Interval(numberOrArrayOrInterval,numberOrArrayOrInterval);
case"array":return new Interval(numberOrArrayOrInterval[0],numberOrArrayOrInterval[1]);
case"object":return numberOrInterval;
}
throw"Cannot create Interval from '"+numberOrArrayOrInterval+"'.";
},

"public",function Interval(a,b){
this.a=a;
this.b=b;
if(a>b){
throw"Interval: IllegalArguments "+this;
}
},

"public",function contains(n){
return this.a<=n&&n<=this.b;
},

"public",function includes(interval){
return this.a<=interval.a&&this.b>=interval.b;
},

"public",function intersection(interval){
var a=Math.max(this.a,interval.a);
var b=Math.min(this.b,interval.b);
return a<=b?new Interval(a,b):null;
},

"public",function compareTo(c){
return this.a>c?1:this.b<c?-1:0;
},

"public",function toString(){
return["[",this.a,",",this.b,"]"].join("");
},

"public var",{a: undefined},
"public var",{b: undefined},
]});joo.Class.prepare(
"package joo.util",

"class GapArrayIterator",function($jscContext){with(joo.util)with($jscContext)return[

"public",function GapArrayIterator(elements){
this[_super]();
this[_elements]=elements;
this[_findNext]();
},
"public",function hasNext(){
return this[_index]<this[_elements].length;
},
"public",function next(){
var next=this[_elements][this[_index]];
this[_findNext]();
return next;
},
"private",function findNext(){
var elements=this[_elements];
for(var i=this[_index]+1;i<=elements.length;++i){
if(elements[i]!==undefined){
this[_index]=i;
return;
}
}
this[_index]=elements.length;
},
"private var",{elements: undefined},
"private var",{index:-1},
]});joo.Class.prepare("package joo.html",

"public class Element",function($jscContext){with(joo.html)with($jscContext)return[

"public",function Element(ownerDocument,peer){
this[_super]();
this.ownerDocument=ownerDocument;
this[_peer]=peer;
},

"public",function getPeer(){
return this[_peer];
},

"private static",function preventDefault(){
this.returnValue=false;
},

"private static",function stopPropagation(){
this.cancelBubble=true;
},

"public",function addEventListener(eventType,listener,capture){
var peer=this[_peer];
if(typeof peer.addEventListener=="function"){
peer.addEventListener(eventType,listener,capture);
}else{

var ownerWindow=this.ownerDocument.ownerWindow;
peer.attachEvent("on"+eventType,function(){
var event=ownerWindow.event;
event.preventDefault=preventDefault;
event.stopPropagation=stopPropagation;
var body=ownerWindow.document.body;
event.pageX=event.clientX+body.scrollLeft;
event.pageY=event.clientY+body.scrollTop;
return listener(event);
});
}
},

"public",function setClassName(className){
this[_peer].className=className;
return this;
},

"public",function setAttributes(attributes){
var className=attributes.className;
if(className){
this.setClassName(className);
delete attributes.className;
}
var style=attributes.style;
if(style){
this.setStyle(style);
delete attributes.style;
}
for(var a in attributes){
var value=attributes[a];
if(typeof value=="number"){
value=String(Math.round(value));
}
this[_peer].setAttribute(a,value);
}
return this;
},

"private static",function convertToString(value){
switch(typeof value){
case"string":
return value;
case"number":
return Math.round(value)+"px";
case"object":
if(value!=null&&value.constructor==Array){
var result=[];
for(var i=0;i<value.length;++i){
result.push(convertToString(value[i]));
}
return result.join(" ");
}
}
return String(value);
},

"private static const",{SUPPORTS_CSS_TEXT:function(){return typeof window.document.getElementsByTagName("body")[0].style.cssText=="string";}},

"public static",function setCssText(element,cssText){
if(typeof element.getPeer=="function"){
element=element.getPeer();
}
if(SUPPORTS_CSS_TEXT){
element.style.cssText=cssText;
}else{
element.setAttribute("style",cssText);
}
},

"public",function setStyle(styleAttributes){
if(typeof styleAttributes=="string"){
setCssText(this[_peer],styleAttributes);
return this;
}
var style=this[_peer].style;
if(styleAttributes.opacity!==undefined&&typeof style.filter=="string"){
var opacity=Math.round(styleAttributes.opacity*100);
try{

this[_peer].filters.item("DXImageTransform.Microsoft.Alpha").Opacity=opacity;
}catch(ex){
style.filter="progid:DXImageTransform.Microsoft.Alpha(opacity="+opacity+")";
}
delete styleAttributes.opacity;
}
for(var s in styleAttributes){
style[s]=convertToString(styleAttributes[s]);
}
return this;
},

"public",function remove(){
return this[_peer].parentNode.removeChild(this[_peer]);
},

"public",function appendChild(element){
var peer=typeof element.getPeer=="function"?element.getPeer():element;
this[_peer].appendChild(peer);
return this;
},

"public",function getParentElement(){
return get(this[_peer].parentNode);
},

"public",function getOffset(){
return{left:this[_peer].offsetLeft,top:this[_peer].offsetTop};
},

"public",function setOffset(offset,top){
var left;
if(top===undefined){
left=offset.left;top=offset.top;
}else{
left=offset;
}
this.setStyle({left:offset.left,top:offset.top});
return this;
},

"public",function getAbsoluteOffset(){
var peer=this[_peer];
var offset={left:0,top:0};
do{
offset.left+=peer.offsetLeft;
offset.top+=peer.offsetTop;
peer=peer.offsetParent;
}while(peer);
return offset;
},

"public",function getDimensions(){
var peer=this[_peer];
return{width:peer.offsetWidth,height:peer.offsetHeight};
},

"public",function toString(){
return this[_peer].id||this[_peer].nodeName;
},

"private var",{peer: undefined},
]});joo.Class.prepare("package joo.html",

"public class Document",function($jscContext){with(joo.html)with($jscContext)return[

"public static const",{INSTANCE:function(){return new Document();}},

"public",function Document(ownerWindow){
this[_super]();
this.ownerWindow=ownerWindow?ownerWindow:window;
this[_peer]=this.ownerWindow.document;
},

"public",function getPeer(){
return this[_peer];
},

"public",function getWindow(){
return this.ownerWindow;
},

"public",function createElement(elementName){
return new Element(this,this[_peer].createElement(elementName));
},

"public",function createTextNode(text){

return this[_peer].createTextNode(text);
},

"public",function getElement(peerOrId){
var peer=typeof peerOrId=="string"?this[_peer].getElementById(peerOrId):peerOrId;
if(!this[_peer]){
throw"Undefined Element "+peerOrId;
}
return new Element(this,peer);
},

"public",function getDocumentElement(){
return this.getElement(this[_peer].documentElement);
},

"public",function getBody(){
if(!this[_body]){
this[_body]=this.getElement(this[_peer].getElementsByTagName("body")[0]);
}
return this[_body];
},

"public",function write(text){
this[_peer].write(text);
},

"public",function close(){
this[_peer].close();
},

"public",function toString(){
return"Document "+this[_peer];
},

"private var",{peer: undefined},
"private var",{body: undefined},
]});joo.Class.prepare("package joo.html.slant",

"public class SlantCanvas extends joo.html.Element",function($jscContext){with(joo.html.slant)with($jscContext)return[

"private static const",{SUPPORTS_FILTERS:function(){return typeof window.document.getElementsByTagName("body")[0].style.filter=="string";}},
"private static const",{SUPPORTS_CSS_TEXT:function(){return typeof window.document.getElementsByTagName("body")[0].style.cssText=="string";}},
"private static const",{IS_OPERA:function(){return window.navigator.userAgent.indexOf("Opera")!=-1;}},

"public",function SlantCanvas(document,canvas){
this[_super](document,canvas);
this.setAttributes({className:"canvas"});
},

"public",function appendChild(child){
this[_appendChild](child);
++this[_divIndex];
},

"private static",function rectCssText(color,x0,y0,x1,y1,borderColor,borderWidth){
var width=x1-x0;
var height=y1-y0;
if(borderWidth){
if(borderWidth<0){

var doubleBW=-2*borderWidth;
if(width>=doubleBW&&height>=doubleBW){
borderWidth=-borderWidth;
width-=doubleBW;
height-=doubleBW;
}else{

color=borderColor;
borderWidth=0;
}
}else{

x0-=borderWidth;
y0-=borderWidth;
}
}

var rect=["background-color:",color,
";left:",x0,"px;top:",y0,"px;width:",width,"px;height:",height,"px;"];
if(borderWidth){
rect.push("border-color:",borderColor,";border-width:",borderWidth,"px;");
}
return rect;
},

"private static",function slantCssText(left,top,borderDir,borderColor,borderWidth,heightOrWidth,heightOrWidthValue){
return["left:",left,"px;top:",top,"px;border-",borderDir,"-color:",borderColor,
";border-width:",borderWidth,"px ",x11-x00,"px ",y11-y01,"px 0;height:",y01-y00,"px;"];
},

"private",function add(cssText,zIndex,opacity){
if(zIndex!==undefined){
cssText.push("z-index:",zIndex,";");
}
if(opacity<1){
if(SUPPORTS_FILTERS){
cssText.push("filter:progid:DXImageTransform.Microsoft.Alpha(opacity=",Math.round(opacity*100),");");
}else{
cssText.push("opacity:",opacity,";");
}
}
cssText=cssText.join("");
var div=this.getPeer().childNodes[this[_divIndex]++];
if(!div){
div=this.ownerDocument.createElement("div").setClassName("slant").getPeer();
this[_appendChild](div);
}
if(SUPPORTS_CSS_TEXT){
div.style.cssText=cssText;
}else{
div.setAttribute("style",cssText);
}
return div;
},














"public",function addRect(color,x0,y0,x1,y1,zIndex,opacity,borderColor,borderWidth){
return this[_add](rectCssText(color,Math.round(x0),Math.round(y0),Math.round(x1),Math.round(y1),borderColor,borderWidth),zIndex,opacity);
},



























"public",function addSlant(color,x00,y00,x01,y01,x10,y10,x11,y11,zIndex,opacity,borderColor,borderWidth){
x00=Math.round(x00);
y00=Math.round(y00);
x10=Math.round(x10);
y10=Math.round(y10);
x01=Math.round(x01);
y01=Math.round(y01);
x11=Math.round(x11);
y11=Math.round(y11);
var cssText;
if(x00==x01&&x10==x11){
if(y00==y10&&y01==y11){

cssText=rectCssText(color,x00,y00,x11,y11,borderColor,borderWidth);
}else if(y00>=y10&&y01<=y11){

cssText=["left:",x00,"px;top:",y10,"px;border-right-color:",color,
";border-width:",y00-y10,"px ",x11-x00,"px ",y11-y01,"px 0;height:",y01-y00,"px;"];
}else if(y00<=y10&&y01>=y11){

cssText=["left:",x00,"px;top:",y00,"px;border-left-color:",color,
";border-width:",y10-y00,"px 0 ",y01-y11,"px ",x11-x00,"px;height:",y11-y10,"px;"];
}
}else if(x00<=x01&&x10>=x11){

cssText=["left:",x00,"px;top:",y00,"px;border-top-color:",color,
";border-width:",y11-y00,"px ",x10-x11,"px 0 ",x01-x00,"px;width:",x11-x01,"px;"];
}else if(x00>=x01&&x10<=x11){

cssText=["left:",x01,"px;top:",y00,"px;border-bottom-color:",color,
";border-width:0 ",x11-x10,"px ",y11-y00,"px ",x00-x01,"px;width:",x10-x00,"px;"];
}
if(cssText===undefined){
throw new Error(["Unsupported Slant coordinates ",x00,"|",y00," ",x10,"|",y10," ",x01,"|",y01," ",x11,"|",y11,"."].join(""));
}
return this[_add](cssText,zIndex,opacity);
},

"public",function rewind(offset){
if(offset===undefined){
offset=0;
}
this[_divIndex]=offset;
},

"public",function clearRest(){
var div=this.getPeer();
for(var i=this[_divIndex];i<this[_lastDivIndex];++i){
div.childNodes[i].style.display="none";
}
if(IS_OPERA){
this.operaClassToggle=!this.operaClassToggle;
this.setClassName("canvas"+(this.operaClassToggle?" foo":""));
}
this[_lastDivIndex]=this[_divIndex];
},

"public",function getItemCount(){
return this[_divIndex];
},

"public",function getItem(index){
return this.getPeer().childNodes[index];
},

"private var",{divIndex:0},
"private var",{lastDivIndex:0},
]});joo.Class.prepare("package joo.css",

"public class Style",function($jscContext){with(joo.css)with($jscContext)return[

"public",function Style(styleAttributes){
this[_super]();
if(styleAttributes){
this.setAttributes(styleAttributes);
}
},

"private static",function convertToString(sb,value){
switch(typeof value){
case"string":
sb.push(value);
break;
case"number":
sb.push(Math.round(value),"px");
break;
case"object":
if(value!=null&&value.constructor==Array){
for(var i=0;i<value.length;++i){
convertToString(sb,value[i]);
}
break;
}
default:
sb.push(String(value));
}
},

"public",function setAttribute(name,value){
this[_attributes][name]=value;
return this;
},

"public",function removeAttribute(name){
delete this[_attributes][name];
},

"public",function setAttributes(styleAttributes){
for(var s in styleAttributes){
this[_attributes][s]=styleAttributes[s];
}
return this;
},

"public",function populate(element){
joo.html.Element_().setCssText(element,this.toString());
return this;
},

"private static const",{PROP2ATTR:/([A-Z])/g},
"private static",function prop2Attr(c){
return"-"+c.toLowerCase();
},

"private static const",{SUPPORTS_FILTERS:function(){return typeof window.document.body.style.filter=="string";}},

"public",function toString(){
var sb=[];
var opacity=this[_attributes].opacity;
if(opacity!==undefined&&SUPPORTS_FILTERS){
sb.push("filter:progid:DXImageTransform.Microsoft.Alpha(opacity=",Math.round(opacity*100),")");
delete this[_attributes].opacity;
}
for(var s in this[_attributes]){
var attr=s.replace(PROP2ATTR,prop2Attr);
sb.push(attr,":",convertToString(sb,this[_attributes][s]),";");
}
if(opacity!=undefined&&SUPPORTS_FILTERS){

this.attribute.opacity=opacity;
}
return sb.join("");
},

"private var",{attributes:function(){return {};}},
]});joo.Class.prepare("package joo.css",

"public class Color",function($jscContext){with(joo.css)with($jscContext)return[

"public static const",{TRANSPARENT:function(){return new Color(-1,-1,-1,0);}},
"public static const",{WHITE:function(){return new Color(255,255,255);}},
"public static const",{BLACK:function(){return new Color(0,0,0);}},
"public static const",{GRAY:function(){return new Color(128,128,128);}},
"public static const",{RED:function(){return new Color(255,0,0);}},
"public static const",{GREEN:function(){return new Color(0,255,0);}},
"public static const",{BLUE:function(){return new Color(0,0,255);}},
"public static const",{DARK_BLUE:function(){return new Color(0,0,128);}},

"private static const",{FACTOR:0.7},

"public",function Color(red,green,blue){
this[_red]=red;
this[_green]=green;
this[_blue]=blue;
if(this[_red]<0){
this[_asString]="transparent";
}else{
this[_asString]=["rgb(",Math.round(this[_red]),",",Math.round(this[_green]),",",Math.round(this[_blue]),")"].join("");
}
},

"public",function clone(){
return new Color(this[_red],this[_green],this[_blue]);
},

"public",function darker(factor){
if(!factor)
factor=FACTOR;
if(factor==FACTOR&&this.darkerColor){
return this.darkerColor;
}
var darkerColor=new Color(
this[_red]*factor,
this[_green]*factor,
this[_blue]*factor
);
if(factor==FACTOR)
this.darkerColor=darkerColor;
return darkerColor;
},

"public",function brighter(factor){
if(!factor)
factor=FACTOR;
if(factor==FACTOR&&this.brighterColor){
return this.brighterColor;
}
var brighterColor;
var r=this[_red];
var g=this[_green];
var b=this[_blue];

var i=1/(1-factor);
if(r==0&&g==0&&b==0){
brighterColor=new Color(i,i,i);
}else{
if(r>0&&r<i)r=i;
if(g>0&&g<i)g=i;
if(b>0&&b<i)b=i;

brighterColor=
new Color(Math.min(r/factor,255),
Math.min(g/factor,255),
Math.min(b/factor,255));
}
if(factor==FACTOR)
this.brighterColor=brighterColor;
return brighterColor;
},

"public",function toString(){
return this[_asString];
},

"private var",{red: undefined},
"private var",{green: undefined},
"private var",{blue: undefined},
"private var",{asString: undefined},
]});joo.Class.prepare("package joo.css",

"public class URL",function($jscContext){with(joo.css)with($jscContext)return[

"private static const",{TO_STRING:function(){return ["url(",")"];}},

"public",function URL(url){
this.url=url;
},

"public",function toString(){
return TO_STRING.join(this.url);
},

"public var",{url: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class Position",function($jscContext){with(net.jangaron)with($jscContext)return[

"public",function Position(x,y,dir){
this.x=x;
this.y=y;
this.dir=dir;
},

"public",function clone(){
return new Position(this.x,this.y,this.dir);
},

"public",function setPosition(position){
this.x=position.x;
this.y=position.y;
this.dir=position.dir;
},

"public",function getPosition(distance){
var dir=this.dir;
return new Position(this.x+dir.dx*distance,this.y+dir.dy*distance,dir);
},

"public",function getOffset(steps){
return this[_offset](this.dir,steps);
},

"public",function getLeftOffset(steps){
return this[_offset](this.dir.turnRight,steps);
},

"private",function offset(dir,steps){
switch(dir){
case Direction_().NORTH:return this.y%steps;
case Direction_().EAST:return this.x%steps;
case Direction_().SOUTH:return steps-1+((-this.y+1)%steps);
default:return steps-1+((-this.x+1)%steps);
}
},

"public",function move(distance){
var dir=this.dir;
this.x+=dir.dx*distance;
this.y+=dir.dy*distance;
},

"public",function turnLeft(){
this.dir=this.dir.turnLeft;
},

"public",function turnRight(){
this.dir=this.dir.turnRight;
},

"public var",{x: undefined},
"public var",{y: undefined},
"public var",{dir: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class Direction",function($jscContext){with(net.jangaron)with($jscContext)return[
"public static const",{DIRS:function(){return [];}},
"public static const",{NORTH:function(){return new Direction("N",0,1,String.fromCharCode(parseInt("25B2",16)));}},
"public static const",{EAST:function(){return new Direction("E",1,0,String.fromCharCode(parseInt("25C4",16)));}},
"public static const",{SOUTH:function(){return new Direction("S",0,-1,String.fromCharCode(parseInt("25BC",16)));}},
"public static const",{WEST:function(){return new Direction("W",-1,0,String.fromCharCode(parseInt("25BA",16)));}},
"public static var",{DIRS_COUNT:0},

"private static const",{init:function(){

function getDir(relative){
return DIRS[(d+relative)%DIRS_COUNT];
};
for(var d=0;d<DIRS_COUNT;++d){
var dir=DIRS[d];
dir.opposite=getDir(DIRS_COUNT/2);
dir.turnRight=getDir(1);
dir.turnLeft=getDir(DIRS_COUNT-1);
}
}},

"public",function Direction(name,dx,dy,compassArrow){
DIRS.push(this);++DIRS_COUNT;
this[_name]=name;
this.dx=dx;
this.dy=dy;
this[_compassArrow]=compassArrow;
},

"public",function getCompassArrow(){
return this[_compassArrow];
},

"public",function toString(){
return this[_name];
},

"private var",{name: undefined},
"private var",{compassArrow: undefined},
"public var",{dx: undefined},
"public var",{dy: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class Trapez",function($jscContext){with(net.jangaron)with($jscContext)return[

"public",function set(color,x1,y1,x2,y2){
this.color=color;
if(x2<x1){

this.x1=x2;
this.y1=y2;
this.x2=x1;
this.y2=y1;
}else{
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}
},

"public",function intersects(trapez){
return Math.max(this.x1,trapez.x1)<=Math.min(this.x2,trapez.x2);
},

"public",function toString(){
return"["+[this.color,this.x1,this.y1,this.x2,this.y2].join(",")+"]";
},

"public var",{color: undefined},
"public var",{x1: undefined},
"public var",{y1: undefined},
"public var",{x2: undefined},
"public var",{y2: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class LightCycle",function($jscContext){with(net.jangaron)with($jscContext)return[

"private static const",{DEFAULT_MIN_SPEED:25},
"private static const",{DEFAULT_MAX_SPEED:80},
"private static const",{DEBUG:false},

"public",function LightCycle(name,color,position,keyMap,minSpeed,maxSpeed){
this[_super]();
this[_name]=name;
this[_color]=color;
this.initialPosition=position;
this[_position]=new Position();
this[_keyMap]=keyMap;
this.minSpeed=minSpeed?minSpeed:DEFAULT_MIN_SPEED;
this.maxSpeed=maxSpeed?maxSpeed:DEFAULT_MAX_SPEED;
},

"public",function setGrid(grid){
this[_grid]=grid;
},

"public",function setName(name){
this[_name]=name;
},

"public",function getName(){
return this[_name];
},

"public",function isUser(){
return! !this[_keyMap];
},

"public",function setColor(color){
this[_color]=color;
},

"public",function getColor(){
return this[_color];
},

"public",function getOpacity(){
return this[_opacity];
},

"public",function getPosition(){
return this[_position];
},

"public",function init(){
this[_opacity]=this[_grid].startOpacity;
this.speed=1;
this[_position].setPosition(this.initialPosition);
this[_turnQueue]=[];
this[_prevCycleWall]=this[_cycleWall]=undefined;
this[_lastWall]=new Wall(this[_color],this[_position].x,this[_position].y,this);
this[_prevCycleWall]=this[_findCrashWall](this[_lastWall],new joo.util.HashSet());
this[_cycleWall]=this[_createCycleWall]();
this[_cycleHeadWall]=this[_createCycleWall]();
this[_updateCycleHeadWall]();
},

"private",function createCycleWall(){
var cycleWall=new Wall(this[_color],this[_position].x,this[_position].y,this);
cycleWall.lightCycle=this;
this[_grid].addWall(cycleWall);
return cycleWall;
},

"private",function updateCycleHeadWall(){
var Direction=Direction_();
switch(this[_position].dir){
case Direction.NORTH:
case Direction.SOUTH:
this[_cycleHeadWall].x1=this[_position].x-0.5;
this[_cycleHeadWall].x2=this[_position].x+0.5;
this[_cycleHeadWall].y1=this[_cycleHeadWall].y2=this[_position].y;
break;
default:
this[_cycleHeadWall].y1=this[_position].y-0.5;
this[_cycleHeadWall].y2=this[_position].y+0.5;
this[_cycleHeadWall].x1=this[_cycleHeadWall].x2=this[_position].x;
}
},

"public",function getCycleWall(){
return this[_cycleWall];
},

"public",function move(passedMillis){
if(this[_opacity]==this[_grid].startOpacity){
this[_updateSpeed](new Date().getTime());
if(this.speed<this.minSpeed){

this.speed=Math.min(this.minSpeed,this.speed+passedMillis*15/1000);
}
this[_position].move(this.speed*passedMillis/1000);
}else if(this[_opacity]>0){
this[_opacity]-=passedMillis/2000;
if(this[_opacity]<0.1){
this[_opacity]=0;
this[_grid].removeLightCycle(this);

}
}
},

"private",function updateSpeed(now){
if(this.fasterDown){
if(this.speed<this.maxSpeed){
this.speed=Math.min(this.maxSpeed,this.speed+(now-this.fasterDown)*0.005);
}
this.fasterDown=now;
}else if(this.slowerDown){
if(this.speed>this.minSpeed){
this.speed=Math.max(this.minSpeed,this.speed-(now-this.slowerDown)*0.015);
}
this.slowerDown=now;
}
},

"public",function update(){
if(!this.isDead()){
var Direction=Direction_();
switch(this[_position].dir){
case Direction.NORTH:
this[_lastWall].y1=this[_cycleWall].y2;
this[_cycleHeadWall].y1=this[_cycleHeadWall].y2=
this[_lastWall].y2=this[_cycleWall].y2=this[_position].y;
break;
case Direction.EAST:
this[_lastWall].x1=this[_cycleWall].x2;
this[_cycleHeadWall].x1=this[_cycleHeadWall].x2=
this[_lastWall].x2=this[_cycleWall].x2=this[_position].x;
break;
case Direction.SOUTH:
this[_lastWall].y2=this[_cycleWall].y1;
this[_cycleHeadWall].y1=this[_cycleHeadWall].y2=
this[_lastWall].y1=this[_cycleWall].y1=this[_position].y;
break;
default:
this[_lastWall].x2=this[_cycleWall].x1;
this[_cycleHeadWall].x1=this[_cycleHeadWall].x2=
this[_lastWall].x1=this[_cycleWall].x1=this[_position].x;
}
if(DEBUG){
this[_cycleWall].check();
this[_cycleHeadWall].check();
}
}
},

"private",function moveWall(distance,turn){
var startPosition=this[_position];
var endPosition=startPosition.clone();
if(turn){
endPosition[turn]();
}
endPosition.move(distance);
return new Wall(this[_color],
[Math.min(startPosition.x,endPosition.x),Math.max(startPosition.x,endPosition.x)],
[Math.min(startPosition.y,endPosition.y),Math.max(startPosition.y,endPosition.y)]);
},

"protected",function computeTurn(){
var turn;
var excludedWalls=new joo.util.HashSet([this[_cycleHeadWall],this[_cycleWall],this[_prevCycleWall]]);
if(this[_findCrashWall](this[_moveWall](this.speed/3,undefined),excludedWalls)){
turn=Math.random()<0.5?"turnLeft":"turnRight";
if(this[_findCrashWall](this[_moveWall](this.speed/10,turn),excludedWalls)){
turn=turn=="turnLeft"?"turnRight":"turnLeft";
if(this[_findCrashWall](this[_moveWall](this.speed/10,turn),excludedWalls)){
turn=undefined;
}
}
}
return turn;
},

"public",function turn(){
if(this[_cycleWall].x2-this[_cycleWall].x1<1&&
this[_cycleWall].y2-this[_cycleWall].y1<1){

return;
}
var turn=this[_keyMap]?this[_turnQueue].shift():this.computeTurn();
if(turn){
this[_position].x=Math.round(this[_position].x);
this[_position].y=Math.round(this[_position].y);
this.update();
this[_position].dir=this[_position].dir[turn];
this[_lastWall].x1=this[_lastWall].x2=this[_position].x;
this[_lastWall].y1=this[_lastWall].y2=this[_position].y;
this[_prevCycleWall]=this[_cycleWall];
this[_cycleWall]=this[_createCycleWall]();
this[_position].move(0.1);
this.update();
this[_updateCycleHeadWall]();
}
},

"public",function checkCrashed(){
if(this.isDead())
return;
var excludedWalls=new joo.util.HashSet([this[_cycleHeadWall],this[_cycleWall],this[_prevCycleWall]]);
var crashWall=this[_findCrashWall](this[_lastWall],excludedWalls);
if(crashWall){

var Direction=Direction_();
switch(this[_position].dir){
case Direction.NORTH:
this[_position].y=crashWall.y1-0.1;
break;
case Direction.EAST:
this[_position].x=crashWall.x1-0.1;
break;
case Direction.SOUTH:
this[_position].y=crashWall.y2+0.1;
break;
default:
this[_position].x=crashWall.x2+0.1;
}
++this.deaths;
this.update();
this[_opacity]-=0.1;
}
},

"private",function findCrashWall(lastWall,excludedWalls){

var walls=this[_grid].walls;
for(var i=0;i<walls.length;++i){
var wall=walls[i];
if(!excludedWalls.contains(wall)&&lastWall.intersects(wall)){
return walls[i];
}
}
return undefined;
},

"public",function isDead(){
return this[_opacity]<this[_grid].startOpacity;
},

"public",function onkeydown(event){
var keyMap=this[_keyMap];
if(keyMap){
switch(event.keyCode){
case keyMap.left:this[_turnQueue].push("turnLeft");break;
case keyMap.right:this[_turnQueue].push("turnRight");break;
case keyMap.faster:
if(!this.fasterDown){
this.fasterDown=new Date().getTime();
}
break;
case keyMap.slower:
if(!this.slowerDown){
this.slowerDown=new Date().getTime();
}
break;
default:return true;
}
return false;
}
return true;
},

"public",function onkeyup(event){
var keyMap=this[_keyMap];
if(keyMap){
switch(event.keyCode){
case keyMap.faster:
this[_updateSpeed](new Date().getTime());
this.fasterDown=undefined;
break;
case keyMap.slower:
this[_updateSpeed](new Date().getTime());
this.slowerDown=undefined;
break;
default:return true;
}
return false;
}
return true;
},

"private var",{grid: undefined},
"private var",{name: undefined},
"private var",{color: undefined},
"private var",{opacity: undefined},
"private var",{position: undefined},
"private var",{turnQueue: undefined},
"private var",{keyMap: undefined},
"private var",{lastWall: undefined},
"private var",{cycleWall: undefined},
"private var",{prevCycleWall: undefined},
"private var",{cycleHeadWall: undefined},
"public var",{speed: undefined},
"public var",{deaths:0},
"public var",{wins:0},
]});joo.Class.prepare("package net.jangaron",

"public class Line",function($jscContext){with(net.jangaron)with($jscContext)return[

"public",function Line(viewPort,y){
this.viewPort=viewPort;
this[_y]=y;
this.HALF_LINE_WIDTH=viewPort.GRID_LINE_WIDTH/2;
},

"public",function render(offset){
this.update(offset,true);
},

"public",function update(offset,create){
var y=this[_y]-offset;
var y1=this.viewPort.getY(y+this.HALF_LINE_WIDTH);
var y2=this.viewPort.getY(y-this.HALF_LINE_WIDTH)+1;
if(create){
this[_div]=this.viewPort.addRect(joo.css.Color_().WHITE,
0,this.viewPort.HORIZON+y1,
this.viewPort.WIDTH,this.viewPort.HORIZON+y2);
}else{
this[_div].style.top=Math.round(this.viewPort.HORIZON+y1)+"px";
this[_div].style.height=Math.round(y2-y1)+"px";
}
},

"private var",{y: undefined},
"private var",{div: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class SlantLine",function($jscContext){with(net.jangaron)with($jscContext)return[

"public",function SlantLine(viewPort,gx){
this.viewPort=viewPort;
this.gx=gx;
},

"private",function clippedPoint(x){
var vp=this.viewPort;
x=Math.abs(x);
if(x>vp.SIDE){
return{x:vp.SIDE,y:Math.round(vp.HORIZON*(1+vp.SIDE/x))};
}else{
return{x:x,y:vp.HEIGHT};
}
},

"public",function update(index){
var vp=this.viewPort;
var HALF_LINE_WIDTH=vp.GRID_LINE_WIDTH/2*vp.SIDE/vp.GRID_LINE_DISTANCE;
var gx=(this.gx+index/vp.GRID_LINE_DISTANCE)*vp.SIDE;
if(gx===0){
vp.addSlant(joo.css.Color_().WHITE,
vp.SIDE,vp.HORIZON,vp.SIDE-HALF_LINE_WIDTH,vp.HEIGHT,
vp.SIDE,vp.HORIZON,vp.SIDE+HALF_LINE_WIDTH,vp.HEIGHT);
}else{
var left=this[_clippedPoint](gx-HALF_LINE_WIDTH);
var right=this[_clippedPoint](gx+HALF_LINE_WIDTH);

var off;
if(gx<0){
vp.addSlant(joo.css.Color_().WHITE,
0,vp.HORIZON,0,right.y,
vp.SIDE,vp.HORIZON,vp.SIDE-right.x,right.y);
off=left.y<vp.HEIGHT?1:0;
vp.addSlant(joo.css.Color_().BLACK,
0,vp.HORIZON-off,0,left.y-off,
vp.SIDE,vp.HORIZON-off,vp.SIDE-left.x,left.y-off);
}else{
vp.addSlant(joo.css.Color_().WHITE,
vp.SIDE,vp.HORIZON,vp.SIDE+left.x,left.y,
vp.WIDTH,vp.HORIZON,vp.WIDTH,left.y);
off=right.y<vp.HEIGHT?1:0;
vp.addSlant(joo.css.Color_().BLACK,
vp.SIDE,vp.HORIZON-off,vp.SIDE+right.x,right.y-off,
vp.WIDTH,vp.HORIZON-off,vp.WIDTH,right.y-off);
}
}
},
]});joo.Class.prepare("package net.jangaron",

"public class Wall",function($jscContext){with(net.jangaron)with($jscContext)return[

"private static const",{DEBUG:false},

"public",function Wall(color,p1,p2,opacityHolder){
this[_super]();
this.color=color;
if(typeof p1=="number"){
this.x1=this.x2=p1;
}else{
this.x1=p1[0];
this.x2=p1[1];
}
if(typeof p2=="number"){
this.y1=this.y2=p2;
}else{
this.y1=p2[0];
this.y2=p2[1];
}
this[_opacityHolder]=opacityHolder;
if(DEBUG){
this.check();
}
},

"public",function getOpacity(){
return this[_opacityHolder]?this[_opacityHolder].getOpacity():1;
},

"public",function check(){
if(!(this.x1<=this.x2&&this.y1<=this.y2)){
throw new Error("Wall: IllegalArgument: "+this);
}
},

"public",function intersects(wall){
return Math.max(this.x1,wall.x1)<=Math.min(this.x2,wall.x2)
&&Math.max(this.y1,wall.y1)<=Math.min(this.y2,wall.y2);
},

"public",function toString(){
return["[",this.x1,",",this.y1,"|",this.x2,",",this.y2,"]"].join("");
},

"public var",{color: undefined},
"public var",{brightColor: undefined},
"public var",{darkColor: undefined},
"private var",{opacityHolder: undefined},
"public var",{x1: undefined},
"public var",{y1: undefined},
"public var",{x2: undefined},
"public var",{y2: undefined},
"public var",{relativeWalls:function(){return {};}},
]});joo.Class.prepare("package net.jangaron",












"public class RelativeWall",function($jscContext){with(net.jangaron)with($jscContext)return[

"private static const",{DEBUG:false},

"public",function RelativeWall(wall){
this[_super]();
this.wall=wall;
this.wallProjection=new Trapez();
},

"public",function update(pos){
this[_maybeHides]=[];
this.hiddenByCnt=0;
this.zIndex=10000;
var wall=this.wall;
var rx1=wall.x1-pos.x;
var ry1=wall.y1-pos.y;
var rx2=wall.x2-pos.x;
var ry2=wall.y2-pos.y;
var Direction=Direction_();
switch(pos.dir){
case Direction.NORTH:
return this.set(rx1,ry1,rx2,ry2);
case Direction.SOUTH:
return this.set(-rx2,-ry2,-rx1,-ry1);
case Direction.EAST:
if(ry1===ry2)
return this.set(-ry1,rx1,-ry2,rx2);
return this.set(-ry2,rx2,-ry1,rx1);
case Direction.WEST:
if(rx1===rx2)
return this.set(ry1,-rx1,ry2,-rx2);
return this.set(ry2,-rx2,ry1,-rx1);
}
throw"Unknown direction "+pos.dir;
},

"public",function set(x1,y1,x2,y2){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
if(DEBUG&&!(x1<=x2&&y1<=y2)){
throw"IllegalArgument:"+this.toString1();
}
this.mayHidePoint=

this.x1==0&&this.x2==0?this[_mayHidePointNever]


:this.x1<0&&this.x2>0?this[_mayHidePointMiddle]


:this.x2<=0?this[_mayHidePointLeft]

:this[_mayHidePointRight];
return this;
},






function mayHidePoint(x,y){

},

"private",function mayHidePointNever(x,y){
return false;
},

"private",function mayHidePointMiddle(x,y){
return y>this.y1||y==this.y1&&this.x1<=x&&x<=this.x2;
},

"private",function mayHidePointLeft(x,y){

return x<this.x2&&(y>this.y1);
},

"private",function mayHidePointRight(x,y){

return x>this.x1&&(y>this.y1);
},





"public",function updateMaybeHides(other,unhiddenElements){
if(this.mayHidePoint(other.x1,other.y1)||this.mayHidePoint(other.x2,other.y2)){
this[_maybeHides].push(other);
if(other.hiddenByCnt++ ==0){
unhiddenElements.remove(other);
}
return true;
}
return false;
},

"public",function removedFromUnhidden(unhiddenElements){
this[_maybeHides].forEach(function(other){
if(--other.hiddenByCnt==0){
unhiddenElements.add(other);
}
});
},

"public",function toString1(){
return"["+[this.wall.color,this.wall.getOpacity(),this.x1,this.y1,this.x2,this.y2].join(",")+"]";
},

"public var",{wall: undefined},
"public var",{wallProjection: undefined},
"public var",{x1: undefined},
"public var",{y1: undefined},
"public var",{x2: undefined},
"public var",{y2: undefined},
"private var",{maybeHides:function(){return [];}},
"public var",{hiddenByCnt:0},
"public var",{zIndex:10000},
]});joo.Class.prepare("package net.jangaron",

"public class ZBuffer",function($jscContext){with(net.jangaron)with($jscContext)return[

"public",function ZBuffer(){
this[_intervals]=[];
},

"public",function isIntervalVisible(a,b){
var intervals=this[_intervals];
var aIndex=intervals.binarySearch(a);

return aIndex<0||b>intervals[aIndex].b;
},

"public",function addInterval(a,b){
var intervals=this[_intervals];
var aIndex=intervals.binarySearch(a);
if(aIndex>=0){
var aInterval=intervals[aIndex];

if(b<=aInterval.b){
return false;
}
a=aInterval.a;
}else{
aIndex=-aIndex-1;
}
var bIndex=intervals.binarySearch(b);
if(bIndex>=0){
b=intervals[bIndex].b;
}else{
bIndex=-bIndex-2;
}
var newInterval=new joo.util.Interval(a,b);

intervals.splice(aIndex,bIndex-aIndex+1,newInterval);
return true;
},

"public",function toString(){
return this[_intervals].join(" ");
},

"private var",{intervals:function(){return [];}},
]});joo.Class.prepare("package net.jangaron",

"public class ViewPort extends joo.html.slant.SlantCanvas",function($jscContext){with(net.jangaron)with($jscContext)return[

"private static const",{COLOR_BY_DIR:function(){return {N:"brighter",S:"darker"};}},
"private static const",{NUMBER_OF_GRID_LINES:20},
"private static const",{DEBUG:false},

"public static",function create(document,style){
if(!document){
var gameWindow=window.open();
document=new joo.html.Document(gameWindow);
document.write("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
+"<html xmlns='http://www.w3.org/1999/xhtml'>"

+"<head><title>Jangaron</title><style>"
+"html { height: 100%; overflow: hidden; }"
+"body { overflow: hidden; padding: 0; margin: 0; border: 0; width: 100%; height: 100%; background-color: black; }"
+".canvas { display: block; overflow: hidden; margin: 0; padding: 0; left: 0; background-color: black; position: absolute; width: 100%; height: 100%; }"
+".slant { position: absolute; padding: 0; margin: 0; line-height: 0; width: 0; height: 0; border-style: solid; border-color: transparent; border-width: 0; }"
+"</style>"
+"<script>"
+"window.opener.joo$prepareWindow(window);"
+"</script>"
+"</head>"
+"<body></body>"
+"</html>");
document.close();
}
var div=document.createElement("div").setStyle(style);
document.getBody().appendChild(div);
return new ViewPort(document,div.getPeer());
},

"public",function ViewPort(document,viewPortId){
this[_super](document,viewPortId);
this.GRID_LINE_DISTANCE=10;
this.GRID_LINE_WIDTH=1;
this.VIEW_DEPTH=1e10;
this[_initDimensions]();
this[_createGridLines]();
document.getWindow().addEventListener("resize",function(){
this[_sizeDirty]=true;
}.bind(this),false);
},

"private",function initDimensions(){
this[_sizeDirty]=false;
var dim=this.getDimensions();
this.WIDTH=dim.width;
this.HEIGHT=dim.height;
this.HORIZON=Math.round(this.HEIGHT/2);
this.SIDE=Math.round(this.WIDTH/2);
this.X_FACTOR=this.SIDE/this.HORIZON/this.GRID_LINE_DISTANCE;
this.Y_FACTOR=this.HORIZON*this.GRID_LINE_DISTANCE;
this.leftOffset=undefined;
},

"public",function setLightCycle(lightCycle){
this[_lightCycle]=lightCycle;
this[_createHUD]();
this[_hudRewindIndex]=this.getItemCount();
},

"public",function setGrid(grid){
this.grid=grid;
},

"public",function getPosition(){
return this[_lightCycle].getPosition();
},

"public",function getY(yIndex){
return yIndex<0?this.HORIZON:this.Y_FACTOR/(this.GRID_LINE_DISTANCE+yIndex);
},

"public",function project(rw){
var y1=this.getY(Math.max(rw.y1,0));
var y2;
var x1;
var x2;
var xFactor=this.X_FACTOR;
var dir=this.getPosition().dir;
var isXWall=rw.y1==rw.y2;
if(isXWall||rw.x1==0&&rw.x2==0){
y2=y1;
if(isXWall){
xFactor*=y1;
x1=rw.x1*xFactor;
x2=rw.x2*xFactor;
if(x1>this.SIDE||x2<-this.SIDE){
return false;
}

x1=Math.max(x1,-this.SIDE-1);
x2=Math.min(x2,this.SIDE+1);
}else{
x1=0;
x2=1;
}
}else{
dir=rw.x1>0?dir.turnRight:dir.turnLeft;
y2=this.getY(rw.y2);
xFactor*=rw.x1;
x2=xFactor*y2;
if(Math.abs(x2)>this.SIDE){
return false;
}
x1=xFactor*y1;
var x1abs=Math.abs(x1);
if(x1abs>this.SIDE||rw.y1<=0&&rw.y2>0&&x1abs<this.SIDE){

var side=rw.x1<0?-this.SIDE-1:this.SIDE+1;
y1=side*y1/x1;
x1=side;
}
}
var color=rw.wall.color;
var colorChange=COLOR_BY_DIR[dir];
if(colorChange){
color=color[colorChange]();
}
rw.wallProjection.set(color,x1,y1,x2,y2);
return true;
},

"private",function insertRelativeWall(relativeWalls,newRelativeWall,unhiddenRelativeWalls){


var isUnhidden=true;
for(var ws=0;ws<relativeWalls.length;++ws){
var relativeWall=relativeWalls[ws];
if(newRelativeWall.wallProjection.intersects(relativeWall.wallProjection)){
if(!newRelativeWall.updateMaybeHides(relativeWall,unhiddenRelativeWalls)){
if(relativeWall.updateMaybeHides(newRelativeWall,unhiddenRelativeWalls)){
isUnhidden=false;
}
}
}
}
relativeWalls.push(newRelativeWall);
if(isUnhidden){
unhiddenRelativeWalls.add(newRelativeWall);
}
},

"public",function update(){
if(this[_sizeDirty]){
this[_initDimensions]();
this.rewind(this[_hudRewindIndex]);
}
this[_updateHUD]();
var position=this.getPosition();
var offset=position.getOffset(this.GRID_LINE_DISTANCE);
if(this[_updateSlantLines]()){
this.lines.broadcast("render",offset);
this[_lineRewindIndex]=this.getItemCount();
}else{
this.lines.broadcast("update",offset);
}
var furthestLine=this.getItem(this[_lineRewindIndex]-1).style;
furthestLine.height=(parseInt(furthestLine.top)+parseInt(furthestLine.height)-this.HORIZON)+"px";
furthestLine.top=this.HORIZON+"px";

var relativeWalls=[];
var unhiddenRelativeWalls=new joo.util.HashSet();
var walls=this.grid.walls;
var relativeWall;
var viewPortKey="_"+this.hashCode();
for(var w=0;w<walls.length;++w){
var relativeWallsByViewPort=walls[w].relativeWalls;
relativeWall=relativeWallsByViewPort[viewPortKey];
if(!relativeWall){
relativeWallsByViewPort[viewPortKey]=relativeWall=new RelativeWall(walls[w]);
}
relativeWall.update(position);
if((relativeWall.y1>=0||relativeWall.y2>=0)&&(relativeWall.y1<this.VIEW_DEPTH||relativeWall.y2<this.VIEW_DEPTH)){

if(this.project(relativeWall)){
this[_insertRelativeWall](relativeWalls,relativeWall,unhiddenRelativeWalls);
}
}
}
var zIndex=10000;
var zBuffer=new ZBuffer();
var slant;
var WHITE=new joo.css.Color(245,245,245);
while(relativeWall=unhiddenRelativeWalls.pop()){
relativeWall.removedFromUnhidden(unhiddenRelativeWalls);
if(DEBUG&&!relativeWalls.remove(relativeWall)){
throw"Element not present: "+relativeWall+" in ["+relativeWalls.join(",")+"]";
}
var wallProjection=relativeWall.wallProjection;
var x1=this.SIDE+wallProjection.x1;
var x2=this.SIDE+wallProjection.x2;

var opacity=relativeWall.wall.getOpacity();
var zBufferMethod=opacity==1?"addInterval":"isIntervalVisible";
if(zBuffer[zBufferMethod](x1,x2)){
var y1=wallProjection.y1;
var y2=wallProjection.y2;
var opaque=opacity==1?1:0;
if(y1==y2){
slant=this.addRect(wallProjection.color,
x1,this.HORIZON-y1,x2,this.HORIZON+y1,
zIndex--,opacity,WHITE,-opaque);
}else{
var x1border=x1+opaque;
var x2border=x2-opaque;
if(y1>this.HORIZON){



x1=x2+(x2-x1)*(1/(y2-y1))*(this.HORIZON-y2);
y1=this.HORIZON;
this.addRect(wallProjection.color,
0,0,x1,this.HEIGHT,
zIndex,opacity);
x1border=x1;
}else if(y2>this.HORIZON){


x2=x1+(x2-x1)*(1/(y2-y1))*(this.HORIZON-y1);
y2=this.HORIZON;
this.addRect(wallProjection.color,
x2,0,this.WIDTH,this.HEIGHT,
zIndex,opacity);
x2border=x2;
}
if(x1border<=x2border){
slant=this.addSlant(wallProjection.color,
x1border,this.HORIZON-y1+opaque,x1border,this.HORIZON+y1-opaque,
x2border,this.HORIZON-y2+opaque,x2border,this.HORIZON+y2-opaque,
zIndex--,opacity);
}
if(opaque){
slant=this.addSlant(WHITE,
x1,this.HORIZON-y1,x1,this.HORIZON+y1,
x2,this.HORIZON-y2,x2,this.HORIZON+y2,
zIndex--,opacity);
}
}
if(DEBUG){
slant.id="rw"+relativeWall.hashCode();slant.rw=relativeWall;
}
}else if(DEBUG){
console.debug("hidden wall: "+relativeWall.toString1());
}
}
if(DEBUG&&relativeWalls.length!=0){
throw"Some walls not rendered: ["+relativeWalls.join(",")+"]";
}
this.clearRest();
},

"private",function createGridLines(){
this.slantLines=[];
for(var li=0;li<NUMBER_OF_GRID_LINES/2;++li){
this.slantLines.push(new SlantLine(this,-li));
this.slantLines.push(new SlantLine(this,li+1));
}
this.lines=[];
for(i=1;i<=NUMBER_OF_GRID_LINES;++i){
this.lines.push(new Line(this,i*this.GRID_LINE_DISTANCE));
}
},

"private",function updateSlantLines(){
var leftOffset=this.getPosition().getLeftOffset(this.GRID_LINE_DISTANCE);
if(leftOffset!=this.leftOffset){
this.leftOffset=leftOffset;
this.rewind(this[_hudRewindIndex]);
this.slantLines.broadcast("update",this.GRID_LINE_DISTANCE-leftOffset);
return true;
}else{
this.rewind(this[_lineRewindIndex]);
return false;
}
},

"private",function createHUD(){
var document=this.ownerDocument;

var hud=document.createElement("div").setAttributes({className:"hud",style:{
color:joo.css.Color_().WHITE,backgroundColor:this[_lightCycle].getColor(),
zIndex:"20000",opacity:".75"
}});
hud.appendChild(document.createTextNode(""));
this.appendChild(hud);

var speedContainer=document.createElement("div").setAttributes({className:"speed",style:{
backgroundColor:this[_lightCycle].getColor(),
zIndex:"20000",opacity:".75"
}});
var speedIndicator=document.createElement("div").setStyle({
backgroundColor:this[_lightCycle].getColor().darker()
});
speedIndicator.appendChild(document.createTextNode(String.fromCharCode(160)));
speedContainer.appendChild(speedIndicator);
var speed=document.createElement("div").setStyle({
color:joo.css.Color_().WHITE,position:"absolute"
});
speed.appendChild(document.createTextNode("0"));
speedContainer.appendChild(speed);
this.appendChild(speedContainer);
this.renderedSpeed=0;

},

"private",function updateHUD(){
var position=this.getPosition();
this.getItem(0).firstChild.data=[this[_lightCycle].getName(),
"| x",Math.round(position.x).format(3),"y",Math.round(position.y).format(3),position.dir.getCompassArrow()].join(" ");

var renderedSpeed=Math.round(this[_lightCycle].speed);
if(renderedSpeed!=this.renderedSpeed){
var speedContainer=this.getItem(1);
var speedIndicator=speedContainer.firstChild;
var maxSpeed=this[_lightCycle].maxSpeed;
var widthPercent=Math.round(100*renderedSpeed/maxSpeed);
speedIndicator.style.width=widthPercent+"%";
speedContainer.lastChild.firstChild.data=String(renderedSpeed);
this.renderedSpeed=renderedSpeed;
}
},

"private var",{lightCycle: undefined},
"private var",{sizeDirty: undefined},
"private var",{hudRewindIndex: undefined},
"private var",{lineRewindIndex: undefined},
]});joo.Class.prepare("package net.jangaron",

"public class Grid",function($jscContext){with(net.jangaron)with($jscContext)return[

"public static const",{MIN_INTERVAL_MS:30},

"public static",function main(width,height,startOpacity,obstacleCnt,viewPortDefs){
var STYLES=
[[{height:"60%",top:"20%",width:"100%",left:"0%"}],
[{height:"47%",top:"4.5%",width:"100%",left:"0%"},
{height:"47%",top:"52.5%",width:"100%",left:"0%"}],
[{height:"47%",top:"4.5%",width:"100%",left:"0%"},
{height:"47%",top:"52.5%",width:"49%",left:"0%"},
{height:"49%",top:"52.5%",width:"49%",left:"50.5%"}],
[{height:"47%",top:"4.5%",width:"49%",left:"0%"},
{height:"47%",top:"4.5%",width:"49%",left:"50.5%"},
{height:"47%",top:"52.5%",width:"49%",left:"0%"},
{height:"47%",top:"52.5%",width:"49%",left:"50.5%"}]
];
var startPositions=[new Position(width/2,10,Direction_().NORTH),
new Position(width/2,height-10,Direction_().SOUTH),
new Position(width/2-10,10,Direction_().NORTH),
new Position(width/2+10,height-10,Direction_().SOUTH),
new Position(width/2+10,10,Direction_().NORTH),
new Position(width/2-10,height-10,Direction_().SOUTH)];
var viewPortCnt=0;
for(var i=0;i<viewPortDefs.length;++i){
var viewPortDef=viewPortDefs[i];
if(viewPortDef.user||viewPortDef.visible){
++viewPortCnt;
}
}
var styles=STYLES[viewPortCnt-1];
var gameDocument=joo.html.Document_().INSTANCE;
var lightCycles=[];
var viewPorts=[];
for(var i=0;i<viewPortDefs.length;++i){
var viewPortDef=viewPortDefs[i];
var lightCycle=new LightCycle(viewPortDef.name,viewPortDef.color,startPositions[i],
viewPortDef.user?viewPortDef.keyMap:null,
viewPortDef.minSpeed,viewPortDef.maxSpeed);
lightCycles.push(lightCycle);
if(viewPortDef.user||viewPortDef.visible){
var style=viewPortDef.style?viewPortDef.style:styles[viewPorts.length];
var viewPort=ViewPort_().create(gameDocument,style);
viewPort.setLightCycle(lightCycle);
viewPorts.push(viewPort);
}
}
grid=new Grid(width,height,startOpacity,obstacleCnt,lightCycles,viewPorts);
},

"public",function Grid(width,height,startOpacity,obstacleCnt,lightCycles,viewPorts){
this[_super]();
this[_lightCycles]=lightCycles;
this[_lightCycles].broadcast("setGrid",this);
this[_viewPorts]=viewPorts;
this[_viewPorts].broadcast("setGrid",this);
this[_stepExecutor]=new joo.util.TimedExecutor(this[_doStep].bind(this),MIN_INTERVAL_MS);
this.width=width;
this.height=height;
this.startOpacity=startOpacity;
this.obstacleCnt=obstacleCnt;

var document=joo.html.Document_().INSTANCE;
this[_createStatusLine](document);
var htmlElement=document.getDocumentElement();
htmlElement.addEventListener("keydown",this[_onkeydown].bind(this),true);
htmlElement.addEventListener("keyup",this[_onkeyup].bind(this),true);
window.addEventListener("blur",this[_onblur].bind(this),true);
window.addEventListener("focus",this[_onfocus].bind(this),true);
this[_init]();
},

"private",function createStatusLine(document){
this[_statusLine]=document.createElement("div").setClassName("status");
document.getBody().appendChild(this[_statusLine]);
this[_statusLines]={};
this[_lightCycles].forEach(this[_addToStatusLine],this);
this[_updateStatusLine]=this[_updateStatusLine].bind(this);
},

"private",function addToStatusLine(lightCycle){
var document=joo.html.Document_().INSTANCE;
var lightCycleStatus=this[_statusLines][lightCycle]=
document.createElement("div").setStyle({
color:joo.css.Color_().WHITE,backgroundColor:lightCycle.getColor()
});
lightCycleStatus.appendChild(document.createTextNode(lightCycle.getName()));
this[_statusLine].appendChild(lightCycleStatus);
},

"public",function getLightCycles(){
return this[_lightCycles];
},

"private",function init(){
this[_keyQueue]=[];
this.walls=[];
var GRAY=joo.css.Color_().GRAY;
[new Wall(GRAY,0,[0,this.height]),
new Wall(GRAY,this.width,[0,this.height]),
new Wall(GRAY,[0,this.width],0),
new Wall(GRAY,[0,this.width],this.height)].forEach(this.addWall,this);
this[_createRandomWalls](this.obstacleCnt);
this[_livingCount]=this[_lightCycles].length;
this[_livingUserCount]=0;
this[_userLightCycles]=[];
for(var lc=0;lc<this[_livingCount];++lc){
if(this[_lightCycles][lc].isUser()){
this[_userLightCycles].push(this[_lightCycles][lc]);
}
}
this[_livingUserCount]=this[_userLightCycles].length;
this[_lightCycles].broadcast("init");
if(window.parent===window){
if(!this[_stepExecutor].start()){

}
}else{
this[_doStep](1);
}
},

"private",function createRandomWalls(number){
var GRAY=joo.css.Color_().GRAY;
for(var i=0;i<number;++i){
var wall;
do{
var x1=Math.round(Math.random()*this.width);
var x2=Math.round(Math.random()*this.width);
var y1=Math.round(10+Math.random()*(this.height-20));
var y2=Math.round(10+Math.random()*(this.height-20));
wall=Math.random()>0.5
?new Wall(GRAY,[Math.min(x1,x2),Math.max(x1,x2)],y1)
:new Wall(GRAY,x1,[Math.min(y1,y2),Math.max(y1,y2)]);
}while(this[_hasIntersectingWall](wall));

if(wall.x1==wall.x2){
this.addWall(new Wall(GRAY,wall.x1-.5,[wall.y1,wall.y2]));
this.addWall(new Wall(GRAY,wall.x1+.5,[wall.y1,wall.y2]));
this.addWall(new Wall(GRAY,[wall.x1-.5,wall.x1+.5],y1));
this.addWall(new Wall(GRAY,[wall.x1-.5,wall.x1+.5],y2));
}else{
this.addWall(new Wall(GRAY,[wall.x1,wall.x2],wall.y1-.5));
this.addWall(new Wall(GRAY,[wall.x1,wall.x2],wall.y1+.5));
this.addWall(new Wall(GRAY,x1,[wall.y1-.5,wall.y1+.5]));
this.addWall(new Wall(GRAY,x2,[wall.y1-.5,wall.y1+.5]));
}
}
},

"private",function hasIntersectingWall(wall){
for(var i=0;i<this.walls.length;++i){
if(this.walls[i].intersects(wall)){
return true;
}
}
return false;
},

"public",function addWall(wall){
this.walls.push(wall);
},

"private static",function isAlive(lightCycle){
return!lightCycle.isDead();
},

"private",function doStep(passedMillis){
this[_updateFrameRate](passedMillis);
this[_lightCycles].broadcast("move",passedMillis);
this[_viewPorts].broadcast("update");
this[_lightCycles].broadcast("update");
this[_lightCycles].broadcast("checkCrashed");
this[_lightCycles].forEach(this[_updateStatusLine]);
if(this[_livingCount]==1||this[_livingUserCount]==0){
this[_viewPorts].broadcast("update");
var survivors=this[_lightCycles].filter(isAlive);
var survivorName;
if(survivors.length>0){

var survivor=survivors[Math.floor(Math.random()*survivors.length)];
++survivor.wins;
survivors.forEach(function(s){if(s!==survivor)++s.deaths;});
survivorName=survivor.getName();
}else{
survivorName="Nobody";
}

window.setTimeout((function(){
window.alert("Game over!\n"+survivorName+" has won!");
this[_init]();
}).bind(this),0);
return false;
}else{
this[_lightCycles].broadcast("turn");
}
},

"private",function updateStatusLine(lightCycle){
var statusLine=this[_statusLines][lightCycle];

statusLine.setStyle({opacity:String(lightCycle.getOpacity()*.75)});

statusLine.getPeer().firstChild.data=[lightCycle.getName(),
"| w",lightCycle.wins.format(2),
"| d",lightCycle.deaths.format(2)].join(" ");
},

"private",function updateFrameRate(passedMillis){
this[_passedMillisSum]+=passedMillis;
++this[_renderedFrames];
if(this[_passedMillisSum]>=1000){
window.status=Math.round(this[_renderedFrames]*100000/this[_passedMillisSum])/100;
this[_passedMillisSum]=0;
this[_renderedFrames]=0;
}
},

"public",function removeLightCycle(lightCycle){
for(var i=0;i<this.walls.length;){
var w=this.walls[i];
if(w.lightCycle===lightCycle){
this.walls.splice(i,1);
}else{
++i;
}
}
--this[_livingCount];
if(lightCycle.isUser()){
--this[_livingUserCount];
}
},

"private",function onkeydown(event){
switch(event.keyCode){
case 32:
if(this[_stepExecutor].isRunning()){
this[_stepExecutor].stop();
}else{
this[_stepExecutor].start();
}
event.preventDefault();
return false;
}
for(var lc=this[_userLightCycles].length-1;lc>=0;--lc){
var lightCycle=this[_userLightCycles][lc];
if(!lightCycle.onkeydown(event)){
event.preventDefault();
return false;
}
}
return true;
},

"private",function onkeyup(event){
for(var lc=this[_userLightCycles].length-1;lc>=0;--lc){
var lightCycle=this[_userLightCycles][lc];
if(!lightCycle.onkeyup(event)){
event.preventDefault();
return false;
}
}
return true;
},

"private",function onblur(){
this[_stepExecutor].stop();
},

"private",function onfocus(){
this[_stepExecutor].start();
},

"public var",{width: undefined},
"public var",{height: undefined},
"public var",{obstacleCnt: undefined},
"public var",{startOpacity:1},
"private var",{lightCycles: undefined},
"private var",{userLightCycles: undefined},
"private var",{livingCount: undefined},
"private var",{livingUserCount: undefined},
"private var",{viewPorts: undefined},
"private var",{stepExecutor: undefined},
"private var",{keyQueue: undefined},
"public var",{walls: undefined},
"private var",{passedMillisSum:0},
"private var",{renderedFrames:0},
"private var",{statusLine: undefined},
"private var",{statusLines: undefined},
]});