var pas = {};

var rtl = {

  quiet: false,
  debug_load_units: false,
  debug_rtti: false,

  debug: function(){
    if (rtl.quiet || !console || !console.log) return;
    console.log(arguments);
  },

  error: function(s){
    rtl.debug('Error: ',s);
    throw s;
  },

  warn: function(s){
    rtl.debug('Warn: ',s);
  },

  hasString: function(s){
    return rtl.isString(s) && (s.length>0);
  },

  isArray: function(a) {
    return Array.isArray(a);
  },

  isFunction: function(f){
    return typeof(f)==="function";
  },

  isModule: function(m){
    return rtl.isObject(m) && rtl.hasString(m.$name) && (pas[m.$name]===m);
  },

  isImplementation: function(m){
    return rtl.isObject(m) && rtl.isModule(m.$module) && (m.$module.$impl===m);
  },

  isNumber: function(n){
    return typeof(n)==="number";
  },

  isObject: function(o){
    var s=typeof(o);
    return (typeof(o)==="object") && (o!=null);
  },

  isString: function(s){
    return typeof(s)==="string";
  },

  getNumber: function(n){
    return typeof(n)==="number"?n:NaN;
  },

  getChar: function(c){
    return ((typeof(c)==="string") && (c.length===1)) ? c : "";
  },

  getObject: function(o){
    return ((typeof(o)==="object") || (typeof(o)==='function')) ? o : null;
  },

  isPasClass: function(type){
    return (rtl.isObject(type) && type.hasOwnProperty('$classname') && rtl.isObject(type.$module));
  },

  isPasClassInstance: function(type){
    return (rtl.isObject(type) && rtl.isPasClass(type.$class));
  },

  hexStr: function(n,digits){
    return ("000000000000000"+n.toString(16).toUpperCase()).slice(-digits);
  },

  m_loading: 0,
  m_loading_intf: 1,
  m_intf_loaded: 2,
  m_loading_impl: 3, // loading all used unit
  m_initializing: 4, // running initialization
  m_initialized: 5,

  module: function(module_name, intfuseslist, intfcode, impluseslist, implcode){
    if (rtl.debug_load_units) rtl.debug('rtl.module name="'+module_name+'" intfuses='+intfuseslist+' impluses='+impluseslist+' hasimplcode='+rtl.isFunction(implcode));
    if (!rtl.hasString(module_name)) rtl.error('invalid module name "'+module_name+'"');
    if (!rtl.isArray(intfuseslist)) rtl.error('invalid interface useslist of "'+module_name+'"');
    if (!rtl.isFunction(intfcode)) rtl.error('invalid interface code of "'+module_name+'"');
    if (!(impluseslist==undefined) && !rtl.isArray(impluseslist)) rtl.error('invalid implementation useslist of "'+module_name+'"');
    if (!(implcode==undefined) && !rtl.isFunction(implcode)) rtl.error('invalid implementation code of "'+module_name+'"');

    if (pas[module_name])
      rtl.error('module "'+module_name+'" is already registered');

    var module = pas[module_name] = {
      $name: module_name,
      $intfuseslist: intfuseslist,
      $impluseslist: impluseslist,
      $state: rtl.m_loading,
      $intfcode: intfcode,
      $implcode: implcode,
      $impl: null,
      $rtti: Object.create(rtl.tSectionRTTI)
    };
    module.$rtti.$module = module;
    if (implcode) module.$impl = {
      $module: module,
      $rtti: module.$rtti
    };
  },

  exitcode: 0,

  run: function(module_name){
  
    function doRun(){
      if (!rtl.hasString(module_name)) module_name='program';
      if (rtl.debug_load_units) rtl.debug('rtl.run module="'+module_name+'"');
      rtl.initRTTI();
      var module = pas[module_name];
      if (!module) rtl.error('rtl.run module "'+module_name+'" missing');
      rtl.loadintf(module);
      rtl.loadimpl(module);
      if (module_name=='program'){
        if (rtl.debug_load_units) rtl.debug('running $main');
        var r = pas.program.$main();
        if (rtl.isNumber(r)) rtl.exitcode = r;
      }
    }
    
    if (rtl.showUncaughtExceptions) {
      try{
        doRun();
      } catch(re) {
        var errMsg = re.hasOwnProperty('$class') ? re.$class.$classname : '';
	    errMsg +=  ((errMsg) ? ': ' : '') + (re.hasOwnProperty('fMessage') ? re.fMessage : re);
        alert('Uncaught Exception : '+errMsg);
        rtl.exitCode = 216;
      }
    } else {
      doRun();
    }
    return rtl.exitcode;
  },

  loadintf: function(module){
    if (module.$state>rtl.m_loading_intf) return; // already finished
    if (rtl.debug_load_units) rtl.debug('loadintf: "'+module.$name+'"');
    if (module.$state===rtl.m_loading_intf)
      rtl.error('unit cycle detected "'+module.$name+'"');
    module.$state=rtl.m_loading_intf;
    // load interfaces of interface useslist
    rtl.loaduseslist(module,module.$intfuseslist,rtl.loadintf);
    // run interface
    if (rtl.debug_load_units) rtl.debug('loadintf: run intf of "'+module.$name+'"');
    module.$intfcode(module.$intfuseslist);
    // success
    module.$state=rtl.m_intf_loaded;
    // Note: units only used in implementations are not yet loaded (not even their interfaces)
  },

  loaduseslist: function(module,useslist,f){
    if (useslist==undefined) return;
    for (var i in useslist){
      var unitname=useslist[i];
      if (rtl.debug_load_units) rtl.debug('loaduseslist of "'+module.$name+'" uses="'+unitname+'"');
      if (pas[unitname]==undefined)
        rtl.error('module "'+module.$name+'" misses "'+unitname+'"');
      f(pas[unitname]);
    }
  },

  loadimpl: function(module){
    if (module.$state>=rtl.m_loading_impl) return; // already processing
    if (module.$state<rtl.m_intf_loaded) rtl.error('loadimpl: interface not loaded of "'+module.$name+'"');
    if (rtl.debug_load_units) rtl.debug('loadimpl: load uses of "'+module.$name+'"');
    module.$state=rtl.m_loading_impl;
    // load interfaces of implementation useslist
    rtl.loaduseslist(module,module.$impluseslist,rtl.loadintf);
    // load implementation of interfaces useslist
    rtl.loaduseslist(module,module.$intfuseslist,rtl.loadimpl);
    // load implementation of implementation useslist
    rtl.loaduseslist(module,module.$impluseslist,rtl.loadimpl);
    // Note: At this point all interfaces used by this unit are loaded. If
    //   there are implementation uses cycles some used units might not yet be
    //   initialized. This is by design.
    // run implementation
    if (rtl.debug_load_units) rtl.debug('loadimpl: run impl of "'+module.$name+'"');
    if (rtl.isFunction(module.$implcode)) module.$implcode(module.$impluseslist);
    // run initialization
    if (rtl.debug_load_units) rtl.debug('loadimpl: run init of "'+module.$name+'"');
    module.$state=rtl.m_initializing;
    if (rtl.isFunction(module.$init)) module.$init();
    // unit initialized
    module.$state=rtl.m_initialized;
  },

  createCallback: function(scope, fn){
    var cb;
    if (typeof(fn)==='string'){
      cb = function(){
        return scope[fn].apply(scope,arguments);
      };
    } else {
      cb = function(){
        return fn.apply(scope,arguments);
      };
    };
    cb.scope = scope;
    cb.fn = fn;
    return cb;
  },

  cloneCallback: function(cb){
    return rtl.createCallback(cb.scope,cb.fn);
  },

  eqCallback: function(a,b){
    // can be a function or a function wrapper
    if (a==b){
      return true;
    } else {
      return (a!=null) && (b!=null) && (a.fn) && (a.scope===b.scope) && (a.fn==b.fn);
    }
  },

  initClass: function(c,parent,name,initfn){
    parent[name] = c;
    c.$classname = name;
    if ((parent.$module) && (parent.$module.$impl===parent)) parent=parent.$module;
    c.$parent = parent;
    c.$fullname = parent.$name+'.'+name;
    if (rtl.isModule(parent)){
      c.$module = parent;
      c.$name = name;
    } else {
      c.$module = parent.$module;
      c.$name = parent.name+'.'+name;
    };
    // rtti
    if (rtl.debug_rtti) rtl.debug('initClass '+c.$fullname);
    var t = c.$module.$rtti.$Class(c.$name,{ "class": c, module: parent });
    c.$rtti = t;
    if (rtl.isObject(c.$ancestor)) t.ancestor = c.$ancestor.$rtti;
    if (!t.ancestor) t.ancestor = null;
    // init members
    initfn.call(c);
  },

  createClass: function(parent,name,ancestor,initfn){
    // create a normal class,
    // ancestor must be null or a normal class,
    // the root ancestor can be an external class
    var c = null;
    if (ancestor != null){
      c = Object.create(ancestor);
      c.$ancestor = ancestor;
      // Note:
      // if root is an "object" then c.$ancestor === Object.getPrototypeOf(c)
      // if root is a "function" then c.$ancestor === c.__proto__, Object.getPrototypeOf(c) returns the root
    } else {
      c = {};
      c.$create = function(fnname,args){
        if (args == undefined) args = [];
        var o = Object.create(this);
        o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
        o.$init();
        try{
          o[fnname].apply(o,args);
          o.AfterConstruction();
        } catch($e){
          o.$destroy;
          throw $e;
        }
        return o;
      };
      c.$destroy = function(fnname){
        this.BeforeDestruction();
        this[fnname]();
        this.$final;
      };
    };
    rtl.initClass(c,parent,name,initfn);
  },

  createClassExt: function(parent,name,ancestor,newinstancefnname,initfn){
    // Create a class using an external ancestor.
    // If newinstancefnname is given, use that function to create the new object.
    // If exist call BeforeDestruction and AfterConstruction.
    var c = null;
    c = Object.create(ancestor);
    c.$create = function(fnname,args){
      if (args == undefined) args = [];
      var o = null;
      if (newinstancefnname.length>0){
        o = this[newinstancefnname](fnname,args);
      } else {
        o = Object.create(this);
      }
      o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
      o.$init();
      try{
        o[fnname].apply(o,args);
        if (o.AfterConstruction) o.AfterConstruction();
      } catch($e){
        o.$destroy;
        throw $e;
      }
      return o;
    };
    c.$destroy = function(fnname){
      if (this.BeforeDestruction) this.BeforeDestruction();
      this[fnname]();
      this.$final;
    };
    rtl.initClass(c,parent,name,initfn);
  },

  tObjectDestroy: "Destroy",

  free: function(obj,name){
    if (obj[name]==null) return;
    obj[name].$destroy(rtl.tObjectDestroy);
    obj[name]=null;
  },

  freeLoc: function(obj){
    if (obj==null) return;
    obj.$destroy(rtl.tObjectDestroy);
    return null;
  },

  is: function(instance,type){
    return type.isPrototypeOf(instance) || (instance===type);
  },

  isExt: function(instance,type,mode){
    // mode===1 means instance must be a Pascal class instance
    // mode===2 means instance must be a Pascal class
    // Notes:
    // isPrototypeOf and instanceof return false on equal
    // isPrototypeOf does not work for Date.isPrototypeOf(new Date())
    //   so if isPrototypeOf is false test with instanceof
    // instanceof needs a function on right side
    if (instance == null) return false; // Note: ==null checks for undefined too
    if ((typeof(type) !== 'object') && (typeof(type) !== 'function')) return false;
    if (instance === type){
      if (mode===1) return false;
      if (mode===2) return rtl.isPasClass(instance);
      return true;
    }
    if (type.isPrototypeOf && type.isPrototypeOf(instance)){
      if (mode===1) return rtl.isPasClassInstance(instance);
      if (mode===2) return rtl.isPasClass(instance);
      return true;
    }
    if ((typeof type == 'function') && (instance instanceof type)) return true;
    return false;
  },

  Exception: null,
  EInvalidCast: null,
  EAbstractError: null,
  ERangeError: null,

  raiseE: function(typename){
    var t = rtl[typename];
    if (t==null){
      var mod = pas.SysUtils;
      if (!mod) mod = pas.sysutils;
      if (mod){
        t = mod[typename];
        if (!t) t = mod[typename.toLowerCase()];
        if (!t) t = mod['Exception'];
        if (!t) t = mod['exception'];
      }
    }
    if (t){
      if (t.Create){
        throw t.$create("Create");
      } else if (t.create){
        throw t.$create("create");
      }
    }
    if (typename === "EInvalidCast") throw "invalid type cast";
    if (typename === "EAbstractError") throw "Abstract method called";
    if (typename === "ERangeError") throw "range error";
    throw typename;
  },

  as: function(instance,type){
    if((instance === null) || rtl.is(instance,type)) return instance;
    rtl.raiseE("EInvalidCast");
  },

  asExt: function(instance,type,mode){
    if((instance === null) || rtl.isExt(instance,type,mode)) return instance;
    rtl.raiseE("EInvalidCast");
  },

  createInterface: function(module, name, guid, fnnames, ancestor, initfn){
    //console.log('createInterface name="'+name+'" guid="'+guid+'" names='+fnnames);
    var i = ancestor?Object.create(ancestor):{};
    module[name] = i;
    i.$module = module;
    i.$name = name;
    i.$fullname = module.$name+'.'+name;
    i.$guid = guid;
    i.$guidr = null;
    i.$names = fnnames?fnnames:[];
    if (rtl.isFunction(initfn)){
      // rtti
      if (rtl.debug_rtti) rtl.debug('createInterface '+i.$fullname);
      var t = i.$module.$rtti.$Interface(name,{ "interface": i, module: module });
      i.$rtti = t;
      if (ancestor) t.ancestor = ancestor.$rtti;
      if (!t.ancestor) t.ancestor = null;
      initfn.call(i);
    }
    return i;
  },

  strToGUIDR: function(s,g){
    var p = 0;
    function n(l){
      var h = s.substr(p,l);
      p+=l;
      return parseInt(h,16);
    }
    p+=1; // skip {
    g.D1 = n(8);
    p+=1; // skip -
    g.D2 = n(4);
    p+=1; // skip -
    g.D3 = n(4);
    p+=1; // skip -
    if (!g.D4) g.D4=[];
    g.D4[0] = n(2);
    g.D4[1] = n(2);
    p+=1; // skip -
    for(var i=2; i<8; i++) g.D4[i] = n(2);
    return g;
  },

  guidrToStr: function(g){
    if (g.$intf) return g.$intf.$guid;
    var h = rtl.hexStr;
    var s='{'+h(g.D1,8)+'-'+h(g.D2,4)+'-'+h(g.D3,4)+'-'+h(g.D4[0],2)+h(g.D4[1],2)+'-';
    for (var i=2; i<8; i++) s+=h(g.D4[i],2);
    s+='}';
    return s;
  },

  createTGUID: function(guid){
    var TGuid = (pas.System)?pas.System.TGuid:pas.system.tguid;
    var g = rtl.strToGUIDR(guid,new TGuid());
    return g;
  },

  getIntfGUIDR: function(intfTypeOrVar){
    if (!intfTypeOrVar) return null;
    if (!intfTypeOrVar.$guidr){
      var g = rtl.createTGUID(intfTypeOrVar.$guid);
      if (!intfTypeOrVar.hasOwnProperty('$guid')) intfTypeOrVar = Object.getPrototypeOf(intfTypeOrVar);
      g.$intf = intfTypeOrVar;
      intfTypeOrVar.$guidr = g;
    }
    return intfTypeOrVar.$guidr;
  },

  addIntf: function (aclass, intf, map){
    function jmp(fn){
      if (typeof(fn)==="function"){
        return function(){ return fn.apply(this.$o,arguments); };
      } else {
        return function(){ rtl.raiseE('EAbstractError'); };
      }
    }
    if(!map) map = {};
    var t = intf;
    var item = Object.create(t);
    aclass.$intfmaps[intf.$guid] = item;
    do{
      var names = t.$names;
      if (!names) break;
      for (var i=0; i<names.length; i++){
        var intfname = names[i];
        var fnname = map[intfname];
        if (!fnname) fnname = intfname;
        //console.log('addIntf: intftype='+t.$name+' index='+i+' intfname="'+intfname+'" fnname="'+fnname+'" proc='+typeof(fn));
        item[intfname] = jmp(aclass[fnname]);
      }
      t = Object.getPrototypeOf(t);
    }while(t!=null);
  },

  getIntfG: function (obj, guid, query){
    if (!obj) return null;
    //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' query='+query);
    // search
    var maps = obj.$intfmaps;
    if (!maps) return null;
    var item = maps[guid];
    if (!item) return null;
    // check delegation
    //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' query='+query+' item='+typeof(item));
    if (typeof item === 'function') return item.call(obj); // COM: contains _AddRef
    // check cache
    var intf = null;
    if (obj.$interfaces){
      intf = obj.$interfaces[guid];
      //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' cache='+typeof(intf));
    }
    if (!intf){ // intf can be undefined!
      intf = Object.create(item);
      intf.$o = obj;
      if (!obj.$interfaces) obj.$interfaces = {};
      obj.$interfaces[guid] = intf;
    }
    if (typeof(query)==='object'){
      // called by queryIntfT
      var o = null;
      if (intf.QueryInterface(rtl.getIntfGUIDR(query),
          {get:function(){ return o; }, set:function(v){ o=v; }}) === 0){
        return o;
      } else {
        return null;
      }
    } else if(query===2){
      // called by TObject.GetInterfaceByStr
      if (intf.$kind === 'com') intf._AddRef();
    }
    return intf;
  },

  getIntfT: function(obj,intftype){
    return rtl.getIntfG(obj,intftype.$guid);
  },

  queryIntfT: function(obj,intftype){
    return rtl.getIntfG(obj,intftype.$guid,intftype);
  },

  queryIntfIsT: function(obj,intftype){
    var i = rtl.queryIntfG(obj,intftype.$guid);
    if (!i) return false;
    if (i.$kind === 'com') i._Release();
    return true;
  },

  asIntfT: function (obj,intftype){
    var i = rtl.getIntfG(obj,intftype.$guid);
    if (i!==null) return i;
    rtl.raiseEInvalidCast();
  },

  intfIsClass: function(intf,classtype){
    return (intf!=null) && (rtl.is(intf.$o,classtype));
  },

  intfAsClass: function(intf,classtype){
    if (intf==null) return null;
    return rtl.as(intf.$o,classtype);
  },

  intfToClass: function(intf,classtype){
    if ((intf!==null) && rtl.is(intf.$o,classtype)) return intf.$o;
    return null;
  },

  // interface reference counting
  intfRefs: { // base object for temporary interface variables
    ref: function(id,intf){
      // called for temporary interface references needing delayed release
      var old = this[id];
      //console.log('rtl.intfRefs.ref: id='+id+' old="'+(old?old.$name:'null')+'" intf="'+(intf?intf.$name:'null'));
      if (old){
        // called again, e.g. in a loop
        delete this[id];
        old._Release(); // may fail
      }
      this[id]=intf;
      return intf;
    },
    free: function(){
      //console.log('rtl.intfRefs.free...');
      for (var id in this){
        if (this.hasOwnProperty(id)) this[id]._Release;
      }
    }
  },

  createIntfRefs: function(){
    //console.log('rtl.createIntfRefs');
    return Object.create(rtl.intfRefs);
  },

  setIntfP: function(path,name,value,skipAddRef){
    var old = path[name];
    //console.log('rtl.setIntfP path='+path+' name='+name+' old="'+(old?old.$name:'null')+'" value="'+(value?value.$name:'null')+'"');
    if (old === value) return;
    if (old !== null){
      path[name]=null;
      old._Release();
    }
    if (value !== null){
      if (!skipAddRef) value._AddRef();
      path[name]=value;
    }
  },

  setIntfL: function(old,value,skipAddRef){
    //console.log('rtl.setIntfL old="'+(old?old.$name:'null')+'" value="'+(value?value.$name:'null')+'"');
    if (old !== value){
      if (value!==null){
        if (!skipAddRef) value._AddRef();
      }
      if (old!==null){
        old._Release();  // Release after AddRef, to avoid double Release if Release creates an exception
      }
    } else if (skipAddRef){
      if (old!==null){
        old._Release();  // value has an AddRef
      }
    }
    return value;
  },

  _AddRef: function(intf){
    //if (intf) console.log('rtl._AddRef intf="'+(intf?intf.$name:'null')+'"');
    if (intf) intf._AddRef();
    return intf;
  },

  _Release: function(intf){
    //if (intf) console.log('rtl._Release intf="'+(intf?intf.$name:'null')+'"');
    if (intf) intf._Release();
    return intf;
  },

  checkMethodCall: function(obj,type){
    if (rtl.isObject(obj) && rtl.is(obj,type)) return;
    rtl.raiseE("EInvalidCast");
  },

  rc: function(i,minval,maxval){
    // range check integer
    if ((Math.floor(i)===i) && (i>=minval) && (i<=maxval)) return i;
    rtl.raiseE('ERangeError');
  },

  rcc: function(c,minval,maxval){
    // range check char
    if ((typeof(c)==='string') && (c.length===1)){
      var i = c.charCodeAt(0);
      if ((i>=minval) && (i<=maxval)) return c;
    }
    rtl.raiseE('ERangeError');
  },

  rcSetCharAt: function(s,index,c){
    // range check setCharAt
    if ((typeof(s)!=='string') || (index<0) || (index>=s.length)) rtl.raiseE('ERangeError');
    return rtl.setCharAt(s,index,c);
  },

  rcCharAt: function(s,index){
    // range check charAt
    if ((typeof(s)!=='string') || (index<0) || (index>=s.length)) rtl.raiseE('ERangeError');
    return s.charAt(index);
  },

  rcArrR: function(arr,index){
    // range check read array
    if (Array.isArray(arr) && (typeof(index)==='number') && (index>=0) && (index<arr.length)){
      if (arguments.length>2){
        // arr,index1,index2,...
        arr=arr[index];
        for (var i=2; i<arguments.length; i++) arr=rtl.rcArrR(arr,arguments[i]);
        return arr;
      }
      return arr[index];
    }
    rtl.raiseE('ERangeError');
  },

  rcArrW: function(arr,index,value){
    // range check write array
    // arr,index1,index2,...,value
    for (var i=3; i<arguments.length; i++){
      arr=rtl.rcArrR(arr,index);
      index=arguments[i-1];
      value=arguments[i];
    }
    if (Array.isArray(arr) && (typeof(index)==='number') && (index>=0) && (index<arr.length)){
      return arr[index]=value;
    }
    rtl.raiseE('ERangeError');
  },

  length: function(arr){
    return (arr == null) ? 0 : arr.length;
  },

  arraySetLength: function(arr,defaultvalue,newlength){
    // multi dim: (arr,defaultvalue,dim1,dim2,...)
    if (arr == null) arr = [];
    var p = arguments;
    function setLength(a,argNo){
      var oldlen = a.length;
      var newlen = p[argNo];
      if (oldlen!==newlength){
        a.length = newlength;
        if (argNo === p.length-1){
          if (rtl.isArray(defaultvalue)){
            for (var i=oldlen; i<newlen; i++) a[i]=[]; // nested array
          } else if (rtl.isFunction(defaultvalue)){
            for (var i=oldlen; i<newlen; i++) a[i]=new defaultvalue(); // e.g. record
          } else if (rtl.isObject(defaultvalue)) {
            for (var i=oldlen; i<newlen; i++) a[i]={}; // e.g. set
          } else {
            for (var i=oldlen; i<newlen; i++) a[i]=defaultvalue;
          }
        } else {
          for (var i=oldlen; i<newlen; i++) a[i]=[]; // nested array
        }
      }
      if (argNo < p.length-1){
        // multi argNo
        for (var i=0; i<newlen; i++) a[i]=setLength(a[i],argNo+1);
      }
      return a;
    }
    return setLength(arr,2);
  },

  arrayEq: function(a,b){
    if (a===null) return b===null;
    if (b===null) return false;
    if (a.length!==b.length) return false;
    for (var i=0; i<a.length; i++) if (a[i]!==b[i]) return false;
    return true;
  },

  arrayClone: function(type,src,srcpos,end,dst,dstpos){
    // type: 0 for references, "refset" for calling refSet(), a function for new type()
    // src must not be null
    // This function does not range check.
    if (rtl.isFunction(type)){
      for (; srcpos<end; srcpos++) dst[dstpos++] = new type(src[srcpos]); // clone record
    } else if((typeof(type)==="string") && (type === 'refSet')) {
      for (; srcpos<end; srcpos++) dst[dstpos++] = rtl.refSet(src[srcpos]); // ref set
    }  else {
      for (; srcpos<end; srcpos++) dst[dstpos++] = src[srcpos]; // reference
    };
  },

  arrayConcat: function(type){
    // type: see rtl.arrayClone
    var a = [];
    var l = 0;
    for (var i=1; i<arguments.length; i++) l+=arguments[i].length;
    a.length = l;
    l=0;
    for (var i=1; i<arguments.length; i++){
      var src = arguments[i];
      if (src == null) continue;
      rtl.arrayClone(type,src,0,src.length,a,l);
      l+=src.length;
    };
    return a;
  },

  arrayCopy: function(type, srcarray, index, count){
    // type: see rtl.arrayClone
    // if count is missing, use srcarray.length
    if (srcarray == null) return [];
    if (index < 0) index = 0;
    if (count === undefined) count=srcarray.length;
    var end = index+count;
    if (end>srcarray.length) end = srcarray.length;
    if (index>=end) return [];
    if (type===0){
      return srcarray.slice(index,end);
    } else {
      var a = [];
      a.length = end-index;
      rtl.arrayClone(type,srcarray,index,end,a,0);
      return a;
    }
  },

  setCharAt: function(s,index,c){
    return s.substr(0,index)+c+s.substr(index+1);
  },

  getResStr: function(mod,name){
    var rs = mod.$resourcestrings[name];
    return rs.current?rs.current:rs.org;
  },

  createSet: function(){
    var s = {};
    for (var i=0; i<arguments.length; i++){
      if (arguments[i]!=null){
        s[arguments[i]]=true;
      } else {
        var first=arguments[i+=1];
        var last=arguments[i+=1];
        for(var j=first; j<=last; j++) s[j]=true;
      }
    }
    return s;
  },

  cloneSet: function(s){
    var r = {};
    for (var key in s) r[key]=true;
    return r;
  },

  refSet: function(s){
    s.$shared = true;
    return s;
  },

  includeSet: function(s,enumvalue){
    if (s.$shared) s = rtl.cloneSet(s);
    s[enumvalue] = true;
    return s;
  },

  excludeSet: function(s,enumvalue){
    if (s.$shared) s = rtl.cloneSet(s);
    delete s[enumvalue];
    return s;
  },

  diffSet: function(s,t){
    var r = {};
    for (var key in s) if (!t[key]) r[key]=true;
    delete r.$shared;
    return r;
  },

  unionSet: function(s,t){
    var r = {};
    for (var key in s) r[key]=true;
    for (var key in t) r[key]=true;
    delete r.$shared;
    return r;
  },

  intersectSet: function(s,t){
    var r = {};
    for (var key in s) if (t[key]) r[key]=true;
    delete r.$shared;
    return r;
  },

  symDiffSet: function(s,t){
    var r = {};
    for (var key in s) if (!t[key]) r[key]=true;
    for (var key in t) if (!s[key]) r[key]=true;
    delete r.$shared;
    return r;
  },

  eqSet: function(s,t){
    for (var key in s) if (!t[key] && (key!='$shared')) return false;
    for (var key in t) if (!s[key] && (key!='$shared')) return false;
    return true;
  },

  neSet: function(s,t){
    return !rtl.eqSet(s,t);
  },

  leSet: function(s,t){
    for (var key in s) if (!t[key] && (key!='$shared')) return false;
    return true;
  },

  geSet: function(s,t){
    for (var key in t) if (!s[key] && (key!='$shared')) return false;
    return true;
  },

  strSetLength: function(s,newlen){
    var oldlen = s.length;
    if (oldlen > newlen){
      return s.substring(0,newlen);
    } else if (s.repeat){
      // Note: repeat needs ECMAScript6!
      return s+' '.repeat(newlen-oldlen);
    } else {
       while (oldlen<newlen){
         s+=' ';
         oldlen++;
       };
       return s;
    }
  },

  spaceLeft: function(s,width){
    var l=s.length;
    if (l>=width) return s;
    if (s.repeat){
      // Note: repeat needs ECMAScript6!
      return ' '.repeat(width-l) + s;
    } else {
      while (l<width){
        s=' '+s;
        l++;
      };
    };
  },

  floatToStr : function(d,w,p){
    // input 1-3 arguments: double, width, precision
    if (arguments.length>2){
      return rtl.spaceLeft(d.toFixed(p),w);
    } else {
	  // exponent width
	  var pad = "";
	  var ad = Math.abs(d);
	  if (ad<1.0e+10) {
		pad='00';
	  } else if (ad<1.0e+100) {
		pad='0';
      }  	
	  if (arguments.length<2) {
	    w=9;		
      } else if (w<9) {
		w=9;
      }		  
      var p = w-8;
      var s=(d>0 ? " " : "" ) + d.toExponential(p);
      s=s.replace(/e(.)/,'E$1'+pad);
      return rtl.spaceLeft(s,w);
    }
  },

  initRTTI: function(){
    if (rtl.debug_rtti) rtl.debug('initRTTI');

    // base types
    rtl.tTypeInfo = { name: "tTypeInfo" };
    function newBaseTI(name,kind,ancestor){
      if (!ancestor) ancestor = rtl.tTypeInfo;
      if (rtl.debug_rtti) rtl.debug('initRTTI.newBaseTI "'+name+'" '+kind+' ("'+ancestor.name+'")');
      var t = Object.create(ancestor);
      t.name = name;
      t.kind = kind;
      rtl[name] = t;
      return t;
    };
    function newBaseInt(name,minvalue,maxvalue,ordtype){
      var t = newBaseTI(name,1 /* tkInteger */,rtl.tTypeInfoInteger);
      t.minvalue = minvalue;
      t.maxvalue = maxvalue;
      t.ordtype = ordtype;
      return t;
    };
    newBaseTI("tTypeInfoInteger",1 /* tkInteger */);
    newBaseInt("shortint",-0x80,0x7f,0);
    newBaseInt("byte",0,0xff,1);
    newBaseInt("smallint",-0x8000,0x7fff,2);
    newBaseInt("word",0,0xffff,3);
    newBaseInt("longint",-0x80000000,0x7fffffff,4);
    newBaseInt("longword",0,0xffffffff,5);
    newBaseInt("nativeint",-0x10000000000000,0xfffffffffffff,6);
    newBaseInt("nativeuint",0,0xfffffffffffff,7);
    newBaseTI("char",2 /* tkChar */);
    newBaseTI("string",3 /* tkString */);
    newBaseTI("tTypeInfoEnum",4 /* tkEnumeration */,rtl.tTypeInfoInteger);
    newBaseTI("tTypeInfoSet",5 /* tkSet */);
    newBaseTI("double",6 /* tkDouble */);
    newBaseTI("boolean",7 /* tkBool */);
    newBaseTI("tTypeInfoProcVar",8 /* tkProcVar */);
    newBaseTI("tTypeInfoMethodVar",9 /* tkMethod */,rtl.tTypeInfoProcVar);
    newBaseTI("tTypeInfoArray",10 /* tkArray */);
    newBaseTI("tTypeInfoDynArray",11 /* tkDynArray */);
    newBaseTI("tTypeInfoPointer",15 /* tkPointer */);
    var t = newBaseTI("pointer",15 /* tkPointer */,rtl.tTypeInfoPointer);
    t.reftype = null;
    newBaseTI("jsvalue",16 /* tkJSValue */);
    newBaseTI("tTypeInfoRefToProcVar",17 /* tkRefToProcVar */,rtl.tTypeInfoProcVar);

    // member kinds
    rtl.tTypeMember = {};
    function newMember(name,kind){
      var m = Object.create(rtl.tTypeMember);
      m.name = name;
      m.kind = kind;
      rtl[name] = m;
    };
    newMember("tTypeMemberField",1); // tmkField
    newMember("tTypeMemberMethod",2); // tmkMethod
    newMember("tTypeMemberProperty",3); // tmkProperty

    // base object for storing members: a simple object
    rtl.tTypeMembers = {};

    // tTypeInfoStruct - base object for tTypeInfoClass, tTypeInfoRecord, tTypeInfoInterface
    var tis = newBaseTI("tTypeInfoStruct",0);
    tis.$addMember = function(name,ancestor,options){
      if (rtl.debug_rtti){
        if (!rtl.hasString(name) || (name.charAt()==='$')) throw 'invalid member "'+name+'", this="'+this.name+'"';
        if (!rtl.is(ancestor,rtl.tTypeMember)) throw 'invalid ancestor "'+ancestor+':'+ancestor.name+'", "'+this.name+'.'+name+'"';
        if ((options!=undefined) && (typeof(options)!='object')) throw 'invalid options "'+options+'", "'+this.name+'.'+name+'"';
      };
      var t = Object.create(ancestor);
      t.name = name;
      this.members[name] = t;
      this.names.push(name);
      if (rtl.isObject(options)){
        for (var key in options) if (options.hasOwnProperty(key)) t[key] = options[key];
      };
      return t;
    };
    tis.addField = function(name,type,options){
      var t = this.$addMember(name,rtl.tTypeMemberField,options);
      if (rtl.debug_rtti){
        if (!rtl.is(type,rtl.tTypeInfo)) throw 'invalid type "'+type+'", "'+this.name+'.'+name+'"';
      };
      t.typeinfo = type;
      this.fields.push(name);
      return t;
    };
    tis.addFields = function(){
      var i=0;
      while(i<arguments.length){
        var name = arguments[i++];
        var type = arguments[i++];
        if ((i<arguments.length) && (typeof(arguments[i])==='object')){
          this.addField(name,type,arguments[i++]);
        } else {
          this.addField(name,type);
        };
      };
    };
    tis.addMethod = function(name,methodkind,params,result,options){
      var t = this.$addMember(name,rtl.tTypeMemberMethod,options);
      t.methodkind = methodkind;
      t.procsig = rtl.newTIProcSig(params);
      t.procsig.resulttype = result?result:null;
      this.methods.push(name);
      return t;
    };
    tis.addProperty = function(name,flags,result,getter,setter,options){
      var t = this.$addMember(name,rtl.tTypeMemberProperty,options);
      t.flags = flags;
      t.typeinfo = result;
      t.getter = getter;
      t.setter = setter;
      // Note: in options: params, stored, defaultvalue
      if (rtl.isArray(t.params)) t.params = rtl.newTIParams(t.params);
      this.properties.push(name);
      if (!rtl.isString(t.stored)) t.stored = "";
      return t;
    };
    tis.getField = function(index){
      return this.members[this.fields[index]];
    };
    tis.getMethod = function(index){
      return this.members[this.methods[index]];
    };
    tis.getProperty = function(index){
      return this.members[this.properties[index]];
    };

    newBaseTI("tTypeInfoRecord",12 /* tkRecord */,rtl.tTypeInfoStruct);
    newBaseTI("tTypeInfoClass",13 /* tkClass */,rtl.tTypeInfoStruct);
    newBaseTI("tTypeInfoClassRef",14 /* tkClassRef */);
    newBaseTI("tTypeInfoInterface",15 /* tkInterface */,rtl.tTypeInfoStruct);
  },

  tSectionRTTI: {
    $module: null,
    $inherited: function(name,ancestor,o){
      if (rtl.debug_rtti){
        rtl.debug('tSectionRTTI.newTI "'+(this.$module?this.$module.$name:"(no module)")
          +'"."'+name+'" ('+ancestor.name+') '+(o?'init':'forward'));
      };
      var t = this[name];
      if (t){
        if (!t.$forward) throw 'duplicate type "'+name+'"';
        if (!ancestor.isPrototypeOf(t)) throw 'typeinfo ancestor mismatch "'+name+'" ancestor="'+ancestor.name+'" t.name="'+t.name+'"';
      } else {
        t = Object.create(ancestor);
        t.name = name;
        t.$module = this.$module;
        this[name] = t;
      }
      if (o){
        delete t.$forward;
        for (var key in o) if (o.hasOwnProperty(key)) t[key]=o[key];
      } else {
        t.$forward = true;
      }
      return t;
    },
    $Scope: function(name,ancestor,o){
      var t=this.$inherited(name,ancestor,o);
      t.members = {};
      t.names = [];
      t.fields = [];
      t.methods = [];
      t.properties = [];
      return t;
    },
    $TI: function(name,kind,o){ var t=this.$inherited(name,rtl.tTypeInfo,o); t.kind = kind; return t; },
    $Int: function(name,o){ return this.$inherited(name,rtl.tTypeInfoInteger,o); },
    $Enum: function(name,o){ return this.$inherited(name,rtl.tTypeInfoEnum,o); },
    $Set: function(name,o){ return this.$inherited(name,rtl.tTypeInfoSet,o); },
    $StaticArray: function(name,o){ return this.$inherited(name,rtl.tTypeInfoArray,o); },
    $DynArray: function(name,o){ return this.$inherited(name,rtl.tTypeInfoDynArray,o); },
    $ProcVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoProcVar,o); },
    $RefToProcVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoRefToProcVar,o); },
    $MethodVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoMethodVar,o); },
    $Record: function(name,o){ return this.$Scope(name,rtl.tTypeInfoRecord,o); },
    $Class: function(name,o){ return this.$Scope(name,rtl.tTypeInfoClass,o); },
    $ClassRef: function(name,o){ return this.$inherited(name,rtl.tTypeInfoClassRef,o); },
    $Pointer: function(name,o){ return this.$inherited(name,rtl.tTypeInfoPointer,o); },
    $Interface: function(name,o){ return this.$Scope(name,rtl.tTypeInfoInterface,o); }
  },

  newTIParam: function(param){
    // param is an array, 0=name, 1=type, 2=optional flags
    var t = {
      name: param[0],
      typeinfo: param[1],
      flags: (rtl.isNumber(param[2]) ? param[2] : 0)
    };
    return t;
  },

  newTIParams: function(list){
    // list: optional array of [paramname,typeinfo,optional flags]
    var params = [];
    if (rtl.isArray(list)){
      for (var i=0; i<list.length; i++) params.push(rtl.newTIParam(list[i]));
    };
    return params;
  },

  newTIProcSig: function(params,result,flags){
    var s = {
      params: rtl.newTIParams(params),
      resulttype: result,
      flags: flags
    };
    return s;
  }
}
rtl.module("System",[],function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  this.LineEnding = "\n";
  this.sLineBreak = $mod.LineEnding;
  rtl.createClass($mod,"TObject",null,function () {
    this.$init = function () {
    };
    this.$final = function () {
    };
    this.Create = function () {
    };
    this.AfterConstruction = function () {
    };
    this.BeforeDestruction = function () {
    };
  });
  this.Random = function (Range) {
    return Math.floor(Math.random()*Range);
  };
  this.Trunc = function (A) {
    if (!Math.trunc) {
      Math.trunc = function(v) {
        v = +v;
        if (!isFinite(v)) return v;
        return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0);
      };
    }
    $mod.Trunc = Math.trunc;
    return Math.trunc(A);
  };
  this.Writeln = function () {
    var i = 0;
    var l = 0;
    var s = "";
    l = rtl.length(arguments) - 1;
    if ($impl.WriteCallBack != null) {
      for (var $l1 = 0, $end2 = l; $l1 <= $end2; $l1++) {
        i = $l1;
        $impl.WriteCallBack(arguments[i],i === l);
      };
    } else {
      s = $impl.WriteBuf;
      for (var $l3 = 0, $end4 = l; $l3 <= $end4; $l3++) {
        i = $l3;
        s = s + ("" + arguments[i]);
      };
      console.log(s);
      $impl.WriteBuf = "";
    };
  };
  this.SetWriteCallBack = function (H) {
    var Result = null;
    Result = $impl.WriteCallBack;
    $impl.WriteCallBack = H;
    return Result;
  };
  $mod.$init = function () {
    rtl.exitcode = 0;
  };
},null,function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  $impl.WriteBuf = "";
  $impl.WriteCallBack = null;
});
rtl.module("JS",["System"],function () {
  "use strict";
  var $mod = this;
});
rtl.module("SysUtils",["System","JS"],function () {
  "use strict";
  var $mod = this;
  rtl.createClass($mod,"Exception",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.fMessage = "";
    };
    this.Create$1 = function (Msg) {
      this.fMessage = Msg;
    };
  });
  this.IntToStr = function (Value) {
    var Result = "";
    Result = "" + Value;
    return Result;
  };
  rtl.createClass($mod,"TFormatSettings",pas.System.TObject,function () {
  });
  this.FormatSettings = null;
  $mod.$init = function () {
    $mod.FormatSettings = $mod.TFormatSettings.$create("Create");
  };
});
rtl.module("math",["System","SysUtils"],function () {
  "use strict";
  var $mod = this;
  this.Floor = function (A) {
    var Result = 0;
    Result = pas.System.Trunc(Math.floor(A));
    return Result;
  };
});
rtl.module("Noise",["System","SysUtils","math"],function () {
  "use strict";
  var $mod = this;
  this.kNoisekPerumationMax = 256;
  rtl.createClass($mod,"TNoise",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.repeatValue = 0;
      this.p = rtl.arraySetLength(null,0,512);
    };
    this.$final = function () {
      this.p = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Create$2 = function (seed) {
      var i = 0;
      this.repeatValue = -1;
      for (i = 0; i <= 511; i++) this.p[i] = seed[i % 256];
    };
    this.GetValue = function (x, y, z) {
      var Self = this;
      var Result = 0.0;
      function FMod(a, b) {
        var Result = 0.0;
        Result = a - (b * pas.System.Trunc(a / b));
        return Result;
      };
      var xi = 0;
      var yi = 0;
      var zi = 0;
      var xf = 0.0;
      var yf = 0.0;
      var zf = 0.0;
      var u = 0.0;
      var v = 0.0;
      var w = 0.0;
      var aaa = 0;
      var aba = 0;
      var aab = 0;
      var abb = 0;
      var baa = 0;
      var bba = 0;
      var bab = 0;
      var bbb = 0;
      var x1 = 0.0;
      var x2 = 0.0;
      var y1 = 0.0;
      var y2 = 0.0;
      if (Self.repeatValue > 0) {
        x = FMod(x,Self.repeatValue);
        y = FMod(y,Self.repeatValue);
        z = FMod(z,Self.repeatValue);
      };
      xi = pas.math.Floor(x) & 255;
      yi = pas.math.Floor(y) & 255;
      zi = pas.math.Floor(z) & 255;
      xf = x - pas.math.Floor(x);
      yf = y - pas.math.Floor(y);
      zf = z - pas.math.Floor(z);
      u = Self.Fade(xf);
      v = Self.Fade(yf);
      w = Self.Fade(zf);
      aaa = Self.p[Self.p[Self.p[xi] + yi] + zi];
      aba = Self.p[Self.p[Self.p[xi] + Self.Inc(yi)] + zi];
      aab = Self.p[Self.p[Self.p[xi] + yi] + Self.Inc(zi)];
      abb = Self.p[Self.p[Self.p[xi] + Self.Inc(yi)] + Self.Inc(zi)];
      baa = Self.p[Self.p[Self.p[Self.Inc(xi)] + yi] + zi];
      bba = Self.p[Self.p[Self.p[Self.Inc(xi)] + Self.Inc(yi)] + zi];
      bab = Self.p[Self.p[Self.p[Self.Inc(xi)] + yi] + Self.Inc(zi)];
      bbb = Self.p[Self.p[Self.p[Self.Inc(xi)] + Self.Inc(yi)] + Self.Inc(zi)];
      x1 = Self.Lerp(Self.Grad(aaa,xf,yf,zf),Self.Grad(baa,xf - 1,yf,zf),u);
      x2 = Self.Lerp(Self.Grad(aba,xf,yf - 1,zf),Self.Grad(bba,xf - 1,yf - 1,zf),u);
      y1 = Self.Lerp(x1,x2,v);
      x1 = Self.Lerp(Self.Grad(aab,xf,yf,zf - 1),Self.Grad(bab,xf - 1,yf,zf - 1),u);
      x2 = Self.Lerp(Self.Grad(abb,xf,yf - 1,zf - 1),Self.Grad(bbb,xf - 1,yf - 1,zf - 1),u);
      y2 = Self.Lerp(x1,x2,v);
      Result = (Self.Lerp(y1,y2,w) + 1) / 2;
      return Result;
    };
    this.GetValue$1 = function (x, y, z, octaves, persistence) {
      var Result = 0.0;
      var total = 0;
      var frequency = 1;
      var amplitude = 1;
      var maxValue = 0;
      var i = 0;
      for (var $l1 = 0, $end2 = octaves - 1; $l1 <= $end2; $l1++) {
        i = $l1;
        total += this.GetValue(x * frequency,y * frequency,z * frequency) * amplitude;
        maxValue += amplitude;
        amplitude *= persistence;
        frequency *= 2;
      };
      Result = total / maxValue;
      return Result;
    };
    this.GetNoise = function (x, y, width, height, scale, frequency) {
      var Result = 0.0;
      var nx = 0.0;
      var ny = 0.0;
      nx = (x / width) - 0.5;
      ny = (y / height) - 0.5;
      Result = (this.GetValue$1(nx * scale,ny * scale,0,frequency,0.5) / 2) + 0.5;
      return Result;
    };
    this.Inc = function (num) {
      var Result = 0;
      num += 1;
      if (this.repeatValue > 0) num = num % this.repeatValue;
      Result = num;
      return Result;
    };
    this.Grad = function (hash, x, y, z) {
      var Result = 0.0;
      var $tmp1 = hash & 0xF;
      if ($tmp1 === 0x0) {
        Result = x + y}
       else if ($tmp1 === 0x1) {
        Result = -x + y}
       else if ($tmp1 === 0x2) {
        Result = x - y}
       else if ($tmp1 === 0x3) {
        Result = -x - y}
       else if ($tmp1 === 0x4) {
        Result = x + z}
       else if ($tmp1 === 0x5) {
        Result = -x + z}
       else if ($tmp1 === 0x6) {
        Result = x - z}
       else if ($tmp1 === 0x7) {
        Result = -x - z}
       else if ($tmp1 === 0x8) {
        Result = y + z}
       else if ($tmp1 === 0x9) {
        Result = -y + z}
       else if ($tmp1 === 0xA) {
        Result = y - z}
       else if ($tmp1 === 0xB) {
        Result = -y - z}
       else if ($tmp1 === 0xC) {
        Result = y + x}
       else if ($tmp1 === 0xD) {
        Result = -y + z}
       else if ($tmp1 === 0xE) {
        Result = y - x}
       else if ($tmp1 === 0xF) {
        Result = -y - z}
       else {
        Result = 0;
      };
      return Result;
    };
    this.Fade = function (t) {
      var Result = 0.0;
      Result = ((t * t) * t) * ((t * ((t * 6) - 15)) + 10);
      return Result;
    };
    this.Lerp = function (a, b, x) {
      var Result = 0.0;
      Result = a + (x * (b - a));
      return Result;
    };
  });
  this.RandomNoiseSeed = function (seed) {
    var Result = rtl.arraySetLength(null,0,256);
    var i = 0;
    for (i = 0; i <= 255; i++) Result[i] = pas.System.Random(256);
    return Result;
  };
});
rtl.module("Matrix",["System","JS"],function () {
  "use strict";
  var $mod = this;
  rtl.createClass($mod,"TMatrix",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.table = null;
      this.width = 0;
      this.height = 0;
    };
    this.$final = function () {
      this.table = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Create$1 = function (w, h) {
      this.width = w;
      this.height = h;
      this.table = new Array(this.width * this.height);
    };
    this.SetValue = function (x, y, value) {
      this.table[this.IndexFor(x,y)] = value;
    };
    this.GetValue = function (x, y) {
      var Result = undefined;
      Result = this.table[this.IndexFor(x,y)];
      return Result;
    };
    this.GetWidth = function () {
      var Result = 0;
      Result = this.width;
      return Result;
    };
    this.GetHeight = function () {
      var Result = 0;
      Result = this.height;
      return Result;
    };
    this.IndexFor = function (x, y) {
      var Result = 0;
      Result = x + (y * this.height);
      return Result;
    };
  });
});
rtl.module("Web",["System","JS"],function () {
  "use strict";
  var $mod = this;
});
rtl.module("webgl",["System","JS","Web"],function () {
  "use strict";
  var $mod = this;
});
rtl.module("GLTypes",["System","webgl","JS","math","SysUtils"],function () {
  "use strict";
  var $mod = this;
  this.TVec2 = function (s) {
    if (s) {
      this.x = s.x;
      this.y = s.y;
    } else {
      this.x = 0.0;
      this.y = 0.0;
    };
    this.$equal = function (b) {
      return (this.x === b.x) && (this.y === b.y);
    };
  };
  this.TVec3 = function (s) {
    if (s) {
      this.x = s.x;
      this.y = s.y;
      this.z = s.z;
    } else {
      this.x = 0.0;
      this.y = 0.0;
      this.z = 0.0;
    };
    this.$equal = function (b) {
      return (this.x === b.x) && ((this.y === b.y) && (this.z === b.z));
    };
  };
  this.V3 = function (x, y, z) {
    var Result = new $mod.TVec3();
    Result.x = x;
    Result.y = y;
    Result.z = z;
    return Result;
  };
  this.Divide = function (v, amount) {
    var Result = new $mod.TVec3();
    Result = new $mod.TVec3($mod.V3(v.x / amount,v.y / amount,v.z / amount));
    return Result;
  };
  this.Magnitude = function (v) {
    var Result = 0.0;
    Result = Math.sqrt((Math.pow(v.x,2) + Math.pow(v.y,2)) + Math.pow(v.z,2));
    return Result;
  };
  this.Normalize = function (v) {
    var Result = new $mod.TVec3();
    Result = new $mod.TVec3($mod.Divide(new $mod.TVec3(v),$mod.Magnitude(new $mod.TVec3(v))));
    return Result;
  };
  this.V2 = function (x, y) {
    var Result = new $mod.TVec2();
    Result.x = x;
    Result.y = y;
    return Result;
  };
});
rtl.module("browserconsole",["System","JS","Web"],function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  this.DefaultMaxConsoleLines = 25;
  this.DefaultConsoleStyle = (((((((((((".pasconsole { " + pas.System.sLineBreak) + "font-family: courier;") + pas.System.sLineBreak) + "font-size: 14px;") + pas.System.sLineBreak) + "background: #FFFFFF;") + pas.System.sLineBreak) + "color: #000000;") + pas.System.sLineBreak) + "display: block;") + pas.System.sLineBreak) + "}";
  this.ConsoleElementID = "";
  this.ConsoleStyle = "";
  this.MaxConsoleLines = 0;
  this.ConsoleLinesToBrowserLog = false;
  this.ResetConsole = function () {
    if ($impl.LinesParent === null) return;
    while ($impl.LinesParent.firstElementChild !== null) $impl.LinesParent.removeChild($impl.LinesParent.firstElementChild);
    $impl.AppendLine();
  };
  this.InitConsole = function () {
    if ($impl.ConsoleElement === null) return;
    if ($impl.ConsoleElement.nodeName.toLowerCase() !== "body") {
      while ($impl.ConsoleElement.firstElementChild !== null) $impl.ConsoleElement.removeChild($impl.ConsoleElement.firstElementChild);
    };
    $impl.StyleElement = document.createElement("style");
    $impl.StyleElement.innerText = $mod.ConsoleStyle;
    $impl.ConsoleElement.appendChild($impl.StyleElement);
    $impl.LinesParent = document.createElement("div");
    $impl.ConsoleElement.appendChild($impl.LinesParent);
  };
  this.HookConsole = function () {
    $impl.ConsoleElement = null;
    if ($mod.ConsoleElementID !== "") $impl.ConsoleElement = document.getElementById($mod.ConsoleElementID);
    if ($impl.ConsoleElement === null) $impl.ConsoleElement = document.body;
    if ($impl.ConsoleElement === null) return;
    $mod.InitConsole();
    $mod.ResetConsole();
    pas.System.SetWriteCallBack($impl.WriteConsole);
  };
  $mod.$init = function () {
    $mod.ConsoleLinesToBrowserLog = true;
    $mod.ConsoleElementID = "pasjsconsole";
    $mod.ConsoleStyle = $mod.DefaultConsoleStyle;
    $mod.MaxConsoleLines = 25;
    $mod.HookConsole();
  };
},null,function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  $impl.LastLine = null;
  $impl.StyleElement = null;
  $impl.LinesParent = null;
  $impl.ConsoleElement = null;
  $impl.AppendLine = function () {
    var CurrentCount = 0;
    var S = null;
    CurrentCount = 0;
    S = $impl.LinesParent.firstChild;
    while (S != null) {
      CurrentCount += 1;
      S = S.nextSibling;
    };
    while (CurrentCount > $mod.MaxConsoleLines) {
      CurrentCount -= 1;
      $impl.LinesParent.removeChild($impl.LinesParent.firstChild);
    };
    $impl.LastLine = document.createElement("div");
    $impl.LastLine.className = "pasconsole";
    $impl.LinesParent.appendChild($impl.LastLine);
  };
  $impl.WriteConsole = function (S, NewLine) {
    var CL = "";
    CL = $impl.LastLine.innerText;
    CL = CL + ("" + S);
    $impl.LastLine.innerText = CL;
    if (NewLine) {
      if ($mod.ConsoleLinesToBrowserLog) window.console.log(CL);
      $impl.AppendLine();
    };
  };
});
rtl.module("Mat4",["System","browserconsole","JS","math"],function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  rtl.createClass($mod,"TMat4",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.RawComponents = rtl.arraySetLength(null,0.0,4,4);
    };
    this.$final = function () {
      this.RawComponents = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Identity = function () {
      this.RawComponents[0][0] = 1.0;
      this.RawComponents[0][1] = 0.0;
      this.RawComponents[0][2] = 0.0;
      this.RawComponents[0][3] = 0.0;
      this.RawComponents[1][0] = 0.0;
      this.RawComponents[1][1] = 1.0;
      this.RawComponents[1][2] = 0.0;
      this.RawComponents[1][3] = 0.0;
      this.RawComponents[2][0] = 0.0;
      this.RawComponents[2][1] = 0.0;
      this.RawComponents[2][2] = 1.0;
      this.RawComponents[2][3] = 0.0;
      this.RawComponents[3][0] = 0.0;
      this.RawComponents[3][1] = 0.0;
      this.RawComponents[3][2] = 0.0;
      this.RawComponents[3][3] = 1.0;
    };
    this.Translate = function (tx, ty, tz) {
      this.RawComponents[0][0] = 1.0;
      this.RawComponents[0][1] = 0.0;
      this.RawComponents[0][2] = 0.0;
      this.RawComponents[0][3] = 0.0;
      this.RawComponents[1][0] = 0.0;
      this.RawComponents[1][1] = 1.0;
      this.RawComponents[1][2] = 0.0;
      this.RawComponents[1][3] = 0.0;
      this.RawComponents[2][0] = 0.0;
      this.RawComponents[2][1] = 0.0;
      this.RawComponents[2][2] = 1.0;
      this.RawComponents[2][3] = 0.0;
      this.RawComponents[3][0] = tx;
      this.RawComponents[3][1] = ty;
      this.RawComponents[3][2] = tz;
      this.RawComponents[3][3] = 1.0;
    };
    this.Perspective = function (fovy, Aspect, zNear, zFar) {
      var Sine = 0.0;
      var Cotangent = 0.0;
      var ZDelta = 0.0;
      var Radians = 0.0;
      Radians = (fovy * 0.5) * 0.017453292519944444;
      ZDelta = zFar - zNear;
      Sine = Math.sin(Radians);
      if (!(((ZDelta === 0) || (Sine === 0)) || (Aspect === 0))) {
        Cotangent = Math.cos(Radians) / Sine;
        this.RawComponents = $impl.Matrix4x4Identity.RawComponents.slice(0);
        this.RawComponents[0][0] = Cotangent / Aspect;
        this.RawComponents[1][1] = Cotangent;
        this.RawComponents[2][2] = -(zFar + zNear) / ZDelta;
        this.RawComponents[2][3] = -1 - 0;
        this.RawComponents[3][2] = -((2.0 * zNear) * zFar) / ZDelta;
        this.RawComponents[3][3] = 0.0;
      };
    };
    this.Multiply = function (m) {
      var Result = null;
      Result = $mod.TMat4.$create("Identity");
      Result.RawComponents[0][0] = (((m.RawComponents[0][0] * this.RawComponents[0][0]) + (m.RawComponents[0][1] * this.RawComponents[1][0])) + (m.RawComponents[0][2] * this.RawComponents[2][0])) + (m.RawComponents[0][3] * this.RawComponents[3][0]);
      Result.RawComponents[0][1] = (((m.RawComponents[0][0] * this.RawComponents[0][1]) + (m.RawComponents[0][1] * this.RawComponents[1][1])) + (m.RawComponents[0][2] * this.RawComponents[2][1])) + (m.RawComponents[0][3] * this.RawComponents[3][1]);
      Result.RawComponents[0][2] = (((m.RawComponents[0][0] * this.RawComponents[0][2]) + (m.RawComponents[0][1] * this.RawComponents[1][2])) + (m.RawComponents[0][2] * this.RawComponents[2][2])) + (m.RawComponents[0][3] * this.RawComponents[3][2]);
      Result.RawComponents[0][3] = (((m.RawComponents[0][0] * this.RawComponents[0][3]) + (m.RawComponents[0][1] * this.RawComponents[1][3])) + (m.RawComponents[0][2] * this.RawComponents[2][3])) + (m.RawComponents[0][3] * this.RawComponents[3][3]);
      Result.RawComponents[1][0] = (((m.RawComponents[1][0] * this.RawComponents[0][0]) + (m.RawComponents[1][1] * this.RawComponents[1][0])) + (m.RawComponents[1][2] * this.RawComponents[2][0])) + (m.RawComponents[1][3] * this.RawComponents[3][0]);
      Result.RawComponents[1][1] = (((m.RawComponents[1][0] * this.RawComponents[0][1]) + (m.RawComponents[1][1] * this.RawComponents[1][1])) + (m.RawComponents[1][2] * this.RawComponents[2][1])) + (m.RawComponents[1][3] * this.RawComponents[3][1]);
      Result.RawComponents[1][2] = (((m.RawComponents[1][0] * this.RawComponents[0][2]) + (m.RawComponents[1][1] * this.RawComponents[1][2])) + (m.RawComponents[1][2] * this.RawComponents[2][2])) + (m.RawComponents[1][3] * this.RawComponents[3][2]);
      Result.RawComponents[1][3] = (((m.RawComponents[1][0] * this.RawComponents[0][3]) + (m.RawComponents[1][1] * this.RawComponents[1][3])) + (m.RawComponents[1][2] * this.RawComponents[2][3])) + (m.RawComponents[1][3] * this.RawComponents[3][3]);
      Result.RawComponents[2][0] = (((m.RawComponents[2][0] * this.RawComponents[0][0]) + (m.RawComponents[2][1] * this.RawComponents[1][0])) + (m.RawComponents[2][2] * this.RawComponents[2][0])) + (m.RawComponents[2][3] * this.RawComponents[3][0]);
      Result.RawComponents[2][1] = (((m.RawComponents[2][0] * this.RawComponents[0][1]) + (m.RawComponents[2][1] * this.RawComponents[1][1])) + (m.RawComponents[2][2] * this.RawComponents[2][1])) + (m.RawComponents[2][3] * this.RawComponents[3][1]);
      Result.RawComponents[2][2] = (((m.RawComponents[2][0] * this.RawComponents[0][2]) + (m.RawComponents[2][1] * this.RawComponents[1][2])) + (m.RawComponents[2][2] * this.RawComponents[2][2])) + (m.RawComponents[2][3] * this.RawComponents[3][2]);
      Result.RawComponents[2][3] = (((m.RawComponents[2][0] * this.RawComponents[0][3]) + (m.RawComponents[2][1] * this.RawComponents[1][3])) + (m.RawComponents[2][2] * this.RawComponents[2][3])) + (m.RawComponents[2][3] * this.RawComponents[3][3]);
      Result.RawComponents[3][0] = (((m.RawComponents[3][0] * this.RawComponents[0][0]) + (m.RawComponents[3][1] * this.RawComponents[1][0])) + (m.RawComponents[3][2] * this.RawComponents[2][0])) + (m.RawComponents[3][3] * this.RawComponents[3][0]);
      Result.RawComponents[3][1] = (((m.RawComponents[3][0] * this.RawComponents[0][1]) + (m.RawComponents[3][1] * this.RawComponents[1][1])) + (m.RawComponents[3][2] * this.RawComponents[2][1])) + (m.RawComponents[3][3] * this.RawComponents[3][1]);
      Result.RawComponents[3][2] = (((m.RawComponents[3][0] * this.RawComponents[0][2]) + (m.RawComponents[3][1] * this.RawComponents[1][2])) + (m.RawComponents[3][2] * this.RawComponents[2][2])) + (m.RawComponents[3][3] * this.RawComponents[3][2]);
      Result.RawComponents[3][3] = (((m.RawComponents[3][0] * this.RawComponents[0][3]) + (m.RawComponents[3][1] * this.RawComponents[1][3])) + (m.RawComponents[3][2] * this.RawComponents[2][3])) + (m.RawComponents[3][3] * this.RawComponents[3][3]);
      return Result;
    };
    this.Inverse = function () {
      var Result = null;
      var t0 = 0.0;
      var t4 = 0.0;
      var t8 = 0.0;
      var t12 = 0.0;
      var d = 0.0;
      t0 = ((((((this.RawComponents[1][1] * this.RawComponents[2][2]) * this.RawComponents[3][3]) - ((this.RawComponents[1][1] * this.RawComponents[2][3]) * this.RawComponents[3][2])) - ((this.RawComponents[2][1] * this.RawComponents[1][2]) * this.RawComponents[3][3])) + ((this.RawComponents[2][1] * this.RawComponents[1][3]) * this.RawComponents[3][2])) + ((this.RawComponents[3][1] * this.RawComponents[1][2]) * this.RawComponents[2][3])) - ((this.RawComponents[3][1] * this.RawComponents[1][3]) * this.RawComponents[2][2]);
      t4 = ((((-((this.RawComponents[1][0] * this.RawComponents[2][2]) * this.RawComponents[3][3]) + ((this.RawComponents[1][0] * this.RawComponents[2][3]) * this.RawComponents[3][2])) + ((this.RawComponents[2][0] * this.RawComponents[1][2]) * this.RawComponents[3][3])) - ((this.RawComponents[2][0] * this.RawComponents[1][3]) * this.RawComponents[3][2])) - ((this.RawComponents[3][0] * this.RawComponents[1][2]) * this.RawComponents[2][3])) + ((this.RawComponents[3][0] * this.RawComponents[1][3]) * this.RawComponents[2][2]);
      t8 = ((((((this.RawComponents[1][0] * this.RawComponents[2][1]) * this.RawComponents[3][3]) - ((this.RawComponents[1][0] * this.RawComponents[2][3]) * this.RawComponents[3][1])) - ((this.RawComponents[2][0] * this.RawComponents[1][1]) * this.RawComponents[3][3])) + ((this.RawComponents[2][0] * this.RawComponents[1][3]) * this.RawComponents[3][1])) + ((this.RawComponents[3][0] * this.RawComponents[1][1]) * this.RawComponents[2][3])) - ((this.RawComponents[3][0] * this.RawComponents[1][3]) * this.RawComponents[2][1]);
      t12 = ((((-((this.RawComponents[1][0] * this.RawComponents[2][1]) * this.RawComponents[3][2]) + ((this.RawComponents[1][0] * this.RawComponents[2][2]) * this.RawComponents[3][1])) + ((this.RawComponents[2][0] * this.RawComponents[1][1]) * this.RawComponents[3][2])) - ((this.RawComponents[2][0] * this.RawComponents[1][2]) * this.RawComponents[3][1])) - ((this.RawComponents[3][0] * this.RawComponents[1][1]) * this.RawComponents[2][2])) + ((this.RawComponents[3][0] * this.RawComponents[1][2]) * this.RawComponents[2][1]);
      d = (((this.RawComponents[0][0] * t0) + (this.RawComponents[0][1] * t4)) + (this.RawComponents[0][2] * t8)) + (this.RawComponents[0][3] * t12);
      Result = $mod.TMat4.$create("Identity");
      if (d !== 0.0) {
        d = 1.0 / d;
        Result.RawComponents[0][0] = t0 * d;
        Result.RawComponents[0][1] = (((((-((this.RawComponents[0][1] * this.RawComponents[2][2]) * this.RawComponents[3][3]) + ((this.RawComponents[0][1] * this.RawComponents[2][3]) * this.RawComponents[3][2])) + ((this.RawComponents[2][1] * this.RawComponents[0][2]) * this.RawComponents[3][3])) - ((this.RawComponents[2][1] * this.RawComponents[0][3]) * this.RawComponents[3][2])) - ((this.RawComponents[3][1] * this.RawComponents[0][2]) * this.RawComponents[2][3])) + ((this.RawComponents[3][1] * this.RawComponents[0][3]) * this.RawComponents[2][2])) * d;
        Result.RawComponents[0][2] = (((((((this.RawComponents[0][1] * this.RawComponents[1][2]) * this.RawComponents[3][3]) - ((this.RawComponents[0][1] * this.RawComponents[1][3]) * this.RawComponents[3][2])) - ((this.RawComponents[1][1] * this.RawComponents[0][2]) * this.RawComponents[3][3])) + ((this.RawComponents[1][1] * this.RawComponents[0][3]) * this.RawComponents[3][2])) + ((this.RawComponents[3][1] * this.RawComponents[0][2]) * this.RawComponents[1][3])) - ((this.RawComponents[3][1] * this.RawComponents[0][3]) * this.RawComponents[1][2])) * d;
        Result.RawComponents[0][3] = (((((-((this.RawComponents[0][1] * this.RawComponents[1][2]) * this.RawComponents[2][3]) + ((this.RawComponents[0][1] * this.RawComponents[1][3]) * this.RawComponents[2][2])) + ((this.RawComponents[1][1] * this.RawComponents[0][2]) * this.RawComponents[2][3])) - ((this.RawComponents[1][1] * this.RawComponents[0][3]) * this.RawComponents[2][2])) - ((this.RawComponents[2][1] * this.RawComponents[0][2]) * this.RawComponents[1][3])) + ((this.RawComponents[2][1] * this.RawComponents[0][3]) * this.RawComponents[1][2])) * d;
        Result.RawComponents[1][0] = t4 * d;
        Result.RawComponents[1][1] = (((((((this.RawComponents[0][0] * this.RawComponents[2][2]) * this.RawComponents[3][3]) - ((this.RawComponents[0][0] * this.RawComponents[2][3]) * this.RawComponents[3][2])) - ((this.RawComponents[2][0] * this.RawComponents[0][2]) * this.RawComponents[3][3])) + ((this.RawComponents[2][0] * this.RawComponents[0][3]) * this.RawComponents[3][2])) + ((this.RawComponents[3][0] * this.RawComponents[0][2]) * this.RawComponents[2][3])) - ((this.RawComponents[3][0] * this.RawComponents[0][3]) * this.RawComponents[2][2])) * d;
        Result.RawComponents[1][2] = (((((-((this.RawComponents[0][0] * this.RawComponents[1][2]) * this.RawComponents[3][3]) + ((this.RawComponents[0][0] * this.RawComponents[1][3]) * this.RawComponents[3][2])) + ((this.RawComponents[1][0] * this.RawComponents[0][2]) * this.RawComponents[3][3])) - ((this.RawComponents[1][0] * this.RawComponents[0][3]) * this.RawComponents[3][2])) - ((this.RawComponents[3][0] * this.RawComponents[0][2]) * this.RawComponents[1][3])) + ((this.RawComponents[3][0] * this.RawComponents[0][3]) * this.RawComponents[1][2])) * d;
        Result.RawComponents[1][3] = (((((((this.RawComponents[0][0] * this.RawComponents[1][2]) * this.RawComponents[2][3]) - ((this.RawComponents[0][0] * this.RawComponents[1][3]) * this.RawComponents[2][2])) - ((this.RawComponents[1][0] * this.RawComponents[0][2]) * this.RawComponents[2][3])) + ((this.RawComponents[1][0] * this.RawComponents[0][3]) * this.RawComponents[2][2])) + ((this.RawComponents[2][0] * this.RawComponents[0][2]) * this.RawComponents[1][3])) - ((this.RawComponents[2][0] * this.RawComponents[0][3]) * this.RawComponents[1][2])) * d;
        Result.RawComponents[2][0] = t8 * d;
        Result.RawComponents[2][1] = (((((-((this.RawComponents[0][0] * this.RawComponents[2][1]) * this.RawComponents[3][3]) + ((this.RawComponents[0][0] * this.RawComponents[2][3]) * this.RawComponents[3][1])) + ((this.RawComponents[2][0] * this.RawComponents[0][1]) * this.RawComponents[3][3])) - ((this.RawComponents[2][0] * this.RawComponents[0][3]) * this.RawComponents[3][1])) - ((this.RawComponents[3][0] * this.RawComponents[0][1]) * this.RawComponents[2][3])) + ((this.RawComponents[3][0] * this.RawComponents[0][3]) * this.RawComponents[2][1])) * d;
        Result.RawComponents[2][2] = (((((((this.RawComponents[0][0] * this.RawComponents[1][1]) * this.RawComponents[3][3]) - ((this.RawComponents[0][0] * this.RawComponents[1][3]) * this.RawComponents[3][1])) - ((this.RawComponents[1][0] * this.RawComponents[0][1]) * this.RawComponents[3][3])) + ((this.RawComponents[1][0] * this.RawComponents[0][3]) * this.RawComponents[3][1])) + ((this.RawComponents[3][0] * this.RawComponents[0][1]) * this.RawComponents[1][3])) - ((this.RawComponents[3][0] * this.RawComponents[0][3]) * this.RawComponents[1][1])) * d;
        Result.RawComponents[2][3] = (((((-((this.RawComponents[0][0] * this.RawComponents[1][1]) * this.RawComponents[2][3]) + ((this.RawComponents[0][0] * this.RawComponents[1][3]) * this.RawComponents[2][1])) + ((this.RawComponents[1][0] * this.RawComponents[0][1]) * this.RawComponents[2][3])) - ((this.RawComponents[1][0] * this.RawComponents[0][3]) * this.RawComponents[2][1])) - ((this.RawComponents[2][0] * this.RawComponents[0][1]) * this.RawComponents[1][3])) + ((this.RawComponents[2][0] * this.RawComponents[0][3]) * this.RawComponents[1][1])) * d;
        Result.RawComponents[3][0] = t12 * d;
        Result.RawComponents[3][1] = (((((((this.RawComponents[0][0] * this.RawComponents[2][1]) * this.RawComponents[3][2]) - ((this.RawComponents[0][0] * this.RawComponents[2][2]) * this.RawComponents[3][1])) - ((this.RawComponents[2][0] * this.RawComponents[0][1]) * this.RawComponents[3][2])) + ((this.RawComponents[2][0] * this.RawComponents[0][2]) * this.RawComponents[3][1])) + ((this.RawComponents[3][0] * this.RawComponents[0][1]) * this.RawComponents[2][2])) - ((this.RawComponents[3][0] * this.RawComponents[0][2]) * this.RawComponents[2][1])) * d;
        Result.RawComponents[3][2] = (((((-((this.RawComponents[0][0] * this.RawComponents[1][1]) * this.RawComponents[3][2]) + ((this.RawComponents[0][0] * this.RawComponents[1][2]) * this.RawComponents[3][1])) + ((this.RawComponents[1][0] * this.RawComponents[0][1]) * this.RawComponents[3][2])) - ((this.RawComponents[1][0] * this.RawComponents[0][2]) * this.RawComponents[3][1])) - ((this.RawComponents[3][0] * this.RawComponents[0][1]) * this.RawComponents[1][2])) + ((this.RawComponents[3][0] * this.RawComponents[0][2]) * this.RawComponents[1][1])) * d;
        Result.RawComponents[3][3] = (((((((this.RawComponents[0][0] * this.RawComponents[1][1]) * this.RawComponents[2][2]) - ((this.RawComponents[0][0] * this.RawComponents[1][2]) * this.RawComponents[2][1])) - ((this.RawComponents[1][0] * this.RawComponents[0][1]) * this.RawComponents[2][2])) + ((this.RawComponents[1][0] * this.RawComponents[0][2]) * this.RawComponents[2][1])) + ((this.RawComponents[2][0] * this.RawComponents[0][1]) * this.RawComponents[1][2])) - ((this.RawComponents[2][0] * this.RawComponents[0][2]) * this.RawComponents[1][1])) * d;
      };
      return Result;
    };
    this.CopyList = function () {
      var Result = [];
      var x = 0;
      var y = 0;
      var list = null;
      list = new Array();
      for (x = 0; x <= 3; x++) for (y = 0; y <= 3; y++) list.push(this.RawComponents[x][y]);
      Result = list;
      return Result;
    };
  });
  $mod.$init = function () {
    $impl.Matrix4x4Identity = $mod.TMat4.$create("Identity");
  };
},null,function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  $impl.PI = 3.14159265359;
  $impl.DEG2RAD = 3.14159265359 / 180.0;
  $impl.Matrix4x4Identity = null;
});
rtl.module("GLUtils",["System","Mat4","GLTypes","browserconsole","Web","webgl","JS","math","SysUtils"],function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  rtl.createClass($mod,"TShader",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.gl = null;
      this.vertexShader = null;
      this.fragmentShader = null;
      this.programID = null;
    };
    this.$final = function () {
      this.gl = undefined;
      this.vertexShader = undefined;
      this.fragmentShader = undefined;
      this.programID = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Create$1 = function (context, vertexShaderSource, fragmentShaderSource) {
      this.gl = context;
      this.vertexShader = this.CreateShader(this.gl.VERTEX_SHADER,vertexShaderSource);
      this.fragmentShader = this.CreateShader(this.gl.FRAGMENT_SHADER,fragmentShaderSource);
    };
    this.Compile = function () {
      this.programID = this.gl.createProgram();
      this.gl.attachShader(this.programID,this.vertexShader);
      this.gl.attachShader(this.programID,this.fragmentShader);
    };
    this.Link = function () {
      this.gl.linkProgram(this.programID);
      if (!this.gl.getProgramParameter(this.programID,this.gl.LINK_STATUS)) {
        $impl.Fatal(this.gl.getProgramInfoLog(this.programID));
      };
    };
    this.Use = function () {
      this.gl.useProgram(this.programID);
    };
    this.BindAttribLocation = function (index, name) {
      this.gl.bindAttribLocation(this.programID,index,name);
    };
    this.SetUniformMat4 = function (name, value) {
      var list = [];
      list = value.CopyList();
      this.gl.uniformMatrix4fv(this.GetUniformLocation(name),false,list);
      $mod.GLFatal(this.gl,"gl.uniformMatrix4fv");
    };
    this.SetUniformVec3 = function (name, value) {
      this.gl.uniform3f(this.GetUniformLocation(name),value.x,value.y,value.z);
      $mod.GLFatal(this.gl,"gl.uniform3fv");
    };
    this.SetUniformFloat = function (name, value) {
      this.gl.uniform1f(this.GetUniformLocation(name),value);
      $mod.GLFatal(this.gl,"gl.uniform1f");
    };
    this.GetUniformLocation = function (name) {
      var Result = null;
      Result = this.gl.getUniformLocation(this.programID,name);
      $mod.GLFatal(this.gl,"gl.getUniformLocation");
      return Result;
    };
    this.CreateShader = function (theType, source) {
      var Result = null;
      var shader = null;
      shader = this.gl.createShader(theType);
      if (shader === null) $impl.Fatal("create shader failed");
      this.gl.shaderSource(shader,source);
      this.gl.compileShader(shader);
      if (this.gl.getShaderParameter(shader,this.gl.COMPILE_STATUS)) {
        return shader;
      } else {
        $impl.Fatal$1(this.gl.getShaderInfoLog(shader));
      };
      return Result;
    };
  });
  this.TModelData = function (s) {
    if (s) {
      this.verticies = s.verticies;
      this.indicies = s.indicies;
      this.floatsPerVertex = s.floatsPerVertex;
    } else {
      this.verticies = null;
      this.indicies = null;
      this.floatsPerVertex = 0;
    };
    this.$equal = function (b) {
      return (this.verticies === b.verticies) && ((this.indicies === b.indicies) && (this.floatsPerVertex === b.floatsPerVertex));
    };
  };
  this.kModelVertexFloats = (3 + 2) + 3;
  this.TModelVertex = function (s) {
    if (s) {
      this.pos = new pas.GLTypes.TVec3(s.pos);
      this.texCoord = new pas.GLTypes.TVec2(s.texCoord);
      this.normal = new pas.GLTypes.TVec3(s.normal);
    } else {
      this.pos = new pas.GLTypes.TVec3();
      this.texCoord = new pas.GLTypes.TVec2();
      this.normal = new pas.GLTypes.TVec3();
    };
    this.$equal = function (b) {
      return this.pos.$equal(b.pos) && (this.texCoord.$equal(b.texCoord) && this.normal.$equal(b.normal));
    };
  };
  this.ModelVertexAddToArray = function (vertex, list) {
    list.push(vertex.pos.x);
    list.push(vertex.pos.y);
    list.push(vertex.pos.z);
    list.push(vertex.texCoord.x);
    list.push(vertex.texCoord.y);
    list.push(vertex.normal.x);
    list.push(vertex.normal.y);
    list.push(vertex.normal.z);
  };
  rtl.createClass($mod,"TModel",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.gl = null;
      this.data = new $mod.TModelData();
      this.vertexBuffer = null;
      this.indexBuffer = null;
    };
    this.$final = function () {
      this.gl = undefined;
      this.data = undefined;
      this.vertexBuffer = undefined;
      this.indexBuffer = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Create$1 = function (context, modelData) {
      this.gl = context;
      this.data = new $mod.TModelData(modelData);
      this.Load();
    };
    this.Draw = function () {
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer);
      this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer);
      this.EnableAttributes();
      this.gl.drawElements(this.gl.TRIANGLES,this.data.indicies.length,this.gl.UNSIGNED_SHORT,0);
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null);
      this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,null);
    };
    this.EnableAttributes = function () {
      var offset = 0;
      var stride = 0;
      offset = 0;
      stride = this.data.floatsPerVertex * $mod.GLSizeof(WebGLRenderingContext.FLOAT);
      this.gl.enableVertexAttribArray(0);
      this.gl.vertexAttribPointer(0,3,this.gl.FLOAT,false,stride,offset);
      offset += $mod.GLSizeof(WebGLRenderingContext.FLOAT) * 3;
      this.gl.enableVertexAttribArray(1);
      this.gl.vertexAttribPointer(1,2,this.gl.FLOAT,false,stride,offset);
      offset += $mod.GLSizeof(WebGLRenderingContext.FLOAT) * 2;
      this.gl.enableVertexAttribArray(2);
      this.gl.vertexAttribPointer(2,3,this.gl.FLOAT,false,stride,offset);
      offset += $mod.GLSizeof(WebGLRenderingContext.FLOAT) * 3;
    };
    this.Load = function () {
      this.indexBuffer = this.gl.createBuffer();
      this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer);
      this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,this.data.indicies,this.gl.STATIC_DRAW);
      this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,null);
      this.vertexBuffer = this.gl.createBuffer();
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer);
      this.gl.bufferData(this.gl.ARRAY_BUFFER,this.data.verticies,this.gl.STATIC_DRAW);
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null);
    };
  });
  this.GLSizeof = function (glType) {
    var Result = 0;
    var $tmp1 = glType;
    if (($tmp1 === WebGLRenderingContext.UNSIGNED_BYTE) || ($tmp1 === WebGLRenderingContext.BYTE)) {
      Result = 1}
     else if (($tmp1 === WebGLRenderingContext.SHORT) || ($tmp1 === WebGLRenderingContext.UNSIGNED_SHORT)) {
      Result = 2}
     else if (($tmp1 === WebGLRenderingContext.INT) || ($tmp1 === WebGLRenderingContext.UNSIGNED_INT)) {
      Result = 4}
     else if ($tmp1 === WebGLRenderingContext.FLOAT) {
      Result = 4}
     else {
      $impl.Fatal("GLSizeof type is invalid.");
    };
    return Result;
  };
  this.GLFatal = function (gl, messageString) {
    var error = 0;
    error = gl.getError();
    if (error !== WebGLRenderingContext.NO_ERROR) {
      var $tmp1 = error;
      if ($tmp1 === WebGLRenderingContext.INVALID_VALUE) {
        messageString = messageString + " (GL_INVALID_VALUE)"}
       else if ($tmp1 === WebGLRenderingContext.INVALID_OPERATION) {
        messageString = messageString + " (GL_INVALID_OPERATION)"}
       else if ($tmp1 === WebGLRenderingContext.INVALID_ENUM) {
        messageString = messageString + " (GL_INVALID_ENUM)"}
       else {
        messageString = (messageString + " ") + pas.SysUtils.IntToStr(error);
      };
      $impl.Fatal(messageString);
    };
  };
},null,function () {
  "use strict";
  var $mod = this;
  var $impl = $mod.$impl;
  $impl.Fatal = function (messageString) {
    pas.System.Writeln("*** FATAL: ",messageString);
    throw pas.SysUtils.Exception.$create("Create$1",["FATAL"]);
  };
  $impl.Fatal$1 = function (messageString) {
    pas.System.Writeln("*** FATAL: ",messageString);
    throw pas.SysUtils.Exception.$create("Create$1",["FATAL"]);
  };
});
rtl.module("Terrain",["System","Noise","Matrix","GLTypes","GLUtils","webgl","JS","math"],function () {
  "use strict";
  var $mod = this;
  rtl.createClass($mod,"TTerrain",pas.System.TObject,function () {
    this.$init = function () {
      pas.System.TObject.$init.call(this);
      this.noise$1 = null;
      this.gl = null;
      this.noiseOffset = new pas.GLTypes.TVec2();
      this.terrainSize = 0;
      this.terrainResolution = 0;
      this.heights = null;
      this.model = null;
    };
    this.$final = function () {
      this.noise$1 = undefined;
      this.gl = undefined;
      this.noiseOffset = undefined;
      this.heights = undefined;
      this.model = undefined;
      pas.System.TObject.$final.call(this);
    };
    this.Create$2 = function (context, inNoise, size, resolution, offset) {
      this.gl = context;
      this.noise$1 = inNoise;
      this.noiseOffset = new pas.GLTypes.TVec2(offset);
      this.terrainSize = size;
      this.terrainResolution = resolution;
    };
    this.GetHeightAtPoint = function (x, y) {
      var Result = 0.0;
      Result = rtl.getNumber(this.heights.GetValue(x,y));
      return Result;
    };
    this.GetWidth = function () {
      var Result = 0;
      Result = this.heights.GetWidth();
      return Result;
    };
    this.GetHeight = function () {
      var Result = 0;
      Result = this.heights.GetHeight();
      return Result;
    };
    this.Draw = function () {
      this.model.Draw();
    };
    this.Generate = function () {
      var vertex = new pas.GLUtils.TModelVertex();
      var topLeft = 0;
      var topRight = 0;
      var bottomLeft = 0;
      var bottomRight = 0;
      var x = 0;
      var y = 0;
      var gz = 0;
      var gx = 0;
      var verticies = null;
      var indicies = null;
      var data = new pas.GLUtils.TModelData();
      if (this.noise$1 === null) this.noise$1 = pas.Noise.TNoise.$create("Create$2",[pas.Noise.RandomNoiseSeed(1).slice(0)]);
      this.heights = pas.Matrix.TMatrix.$create("Create$1",[this.terrainResolution,this.terrainResolution]);
      verticies = new Array();
      indicies = new Array();
      for (var $l1 = 0, $end2 = this.heights.GetWidth() - 1; $l1 <= $end2; $l1++) {
        y = $l1;
        for (var $l3 = 0, $end4 = this.heights.GetHeight() - 1; $l3 <= $end4; $l3++) {
          x = $l3;
          vertex.pos.x = (x / (this.heights.GetWidth() - 1)) * this.terrainSize;
          vertex.pos.y = this.GetHeightForVertex(x,y,pas.System.Trunc(this.noiseOffset.x) + x,pas.System.Trunc(this.noiseOffset.y) + y);
          vertex.pos.z = (y / (this.heights.GetHeight() - 1)) * this.terrainSize;
          this.heights.SetValue(x,y,vertex.pos.y);
          vertex.normal = new pas.GLTypes.TVec3(this.CalculateNormal(x,y,pas.System.Trunc(this.noiseOffset.x) + x,pas.System.Trunc(this.noiseOffset.y) + y));
          vertex.texCoord.x = x / (this.heights.GetWidth() - 1);
          vertex.texCoord.y = y / (this.heights.GetHeight() - 1);
          pas.GLUtils.ModelVertexAddToArray(new pas.GLUtils.TModelVertex(vertex),verticies);
        };
      };
      for (var $l5 = 0, $end6 = this.heights.GetWidth() - 2; $l5 <= $end6; $l5++) {
        gz = $l5;
        for (var $l7 = 0, $end8 = this.heights.GetHeight() - 2; $l7 <= $end8; $l7++) {
          gx = $l7;
          topLeft = (gz * this.heights.GetWidth()) + gx;
          topRight = topLeft + 1;
          bottomLeft = ((gz + 1) * this.heights.GetWidth()) + gx;
          bottomRight = bottomLeft + 1;
          indicies.push(topLeft);
          indicies.push(bottomLeft);
          indicies.push(topRight);
          indicies.push(topRight);
          indicies.push(bottomLeft);
          indicies.push(bottomRight);
        };
      };
      data.verticies = new Float32Array(verticies);
      data.indicies = new Uint16Array(indicies);
      data.floatsPerVertex = 8;
      this.model = pas.GLUtils.TModel.$create("Create$1",[this.gl,new pas.GLUtils.TModelData(data)]);
    };
    this.GetHeightForVertex = function (localX, localY, x, y) {
      var Result = 0.0;
      Result = this.noise$1.GetNoise(x,y,this.heights.GetWidth(),this.heights.GetHeight(),4,3);
      Result = Math.pow(Result,5);
      Result = (Result * 20) - 6;
      return Result;
    };
    this.CalculateNormal = function (localX, localY, x, y) {
      var Result = new pas.GLTypes.TVec3();
      var heightL = 0.0;
      var heightR = 0.0;
      var heightD = 0.0;
      var heightU = 0.0;
      heightL = this.GetHeightForVertex(localX,localY,x - 1,y);
      heightR = this.GetHeightForVertex(localX,localY,x + 1,y);
      heightD = this.GetHeightForVertex(localX,localY,x,y - 1);
      heightU = this.GetHeightForVertex(localX,localY,x,y + 1);
      Result = new pas.GLTypes.TVec3(pas.GLTypes.V3(heightL - heightR,2.0,heightD - heightU));
      Result = new pas.GLTypes.TVec3(pas.GLTypes.Normalize(new pas.GLTypes.TVec3(Result)));
      return Result;
    };
  });
});
rtl.module("program",["System","Terrain","Noise","Mat4","GLUtils","GLTypes","SysUtils","browserconsole","Web","webgl","JS","math"],function () {
  "use strict";
  var $mod = this;
  this.gl = null;
  this.shader = null;
  this.projTransform = null;
  this.viewTransform = null;
  this.modelTransform = null;
  this.debugConsole = null;
  this.canvasAnimationHandler = 0;
  this.maps = null;
  this.camera = new pas.GLTypes.TVec3();
  this.lightPosition = new pas.GLTypes.TVec3();
  this.terrainNoise = null;
  this.terrainSize = 64 * 3;
  this.terrainResolution = 128;
  this.flySpeed = 1.3;
  this.visiblity = 4;
  this.textureLoaded = false;
  rtl.createClass($mod,"TTilingTerrain",pas.Terrain.TTerrain,function () {
    this.$init = function () {
      pas.Terrain.TTerrain.$init.call(this);
      this.neighbor = null;
    };
    this.$final = function () {
      this.neighbor = undefined;
      pas.Terrain.TTerrain.$final.call(this);
    };
    this.GetHeightForVertex = function (localX, localY, x, y) {
      var Result = 0.0;
      if ((localY === 0) && (this.neighbor !== null)) {
        Result = this.neighbor.GetHeightAtPoint(localX,(localY + this.neighbor.GetWidth()) - 1)}
       else {
        Result = this.noise$1.GetNoise(x,y,this.GetWidth(),this.GetHeight(),6,3);
        Result = Math.pow(Result,9) * 60;
      };
      return Result;
    };
  });
  this.DrawCanvas = function () {
    var terrainCoord = new pas.GLTypes.TVec3();
    var startIndex = 0;
    var endIndex = 0;
    var i = 0;
    var map = null;
    $mod.gl.clear($mod.gl.COLOR_BUFFER_BIT + $mod.gl.DEPTH_BUFFER_BIT);
    $mod.viewTransform = pas.Mat4.TMat4.$create("Identity");
    $mod.viewTransform = $mod.viewTransform.Multiply(pas.Mat4.TMat4.$create("Translate",[$mod.camera.x,$mod.camera.y,$mod.camera.z]));
    $mod.shader.SetUniformMat4("viewTransform",$mod.viewTransform);
    $mod.shader.SetUniformMat4("inverseViewTransform",$mod.viewTransform.Inverse());
    $mod.lightPosition.z += $mod.flySpeed;
    $mod.shader.SetUniformVec3("lightPosition",new pas.GLTypes.TVec3($mod.lightPosition));
    $mod.camera.z -= $mod.flySpeed;
    $mod.camera.y = -($mod.terrainSize / 4) + (Math.sin($mod.camera.z / $mod.terrainSize) * 14);
    $mod.camera.x = -($mod.terrainSize / 2) + (Math.cos($mod.camera.z / $mod.terrainSize) * 20);
    terrainCoord = new pas.GLTypes.TVec3(pas.GLTypes.Divide(new pas.GLTypes.TVec3($mod.camera),$mod.terrainSize));
    endIndex = pas.System.Trunc(Math.abs(terrainCoord.z));
    startIndex = endIndex - $mod.visiblity;
    if (startIndex < 0) startIndex = 0;
    for (var $l1 = startIndex, $end2 = endIndex; $l1 <= $end2; $l1++) {
      i = $l1;
      if (($mod.maps.length === 0) || (($mod.maps.length === i) && ($mod.maps[i] == null))) {
        map = $mod.TTilingTerrain.$create("Create$2",[$mod.gl,$mod.terrainNoise,$mod.terrainSize,$mod.terrainResolution,new pas.GLTypes.TVec2(pas.GLTypes.V2(0,$mod.terrainSize * i))]);
        if ((i - 1) >= 0) map.neighbor = rtl.getObject($mod.maps[i - 1]);
        map.Generate();
        $mod.maps.push(map);
        if ((startIndex - 1) >= 0) $mod.maps[startIndex - 1] = null;
      };
      map = rtl.getObject($mod.maps[i]);
      $mod.modelTransform = pas.Mat4.TMat4.$create("Identity");
      $mod.modelTransform = $mod.modelTransform.Multiply(pas.Mat4.TMat4.$create("Translate",[0,0,$mod.terrainSize * i]));
      $mod.shader.SetUniformMat4("modelTransform",$mod.modelTransform);
      map.Draw();
    };
  };
  this.AnimateCanvas = function (time) {
    if ($mod.textureLoaded) $mod.DrawCanvas();
    if ($mod.canvasAnimationHandler !== 0) $mod.canvasAnimationHandler = window.requestAnimationFrame($mod.AnimateCanvas);
  };
  this.StartAnimatingCanvas = function () {
    $mod.canvasAnimationHandler = window.requestAnimationFrame($mod.AnimateCanvas);
  };
  this.LoadedTexture = function (event) {
    var Result = false;
    var texture = null;
    texture = $mod.gl.createTexture();
    $mod.gl.bindTexture($mod.gl.TEXTURE_2D,texture);
    $mod.gl.texParameteri($mod.gl.TEXTURE_2D,$mod.gl.TEXTURE_WRAP_S,$mod.gl.CLAMP_TO_EDGE);
    $mod.gl.texParameteri($mod.gl.TEXTURE_2D,$mod.gl.TEXTURE_WRAP_T,$mod.gl.CLAMP_TO_EDGE);
    $mod.gl.texParameteri($mod.gl.TEXTURE_2D,$mod.gl.TEXTURE_MIN_FILTER,$mod.gl.LINEAR);
    $mod.gl.texParameteri($mod.gl.TEXTURE_2D,$mod.gl.TEXTURE_MAG_FILTER,$mod.gl.LINEAR);
    $mod.gl.texImage2D($mod.gl.TEXTURE_2D,0,$mod.gl.RGBA,$mod.gl.RGBA,$mod.gl.UNSIGNED_BYTE,rtl.getObject(event.target));
    $mod.textureLoaded = true;
    Result = true;
    return Result;
  };
  this.canvas = null;
  this.vertexShaderSource = "";
  this.fragmentShaderSource = "";
  this.img = null;
  $mod.$main = function () {
    $mod.debugConsole = document.getElementById("debug-console");
    $mod.canvas = document.createElement("canvas");
    $mod.canvas.width = 800;
    $mod.canvas.height = 600;
    document.body.appendChild($mod.canvas);
    $mod.gl = $mod.canvas.getContext("webgl");
    if ($mod.gl === null) {
      pas.System.Writeln("failed to load webgl!");
      return;
    };
    $mod.vertexShaderSource = document.getElementById("vertex.glsl").textContent;
    $mod.fragmentShaderSource = document.getElementById("fragment.glsl").textContent;
    $mod.shader = pas.GLUtils.TShader.$create("Create$1",[$mod.gl,$mod.vertexShaderSource,$mod.fragmentShaderSource]);
    $mod.shader.Compile();
    $mod.shader.BindAttribLocation(0,"in_position");
    $mod.shader.BindAttribLocation(1,"in_texCoord");
    $mod.shader.BindAttribLocation(2,"in_normal");
    $mod.shader.Link();
    $mod.shader.Use();
    $mod.gl.clearColor(0.9,0.9,0.9,1);
    $mod.gl.viewport(0,0,$mod.canvas.width,$mod.canvas.height);
    $mod.gl.enable($mod.gl.DEPTH_TEST);
    $mod.gl.enable($mod.gl.BLEND);
    $mod.gl.enable($mod.gl.CULL_FACE);
    $mod.gl.cullFace($mod.gl.BACK);
    $mod.projTransform = pas.Mat4.TMat4.$create("Perspective",[60.0,$mod.canvas.width / $mod.canvas.height,0.1,2000]);
    $mod.shader.SetUniformMat4("projTransform",$mod.projTransform);
    $mod.lightPosition = new pas.GLTypes.TVec3(pas.GLTypes.V3(0,$mod.terrainSize / 2,-($mod.terrainSize / 2)));
    $mod.shader.SetUniformVec3("lightPosition",new pas.GLTypes.TVec3($mod.lightPosition));
    $mod.shader.SetUniformVec3("lightColor",new pas.GLTypes.TVec3(pas.GLTypes.V3(1,1,1)));
    $mod.shader.SetUniformFloat("shineDamper",1000);
    $mod.shader.SetUniformFloat("reflectivity",1);
    $mod.gl.clear($mod.gl.COLOR_BUFFER_BIT + $mod.gl.DEPTH_BUFFER_BIT);
    $mod.camera.x = -($mod.terrainSize / 2);
    $mod.camera.y = -($mod.terrainSize / 4);
    $mod.camera.z = -($mod.terrainSize / 2);
    $mod.img = document.createElement("IMG");
    $mod.img.setAttribute("height","512");
    $mod.img.setAttribute("width","512");
    $mod.img.setAttribute("crossOrigin","anonymous");
    $mod.img.setAttribute("src","res\/ground.jpg");
    $mod.img.onload = $mod.LoadedTexture;
    $mod.terrainNoise = pas.Noise.TNoise.$create("Create$2",[pas.Noise.RandomNoiseSeed(1).slice(0)]);
    $mod.maps = new Array();
    $mod.StartAnimatingCanvas();
  };
});
