浏览代码

initial commit

Ryan Joseph 7 年之前
当前提交
9ca87e09b4
共有 15 个文件被更改,包括 20774 次插入0 次删除
  1. 2366 0
      GLTypes.js
  2. 60 0
      GLTypes.pas
  3. 2447 0
      GLUtils.js
  4. 140 0
      GLUtils.pas
  5. 6356 0
      Mat4.js
  6. 275 0
      Mat4.pas
  7. 1958 0
      MemoryBuffer.js
  8. 51 0
      MemoryBuffer.pas
  9. 34 0
      Pas2JS_WebGL.html
  10. 1728 0
      Pas2JS_WebGL.js
  11. 168 0
      Pas2JS_WebGL.pas
  12. 2383 0
      Vectors.js
  13. 54 0
      Vectors.pas
  14. 2284 0
      webgl2.js
  15. 470 0
      webgl2.pas

+ 2366 - 0
GLTypes.js

@@ -0,0 +1,2366 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{477BDEF6-BCEE-3E15-AE5E-F143062B3074}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{D196F09F-E044-3F6B-A5AF-66A528E7F308}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{D19D1377-E638-3855-AA5D-ACEB81F26308}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("Web",["System","Types","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TJSClientRect = function (s) {
+    if (s) {
+      this.left = s.left;
+      this.top = s.top;
+      this.right = s.right;
+      this.bottom = s.bottom;
+    } else {
+      this.left = 0.0;
+      this.top = 0.0;
+      this.right = 0.0;
+      this.bottom = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.left === b.left) && ((this.top === b.top) && ((this.right === b.right) && (this.bottom === b.bottom)));
+    };
+  };
+  this.TJSElementCreationOptions = function (s) {
+    if (s) {
+      this.named = s.named;
+    } else {
+      this.named = "";
+    };
+    this.$equal = function (b) {
+      return this.named === b.named;
+    };
+  };
+  this.TJSEventInit = function (s) {
+    if (s) {
+      this.bubbles = s.bubbles;
+      this.cancelable = s.cancelable;
+      this.scoped = s.scoped;
+      this.composed = s.composed;
+    } else {
+      this.bubbles = false;
+      this.cancelable = false;
+      this.scoped = false;
+      this.composed = false;
+    };
+    this.$equal = function (b) {
+      return (this.bubbles === b.bubbles) && ((this.cancelable === b.cancelable) && ((this.scoped === b.scoped) && (this.composed === b.composed)));
+    };
+  };
+  rtl.createClassExt($mod,"TJSAnimationEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJSLoadEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJsPageTransitionEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClass($mod,"TJSIDBTransactionMode",pas.System.TObject,function () {
+    this.readonly = "readonly";
+    this.readwrite = "readwrite";
+    this.versionchange = "versionchange";
+  });
+  this.TJSIDBIndexParameters = function (s) {
+    if (s) {
+      this.unique = s.unique;
+      this.multiEntry = s.multiEntry;
+      this.locale = s.locale;
+    } else {
+      this.unique = false;
+      this.multiEntry = false;
+      this.locale = "";
+    };
+    this.$equal = function (b) {
+      return (this.unique === b.unique) && ((this.multiEntry === b.multiEntry) && (this.locale === b.locale));
+    };
+  };
+  this.TJSCreateObjectStoreOptions = function (s) {
+    if (s) {
+      this.keyPath = s.keyPath;
+      this.autoIncrement = s.autoIncrement;
+    } else {
+      this.keyPath = undefined;
+      this.autoIncrement = false;
+    };
+    this.$equal = function (b) {
+      return (this.keyPath === b.keyPath) && (this.autoIncrement === b.autoIncrement);
+    };
+  };
+  this.TJSPositionError = function (s) {
+    if (s) {
+      this.code = s.code;
+      this.message = s.message;
+    } else {
+      this.code = 0;
+      this.message = "";
+    };
+    this.$equal = function (b) {
+      return (this.code === b.code) && (this.message === b.message);
+    };
+  };
+  this.TJSPositionOptions = function (s) {
+    if (s) {
+      this.enableHighAccuracy = s.enableHighAccuracy;
+      this.timeout = s.timeout;
+      this.maximumAge = s.maximumAge;
+    } else {
+      this.enableHighAccuracy = false;
+      this.timeout = 0;
+      this.maximumAge = 0;
+    };
+    this.$equal = function (b) {
+      return (this.enableHighAccuracy === b.enableHighAccuracy) && ((this.timeout === b.timeout) && (this.maximumAge === b.maximumAge));
+    };
+  };
+  this.TJSCoordinates = function (s) {
+    if (s) {
+      this.latitude = s.latitude;
+      this.longitude = s.longitude;
+      this.altitude = s.altitude;
+      this.accuracy = s.accuracy;
+      this.altitudeAccuracy = s.altitudeAccuracy;
+      this.heading = s.heading;
+      this.speed = s.speed;
+    } else {
+      this.latitude = 0.0;
+      this.longitude = 0.0;
+      this.altitude = 0.0;
+      this.accuracy = 0.0;
+      this.altitudeAccuracy = 0.0;
+      this.heading = 0.0;
+      this.speed = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.latitude === b.latitude) && ((this.longitude === b.longitude) && ((this.altitude === b.altitude) && ((this.accuracy === b.accuracy) && ((this.altitudeAccuracy === b.altitudeAccuracy) && ((this.heading === b.heading) && (this.speed === b.speed))))));
+    };
+  };
+  this.TJSPosition = function (s) {
+    if (s) {
+      this.coords = new $mod.TJSCoordinates(s.coords);
+      this.timestamp = s.timestamp;
+    } else {
+      this.coords = new $mod.TJSCoordinates();
+      this.timestamp = "";
+    };
+    this.$equal = function (b) {
+      return this.coords.$equal(b.coords) && (this.timestamp === b.timestamp);
+    };
+  };
+  this.TJSServiceWorkerContainerOptions = function (s) {
+    if (s) {
+      this.scope = s.scope;
+    } else {
+      this.scope = "";
+    };
+    this.$equal = function (b) {
+      return this.scope === b.scope;
+    };
+  };
+  this.TJSTextMetrics = function (s) {
+    if (s) {
+      this.width = s.width;
+      this.actualBoundingBoxLeft = s.actualBoundingBoxLeft;
+      this.actualBoundingBoxRight = s.actualBoundingBoxRight;
+      this.fontBoundingBoxAscent = s.fontBoundingBoxAscent;
+      this.fontBoundingBoxDescent = s.fontBoundingBoxDescent;
+      this.actualBoundingBoxAscent = s.actualBoundingBoxAscent;
+      this.actualBoundingBoxDescent = s.actualBoundingBoxDescent;
+      this.emHeightAscent = s.emHeightAscent;
+      this.emHeightDescent = s.emHeightDescent;
+      this.hangingBaseline = s.hangingBaseline;
+      this.alphabeticBaseline = s.alphabeticBaseline;
+      this.ideographicBaseline = s.ideographicBaseline;
+    } else {
+      this.width = 0.0;
+      this.actualBoundingBoxLeft = 0.0;
+      this.actualBoundingBoxRight = 0.0;
+      this.fontBoundingBoxAscent = 0.0;
+      this.fontBoundingBoxDescent = 0.0;
+      this.actualBoundingBoxAscent = 0.0;
+      this.actualBoundingBoxDescent = 0.0;
+      this.emHeightAscent = 0.0;
+      this.emHeightDescent = 0.0;
+      this.hangingBaseline = 0.0;
+      this.alphabeticBaseline = 0.0;
+      this.ideographicBaseline = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.width === b.width) && ((this.actualBoundingBoxLeft === b.actualBoundingBoxLeft) && ((this.actualBoundingBoxRight === b.actualBoundingBoxRight) && ((this.fontBoundingBoxAscent === b.fontBoundingBoxAscent) && ((this.fontBoundingBoxDescent === b.fontBoundingBoxDescent) && ((this.actualBoundingBoxAscent === b.actualBoundingBoxAscent) && ((this.actualBoundingBoxDescent === b.actualBoundingBoxDescent) && ((this.emHeightAscent === b.emHeightAscent) && ((this.emHeightDescent === b.emHeightDescent) && ((this.hangingBaseline === b.hangingBaseline) && ((this.alphabeticBaseline === b.alphabeticBaseline) && (this.ideographicBaseline === b.ideographicBaseline)))))))))));
+    };
+  };
+  this.TJSWheelEventInit = function (s) {
+    if (s) {
+      this.deltaX = s.deltaX;
+      this.deltaY = s.deltaY;
+      this.deltaZ = s.deltaZ;
+      this.deltaMode = s.deltaMode;
+    } else {
+      this.deltaX = 0.0;
+      this.deltaY = 0.0;
+      this.deltaZ = 0.0;
+      this.deltaMode = 0;
+    };
+    this.$equal = function (b) {
+      return (this.deltaX === b.deltaX) && ((this.deltaY === b.deltaY) && ((this.deltaZ === b.deltaZ) && (this.deltaMode === b.deltaMode)));
+    };
+  };
+  rtl.createClass($mod,"TJSKeyNames",pas.System.TObject,function () {
+    this.Alt = "Alt";
+    this.AltGraph = "AltGraph";
+    this.CapsLock = "CapsLock";
+    this.Control = "Control";
+    this.Fn = "Fn";
+    this.FnLock = "FnLock";
+    this.Hyper = "Hyper";
+    this.Meta = "Meta";
+    this.NumLock = "NumLock";
+    this.ScrollLock = "ScrollLock";
+    this.Shift = "Shift";
+    this.Super = "Super";
+    this.symbol = "Symbol";
+    this.SymbolLock = "SymbolLock";
+    this.Enter = "Enter";
+    this.Tab = "Tab";
+    this.Space = " ";
+    this.ArrowDown = "ArrowDown";
+    this.ArrowLeft = "ArrowLeft";
+    this.ArrowRight = "ArrowRight";
+    this.ArrowUp = "ArrowUp";
+    this._End = "End";
+    this.Home = "Home";
+    this.PageDown = "PageDown";
+    this.PageUp = "PageUp";
+    this.BackSpace = "Backspace";
+    this.Clear = "Clear";
+    this.Copy = "Copy";
+    this.CrSel = "CrSel";
+    this.Cut = "Cut";
+    this.Delete = "Delete";
+    this.EraseEof = "EraseEof";
+    this.ExSel = "ExSel";
+    this.Insert = "Insert";
+    this.Paste = "Paste";
+    this.Redo = "Redo";
+    this.Undo = "Undo";
+    this.Accept = "Accept";
+    this.Again = "Again";
+    this.Attn = "Attn";
+    this.Cancel = "Cancel";
+    this.ContextMenu = "Contextmenu";
+    this.Escape = "Escape";
+    this.Execute = "Execute";
+    this.Find = "Find";
+    this.Finish = "Finish";
+    this.Help = "Help";
+    this.Pause = "Pause";
+    this.Play = "Play";
+    this.Props = "Props";
+    this.Select = "Select";
+    this.ZoomIn = "ZoomIn";
+    this.ZoomOut = "ZoomOut";
+    this.BrightnessDown = "BrightnessDown";
+    this.BrightnessUp = "BrightnessUp";
+    this.Eject = "Eject";
+    this.LogOff = "LogOff";
+    this.Power = "Power";
+    this.PowerOff = "PowerOff";
+    this.PrintScreen = "PrintScreen";
+    this.Hibernate = "Hibernate";
+    this.Standby = "Standby";
+    this.WakeUp = "WakeUp";
+    this.AllCandidates = "AllCandidates";
+    this.Alphanumeric = "Alphanumeric";
+    this.CodeInput = "CodeInput";
+    this.Compose = "Compose";
+    this.Convert = "Convert";
+    this.Dead = "Dead";
+    this.FinalMode = "FinalMode";
+    this.GroupFirst = "GroupFirst";
+    this.GroupLast = "GroupLast";
+    this.GroupNext = "GroupNext";
+    this.GroupPrevious = "GroupPrevious";
+    this.ModelChange = "ModelChange";
+    this.NextCandidate = "NextCandidate";
+    this.NonConvert = "NonConvert";
+    this.PreviousCandidate = "PreviousCandidate";
+    this.Process = "Process";
+    this.SingleCandidate = "SingleCandidate";
+    this.HangulMode = "HangulMode";
+    this.HanjaMode = "HanjaMode";
+    this.JunjaMode = "JunjaMode";
+    this.Eisu = "Eisu";
+    this.Hankaku = "Hankaku";
+    this.Hiranga = "Hiranga";
+    this.HirangaKatakana = "HirangaKatakana";
+    this.KanaMode = "KanaMode";
+    this.Katakana = "Katakana";
+    this.Romaji = "Romaji";
+    this.Zenkaku = "Zenkaku";
+    this.ZenkakuHanaku = "ZenkakuHanaku";
+    this.F1 = "F1";
+    this.F2 = "F2";
+    this.F3 = "F3";
+    this.F4 = "F4";
+    this.F5 = "F5";
+    this.F6 = "F6";
+    this.F7 = "F7";
+    this.F8 = "F8";
+    this.F9 = "F9";
+    this.F10 = "F10";
+    this.F11 = "F11";
+    this.F12 = "F12";
+    this.F13 = "F13";
+    this.F14 = "F14";
+    this.F15 = "F15";
+    this.F16 = "F16";
+    this.F17 = "F17";
+    this.F18 = "F18";
+    this.F19 = "F19";
+    this.F20 = "F20";
+    this.Soft1 = "Soft1";
+    this.Soft2 = "Soft2";
+    this.Soft3 = "Soft3";
+    this.Soft4 = "Soft4";
+    this.Decimal = "Decimal";
+    this.Key11 = "Key11";
+    this.Key12 = "Key12";
+    this.Multiply = "Multiply";
+    this.Add = "Add";
+    this.NumClear = "Clear";
+    this.Divide = "Divide";
+    this.Subtract = "Subtract";
+    this.Separator = "Separator";
+    this.AppSwitch = "AppSwitch";
+    this.Call = "Call";
+    this.Camera = "Camera";
+    this.CameraFocus = "CameraFocus";
+    this.EndCall = "EndCall";
+    this.GoBack = "GoBack";
+    this.GoHome = "GoHome";
+    this.HeadsetHook = "HeadsetHook";
+    this.LastNumberRedial = "LastNumberRedial";
+    this.Notification = "Notification";
+    this.MannerMode = "MannerMode";
+    this.VoiceDial = "VoiceDial";
+  });
+});
+rtl.module("webgl",["System","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("GLTypes",["System","webgl"],function () {
+  "use strict";
+  var $mod = this;
+  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.GLTexVertex = function (s) {
+    if (s) {
+      this.pos = s.pos;
+      this.color = s.color;
+    } else {
+      this.pos = [];
+      this.color = [];
+    };
+    this.$equal = function (b) {
+      return (this.pos === b.pos) && (this.color === b.color);
+    };
+  };
+  this.GLTexVertex_Sizeof = function () {
+    var Result = 0;
+    Result = $mod.TVec2_Sizeof() + $mod.TRGBAf_Sizeof();
+    return Result;
+  };
+  this.TVec2_Sizeof = function () {
+    var Result = 0;
+    Result = 4 * 2;
+    return Result;
+  };
+  this.TRGBAb_Sizeof = function () {
+    var Result = 0;
+    Result = 1 * 4;
+    return Result;
+  };
+  this.TRGBAf_Sizeof = function () {
+    var Result = 0;
+    Result = 4 * 4;
+    return Result;
+  };
+  this.V2 = function (x, y) {
+    var Result = [];
+    Result[0] = x;
+    Result[1] = y;
+    return Result;
+  };
+  this.RGBAb = function (r, g, b, a) {
+    var Result = [];
+    Result[0] = r;
+    Result[1] = g;
+    Result[2] = b;
+    Result[3] = a;
+    return Result;
+  };
+  this.RGBAf = function (r, g, b, a) {
+    var Result = [];
+    Result[0] = r;
+    Result[1] = g;
+    Result[2] = b;
+    Result[3] = a;
+    return Result;
+  };
+});

+ 60 - 0
GLTypes.pas

@@ -0,0 +1,60 @@
+unit GLTypes;
+interface
+uses
+	WebGL, JS;
+
+// TODO: when advanced record syntax is added move these to records
+
+type
+	TVec2 = array of GLfloat;
+	TRGBAb = array of GLubyte;
+	TRGBAf = array of GLfloat;
+
+function TVec2_Sizeof: integer;
+function TRGBAb_Sizeof: integer;
+function TRGBAf_Sizeof: integer;
+
+function V2(x, y: GLfloat): TVec2;
+function RGBAb(r, g, b, a: GLubyte): TRGBAb;
+function RGBAf(r, g, b, a: GLfloat): TRGBAf;
+
+implementation
+
+function V2(x, y: GLfloat): TVec2;
+begin
+	result[0] := x;
+	result[1] := y;
+end;
+
+function RGBAb(r, g, b, a: GLubyte): TRGBAb;
+begin
+	result[0] := r;
+	result[1] := g;
+	result[2] := b;
+	result[3] := a;
+end;
+
+function RGBAf(r, g, b, a: GLfloat): TRGBAf;
+begin
+	result[0] := r;
+	result[1] := g;
+	result[2] := b;
+	result[3] := a;
+end;
+
+function TVec2_Sizeof: integer;
+begin
+	result := (4 * 2);
+end;
+
+function TRGBAb_Sizeof: integer;
+begin
+	result := (1 * 4);
+end;
+
+function TRGBAf_Sizeof: integer;
+begin
+	result := (4 * 4);
+end;
+
+end.

+ 2447 - 0
GLUtils.js

@@ -0,0 +1,2447 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{88387EF6-BCEE-3E17-9E85-5D491ED4FC10}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{ECEC7568-4E50-30C9-A2F0-439342DE2ADB}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{9791C368-4E51-3424-A3CE-D4911D54F385}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("Web",["System","Types","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TJSClientRect = function (s) {
+    if (s) {
+      this.left = s.left;
+      this.top = s.top;
+      this.right = s.right;
+      this.bottom = s.bottom;
+    } else {
+      this.left = 0.0;
+      this.top = 0.0;
+      this.right = 0.0;
+      this.bottom = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.left === b.left) && ((this.top === b.top) && ((this.right === b.right) && (this.bottom === b.bottom)));
+    };
+  };
+  this.TJSElementCreationOptions = function (s) {
+    if (s) {
+      this.named = s.named;
+    } else {
+      this.named = "";
+    };
+    this.$equal = function (b) {
+      return this.named === b.named;
+    };
+  };
+  this.TJSEventInit = function (s) {
+    if (s) {
+      this.bubbles = s.bubbles;
+      this.cancelable = s.cancelable;
+      this.scoped = s.scoped;
+      this.composed = s.composed;
+    } else {
+      this.bubbles = false;
+      this.cancelable = false;
+      this.scoped = false;
+      this.composed = false;
+    };
+    this.$equal = function (b) {
+      return (this.bubbles === b.bubbles) && ((this.cancelable === b.cancelable) && ((this.scoped === b.scoped) && (this.composed === b.composed)));
+    };
+  };
+  rtl.createClassExt($mod,"TJSAnimationEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJSLoadEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJsPageTransitionEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClass($mod,"TJSIDBTransactionMode",pas.System.TObject,function () {
+    this.readonly = "readonly";
+    this.readwrite = "readwrite";
+    this.versionchange = "versionchange";
+  });
+  this.TJSIDBIndexParameters = function (s) {
+    if (s) {
+      this.unique = s.unique;
+      this.multiEntry = s.multiEntry;
+      this.locale = s.locale;
+    } else {
+      this.unique = false;
+      this.multiEntry = false;
+      this.locale = "";
+    };
+    this.$equal = function (b) {
+      return (this.unique === b.unique) && ((this.multiEntry === b.multiEntry) && (this.locale === b.locale));
+    };
+  };
+  this.TJSCreateObjectStoreOptions = function (s) {
+    if (s) {
+      this.keyPath = s.keyPath;
+      this.autoIncrement = s.autoIncrement;
+    } else {
+      this.keyPath = undefined;
+      this.autoIncrement = false;
+    };
+    this.$equal = function (b) {
+      return (this.keyPath === b.keyPath) && (this.autoIncrement === b.autoIncrement);
+    };
+  };
+  this.TJSPositionError = function (s) {
+    if (s) {
+      this.code = s.code;
+      this.message = s.message;
+    } else {
+      this.code = 0;
+      this.message = "";
+    };
+    this.$equal = function (b) {
+      return (this.code === b.code) && (this.message === b.message);
+    };
+  };
+  this.TJSPositionOptions = function (s) {
+    if (s) {
+      this.enableHighAccuracy = s.enableHighAccuracy;
+      this.timeout = s.timeout;
+      this.maximumAge = s.maximumAge;
+    } else {
+      this.enableHighAccuracy = false;
+      this.timeout = 0;
+      this.maximumAge = 0;
+    };
+    this.$equal = function (b) {
+      return (this.enableHighAccuracy === b.enableHighAccuracy) && ((this.timeout === b.timeout) && (this.maximumAge === b.maximumAge));
+    };
+  };
+  this.TJSCoordinates = function (s) {
+    if (s) {
+      this.latitude = s.latitude;
+      this.longitude = s.longitude;
+      this.altitude = s.altitude;
+      this.accuracy = s.accuracy;
+      this.altitudeAccuracy = s.altitudeAccuracy;
+      this.heading = s.heading;
+      this.speed = s.speed;
+    } else {
+      this.latitude = 0.0;
+      this.longitude = 0.0;
+      this.altitude = 0.0;
+      this.accuracy = 0.0;
+      this.altitudeAccuracy = 0.0;
+      this.heading = 0.0;
+      this.speed = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.latitude === b.latitude) && ((this.longitude === b.longitude) && ((this.altitude === b.altitude) && ((this.accuracy === b.accuracy) && ((this.altitudeAccuracy === b.altitudeAccuracy) && ((this.heading === b.heading) && (this.speed === b.speed))))));
+    };
+  };
+  this.TJSPosition = function (s) {
+    if (s) {
+      this.coords = new $mod.TJSCoordinates(s.coords);
+      this.timestamp = s.timestamp;
+    } else {
+      this.coords = new $mod.TJSCoordinates();
+      this.timestamp = "";
+    };
+    this.$equal = function (b) {
+      return this.coords.$equal(b.coords) && (this.timestamp === b.timestamp);
+    };
+  };
+  this.TJSServiceWorkerContainerOptions = function (s) {
+    if (s) {
+      this.scope = s.scope;
+    } else {
+      this.scope = "";
+    };
+    this.$equal = function (b) {
+      return this.scope === b.scope;
+    };
+  };
+  this.TJSTextMetrics = function (s) {
+    if (s) {
+      this.width = s.width;
+      this.actualBoundingBoxLeft = s.actualBoundingBoxLeft;
+      this.actualBoundingBoxRight = s.actualBoundingBoxRight;
+      this.fontBoundingBoxAscent = s.fontBoundingBoxAscent;
+      this.fontBoundingBoxDescent = s.fontBoundingBoxDescent;
+      this.actualBoundingBoxAscent = s.actualBoundingBoxAscent;
+      this.actualBoundingBoxDescent = s.actualBoundingBoxDescent;
+      this.emHeightAscent = s.emHeightAscent;
+      this.emHeightDescent = s.emHeightDescent;
+      this.hangingBaseline = s.hangingBaseline;
+      this.alphabeticBaseline = s.alphabeticBaseline;
+      this.ideographicBaseline = s.ideographicBaseline;
+    } else {
+      this.width = 0.0;
+      this.actualBoundingBoxLeft = 0.0;
+      this.actualBoundingBoxRight = 0.0;
+      this.fontBoundingBoxAscent = 0.0;
+      this.fontBoundingBoxDescent = 0.0;
+      this.actualBoundingBoxAscent = 0.0;
+      this.actualBoundingBoxDescent = 0.0;
+      this.emHeightAscent = 0.0;
+      this.emHeightDescent = 0.0;
+      this.hangingBaseline = 0.0;
+      this.alphabeticBaseline = 0.0;
+      this.ideographicBaseline = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.width === b.width) && ((this.actualBoundingBoxLeft === b.actualBoundingBoxLeft) && ((this.actualBoundingBoxRight === b.actualBoundingBoxRight) && ((this.fontBoundingBoxAscent === b.fontBoundingBoxAscent) && ((this.fontBoundingBoxDescent === b.fontBoundingBoxDescent) && ((this.actualBoundingBoxAscent === b.actualBoundingBoxAscent) && ((this.actualBoundingBoxDescent === b.actualBoundingBoxDescent) && ((this.emHeightAscent === b.emHeightAscent) && ((this.emHeightDescent === b.emHeightDescent) && ((this.hangingBaseline === b.hangingBaseline) && ((this.alphabeticBaseline === b.alphabeticBaseline) && (this.ideographicBaseline === b.ideographicBaseline)))))))))));
+    };
+  };
+  this.TJSWheelEventInit = function (s) {
+    if (s) {
+      this.deltaX = s.deltaX;
+      this.deltaY = s.deltaY;
+      this.deltaZ = s.deltaZ;
+      this.deltaMode = s.deltaMode;
+    } else {
+      this.deltaX = 0.0;
+      this.deltaY = 0.0;
+      this.deltaZ = 0.0;
+      this.deltaMode = 0;
+    };
+    this.$equal = function (b) {
+      return (this.deltaX === b.deltaX) && ((this.deltaY === b.deltaY) && ((this.deltaZ === b.deltaZ) && (this.deltaMode === b.deltaMode)));
+    };
+  };
+  rtl.createClass($mod,"TJSKeyNames",pas.System.TObject,function () {
+    this.Alt = "Alt";
+    this.AltGraph = "AltGraph";
+    this.CapsLock = "CapsLock";
+    this.Control = "Control";
+    this.Fn = "Fn";
+    this.FnLock = "FnLock";
+    this.Hyper = "Hyper";
+    this.Meta = "Meta";
+    this.NumLock = "NumLock";
+    this.ScrollLock = "ScrollLock";
+    this.Shift = "Shift";
+    this.Super = "Super";
+    this.symbol = "Symbol";
+    this.SymbolLock = "SymbolLock";
+    this.Enter = "Enter";
+    this.Tab = "Tab";
+    this.Space = " ";
+    this.ArrowDown = "ArrowDown";
+    this.ArrowLeft = "ArrowLeft";
+    this.ArrowRight = "ArrowRight";
+    this.ArrowUp = "ArrowUp";
+    this._End = "End";
+    this.Home = "Home";
+    this.PageDown = "PageDown";
+    this.PageUp = "PageUp";
+    this.BackSpace = "Backspace";
+    this.Clear = "Clear";
+    this.Copy = "Copy";
+    this.CrSel = "CrSel";
+    this.Cut = "Cut";
+    this.Delete = "Delete";
+    this.EraseEof = "EraseEof";
+    this.ExSel = "ExSel";
+    this.Insert = "Insert";
+    this.Paste = "Paste";
+    this.Redo = "Redo";
+    this.Undo = "Undo";
+    this.Accept = "Accept";
+    this.Again = "Again";
+    this.Attn = "Attn";
+    this.Cancel = "Cancel";
+    this.ContextMenu = "Contextmenu";
+    this.Escape = "Escape";
+    this.Execute = "Execute";
+    this.Find = "Find";
+    this.Finish = "Finish";
+    this.Help = "Help";
+    this.Pause = "Pause";
+    this.Play = "Play";
+    this.Props = "Props";
+    this.Select = "Select";
+    this.ZoomIn = "ZoomIn";
+    this.ZoomOut = "ZoomOut";
+    this.BrightnessDown = "BrightnessDown";
+    this.BrightnessUp = "BrightnessUp";
+    this.Eject = "Eject";
+    this.LogOff = "LogOff";
+    this.Power = "Power";
+    this.PowerOff = "PowerOff";
+    this.PrintScreen = "PrintScreen";
+    this.Hibernate = "Hibernate";
+    this.Standby = "Standby";
+    this.WakeUp = "WakeUp";
+    this.AllCandidates = "AllCandidates";
+    this.Alphanumeric = "Alphanumeric";
+    this.CodeInput = "CodeInput";
+    this.Compose = "Compose";
+    this.Convert = "Convert";
+    this.Dead = "Dead";
+    this.FinalMode = "FinalMode";
+    this.GroupFirst = "GroupFirst";
+    this.GroupLast = "GroupLast";
+    this.GroupNext = "GroupNext";
+    this.GroupPrevious = "GroupPrevious";
+    this.ModelChange = "ModelChange";
+    this.NextCandidate = "NextCandidate";
+    this.NonConvert = "NonConvert";
+    this.PreviousCandidate = "PreviousCandidate";
+    this.Process = "Process";
+    this.SingleCandidate = "SingleCandidate";
+    this.HangulMode = "HangulMode";
+    this.HanjaMode = "HanjaMode";
+    this.JunjaMode = "JunjaMode";
+    this.Eisu = "Eisu";
+    this.Hankaku = "Hankaku";
+    this.Hiranga = "Hiranga";
+    this.HirangaKatakana = "HirangaKatakana";
+    this.KanaMode = "KanaMode";
+    this.Katakana = "Katakana";
+    this.Romaji = "Romaji";
+    this.Zenkaku = "Zenkaku";
+    this.ZenkakuHanaku = "ZenkakuHanaku";
+    this.F1 = "F1";
+    this.F2 = "F2";
+    this.F3 = "F3";
+    this.F4 = "F4";
+    this.F5 = "F5";
+    this.F6 = "F6";
+    this.F7 = "F7";
+    this.F8 = "F8";
+    this.F9 = "F9";
+    this.F10 = "F10";
+    this.F11 = "F11";
+    this.F12 = "F12";
+    this.F13 = "F13";
+    this.F14 = "F14";
+    this.F15 = "F15";
+    this.F16 = "F16";
+    this.F17 = "F17";
+    this.F18 = "F18";
+    this.F19 = "F19";
+    this.F20 = "F20";
+    this.Soft1 = "Soft1";
+    this.Soft2 = "Soft2";
+    this.Soft3 = "Soft3";
+    this.Soft4 = "Soft4";
+    this.Decimal = "Decimal";
+    this.Key11 = "Key11";
+    this.Key12 = "Key12";
+    this.Multiply = "Multiply";
+    this.Add = "Add";
+    this.NumClear = "Clear";
+    this.Divide = "Divide";
+    this.Subtract = "Subtract";
+    this.Separator = "Separator";
+    this.AppSwitch = "AppSwitch";
+    this.Call = "Call";
+    this.Camera = "Camera";
+    this.CameraFocus = "CameraFocus";
+    this.EndCall = "EndCall";
+    this.GoBack = "GoBack";
+    this.GoHome = "GoHome";
+    this.HeadsetHook = "HeadsetHook";
+    this.LastNumberRedial = "LastNumberRedial";
+    this.Notification = "Notification";
+    this.MannerMode = "MannerMode";
+    this.VoiceDial = "VoiceDial";
+  });
+});
+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.DefaultCRTConsoleStyle = (((((((((((".pasconsole { " + pas.System.sLineBreak) + "font-family: courier;") + pas.System.sLineBreak) + "font-size: 14px;") + pas.System.sLineBreak) + "background: #000;") + pas.System.sLineBreak) + "color: #14fdce;") + 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("webgl",["System","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("GLUtils",["System","browserconsole","webgl","JS"],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.GetAttribLocation = function (name) {
+      var Result = 0;
+      Result = this.gl.getAttribLocation(this.programID,name);
+      return Result;
+    };
+    this.BindAttribLocation = function (index, name) {
+      this.gl.bindAttribLocation(this.programID,index,name);
+    };
+    this.SetUniformMat4 = function (name, value) {
+      var location = null;
+      location = this.gl.getUniformLocation(this.programID,name);
+      $impl.GLFatal(this.gl,"gl.getUniformLocation");
+      this.gl.uniformMatrix4fv(location,false,value);
+      $impl.GLFatal(this.gl,"gl.uniformMatrix4fv");
+    };
+    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;
+    };
+  });
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.Fatal = function (messageString) {
+    pas.System.Writeln("*** FATAL: ",messageString);
+    return;
+  };
+  $impl.Fatal$1 = function (messageString) {
+    pas.System.Writeln("*** FATAL: ",messageString);
+    return;
+  };
+  $impl.GLFatal = function (gl, messageString) {
+    var error = 0;
+    error = gl.getError();
+    if (error !== gl.NO_ERROR) {
+      $impl.Fatal(messageString);
+    };
+  };
+});

+ 140 - 0
GLUtils.pas

@@ -0,0 +1,140 @@
+unit GLUtils;
+interface
+uses
+	BrowserConsole, Web, WebGL, JS;
+
+type
+	TShader = class
+		public
+			constructor Create (context: TJSWebGLRenderingContext; vertexShaderSource, fragmentShaderSource: string);
+			procedure Compile;
+			procedure Link;
+			procedure Use;
+
+			function GetAttribLocation (name: string): GLint;
+			procedure BindAttribLocation (index: GLuint; name: string);
+
+			procedure SetUniformMat4 (name: string; value: TJSFloat32List);
+
+		private
+			gl: TJSWebGLRenderingContext;
+			vertexShader: TJSWebGLShader;
+			fragmentShader: TJSWebGLShader;
+			programID: TJSWebGLProgram;
+
+			function CreateShader (theType: GLenum; source: string): TJSWebGLShader;
+	end;
+
+
+implementation
+
+procedure Fatal (messageString: string); overload;
+begin
+	writeln('*** FATAL: ', messageString);
+	exit;
+end;
+
+// TODO: toll free bridge to FPC strings
+procedure Fatal (messageString: TJSString); overload;
+begin
+	writeln('*** FATAL: ', messageString);
+	exit;
+end;
+
+procedure GLFatal (gl: TJSWebGLRenderingContext; messageString: string = 'Fatal OpenGL error'); 
+var
+	error: integer;
+begin
+	error := gl.getError();
+	if error <> gl.NO_ERROR then
+		begin
+			// TODO: case doesn't work?
+			//case error of
+			//	gl.INVALID_VALUE:
+			//		messageString := messageString+' (GL_INVALID_VALUE)';
+			//	gl.INVALID_OPERATION:
+			//		messageString := messageString+' (GL_INVALID_OPERATION)';
+			//	gl.INVALID_ENUM:
+			//		messageString := messageString+' (GL_INVALID_ENUM)';
+			//	otherwise
+			//		messageString := messageString+' '+IntToStr(error);
+			//end;
+			// TODO: IntoStr doesn't work? cast to string or TJSString doesn't work either
+			//messageString := messageString+' '+IntToStr(error);
+			Fatal(messageString);
+		end;
+end;
+
+procedure TShader.SetUniformMat4 (name: string; value: TJSFloat32List);
+var
+	location: TJSWebGLUniformLocation;
+begin
+	location := gl.getUniformLocation(programID, name);
+	//writeln(name, ' = ', location, ' => ', value);
+	GLFatal(gl, 'gl.getUniformLocation');
+	gl.uniformMatrix4fv(location, false, value);
+	GLFatal(gl, 'gl.uniformMatrix4fv');
+end;
+
+function TShader.GetAttribLocation (name: string): GLint;
+begin
+	result := gl.getAttribLocation(programID, name);
+end;
+
+procedure TShader.BindAttribLocation (index: GLuint; name: string);
+begin
+	gl.bindAttribLocation(programID, index, name);
+	//GLFatal('glBindAttribLocation '+IntToStr(index)+':'+name);
+end;
+
+constructor TShader.Create (context: TJSWebGLRenderingContext; vertexShaderSource, fragmentShaderSource: string);
+begin
+	gl := context;
+	vertexShader := CreateShader(gl.VERTEX_SHADER, vertexShaderSource);
+	fragmentShader := CreateShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
+end;
+
+function TShader.CreateShader(theType: GLenum; source: string): TJSWebGLShader; 
+var
+	shader: TJSWebGLShader;
+begin
+	shader := gl.createShader(theType);
+	if shader = nil then
+		Fatal('create shader failed');
+	gl.shaderSource(shader, source);
+	gl.compileShader(shader);
+	if gl.getShaderParameter(shader, gl.COMPILE_STATUS) then
+		begin
+			//writeln('loaded shader ', theType);
+			exit(shader);
+		end
+	else
+		begin
+			Fatal(gl.getShaderInfoLog(shader));
+			//gl.deleteShader(shader);
+		end;
+end;
+
+procedure TShader.Compile; 
+begin
+	programID := gl.createProgram;
+	gl.attachShader(programID, vertexShader);
+	gl.attachShader(programID, fragmentShader);
+end;
+
+procedure TShader.Link; 
+begin
+  gl.linkProgram(programID);
+  if not gl.getProgramParameter(programID, gl.LINK_STATUS) then
+  	begin
+  		Fatal(gl.getProgramInfoLog(programID));
+  		//gl.deleteProgram(programID);
+  	end;
+end;
+
+procedure TShader.Use; 
+begin
+	gl.useProgram(programID);
+end;
+
+end.

+ 6356 - 0
Mat4.js

@@ -0,0 +1,6356 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{88387EF6-BCEE-3E17-9E85-5D491ED4FC10}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{ECEC7568-4E50-30C9-A2F0-439342DE2ADB}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{9791C368-4E51-3424-A3CE-D4911D54F385}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("Web",["System","Types","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TJSClientRect = function (s) {
+    if (s) {
+      this.left = s.left;
+      this.top = s.top;
+      this.right = s.right;
+      this.bottom = s.bottom;
+    } else {
+      this.left = 0.0;
+      this.top = 0.0;
+      this.right = 0.0;
+      this.bottom = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.left === b.left) && ((this.top === b.top) && ((this.right === b.right) && (this.bottom === b.bottom)));
+    };
+  };
+  this.TJSElementCreationOptions = function (s) {
+    if (s) {
+      this.named = s.named;
+    } else {
+      this.named = "";
+    };
+    this.$equal = function (b) {
+      return this.named === b.named;
+    };
+  };
+  this.TJSEventInit = function (s) {
+    if (s) {
+      this.bubbles = s.bubbles;
+      this.cancelable = s.cancelable;
+      this.scoped = s.scoped;
+      this.composed = s.composed;
+    } else {
+      this.bubbles = false;
+      this.cancelable = false;
+      this.scoped = false;
+      this.composed = false;
+    };
+    this.$equal = function (b) {
+      return (this.bubbles === b.bubbles) && ((this.cancelable === b.cancelable) && ((this.scoped === b.scoped) && (this.composed === b.composed)));
+    };
+  };
+  rtl.createClassExt($mod,"TJSAnimationEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJSLoadEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJsPageTransitionEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClass($mod,"TJSIDBTransactionMode",pas.System.TObject,function () {
+    this.readonly = "readonly";
+    this.readwrite = "readwrite";
+    this.versionchange = "versionchange";
+  });
+  this.TJSIDBIndexParameters = function (s) {
+    if (s) {
+      this.unique = s.unique;
+      this.multiEntry = s.multiEntry;
+      this.locale = s.locale;
+    } else {
+      this.unique = false;
+      this.multiEntry = false;
+      this.locale = "";
+    };
+    this.$equal = function (b) {
+      return (this.unique === b.unique) && ((this.multiEntry === b.multiEntry) && (this.locale === b.locale));
+    };
+  };
+  this.TJSCreateObjectStoreOptions = function (s) {
+    if (s) {
+      this.keyPath = s.keyPath;
+      this.autoIncrement = s.autoIncrement;
+    } else {
+      this.keyPath = undefined;
+      this.autoIncrement = false;
+    };
+    this.$equal = function (b) {
+      return (this.keyPath === b.keyPath) && (this.autoIncrement === b.autoIncrement);
+    };
+  };
+  this.TJSPositionError = function (s) {
+    if (s) {
+      this.code = s.code;
+      this.message = s.message;
+    } else {
+      this.code = 0;
+      this.message = "";
+    };
+    this.$equal = function (b) {
+      return (this.code === b.code) && (this.message === b.message);
+    };
+  };
+  this.TJSPositionOptions = function (s) {
+    if (s) {
+      this.enableHighAccuracy = s.enableHighAccuracy;
+      this.timeout = s.timeout;
+      this.maximumAge = s.maximumAge;
+    } else {
+      this.enableHighAccuracy = false;
+      this.timeout = 0;
+      this.maximumAge = 0;
+    };
+    this.$equal = function (b) {
+      return (this.enableHighAccuracy === b.enableHighAccuracy) && ((this.timeout === b.timeout) && (this.maximumAge === b.maximumAge));
+    };
+  };
+  this.TJSCoordinates = function (s) {
+    if (s) {
+      this.latitude = s.latitude;
+      this.longitude = s.longitude;
+      this.altitude = s.altitude;
+      this.accuracy = s.accuracy;
+      this.altitudeAccuracy = s.altitudeAccuracy;
+      this.heading = s.heading;
+      this.speed = s.speed;
+    } else {
+      this.latitude = 0.0;
+      this.longitude = 0.0;
+      this.altitude = 0.0;
+      this.accuracy = 0.0;
+      this.altitudeAccuracy = 0.0;
+      this.heading = 0.0;
+      this.speed = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.latitude === b.latitude) && ((this.longitude === b.longitude) && ((this.altitude === b.altitude) && ((this.accuracy === b.accuracy) && ((this.altitudeAccuracy === b.altitudeAccuracy) && ((this.heading === b.heading) && (this.speed === b.speed))))));
+    };
+  };
+  this.TJSPosition = function (s) {
+    if (s) {
+      this.coords = new $mod.TJSCoordinates(s.coords);
+      this.timestamp = s.timestamp;
+    } else {
+      this.coords = new $mod.TJSCoordinates();
+      this.timestamp = "";
+    };
+    this.$equal = function (b) {
+      return this.coords.$equal(b.coords) && (this.timestamp === b.timestamp);
+    };
+  };
+  this.TJSServiceWorkerContainerOptions = function (s) {
+    if (s) {
+      this.scope = s.scope;
+    } else {
+      this.scope = "";
+    };
+    this.$equal = function (b) {
+      return this.scope === b.scope;
+    };
+  };
+  this.TJSTextMetrics = function (s) {
+    if (s) {
+      this.width = s.width;
+      this.actualBoundingBoxLeft = s.actualBoundingBoxLeft;
+      this.actualBoundingBoxRight = s.actualBoundingBoxRight;
+      this.fontBoundingBoxAscent = s.fontBoundingBoxAscent;
+      this.fontBoundingBoxDescent = s.fontBoundingBoxDescent;
+      this.actualBoundingBoxAscent = s.actualBoundingBoxAscent;
+      this.actualBoundingBoxDescent = s.actualBoundingBoxDescent;
+      this.emHeightAscent = s.emHeightAscent;
+      this.emHeightDescent = s.emHeightDescent;
+      this.hangingBaseline = s.hangingBaseline;
+      this.alphabeticBaseline = s.alphabeticBaseline;
+      this.ideographicBaseline = s.ideographicBaseline;
+    } else {
+      this.width = 0.0;
+      this.actualBoundingBoxLeft = 0.0;
+      this.actualBoundingBoxRight = 0.0;
+      this.fontBoundingBoxAscent = 0.0;
+      this.fontBoundingBoxDescent = 0.0;
+      this.actualBoundingBoxAscent = 0.0;
+      this.actualBoundingBoxDescent = 0.0;
+      this.emHeightAscent = 0.0;
+      this.emHeightDescent = 0.0;
+      this.hangingBaseline = 0.0;
+      this.alphabeticBaseline = 0.0;
+      this.ideographicBaseline = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.width === b.width) && ((this.actualBoundingBoxLeft === b.actualBoundingBoxLeft) && ((this.actualBoundingBoxRight === b.actualBoundingBoxRight) && ((this.fontBoundingBoxAscent === b.fontBoundingBoxAscent) && ((this.fontBoundingBoxDescent === b.fontBoundingBoxDescent) && ((this.actualBoundingBoxAscent === b.actualBoundingBoxAscent) && ((this.actualBoundingBoxDescent === b.actualBoundingBoxDescent) && ((this.emHeightAscent === b.emHeightAscent) && ((this.emHeightDescent === b.emHeightDescent) && ((this.hangingBaseline === b.hangingBaseline) && ((this.alphabeticBaseline === b.alphabeticBaseline) && (this.ideographicBaseline === b.ideographicBaseline)))))))))));
+    };
+  };
+  this.TJSWheelEventInit = function (s) {
+    if (s) {
+      this.deltaX = s.deltaX;
+      this.deltaY = s.deltaY;
+      this.deltaZ = s.deltaZ;
+      this.deltaMode = s.deltaMode;
+    } else {
+      this.deltaX = 0.0;
+      this.deltaY = 0.0;
+      this.deltaZ = 0.0;
+      this.deltaMode = 0;
+    };
+    this.$equal = function (b) {
+      return (this.deltaX === b.deltaX) && ((this.deltaY === b.deltaY) && ((this.deltaZ === b.deltaZ) && (this.deltaMode === b.deltaMode)));
+    };
+  };
+  rtl.createClass($mod,"TJSKeyNames",pas.System.TObject,function () {
+    this.Alt = "Alt";
+    this.AltGraph = "AltGraph";
+    this.CapsLock = "CapsLock";
+    this.Control = "Control";
+    this.Fn = "Fn";
+    this.FnLock = "FnLock";
+    this.Hyper = "Hyper";
+    this.Meta = "Meta";
+    this.NumLock = "NumLock";
+    this.ScrollLock = "ScrollLock";
+    this.Shift = "Shift";
+    this.Super = "Super";
+    this.symbol = "Symbol";
+    this.SymbolLock = "SymbolLock";
+    this.Enter = "Enter";
+    this.Tab = "Tab";
+    this.Space = " ";
+    this.ArrowDown = "ArrowDown";
+    this.ArrowLeft = "ArrowLeft";
+    this.ArrowRight = "ArrowRight";
+    this.ArrowUp = "ArrowUp";
+    this._End = "End";
+    this.Home = "Home";
+    this.PageDown = "PageDown";
+    this.PageUp = "PageUp";
+    this.BackSpace = "Backspace";
+    this.Clear = "Clear";
+    this.Copy = "Copy";
+    this.CrSel = "CrSel";
+    this.Cut = "Cut";
+    this.Delete = "Delete";
+    this.EraseEof = "EraseEof";
+    this.ExSel = "ExSel";
+    this.Insert = "Insert";
+    this.Paste = "Paste";
+    this.Redo = "Redo";
+    this.Undo = "Undo";
+    this.Accept = "Accept";
+    this.Again = "Again";
+    this.Attn = "Attn";
+    this.Cancel = "Cancel";
+    this.ContextMenu = "Contextmenu";
+    this.Escape = "Escape";
+    this.Execute = "Execute";
+    this.Find = "Find";
+    this.Finish = "Finish";
+    this.Help = "Help";
+    this.Pause = "Pause";
+    this.Play = "Play";
+    this.Props = "Props";
+    this.Select = "Select";
+    this.ZoomIn = "ZoomIn";
+    this.ZoomOut = "ZoomOut";
+    this.BrightnessDown = "BrightnessDown";
+    this.BrightnessUp = "BrightnessUp";
+    this.Eject = "Eject";
+    this.LogOff = "LogOff";
+    this.Power = "Power";
+    this.PowerOff = "PowerOff";
+    this.PrintScreen = "PrintScreen";
+    this.Hibernate = "Hibernate";
+    this.Standby = "Standby";
+    this.WakeUp = "WakeUp";
+    this.AllCandidates = "AllCandidates";
+    this.Alphanumeric = "Alphanumeric";
+    this.CodeInput = "CodeInput";
+    this.Compose = "Compose";
+    this.Convert = "Convert";
+    this.Dead = "Dead";
+    this.FinalMode = "FinalMode";
+    this.GroupFirst = "GroupFirst";
+    this.GroupLast = "GroupLast";
+    this.GroupNext = "GroupNext";
+    this.GroupPrevious = "GroupPrevious";
+    this.ModelChange = "ModelChange";
+    this.NextCandidate = "NextCandidate";
+    this.NonConvert = "NonConvert";
+    this.PreviousCandidate = "PreviousCandidate";
+    this.Process = "Process";
+    this.SingleCandidate = "SingleCandidate";
+    this.HangulMode = "HangulMode";
+    this.HanjaMode = "HanjaMode";
+    this.JunjaMode = "JunjaMode";
+    this.Eisu = "Eisu";
+    this.Hankaku = "Hankaku";
+    this.Hiranga = "Hiranga";
+    this.HirangaKatakana = "HirangaKatakana";
+    this.KanaMode = "KanaMode";
+    this.Katakana = "Katakana";
+    this.Romaji = "Romaji";
+    this.Zenkaku = "Zenkaku";
+    this.ZenkakuHanaku = "ZenkakuHanaku";
+    this.F1 = "F1";
+    this.F2 = "F2";
+    this.F3 = "F3";
+    this.F4 = "F4";
+    this.F5 = "F5";
+    this.F6 = "F6";
+    this.F7 = "F7";
+    this.F8 = "F8";
+    this.F9 = "F9";
+    this.F10 = "F10";
+    this.F11 = "F11";
+    this.F12 = "F12";
+    this.F13 = "F13";
+    this.F14 = "F14";
+    this.F15 = "F15";
+    this.F16 = "F16";
+    this.F17 = "F17";
+    this.F18 = "F18";
+    this.F19 = "F19";
+    this.F20 = "F20";
+    this.Soft1 = "Soft1";
+    this.Soft2 = "Soft2";
+    this.Soft3 = "Soft3";
+    this.Soft4 = "Soft4";
+    this.Decimal = "Decimal";
+    this.Key11 = "Key11";
+    this.Key12 = "Key12";
+    this.Multiply = "Multiply";
+    this.Add = "Add";
+    this.NumClear = "Clear";
+    this.Divide = "Divide";
+    this.Subtract = "Subtract";
+    this.Separator = "Separator";
+    this.AppSwitch = "AppSwitch";
+    this.Call = "Call";
+    this.Camera = "Camera";
+    this.CameraFocus = "CameraFocus";
+    this.EndCall = "EndCall";
+    this.GoBack = "GoBack";
+    this.GoHome = "GoHome";
+    this.HeadsetHook = "HeadsetHook";
+    this.LastNumberRedial = "LastNumberRedial";
+    this.Notification = "Notification";
+    this.MannerMode = "MannerMode";
+    this.VoiceDial = "VoiceDial";
+  });
+});
+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.DefaultCRTConsoleStyle = (((((((((((".pasconsole { " + pas.System.sLineBreak) + "font-family: courier;") + pas.System.sLineBreak) + "font-size: 14px;") + pas.System.sLineBreak) + "background: #000;") + pas.System.sLineBreak) + "color: #14fdce;") + 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("vectors",["System","browserconsole","JS"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClassExt($mod,"TVec2",Array,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.SetX = function (newValue) {
+      this[0] = newValue;
+    };
+    this.SetY = function (newValue) {
+      this[1] = newValue;
+    };
+    this.GetX = function () {
+      var Result = 0.0;
+      Result = rtl.getNumber(this[0]);
+      return Result;
+    };
+    this.GetY = function () {
+      var Result = 0.0;
+      Result = rtl.getNumber(this[1]);
+      return Result;
+    };
+    this.Create = function () {
+    };
+  });
+});
+rtl.module("webgl",["System","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("RTLConsts",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.SArgumentMissing = 'Missing argument in format "%s"';
+  this.SInvalidFormat = 'Invalid format specifier : "%s"';
+  this.SInvalidArgIndex = 'Invalid argument index in format: "%s"';
+  this.SListCapacityError = "List capacity (%s) exceeded.";
+  this.SListCountError = "List count (%s) out of bounds.";
+  this.SListIndexError = "List index (%s) out of bounds";
+  this.SSortedListError = "Operation not allowed on sorted list";
+  this.SDuplicateString = "String list does not allow duplicates";
+  this.SErrFindNeedsSortedList = "Cannot use find on unsorted list";
+  this.SInvalidName = 'Invalid component name: "%s"';
+  this.SInvalidBoolean = '"%s" is not a valid boolean.';
+  this.SDuplicateName = 'Duplicate component name: "%s"';
+  this.SErrInvalidDate = 'Invalid date: "%s"';
+  this.SErrInvalidTimeFormat = 'Invalid time format: "%s"';
+  this.SInvalidDateFormat = 'Invalid date format: "%s"';
+  this.SCantReadPropertyS = 'Cannot read property "%s"';
+  this.SCantWritePropertyS = 'Cannot write property "%s"';
+  this.SErrPropertyNotFound = 'Unknown property: "%s"';
+  this.SIndexedPropertyNeedsParams = 'Indexed property "%s" needs parameters';
+  this.SErrInvalidInteger = 'Invalid integer value: "%s"';
+  this.SErrInvalidFloat = 'Invalid floating-point value: "%s"';
+  this.SInvalidDateTime = "Invalid date-time value: %s";
+  this.SInvalidCurrency = "Invalid currency value: %s";
+  this.SErrInvalidDayOfWeek = "%d is not a valid day of the week";
+  this.SErrInvalidTimeStamp = 'Invalid date\/timestamp : "%s"';
+  this.SErrInvalidDateWeek = "%d %d %d is not a valid dateweek";
+  this.SErrInvalidDayOfYear = "Year %d does not have a day number %d";
+  this.SErrInvalidDateMonthWeek = "Year %d, month %d, Week %d and day %d is not a valid date.";
+  this.SErrInvalidDayOfWeekInMonth = "Year %d Month %d NDow %d DOW %d is not a valid date";
+  this.SInvalidJulianDate = "%f Julian cannot be represented as a DateTime";
+  this.SErrInvalidHourMinuteSecMsec = "%d:%d:%d.%d is not a valid time specification";
+  this.SInvalidGUID = '"%s" is not a valid GUID value';
+});
+rtl.module("SysUtils",["System","RTLConsts","JS"],function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  this.FreeAndNil = function (Obj) {
+    var o = null;
+    o = Obj.get();
+    if (o === null) return;
+    Obj.set(null);
+    o.$destroy("Destroy");
+  };
+  this.TFloatRec = function (s) {
+    if (s) {
+      this.Exponent = s.Exponent;
+      this.Negative = s.Negative;
+      this.Digits = s.Digits;
+    } else {
+      this.Exponent = 0;
+      this.Negative = false;
+      this.Digits = [];
+    };
+    this.$equal = function (b) {
+      return (this.Exponent === b.Exponent) && ((this.Negative === b.Negative) && (this.Digits === b.Digits));
+    };
+  };
+  this.TEndian = {"0": "Little", Little: 0, "1": "Big", Big: 1};
+  rtl.createClass($mod,"Exception",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.fMessage = "";
+      this.fHelpContext = 0;
+    };
+    this.Create$1 = function (Msg) {
+      this.fMessage = Msg;
+    };
+    this.CreateFmt = function (Msg, Args) {
+      this.fMessage = $mod.Format(Msg,Args);
+    };
+    this.CreateHelp = function (Msg, AHelpContext) {
+      this.fMessage = Msg;
+      this.fHelpContext = AHelpContext;
+    };
+    this.CreateFmtHelp = function (Msg, Args, AHelpContext) {
+      this.fMessage = $mod.Format(Msg,Args);
+      this.fHelpContext = AHelpContext;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = (this.$classname + ": ") + this.fMessage;
+      return Result;
+    };
+  });
+  rtl.createClass($mod,"EExternal",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EMathError",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EInvalidOp",$mod.EMathError,function () {
+  });
+  rtl.createClass($mod,"EZeroDivide",$mod.EMathError,function () {
+  });
+  rtl.createClass($mod,"EOverflow",$mod.EMathError,function () {
+  });
+  rtl.createClass($mod,"EUnderflow",$mod.EMathError,function () {
+  });
+  rtl.createClass($mod,"EAbort",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EInvalidCast",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EAssertionFailed",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EObjectCheck",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EConvertError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EFormatError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EIntError",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EDivByZero",$mod.EIntError,function () {
+  });
+  rtl.createClass($mod,"ERangeError",$mod.EIntError,function () {
+  });
+  rtl.createClass($mod,"EIntOverflow",$mod.EIntError,function () {
+  });
+  rtl.createClass($mod,"EInOutError",$mod.Exception,function () {
+    this.$init = function () {
+      $mod.Exception.$init.call(this);
+      this.ErrorCode = 0;
+    };
+  });
+  rtl.createClass($mod,"EHeapMemoryError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EExternalException",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EInvalidPointer",$mod.EHeapMemoryError,function () {
+  });
+  rtl.createClass($mod,"EOutOfMemory",$mod.EHeapMemoryError,function () {
+  });
+  rtl.createClass($mod,"EVariantError",$mod.Exception,function () {
+    this.$init = function () {
+      $mod.Exception.$init.call(this);
+      this.ErrCode = 0;
+    };
+    this.CreateCode = function (Code) {
+      this.ErrCode = Code;
+    };
+  });
+  rtl.createClass($mod,"EAccessViolation",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EBusError",$mod.EAccessViolation,function () {
+  });
+  rtl.createClass($mod,"EPrivilege",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EStackOverflow",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EControlC",$mod.EExternal,function () {
+  });
+  rtl.createClass($mod,"EAbstractError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EPropReadOnly",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EPropWriteOnly",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EIntfCastError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EInvalidContainer",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EInvalidInsert",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EPackageError",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EOSError",$mod.Exception,function () {
+    this.$init = function () {
+      $mod.Exception.$init.call(this);
+      this.ErrorCode = 0;
+    };
+  });
+  rtl.createClass($mod,"ESafecallException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"ENoThreadSupport",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"ENoWideStringSupport",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"ENotImplemented",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EArgumentException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EArgumentOutOfRangeException",$mod.EArgumentException,function () {
+  });
+  rtl.createClass($mod,"EArgumentNilException",$mod.EArgumentException,function () {
+  });
+  rtl.createClass($mod,"EPathTooLongException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"ENotSupportedException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EDirectoryNotFoundException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EFileNotFoundException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"EPathNotFoundException",$mod.Exception,function () {
+  });
+  rtl.createClass($mod,"ENoConstructException",$mod.Exception,function () {
+  });
+  this.EmptyStr = "";
+  this.EmptyWideStr = "";
+  this.HexDisplayPrefix = "$";
+  this.LeadBytes = {};
+  this.CharInSet = function (Ch, CSet) {
+    var Result = false;
+    var I = 0;
+    Result = false;
+    I = rtl.length(CSet) - 1;
+    while (!Result && (I >= 0)) {
+      Result = Ch === CSet[I];
+      I -= 1;
+    };
+    return Result;
+  };
+  this.LeftStr = function (S, Count) {
+    return (Count>0) ? S.substr(0,Count) : "";
+  };
+  this.RightStr = function (S, Count) {
+    var l = S.length;
+    return (Count<1) ? "" : ( Count>=l ? S : S.substr(l-Count));
+  };
+  this.Trim = function (S) {
+    return S.trim();
+  };
+  this.TrimLeft = function (S) {
+    return S.replace(/^[\s\uFEFF\xA0\x00-\x1f]+/,'');
+  };
+  this.TrimRight = function (S) {
+    return S.replace(/[\s\uFEFF\xA0\x00-\x1f]+$/,'');
+  };
+  this.UpperCase = function (s) {
+    return s.toUpperCase();
+  };
+  this.LowerCase = function (s) {
+    return s.toLowerCase();
+  };
+  this.CompareStr = function (s1, s2) {
+    var l1 = s1.length;
+    var l2 = s2.length;
+    if (l1<=l2){
+      var s = s2.substr(0,l1);
+      if (s1<s){ return -1;
+      } else if (s1>s){ return 1;
+      } else { return l1<l2 ? -1 : 0; };
+    } else {
+      var s = s1.substr(0,l2);
+      if (s<s2){ return -1;
+      } else { return 1; };
+    };
+  };
+  this.SameStr = function (s1, s2) {
+    return s1 == s2;
+  };
+  this.CompareText = function (s1, s2) {
+    var l1 = s1.toLowerCase();
+    var l2 = s2.toLowerCase();
+    if (l1>l2){ return 1;
+    } else if (l1<l2){ return -1;
+    } else { return 0; };
+  };
+  this.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  this.AnsiCompareText = function (s1, s2) {
+    return s1.localeCompare(s2);
+  };
+  this.AnsiSameText = function (s1, s2) {
+    return s1.localeCompare(s2) == 0;
+  };
+  this.AnsiCompareStr = function (s1, s2) {
+    var Result = 0;
+    Result = $mod.CompareText(s1,s2);
+    return Result;
+  };
+  this.AppendStr = function (Dest, S) {
+    Dest.set(Dest.get() + S);
+  };
+  this.Format = function (Fmt, Args) {
+    var Result = "";
+    var ChPos = 0;
+    var OldPos = 0;
+    var ArgPos = 0;
+    var DoArg = 0;
+    var Len = 0;
+    var Hs = "";
+    var ToAdd = "";
+    var Index = 0;
+    var Width = 0;
+    var Prec = 0;
+    var Left = false;
+    var Fchar = "";
+    var vq = 0;
+    function ReadFormat() {
+      var Result = "";
+      var Value = 0;
+      function ReadInteger() {
+        var Code = 0;
+        var ArgN = 0;
+        if (Value !== -1) return;
+        OldPos = ChPos;
+        while (((ChPos <= Len) && (Fmt.charAt(ChPos - 1) <= "9")) && (Fmt.charAt(ChPos - 1) >= "0")) ChPos += 1;
+        if (ChPos > Len) $impl.DoFormatError(1,Fmt);
+        if (Fmt.charAt(ChPos - 1) === "*") {
+          if (Index === -1) {
+            ArgN = ArgPos}
+           else {
+            ArgN = Index;
+            Index += 1;
+          };
+          if ((ChPos > OldPos) || (ArgN > (rtl.length(Args) - 1))) $impl.DoFormatError(1,Fmt);
+          ArgPos = ArgN + 1;
+          if (rtl.isNumber(Args[ArgN]) && pas.JS.isInteger(Args[ArgN])) {
+            Value = Math.floor(Args[ArgN])}
+           else $impl.DoFormatError(1,Fmt);
+          ChPos += 1;
+        } else {
+          if (OldPos < ChPos) {
+            pas.System.val(pas.System.Copy(Fmt,OldPos,ChPos - OldPos),{get: function () {
+                return Value;
+              }, set: function (v) {
+                Value = v;
+              }},{get: function () {
+                return Code;
+              }, set: function (v) {
+                Code = v;
+              }});
+            if (Code > 0) $impl.DoFormatError(1,Fmt);
+          } else Value = -1;
+        };
+      };
+      function ReadIndex() {
+        if (Fmt.charAt(ChPos - 1) !== ":") {
+          ReadInteger()}
+         else Value = 0;
+        if (Fmt.charAt(ChPos - 1) === ":") {
+          if (Value === -1) $impl.DoFormatError(2,Fmt);
+          Index = Value;
+          Value = -1;
+          ChPos += 1;
+        };
+      };
+      function ReadLeft() {
+        if (Fmt.charAt(ChPos - 1) === "-") {
+          Left = true;
+          ChPos += 1;
+        } else Left = false;
+      };
+      function ReadWidth() {
+        ReadInteger();
+        if (Value !== -1) {
+          Width = Value;
+          Value = -1;
+        };
+      };
+      function ReadPrec() {
+        if (Fmt.charAt(ChPos - 1) === ".") {
+          ChPos += 1;
+          ReadInteger();
+          if (Value === -1) Value = 0;
+          Prec = Value;
+        };
+      };
+      Index = -1;
+      Width = -1;
+      Prec = -1;
+      Value = -1;
+      ChPos += 1;
+      if (Fmt.charAt(ChPos - 1) === "%") {
+        Result = "%";
+        return Result;
+      };
+      ReadIndex();
+      ReadLeft();
+      ReadWidth();
+      ReadPrec();
+      Result = pas.System.upcase(Fmt.charAt(ChPos - 1));
+      return Result;
+    };
+    function Checkarg(AT, err) {
+      var Result = false;
+      Result = false;
+      if (Index === -1) {
+        DoArg = ArgPos}
+       else DoArg = Index;
+      ArgPos = DoArg + 1;
+      if ((DoArg > (rtl.length(Args) - 1)) || (pas.JS.GetValueType(Args[DoArg]) !== AT)) {
+        if (err) $impl.DoFormatError(3,Fmt);
+        ArgPos -= 1;
+        return Result;
+      };
+      Result = true;
+      return Result;
+    };
+    Result = "";
+    Len = Fmt.length;
+    ChPos = 1;
+    OldPos = 1;
+    ArgPos = 0;
+    while (ChPos <= Len) {
+      while ((ChPos <= Len) && (Fmt.charAt(ChPos - 1) !== "%")) ChPos += 1;
+      if (ChPos > OldPos) Result = Result + pas.System.Copy(Fmt,OldPos,ChPos - OldPos);
+      if (ChPos < Len) {
+        Fchar = ReadFormat();
+        var $tmp1 = Fchar;
+        if ($tmp1 === "D") {
+          Checkarg(pas.JS.TJSValueType.jvtInteger,true);
+          ToAdd = $mod.IntToStr(Math.floor(Args[DoArg]));
+          Width = Math.abs(Width);
+          Index = Prec - ToAdd.length;
+          if (ToAdd.charAt(0) !== "-") {
+            ToAdd = pas.System.StringOfChar("0",Index) + ToAdd}
+           else pas.System.Insert(pas.System.StringOfChar("0",Index + 1),{get: function () {
+              return ToAdd;
+            }, set: function (v) {
+              ToAdd = v;
+            }},2);
+        } else if ($tmp1 === "U") {
+          Checkarg(pas.JS.TJSValueType.jvtInteger,true);
+          if (Math.floor(Args[DoArg]) < 0) $impl.DoFormatError(3,Fmt);
+          ToAdd = $mod.IntToStr(Math.floor(Args[DoArg]));
+          Width = Math.abs(Width);
+          Index = Prec - ToAdd.length;
+          ToAdd = pas.System.StringOfChar("0",Index) + ToAdd;
+        } else if ($tmp1 === "E") {
+          if (Checkarg(pas.JS.TJSValueType.jvtFloat,false) || Checkarg(pas.JS.TJSValueType.jvtInteger,true)) ToAdd = $mod.FloatToStrF(rtl.getNumber(Args[DoArg]),$mod.TFloatFormat.ffFixed,9999,Prec);
+        } else if ($tmp1 === "F") {
+          if (Checkarg(pas.JS.TJSValueType.jvtFloat,false) || Checkarg(pas.JS.TJSValueType.jvtInteger,true)) ToAdd = $mod.FloatToStrF(rtl.getNumber(Args[DoArg]),$mod.TFloatFormat.ffFixed,9999,Prec);
+        } else if ($tmp1 === "G") {
+          if (Checkarg(pas.JS.TJSValueType.jvtFloat,false) || Checkarg(pas.JS.TJSValueType.jvtInteger,true)) ToAdd = $mod.FloatToStrF(rtl.getNumber(Args[DoArg]),$mod.TFloatFormat.ffGeneral,Prec,3);
+        } else if ($tmp1 === "N") {
+          if (Checkarg(pas.JS.TJSValueType.jvtFloat,false) || Checkarg(pas.JS.TJSValueType.jvtInteger,true)) ToAdd = $mod.FloatToStrF(rtl.getNumber(Args[DoArg]),$mod.TFloatFormat.ffNumber,9999,Prec);
+        } else if ($tmp1 === "M") {
+          if (Checkarg(pas.JS.TJSValueType.jvtFloat,false) || Checkarg(pas.JS.TJSValueType.jvtInteger,true)) ToAdd = $mod.FloatToStrF(rtl.getNumber(Args[DoArg]),$mod.TFloatFormat.ffCurrency,9999,Prec);
+        } else if ($tmp1 === "S") {
+          Checkarg(pas.JS.TJSValueType.jvtString,true);
+          Hs = "" + Args[DoArg];
+          Index = Hs.length;
+          if ((Prec !== -1) && (Index > Prec)) Index = Prec;
+          ToAdd = pas.System.Copy(Hs,1,Index);
+        } else if ($tmp1 === "P") {
+          Checkarg(pas.JS.TJSValueType.jvtInteger,true);
+          ToAdd = $mod.IntToHex(Math.floor(Args[DoArg]),31);
+        } else if ($tmp1 === "X") {
+          Checkarg(pas.JS.TJSValueType.jvtInteger,true);
+          vq = Math.floor(Args[DoArg]);
+          Index = 31;
+          if (Prec > Index) {
+            ToAdd = $mod.IntToHex(vq,Index)}
+           else {
+            Index = 1;
+            while (((1 << (Index * 4)) <= vq) && (Index < 16)) Index += 1;
+            if (Index > Prec) Prec = Index;
+            ToAdd = $mod.IntToHex(vq,Prec);
+          };
+        } else if ($tmp1 === "%") ToAdd = "%";
+        if (Width !== -1) if (ToAdd.length < Width) if (!Left) {
+          ToAdd = pas.System.StringOfChar(" ",Width - ToAdd.length) + ToAdd}
+         else ToAdd = ToAdd + pas.System.StringOfChar(" ",Width - ToAdd.length);
+        Result = Result + ToAdd;
+      };
+      ChPos += 1;
+      OldPos = ChPos;
+    };
+    return Result;
+  };
+  this.LocaleCompare = function (s1, s2, locales) {
+    return s1.localeCompare(s2,locales) == 0;
+  };
+  this.NormalizeStr = function (S, Norm) {
+    return S.normalize(Norm);
+  };
+  var Alpha = rtl.createSet(null,65,90,null,97,122,95);
+  var AlphaNum = rtl.unionSet(Alpha,rtl.createSet(null,48,57));
+  var Dot = ".";
+  this.IsValidIdent = function (Ident, AllowDots, StrictDots) {
+    var Result = false;
+    var First = false;
+    var I = 0;
+    var Len = 0;
+    Len = Ident.length;
+    if (Len < 1) return false;
+    First = true;
+    Result = false;
+    I = 1;
+    while (I <= Len) {
+      if (First) {
+        if (!(Ident.charCodeAt(I - 1) in Alpha)) return Result;
+        First = false;
+      } else if (AllowDots && (Ident.charAt(I - 1) === Dot)) {
+        if (StrictDots) {
+          if (I >= Len) return Result;
+          First = true;
+        };
+      } else if (!(Ident.charCodeAt(I - 1) in AlphaNum)) return Result;
+      I = I + 1;
+    };
+    Result = true;
+    return Result;
+  };
+  this.TStringReplaceFlag = {"0": "rfReplaceAll", rfReplaceAll: 0, "1": "rfIgnoreCase", rfIgnoreCase: 1};
+  this.StringReplace = function (aOriginal, aSearch, aReplace, Flags) {
+    var Result = "";
+    var REFlags = "";
+    var REString = "";
+    REFlags = "";
+    if ($mod.TStringReplaceFlag.rfReplaceAll in Flags) REFlags = "g";
+    if ($mod.TStringReplaceFlag.rfIgnoreCase in Flags) REFlags = REFlags + "i";
+    REString = aSearch.replace(new RegExp($impl.RESpecials,"g"),"\\$1");
+    Result = aOriginal.replace(new RegExp(REString,REFlags),aReplace);
+    return Result;
+  };
+  this.QuoteString = function (aOriginal, AQuote) {
+    var Result = "";
+    var REString = "";
+    REString = AQuote.replace(new RegExp(aOriginal,"g"),"\\\\$1");
+    Result = (AQuote + aOriginal.replace(new RegExp(REString,"g"),"$1\\$1")) + AQuote;
+    return Result;
+  };
+  this.IsDelimiter = function (Delimiters, S, Index) {
+    var Result = false;
+    Result = false;
+    if ((Index > 0) && (Index <= S.length)) Result = pas.System.Pos(S.charAt(Index - 1),Delimiters) !== 0;
+    return Result;
+  };
+  this.AdjustLineBreaks = function (S) {
+    var Result = "";
+    Result = $mod.AdjustLineBreaks$1(S,pas.System.DefaultTextLineBreakStyle);
+    return Result;
+  };
+  this.AdjustLineBreaks$1 = function (S, Style) {
+    var Result = "";
+    var I = 0;
+    var L = 0;
+    var Res = "";
+    function Add(C) {
+      Res = Res + C;
+    };
+    I = 0;
+    L = S.length;
+    Result = "";
+    while (I <= L) {
+      var $tmp1 = S.charAt(I - 1);
+      if ($tmp1 === "\n") {
+        if (Style in rtl.createSet(pas.System.TTextLineBreakStyle.tlbsCRLF,pas.System.TTextLineBreakStyle.tlbsCR)) Add("\r");
+        if (Style === pas.System.TTextLineBreakStyle.tlbsCRLF) Add("\n");
+        I += 1;
+      } else if ($tmp1 === "\r") {
+        if (Style === pas.System.TTextLineBreakStyle.tlbsCRLF) Add("\r");
+        Add("\n");
+        I += 1;
+        if (S.charAt(I - 1) === "\n") I += 1;
+      } else {
+        Add(S.charAt(I - 1));
+        I += 1;
+      };
+    };
+    Result = Res;
+    return Result;
+  };
+  var Quotes = rtl.createSet(39,34);
+  this.WrapText = function (Line, BreakStr, BreakChars, MaxCol) {
+    var Result = "";
+    var L = "";
+    var C = "";
+    var LQ = "";
+    var BC = "";
+    var P = 0;
+    var BLen = 0;
+    var Len = 0;
+    var HB = false;
+    var IBC = false;
+    Result = "";
+    L = Line;
+    BLen = BreakStr.length;
+    if (BLen > 0) {
+      BC = BreakStr.charAt(0)}
+     else BC = "\x00";
+    Len = L.length;
+    while (Len > 0) {
+      P = 1;
+      LQ = "\x00";
+      HB = false;
+      IBC = false;
+      while (((P <= Len) && ((P <= MaxCol) || !IBC)) && ((LQ !== "\x00") || !HB)) {
+        C = L.charAt(P - 1);
+        if (C === LQ) {
+          LQ = "\x00"}
+         else if (C.charCodeAt() in Quotes) LQ = C;
+        if (LQ !== "\x00") {
+          P += 1}
+         else {
+          HB = (C === BC) && (BreakStr === pas.System.Copy(L,P,BLen));
+          if (HB) {
+            P += BLen}
+           else {
+            if (P >= MaxCol) IBC = $mod.CharInSet(C,BreakChars);
+            P += 1;
+          };
+        };
+      };
+      Result = Result + pas.System.Copy(L,1,P - 1);
+      pas.System.Delete({get: function () {
+          return L;
+        }, set: function (v) {
+          L = v;
+        }},1,P - 1);
+      Len = L.length;
+      if ((Len > 0) && !HB) Result = Result + BreakStr;
+    };
+    return Result;
+  };
+  this.WrapText$1 = function (Line, MaxCol) {
+    var Result = "";
+    Result = $mod.WrapText(Line,pas.System.sLineBreak,[" ","-","\t"],MaxCol);
+    return Result;
+  };
+  this.IntToStr = function (Value) {
+    var Result = "";
+    Result = "" + Value;
+    return Result;
+  };
+  this.TryStrToInt = function (S, res) {
+    var Result = false;
+    var NI = 0;
+    Result = $mod.TryStrToInt$1(S,{get: function () {
+        return NI;
+      }, set: function (v) {
+        NI = v;
+      }});
+    if (Result) res.set(NI);
+    return Result;
+  };
+  this.TryStrToInt$1 = function (S, res) {
+    var Result = false;
+    var Radix = 10;
+    var F = "";
+    var N = "";
+    var J = undefined;
+    N = S;
+    F = pas.System.Copy(N,1,1);
+    if (F === "$") {
+      Radix = 16}
+     else if (F === "&") {
+      Radix = 8}
+     else if (F === "%") Radix = 2;
+    if (Radix !== 10) pas.System.Delete({get: function () {
+        return N;
+      }, set: function (v) {
+        N = v;
+      }},1,1);
+    J = parseInt(N,Radix);
+    Result = !isNaN(J);
+    if (Result) res.set(Math.floor(J));
+    return Result;
+  };
+  this.StrToIntDef = function (S, aDef) {
+    var Result = 0;
+    var R = 0;
+    if ($mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }})) {
+      Result = R}
+     else Result = aDef;
+    return Result;
+  };
+  this.StrToIntDef$1 = function (S, aDef) {
+    var Result = 0;
+    var R = 0;
+    if ($mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }})) {
+      Result = R}
+     else Result = aDef;
+    return Result;
+  };
+  this.StrToInt = function (S) {
+    var Result = 0;
+    var R = 0;
+    if (!$mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    Result = R;
+    return Result;
+  };
+  this.StrToNativeInt = function (S) {
+    var Result = 0;
+    if (!$mod.TryStrToInt$1(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    return Result;
+  };
+  this.StrToInt64 = function (S) {
+    var Result = 0;
+    var N = 0;
+    if (!$mod.TryStrToInt$1(S,{get: function () {
+        return N;
+      }, set: function (v) {
+        N = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    Result = N;
+    return Result;
+  };
+  this.StrToInt64Def = function (S, ADefault) {
+    var Result = 0;
+    if ($mod.TryStrToInt64(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = ADefault;
+    return Result;
+  };
+  this.TryStrToInt64 = function (S, res) {
+    var Result = false;
+    var R = 0;
+    Result = $mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }});
+    if (Result) res.set(R);
+    return Result;
+  };
+  this.StrToQWord = function (S) {
+    var Result = 0;
+    var N = 0;
+    if (!$mod.TryStrToInt$1(S,{get: function () {
+        return N;
+      }, set: function (v) {
+        N = v;
+      }}) || (N < 0)) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    Result = N;
+    return Result;
+  };
+  this.StrToQWordDef = function (S, ADefault) {
+    var Result = 0;
+    if (!$mod.TryStrToQWord(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = ADefault;
+    return Result;
+  };
+  this.TryStrToQWord = function (S, res) {
+    var Result = false;
+    var R = 0;
+    Result = $mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }}) && (R >= 0);
+    if (Result) res.set(R);
+    return Result;
+  };
+  this.StrToUInt64 = function (S) {
+    var Result = 0;
+    var N = 0;
+    if (!$mod.TryStrToInt$1(S,{get: function () {
+        return N;
+      }, set: function (v) {
+        N = v;
+      }}) || (N < 0)) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    Result = N;
+    return Result;
+  };
+  this.StrToUInt64Def = function (S, ADefault) {
+    var Result = 0;
+    if (!$mod.TryStrToUInt64(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = ADefault;
+    return Result;
+  };
+  this.TryStrToUInt64 = function (S, res) {
+    var Result = false;
+    var R = 0;
+    Result = $mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }}) && (R >= 0);
+    if (Result) res.set(R);
+    return Result;
+  };
+  this.StrToDWord = function (S) {
+    var Result = 0;
+    if (!$mod.TryStrToDWord(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidInteger,[S]]);
+    return Result;
+  };
+  this.StrToDWordDef = function (S, ADefault) {
+    var Result = 0;
+    if (!$mod.TryStrToDWord(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = ADefault;
+    return Result;
+  };
+  this.TryStrToDWord = function (S, res) {
+    var Result = false;
+    var R = 0;
+    Result = ($mod.TryStrToInt$1(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }}) && (R >= 0)) && (R <= 0xFFFFFFFF);
+    if (Result) res.set(R);
+    return Result;
+  };
+  var HexDigits = "0123456789ABCDEF";
+  this.IntToHex = function (Value, Digits) {
+    var Result = "";
+    if (Digits === 0) Digits = 1;
+    Result = "";
+    while (Value > 0) {
+      Result = HexDigits.charAt(((Value & 15) + 1) - 1) + Result;
+      Value = Value >>> 4;
+    };
+    while (Result.length < Digits) Result = "0" + Result;
+    return Result;
+  };
+  this.MaxCurrency = 450359962737.0495;
+  this.MinCurrency = -450359962737.0496;
+  this.TFloatFormat = {"0": "ffFixed", ffFixed: 0, "1": "ffGeneral", ffGeneral: 1, "2": "ffExponent", ffExponent: 2, "3": "ffNumber", ffNumber: 3, "4": "ffCurrency", ffCurrency: 4};
+  var Rounds = "234567890";
+  this.FloatToDecimal = function (Value, Precision, Decimals) {
+    var Result = new $mod.TFloatRec();
+    var Buffer = "";
+    var InfNan = "";
+    var error = 0;
+    var N = 0;
+    var L = 0;
+    var Start = 0;
+    var C = 0;
+    var GotNonZeroBeforeDot = false;
+    var BeforeDot = false;
+    if (Value === 0) ;
+    Result.Digits = rtl.arraySetLength(Result.Digits,"",19);
+    Buffer=Value.toPrecision(21); // Double precision;
+    N = 1;
+    L = Buffer.length;
+    while (Buffer.charAt(N - 1) === " ") N += 1;
+    Result.Negative = Buffer.charAt(N - 1) === "-";
+    if (Result.Negative) {
+      N += 1}
+     else if (Buffer.charAt(N - 1) === "+") N += 1;
+    if (L >= (N + 2)) {
+      InfNan = pas.System.Copy(Buffer,N,3);
+      if (InfNan === "Inf") {
+        Result.Digits[0] = "\x00";
+        Result.Exponent = 32767;
+        return Result;
+      };
+      if (InfNan === "Nan") {
+        Result.Digits[0] = "\x00";
+        Result.Exponent = -32768;
+        return Result;
+      };
+    };
+    Start = N;
+    Result.Exponent = 0;
+    BeforeDot = true;
+    GotNonZeroBeforeDot = false;
+    while ((L >= N) && (Buffer.charAt(N - 1) !== "E")) {
+      if (Buffer.charAt(N - 1) === ".") {
+        BeforeDot = false}
+       else {
+        if (BeforeDot) {
+          Result.Exponent += 1;
+          Result.Digits[N - Start] = Buffer.charAt(N - 1);
+          if (Buffer.charAt(N - 1) !== "0") GotNonZeroBeforeDot = true;
+        } else Result.Digits[(N - Start) - 1] = Buffer.charAt(N - 1);
+      };
+      N += 1;
+    };
+    N += 1;
+    if (N <= L) {
+      pas.System.val$5(pas.System.Copy(Buffer,N,(L - N) + 1),{get: function () {
+          return C;
+        }, set: function (v) {
+          C = v;
+        }},{get: function () {
+          return error;
+        }, set: function (v) {
+          error = v;
+        }});
+      Result.Exponent += C;
+    };
+    if (BeforeDot) {
+      N = (N - Start) - 1}
+     else N = (N - Start) - 2;
+    L = rtl.length(Result.Digits);
+    if (N < L) Result.Digits[N] = "0";
+    if ((Decimals + Result.Exponent) < Precision) {
+      N = Decimals + Result.Exponent}
+     else N = Precision;
+    if (N >= L) N = L - 1;
+    if (N === 0) {
+      if (Result.Digits[0] >= "5") {
+        Result.Digits[0] = "1";
+        Result.Digits[1] = "\x00";
+        Result.Exponent += 1;
+      } else Result.Digits[0] = "\x00";
+    } else if (N > 0) {
+      if (Result.Digits[N] >= "5") {
+        do {
+          Result.Digits[N] = "\x00";
+          N -= 1;
+          Result.Digits[N] = Rounds.charAt($mod.StrToInt(Result.Digits[N]) - 1);
+        } while (!((N === 0) || (Result.Digits[N] < ":")));
+        if (Result.Digits[0] === ":") {
+          Result.Digits[0] = "1";
+          Result.Exponent += 1;
+        };
+      } else {
+        Result.Digits[N] = "0";
+        while ((N > -1) && (Result.Digits[N] === "0")) {
+          Result.Digits[N] = "\x00";
+          N -= 1;
+        };
+      };
+    } else Result.Digits[0] = "\x00";
+    if ((Result.Digits[0] === "\x00") && !GotNonZeroBeforeDot) {
+      Result.Exponent = 0;
+      Result.Negative = false;
+    };
+    return Result;
+  };
+  this.FloatToStr = function (Value) {
+    var Result = "";
+    Result = $mod.FloatToStrF(Value,$mod.TFloatFormat.ffGeneral,15,0);
+    return Result;
+  };
+  this.FloatToStrF = function (Value, format, Precision, Digits) {
+    var Result = "";
+    var DS = "";
+    DS = $mod.DecimalSeparator;
+    var $tmp1 = format;
+    if ($tmp1 === $mod.TFloatFormat.ffGeneral) {
+      Result = $impl.FormatGeneralFloat(Value,Precision,DS)}
+     else if ($tmp1 === $mod.TFloatFormat.ffExponent) {
+      Result = $impl.FormatExponentFloat(Value,Precision,Digits,DS)}
+     else if ($tmp1 === $mod.TFloatFormat.ffFixed) {
+      Result = $impl.FormatFixedFloat(Value,Digits,DS)}
+     else if ($tmp1 === $mod.TFloatFormat.ffNumber) {
+      Result = $impl.FormatNumberFloat(Value,Digits,DS,$mod.ThousandSeparator)}
+     else if ($tmp1 === $mod.TFloatFormat.ffCurrency) Result = $impl.FormatNumberCurrency(Value * 10000,Digits,DS,$mod.ThousandSeparator);
+    if (((format !== $mod.TFloatFormat.ffCurrency) && (Result.length > 1)) && (Result.charAt(0) === "-")) $impl.RemoveLeadingNegativeSign({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},DS);
+    return Result;
+  };
+  this.TryStrToFloat = function (S, res) {
+    var Result = false;
+    var J = undefined;
+    var N = "";
+    N = S;
+    if ($mod.ThousandSeparator !== "") N = $mod.StringReplace(N,$mod.ThousandSeparator,"",rtl.createSet($mod.TStringReplaceFlag.rfReplaceAll));
+    if ($mod.DecimalSeparator !== ".") N = $mod.StringReplace(N,$mod.DecimalSeparator,".",{});
+    J = parseFloat(N);
+    Result = !isNaN(J);
+    if (Result) res.set(rtl.getNumber(J));
+    return Result;
+  };
+  this.StrToFloatDef = function (S, aDef) {
+    var Result = 0.0;
+    if (!$mod.TryStrToFloat(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = aDef;
+    return Result;
+  };
+  this.StrToFloat = function (S) {
+    var Result = 0.0;
+    if (!$mod.TryStrToFloat(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SErrInvalidFloat,[S]]);
+    return Result;
+  };
+  var MaxPrecision = 18;
+  this.FormatFloat = function (Fmt, aValue) {
+    var Result = "";
+    var E = 0.0;
+    var FV = new $mod.TFloatRec();
+    var Section = "";
+    var SectionLength = 0;
+    var ThousandSep = false;
+    var IsScientific = false;
+    var DecimalPos = 0;
+    var FirstDigit = 0;
+    var LastDigit = 0;
+    var RequestedDigits = 0;
+    var ExpSize = 0;
+    var Available = 0;
+    var Current = 0;
+    var PadZeroes = 0;
+    var DistToDecimal = 0;
+    function InitVars() {
+      E = aValue;
+      Section = "";
+      SectionLength = 0;
+      ThousandSep = false;
+      IsScientific = false;
+      DecimalPos = 0;
+      FirstDigit = 2147483647;
+      LastDigit = 0;
+      RequestedDigits = 0;
+      ExpSize = 0;
+      Available = -1;
+    };
+    function ToResult(AChar) {
+      Result = Result + AChar;
+    };
+    function AddToResult(AStr) {
+      Result = Result + AStr;
+    };
+    function WriteDigit(ADigit) {
+      if (ADigit === "\x00") return;
+      DistToDecimal -= 1;
+      if (DistToDecimal === -1) {
+        AddToResult($mod.DecimalSeparator);
+        ToResult(ADigit);
+      } else {
+        ToResult(ADigit);
+        if ((ThousandSep && ((DistToDecimal % 3) === 0)) && (DistToDecimal > 1)) AddToResult($mod.ThousandSeparator);
+      };
+    };
+    function GetDigit() {
+      var Result = "";
+      Result = "\x00";
+      if (Current <= Available) {
+        Result = FV.Digits[Current];
+        Current += 1;
+      } else if (DistToDecimal <= LastDigit) {
+        DistToDecimal -= 1}
+       else Result = "0";
+      return Result;
+    };
+    function CopyDigit() {
+      if (PadZeroes === 0) {
+        WriteDigit(GetDigit())}
+       else if (PadZeroes < 0) {
+        PadZeroes += 1;
+        if (DistToDecimal <= FirstDigit) {
+          WriteDigit("0")}
+         else DistToDecimal -= 1;
+      } else {
+        while (PadZeroes > 0) {
+          WriteDigit(GetDigit());
+          PadZeroes -= 1;
+        };
+        WriteDigit(GetDigit());
+      };
+    };
+    function GetSections(SP) {
+      var Result = 0;
+      var FL = 0;
+      var i = 0;
+      var C = "";
+      var Q = "";
+      var inQuote = false;
+      Result = 1;
+      SP.get()[1] = -1;
+      SP.get()[2] = -1;
+      SP.get()[3] = -1;
+      inQuote = false;
+      Q = "\x00";
+      i = 1;
+      FL = Fmt.length;
+      while (i <= FL) {
+        C = Fmt.charAt(i - 1);
+        var $tmp1 = C;
+        if ($tmp1 === ";") {
+          if (!inQuote) {
+            if (Result > 3) throw $mod.Exception.$create("Create$1",["Invalid float format"]);
+            SP.get()[Result] = i + 1;
+            Result += 1;
+          };
+        } else if (($tmp1 === '"') || ($tmp1 === "'")) {
+          if (inQuote) {
+            inQuote = C !== Q}
+           else {
+            inQuote = true;
+            Q = C;
+          };
+        };
+        i += 1;
+      };
+      if (SP.get()[Result] === -1) SP.get()[Result] = FL + 1;
+      return Result;
+    };
+    function AnalyzeFormat() {
+      var I = 0;
+      var Len = 0;
+      var Q = "";
+      var C = "";
+      var InQuote = false;
+      Len = Section.length;
+      I = 1;
+      InQuote = false;
+      Q = "\x00";
+      while (I <= Len) {
+        C = Section.charAt(I - 1);
+        if (C.charCodeAt() in rtl.createSet(34,39)) {
+          if (InQuote) {
+            InQuote = C !== Q}
+           else {
+            InQuote = true;
+            Q = C;
+          };
+        } else if (!InQuote) {
+          var $tmp1 = C;
+          if ($tmp1 === ".") {
+            if (DecimalPos === 0) DecimalPos = RequestedDigits + 1}
+           else if ($tmp1 === ",") {
+            ThousandSep = $mod.ThousandSeparator !== "\x00"}
+           else if (($tmp1 === "e") || ($tmp1 === "E")) {
+            I += 1;
+            if (I < Len) {
+              C = Section.charAt(I - 1);
+              IsScientific = C.charCodeAt() in rtl.createSet(45,43);
+              if (IsScientific) while ((I < Len) && (Section.charAt((I + 1) - 1) === "0")) {
+                ExpSize += 1;
+                I += 1;
+              };
+              if (ExpSize > 4) ExpSize = 4;
+            };
+          } else if ($tmp1 === "#") {
+            RequestedDigits += 1}
+           else if ($tmp1 === "0") {
+            if (RequestedDigits < FirstDigit) FirstDigit = RequestedDigits + 1;
+            RequestedDigits += 1;
+            LastDigit = RequestedDigits + 1;
+          };
+        };
+        I += 1;
+      };
+      if (DecimalPos === 0) DecimalPos = RequestedDigits + 1;
+      LastDigit = DecimalPos - LastDigit;
+      if (LastDigit > 0) LastDigit = 0;
+      FirstDigit = DecimalPos - FirstDigit;
+      if (FirstDigit < 0) FirstDigit = 0;
+    };
+    function ValueOutSideScope() {
+      var Result = false;
+      Result = (((FV.Exponent >= 18) && !IsScientific) || (FV.Exponent === 0x7FF)) || (FV.Exponent === 0x800);
+      return Result;
+    };
+    function CalcRunVars() {
+      var D = 0;
+      var P = 0;
+      if (IsScientific) {
+        P = RequestedDigits;
+        D = 9999;
+      } else {
+        P = 18;
+        D = (RequestedDigits - DecimalPos) + 1;
+      };
+      FV = new $mod.TFloatRec($mod.FloatToDecimal(aValue,P,D));
+      DistToDecimal = DecimalPos - 1;
+      if (IsScientific) {
+        PadZeroes = 0}
+       else {
+        PadZeroes = FV.Exponent - (DecimalPos - 1);
+        if (PadZeroes >= 0) DistToDecimal = FV.Exponent;
+      };
+      Available = -1;
+      while ((Available < (rtl.length(FV.Digits) - 1)) && (FV.Digits[Available + 1] !== "\x00")) Available += 1;
+    };
+    function FormatExponent(ASign, aExponent) {
+      var Result = "";
+      Result = $mod.IntToStr(aExponent);
+      Result = pas.System.StringOfChar("0",ExpSize - Result.length) + Result;
+      if (aExponent < 0) {
+        Result = "-" + Result}
+       else if ((aExponent > 0) && (ASign === "+")) Result = ASign + Result;
+      return Result;
+    };
+    var I = 0;
+    var S = 0;
+    var C = "";
+    var Q = "";
+    var PA = [];
+    var InLiteral = false;
+    PA = rtl.arraySetLength(PA,0,4);
+    Result = "";
+    InitVars();
+    if (E > 0) {
+      S = 1}
+     else if (E < 0) {
+      S = 2}
+     else S = 3;
+    PA[0] = 0;
+    I = GetSections({get: function () {
+        return PA;
+      }, set: function (v) {
+        PA = v;
+      }});
+    if ((I < S) || ((PA[S] - PA[S - 1]) === 0)) S = 1;
+    SectionLength = (PA[S] - PA[S - 1]) - 1;
+    Section = pas.System.Copy(Fmt,PA[S - 1] + 1,SectionLength);
+    Section = rtl.strSetLength(Section,SectionLength);
+    AnalyzeFormat();
+    CalcRunVars();
+    if ((SectionLength === 0) || ValueOutSideScope()) {
+      Section=E.toPrecision(15);
+      Result = Section;
+    };
+    I = 1;
+    Current = 0;
+    Q = " ";
+    InLiteral = false;
+    if (FV.Negative && (S === 1)) ToResult("-");
+    while (I <= SectionLength) {
+      C = Section.charAt(I - 1);
+      if (C.charCodeAt() in rtl.createSet(34,39)) {
+        if (InLiteral) {
+          InLiteral = C !== Q}
+         else {
+          InLiteral = true;
+          Q = C;
+        };
+      } else if (InLiteral) {
+        ToResult(C)}
+       else {
+        var $tmp1 = C;
+        if (($tmp1 === "0") || ($tmp1 === "#")) {
+          CopyDigit()}
+         else if (($tmp1 === ".") || ($tmp1 === ",")) {}
+        else if (($tmp1 === "e") || ($tmp1 === "E")) {
+          ToResult(C);
+          I += 1;
+          if (I <= Section.length) {
+            C = Section.charAt(I - 1);
+            if (C.charCodeAt() in rtl.createSet(43,45)) {
+              AddToResult(FormatExponent(C,(FV.Exponent - DecimalPos) + 1));
+              while ((I < SectionLength) && (Section.charAt((I + 1) - 1) === "0")) I += 1;
+            };
+          };
+        } else {
+          ToResult(C);
+        };
+      };
+      I += 1;
+    };
+    return Result;
+  };
+  this.TrueBoolStrs = [];
+  this.FalseBoolStrs = [];
+  this.StrToBool = function (S) {
+    var Result = false;
+    if (!$mod.TryStrToBool(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidBoolean,[S]]);
+    return Result;
+  };
+  this.BoolToStr = function (B, UseBoolStrs) {
+    var Result = "";
+    if (UseBoolStrs) {
+      $impl.CheckBoolStrs();
+      if (B) {
+        Result = $mod.TrueBoolStrs[0]}
+       else Result = $mod.FalseBoolStrs[0];
+    } else if (B) {
+      Result = "-1"}
+     else Result = "0";
+    return Result;
+  };
+  this.BoolToStr$1 = function (B, TrueS, FalseS) {
+    var Result = "";
+    if (B) {
+      Result = TrueS}
+     else Result = FalseS;
+    return Result;
+  };
+  this.StrToBoolDef = function (S, Default) {
+    var Result = false;
+    if (!$mod.TryStrToBool(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = Default;
+    return Result;
+  };
+  this.TryStrToBool = function (S, Value) {
+    var Result = false;
+    var Temp = "";
+    var I = 0;
+    var D = 0.0;
+    var Code = 0;
+    Temp = $mod.UpperCase(S);
+    pas.System.val$7(Temp,{get: function () {
+        return D;
+      }, set: function (v) {
+        D = v;
+      }},{get: function () {
+        return Code;
+      }, set: function (v) {
+        Code = v;
+      }});
+    Result = true;
+    if (Code === 0) {
+      Value.set(D !== 0.0)}
+     else {
+      $impl.CheckBoolStrs();
+      for (var $l1 = 0, $end2 = rtl.length($mod.TrueBoolStrs) - 1; $l1 <= $end2; $l1++) {
+        I = $l1;
+        if (Temp === $mod.UpperCase($mod.TrueBoolStrs[I])) {
+          Value.set(true);
+          return Result;
+        };
+      };
+      for (var $l3 = 0, $end4 = rtl.length($mod.FalseBoolStrs) - 1; $l3 <= $end4; $l3++) {
+        I = $l3;
+        if (Temp === $mod.UpperCase($mod.FalseBoolStrs[I])) {
+          Value.set(false);
+          return Result;
+        };
+      };
+      Result = false;
+    };
+    return Result;
+  };
+  this.ConfigExtension = ".cfg";
+  this.SysConfigDir = "";
+  this.OnGetEnvironmentVariable = null;
+  this.OnGetEnvironmentString = null;
+  this.OnGetEnvironmentVariableCount = null;
+  this.GetEnvironmentVariable = function (EnvVar) {
+    var Result = "";
+    if ($mod.OnGetEnvironmentVariable != null) {
+      Result = $mod.OnGetEnvironmentVariable(EnvVar)}
+     else Result = "";
+    return Result;
+  };
+  this.GetEnvironmentVariableCount = function () {
+    var Result = 0;
+    if ($mod.OnGetEnvironmentVariableCount != null) {
+      Result = $mod.OnGetEnvironmentVariableCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.GetEnvironmentString = function (Index) {
+    var Result = "";
+    if ($mod.OnGetEnvironmentString != null) {
+      Result = $mod.OnGetEnvironmentString(Index)}
+     else Result = "";
+    return Result;
+  };
+  this.ShowException = function (ExceptObject, ExceptAddr) {
+    var S = "";
+    S = "Application raised an exception " + ExceptObject.$classname;
+    if ($mod.Exception.isPrototypeOf(ExceptObject)) S = (S + " : ") + ExceptObject.fMessage;
+    window.alert(S);
+    if (ExceptAddr === null) ;
+  };
+  this.Abort = function () {
+    throw $mod.EAbort.$create("Create$1",[$impl.SAbortError]);
+  };
+  this.TEventType = {"0": "etCustom", etCustom: 0, "1": "etInfo", etInfo: 1, "2": "etWarning", etWarning: 2, "3": "etError", etError: 3, "4": "etDebug", etDebug: 4};
+  this.TSystemTime = function (s) {
+    if (s) {
+      this.Year = s.Year;
+      this.Month = s.Month;
+      this.Day = s.Day;
+      this.DayOfWeek = s.DayOfWeek;
+      this.Hour = s.Hour;
+      this.Minute = s.Minute;
+      this.Second = s.Second;
+      this.MilliSecond = s.MilliSecond;
+    } else {
+      this.Year = 0;
+      this.Month = 0;
+      this.Day = 0;
+      this.DayOfWeek = 0;
+      this.Hour = 0;
+      this.Minute = 0;
+      this.Second = 0;
+      this.MilliSecond = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Year === b.Year) && ((this.Month === b.Month) && ((this.Day === b.Day) && ((this.DayOfWeek === b.DayOfWeek) && ((this.Hour === b.Hour) && ((this.Minute === b.Minute) && ((this.Second === b.Second) && (this.MilliSecond === b.MilliSecond)))))));
+    };
+  };
+  this.TTimeStamp = function (s) {
+    if (s) {
+      this.Time = s.Time;
+      this.date = s.date;
+    } else {
+      this.Time = 0;
+      this.date = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Time === b.Time) && (this.date === b.date);
+    };
+  };
+  this.TimeSeparator = ":";
+  this.DateSeparator = "-";
+  this.ShortDateFormat = "yyyy-mm-dd";
+  this.LongDateFormat = "ddd, yyyy-mm-dd";
+  this.ShortTimeFormat = "hh:nn";
+  this.LongTimeFormat = "hh:nn:ss";
+  this.DecimalSeparator = ".";
+  this.ThousandSeparator = "";
+  this.TimeAMString = "AM";
+  this.TimePMString = "PM";
+  this.HoursPerDay = 24;
+  this.MinsPerHour = 60;
+  this.SecsPerMin = 60;
+  this.MSecsPerSec = 1000;
+  this.MinsPerDay = 24 * 60;
+  this.SecsPerDay = 1440 * 60;
+  this.MSecsPerDay = 86400 * 1000;
+  this.MaxDateTime = 2958465.99999999;
+  this.MinDateTime = -693593.99999999;
+  this.JulianEpoch = -2415018.5;
+  this.UnixEpoch = -2415018.5 + 2440587.5;
+  this.DateDelta = 693594;
+  this.UnixDateDelta = 25569;
+  this.MonthDays = [[31,28,31,30,31,30,31,31,30,31,30,31],[31,29,31,30,31,30,31,31,30,31,30,31]];
+  this.ShortMonthNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
+  this.LongMonthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
+  this.ShortDayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
+  this.LongDayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
+  rtl.createClass($mod,"TFormatSettings",pas.System.TObject,function () {
+    this.GetCurrencyDecimals = function () {
+      var Result = 0;
+      Result = $mod.CurrencyDecimals;
+      return Result;
+    };
+    this.GetCurrencyFormat = function () {
+      var Result = 0;
+      Result = $mod.CurrencyFormat;
+      return Result;
+    };
+    this.GetCurrencyString = function () {
+      var Result = "";
+      Result = $mod.CurrencyString;
+      return Result;
+    };
+    this.GetDateSeparator = function () {
+      var Result = "";
+      Result = $mod.DateSeparator;
+      return Result;
+    };
+    this.GetDecimalSeparator = function () {
+      var Result = "";
+      Result = $mod.DecimalSeparator;
+      return Result;
+    };
+    this.GetLongDateFormat = function () {
+      var Result = "";
+      Result = $mod.LongDateFormat;
+      return Result;
+    };
+    this.GetLongDayNames = function () {
+      var Result = rtl.arraySetLength(null,"",7);
+      Result = $mod.LongDayNames.slice(0);
+      return Result;
+    };
+    this.GetLongMonthNames = function () {
+      var Result = rtl.arraySetLength(null,"",12);
+      Result = $mod.LongMonthNames.slice(0);
+      return Result;
+    };
+    this.GetLongTimeFormat = function () {
+      var Result = "";
+      Result = $mod.LongTimeFormat;
+      return Result;
+    };
+    this.GetNegCurrFormat = function () {
+      var Result = 0;
+      Result = $mod.NegCurrFormat;
+      return Result;
+    };
+    this.GetShortDateFormat = function () {
+      var Result = "";
+      Result = $mod.ShortDateFormat;
+      return Result;
+    };
+    this.GetShortDayNames = function () {
+      var Result = rtl.arraySetLength(null,"",7);
+      Result = $mod.ShortDayNames.slice(0);
+      return Result;
+    };
+    this.GetShortMonthNames = function () {
+      var Result = rtl.arraySetLength(null,"",12);
+      Result = $mod.ShortMonthNames.slice(0);
+      return Result;
+    };
+    this.GetShortTimeFormat = function () {
+      var Result = "";
+      Result = $mod.ShortTimeFormat;
+      return Result;
+    };
+    this.GetThousandSeparator = function () {
+      var Result = "";
+      Result = $mod.ThousandSeparator;
+      return Result;
+    };
+    this.GetTimeAMString = function () {
+      var Result = "";
+      Result = $mod.TimeAMString;
+      return Result;
+    };
+    this.GetTimePMString = function () {
+      var Result = "";
+      Result = $mod.TimePMString;
+      return Result;
+    };
+    this.GetTimeSeparator = function () {
+      var Result = "";
+      Result = $mod.TimeSeparator;
+      return Result;
+    };
+    this.SetCurrencyFormat = function (AValue) {
+      $mod.CurrencyFormat = AValue;
+    };
+    this.SetCurrencyString = function (AValue) {
+      $mod.CurrencyString = AValue;
+    };
+    this.SetDateSeparator = function (Value) {
+      $mod.DateSeparator = Value;
+    };
+    this.SetDecimalSeparator = function (Value) {
+      $mod.DecimalSeparator = Value;
+    };
+    this.SetLongDateFormat = function (Value) {
+      $mod.LongDateFormat = Value;
+    };
+    this.SetLongDayNames = function (AValue) {
+      $mod.LongDayNames = AValue.slice(0);
+    };
+    this.SetLongMonthNames = function (AValue) {
+      $mod.LongMonthNames = AValue.slice(0);
+    };
+    this.SetLongTimeFormat = function (Value) {
+      $mod.LongTimeFormat = Value;
+    };
+    this.SetNegCurrFormat = function (AValue) {
+      $mod.NegCurrFormat = AValue;
+    };
+    this.SetShortDateFormat = function (Value) {
+      $mod.ShortDateFormat = Value;
+    };
+    this.SetShortDayNames = function (AValue) {
+      $mod.ShortDayNames = AValue.slice(0);
+    };
+    this.SetShortMonthNames = function (AValue) {
+      $mod.ShortMonthNames = AValue.slice(0);
+    };
+    this.SetShortTimeFormat = function (Value) {
+      $mod.ShortTimeFormat = Value;
+    };
+    this.SetCurrencyDecimals = function (AValue) {
+      $mod.CurrencyDecimals = AValue;
+    };
+    this.SetThousandSeparator = function (Value) {
+      $mod.ThousandSeparator = Value;
+    };
+    this.SetTimeAMString = function (Value) {
+      $mod.TimeAMString = Value;
+    };
+    this.SetTimePMString = function (Value) {
+      $mod.TimePMString = Value;
+    };
+    this.SetTimeSeparator = function (Value) {
+      $mod.TimeSeparator = Value;
+    };
+  });
+  this.FormatSettings = null;
+  this.TwoDigitYearCenturyWindow = 50;
+  this.DateTimeToJSDate = function (aDateTime) {
+    var Result = null;
+    var Y = 0;
+    var M = 0;
+    var D = 0;
+    var h = 0;
+    var n = 0;
+    var s = 0;
+    var z = 0;
+    $mod.DecodeDate(pas.System.Trunc(aDateTime),{get: function () {
+        return Y;
+      }, set: function (v) {
+        Y = v;
+      }},{get: function () {
+        return M;
+      }, set: function (v) {
+        M = v;
+      }},{get: function () {
+        return D;
+      }, set: function (v) {
+        D = v;
+      }});
+    $mod.DecodeTime(pas.System.Frac(aDateTime),{get: function () {
+        return h;
+      }, set: function (v) {
+        h = v;
+      }},{get: function () {
+        return n;
+      }, set: function (v) {
+        n = v;
+      }},{get: function () {
+        return s;
+      }, set: function (v) {
+        s = v;
+      }},{get: function () {
+        return z;
+      }, set: function (v) {
+        z = v;
+      }});
+    Result = new Date(Y,M,D,h,n,s,z);
+    return Result;
+  };
+  this.JSDateToDateTime = function (aDate) {
+    var Result = 0.0;
+    Result = $mod.EncodeDate(aDate.getFullYear(),aDate.getMonth() + 1,aDate.getDate()) + $mod.EncodeTime(aDate.getHours(),aDate.getMinutes(),aDate.getSeconds(),aDate.getMilliseconds());
+    return Result;
+  };
+  this.DateTimeToTimeStamp = function (DateTime) {
+    var Result = new $mod.TTimeStamp();
+    var D = 0.0;
+    D = DateTime * 86400000;
+    if (D < 0) {
+      D = D - 0.5}
+     else D = D + 0.5;
+    Result.Time = pas.System.Trunc(Math.abs(pas.System.Trunc(D)) % 86400000);
+    Result.date = 693594 + Math.floor(pas.System.Trunc(D) / 86400000);
+    return Result;
+  };
+  this.TimeStampToDateTime = function (TimeStamp) {
+    var Result = 0.0;
+    Result = $mod.ComposeDateTime(TimeStamp.date - 693594,TimeStamp.Time / 86400000);
+    return Result;
+  };
+  this.MSecsToTimeStamp = function (MSecs) {
+    var Result = new $mod.TTimeStamp();
+    Result.date = pas.System.Trunc(MSecs / 86400000);
+    MSecs = MSecs - (Result.date * 86400000);
+    Result.Time = Math.round(MSecs);
+    return Result;
+  };
+  this.TimeStampToMSecs = function (TimeStamp) {
+    var Result = 0;
+    Result = TimeStamp.Time + (TimeStamp.date * 86400000);
+    return Result;
+  };
+  this.TryEncodeDate = function (Year, Month, Day, date) {
+    var Result = false;
+    var c = 0;
+    var ya = 0;
+    Result = (((((Year > 0) && (Year < 10000)) && (Month >= 1)) && (Month <= 12)) && (Day > 0)) && (Day <= $mod.MonthDays[+$mod.IsLeapYear(Year)][Month - 1]);
+    if (Result) {
+      if (Month > 2) {
+        Month -= 3}
+       else {
+        Month += 9;
+        Year -= 1;
+      };
+      c = Math.floor(Year / 100);
+      ya = Year - (100 * c);
+      date.set(((((146097 * c) >>> 2) + ((1461 * ya) >>> 2)) + Math.floor(((153 * Month) + 2) / 5)) + Day);
+      date.set(date.get() - 693900);
+    };
+    return Result;
+  };
+  this.TryEncodeTime = function (Hour, Min, Sec, MSec, Time) {
+    var Result = false;
+    Result = (((Hour < 24) && (Min < 60)) && (Sec < 60)) && (MSec < 1000);
+    if (Result) Time.set(((((Hour * 3600000) + (Min * 60000)) + (Sec * 1000)) + MSec) / 86400000);
+    return Result;
+  };
+  this.EncodeDate = function (Year, Month, Day) {
+    var Result = 0.0;
+    if (!$mod.TryEncodeDate(Year,Month,Day,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",["%s-%s-%s is not a valid date specification",[$mod.IntToStr(Year),$mod.IntToStr(Month),$mod.IntToStr(Day)]]);
+    return Result;
+  };
+  this.EncodeTime = function (Hour, Minute, Second, MilliSecond) {
+    var Result = 0.0;
+    if (!$mod.TryEncodeTime(Hour,Minute,Second,MilliSecond,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",["%s:%s:%s.%s is not a valid time specification",[$mod.IntToStr(Hour),$mod.IntToStr(Minute),$mod.IntToStr(Second),$mod.IntToStr(MilliSecond)]]);
+    return Result;
+  };
+  this.ComposeDateTime = function (date, Time) {
+    var Result = 0.0;
+    if (date < 0) {
+      Result = pas.System.Trunc(date) - Math.abs(pas.System.Frac(Time))}
+     else Result = pas.System.Trunc(date) + Math.abs(pas.System.Frac(Time));
+    return Result;
+  };
+  this.DecodeDate = function (date, Year, Month, Day) {
+    var ly = 0;
+    var ld = 0;
+    var lm = 0;
+    var j = 0;
+    if (date <= -693594) {
+      Year.set(0);
+      Month.set(0);
+      Day.set(0);
+    } else {
+      if (date > 0) {
+        date = date + (1 / (86400000 * 2))}
+       else date = date - (1 / (86400000 * 2));
+      if (date > $mod.MaxDateTime) date = $mod.MaxDateTime;
+      j = ((pas.System.Trunc(date) + 693900) << 2) - 1;
+      ly = Math.floor(j / 146097);
+      j = j - (146097 * ly);
+      ld = j >>> 2;
+      j = Math.floor(((ld << 2) + 3) / 1461);
+      ld = (((ld << 2) + 7) - (1461 * j)) >>> 2;
+      lm = Math.floor(((5 * ld) - 3) / 153);
+      ld = Math.floor((((5 * ld) + 2) - (153 * lm)) / 5);
+      ly = (100 * ly) + j;
+      if (lm < 10) {
+        lm += 3}
+       else {
+        lm -= 9;
+        ly += 1;
+      };
+      Year.set(ly);
+      Month.set(lm);
+      Day.set(ld);
+    };
+  };
+  this.DecodeDateFully = function (DateTime, Year, Month, Day, DOW) {
+    var Result = false;
+    $mod.DecodeDate(DateTime,Year,Month,Day);
+    DOW.set($mod.DayOfWeek(DateTime));
+    Result = $mod.IsLeapYear(Year.get());
+    return Result;
+  };
+  this.DecodeTime = function (Time, Hour, Minute, Second, MilliSecond) {
+    var l = 0;
+    l = $mod.DateTimeToTimeStamp(Time).Time;
+    Hour.set(Math.floor(l / 3600000));
+    l = l % 3600000;
+    Minute.set(Math.floor(l / 60000));
+    l = l % 60000;
+    Second.set(Math.floor(l / 1000));
+    l = l % 1000;
+    MilliSecond.set(l);
+  };
+  this.DateTimeToSystemTime = function (DateTime, SystemTime) {
+    $mod.DecodeDateFully(DateTime,{p: SystemTime.get(), get: function () {
+        return this.p.Year;
+      }, set: function (v) {
+        this.p.Year = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.Month;
+      }, set: function (v) {
+        this.p.Month = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.Day;
+      }, set: function (v) {
+        this.p.Day = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.DayOfWeek;
+      }, set: function (v) {
+        this.p.DayOfWeek = v;
+      }});
+    $mod.DecodeTime(DateTime,{p: SystemTime.get(), get: function () {
+        return this.p.Hour;
+      }, set: function (v) {
+        this.p.Hour = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.Minute;
+      }, set: function (v) {
+        this.p.Minute = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.Second;
+      }, set: function (v) {
+        this.p.Second = v;
+      }},{p: SystemTime.get(), get: function () {
+        return this.p.MilliSecond;
+      }, set: function (v) {
+        this.p.MilliSecond = v;
+      }});
+    SystemTime.get().DayOfWeek -= 1;
+  };
+  this.SystemTimeToDateTime = function (SystemTime) {
+    var Result = 0.0;
+    Result = $mod.ComposeDateTime($impl.DoEncodeDate(SystemTime.Year,SystemTime.Month,SystemTime.Day),$impl.DoEncodeTime(SystemTime.Hour,SystemTime.Minute,SystemTime.Second,SystemTime.MilliSecond));
+    return Result;
+  };
+  this.DayOfWeek = function (DateTime) {
+    var Result = 0;
+    Result = 1 + ((pas.System.Trunc(DateTime) - 1) % 7);
+    if (Result <= 0) Result += 7;
+    return Result;
+  };
+  this.date = function () {
+    var Result = 0.0;
+    Result = pas.System.Trunc($mod.Now());
+    return Result;
+  };
+  this.Time = function () {
+    var Result = 0.0;
+    Result = $mod.Now() - $mod.date();
+    return Result;
+  };
+  this.Now = function () {
+    var Result = 0.0;
+    Result = $mod.JSDateToDateTime(new Date());
+    return Result;
+  };
+  this.IncMonth = function (DateTime, NumberOfMonths) {
+    var Result = 0.0;
+    var Year = 0;
+    var Month = 0;
+    var Day = 0;
+    $mod.DecodeDate(DateTime,{get: function () {
+        return Year;
+      }, set: function (v) {
+        Year = v;
+      }},{get: function () {
+        return Month;
+      }, set: function (v) {
+        Month = v;
+      }},{get: function () {
+        return Day;
+      }, set: function (v) {
+        Day = v;
+      }});
+    $mod.IncAMonth({get: function () {
+        return Year;
+      }, set: function (v) {
+        Year = v;
+      }},{get: function () {
+        return Month;
+      }, set: function (v) {
+        Month = v;
+      }},{get: function () {
+        return Day;
+      }, set: function (v) {
+        Day = v;
+      }},NumberOfMonths);
+    Result = $mod.ComposeDateTime($impl.DoEncodeDate(Year,Month,Day),DateTime);
+    return Result;
+  };
+  this.IncAMonth = function (Year, Month, Day, NumberOfMonths) {
+    var TempMonth = 0;
+    var S = 0;
+    if (NumberOfMonths >= 0) {
+      S = 1}
+     else S = -1;
+    Year.set(Year.get() + Math.floor(NumberOfMonths / 12));
+    TempMonth = (Month.get() + (NumberOfMonths % 12)) - 1;
+    if ((TempMonth > 11) || (TempMonth < 0)) {
+      TempMonth -= S * 12;
+      Year.set(Year.get() + S);
+    };
+    Month.set(TempMonth + 1);
+    if (Day.get() > $mod.MonthDays[+$mod.IsLeapYear(Year.get())][Month.get() - 1]) Day.set($mod.MonthDays[+$mod.IsLeapYear(Year.get())][Month.get() - 1]);
+  };
+  this.IsLeapYear = function (Year) {
+    var Result = false;
+    Result = ((Year % 4) === 0) && (((Year % 100) !== 0) || ((Year % 400) === 0));
+    return Result;
+  };
+  this.DateToStr = function (date) {
+    var Result = "";
+    Result = $mod.FormatDateTime("ddddd",date);
+    return Result;
+  };
+  this.TimeToStr = function (Time) {
+    var Result = "";
+    Result = $mod.FormatDateTime("tt",Time);
+    return Result;
+  };
+  this.DateTimeToStr = function (DateTime, ForceTimeIfZero) {
+    var Result = "";
+    Result = $mod.FormatDateTime($impl.DateTimeToStrFormat[+ForceTimeIfZero],DateTime);
+    return Result;
+  };
+  this.StrToDate = function (S) {
+    var Result = 0.0;
+    Result = $mod.StrToDate$2(S,$mod.ShortDateFormat,"\x00");
+    return Result;
+  };
+  this.StrToDate$1 = function (S, separator) {
+    var Result = 0.0;
+    Result = $mod.StrToDate$2(S,$mod.ShortDateFormat,separator);
+    return Result;
+  };
+  this.StrToDate$2 = function (S, useformat, separator) {
+    var Result = 0.0;
+    var MSg = "";
+    Result = $impl.IntStrToDate({get: function () {
+        return MSg;
+      }, set: function (v) {
+        MSg = v;
+      }},S,useformat,separator);
+    if (MSg !== "") throw $mod.EConvertError.$create("Create$1",[MSg]);
+    return Result;
+  };
+  this.StrToTime = function (S) {
+    var Result = 0.0;
+    Result = $mod.StrToTime$1(S,$mod.TimeSeparator);
+    return Result;
+  };
+  this.StrToTime$1 = function (S, separator) {
+    var Result = 0.0;
+    var Msg = "";
+    Result = $impl.IntStrToTime({get: function () {
+        return Msg;
+      }, set: function (v) {
+        Msg = v;
+      }},S,S.length,separator);
+    if (Msg !== "") throw $mod.EConvertError.$create("Create$1",[Msg]);
+    return Result;
+  };
+  this.StrToDateTime = function (S) {
+    var Result = 0.0;
+    var TimeStr = "";
+    var DateStr = "";
+    var PartsFound = 0;
+    PartsFound = $impl.SplitDateTimeStr(S,{get: function () {
+        return DateStr;
+      }, set: function (v) {
+        DateStr = v;
+      }},{get: function () {
+        return TimeStr;
+      }, set: function (v) {
+        TimeStr = v;
+      }});
+    var $tmp1 = PartsFound;
+    if ($tmp1 === 0) {
+      Result = $mod.StrToDate("")}
+     else if ($tmp1 === 1) {
+      if (DateStr.length > 0) {
+        Result = $mod.StrToDate$2(DateStr,$mod.ShortDateFormat,$mod.DateSeparator)}
+       else Result = $mod.StrToTime(TimeStr)}
+     else if ($tmp1 === 2) Result = $mod.ComposeDateTime($mod.StrToDate$2(DateStr,$mod.ShortDateFormat,$mod.DateSeparator),$mod.StrToTime(TimeStr));
+    return Result;
+  };
+  this.FormatDateTime = function (FormatStr, DateTime) {
+    var Result = "";
+    function StoreStr(APos, Len) {
+      Result = Result + pas.System.Copy(FormatStr,APos,Len);
+    };
+    function StoreString(AStr) {
+      Result = Result + AStr;
+    };
+    function StoreInt(Value, Digits) {
+      var S = "";
+      S = $mod.IntToStr(Value);
+      while (S.length < Digits) S = "0" + S;
+      StoreString(S);
+    };
+    var Year = 0;
+    var Month = 0;
+    var Day = 0;
+    var DayOfWeek = 0;
+    var Hour = 0;
+    var Minute = 0;
+    var Second = 0;
+    var MilliSecond = 0;
+    function StoreFormat(FormatStr, Nesting, TimeFlag) {
+      var Token = "";
+      var lastformattoken = "";
+      var prevlasttoken = "";
+      var Count = 0;
+      var Clock12 = false;
+      var tmp = 0;
+      var isInterval = false;
+      var P = 0;
+      var FormatCurrent = 0;
+      var FormatEnd = 0;
+      if (Nesting > 1) return;
+      FormatCurrent = 1;
+      FormatEnd = FormatStr.length;
+      Clock12 = false;
+      isInterval = false;
+      P = 1;
+      while (P <= FormatEnd) {
+        Token = FormatStr.charAt(P - 1);
+        var $tmp1 = Token;
+        if (($tmp1 === "'") || ($tmp1 === '"')) {
+          P += 1;
+          while ((P < FormatEnd) && (FormatStr.charAt(P - 1) !== Token)) P += 1;
+        } else if (($tmp1 === "A") || ($tmp1 === "a")) {
+          if ((($mod.CompareText(pas.System.Copy(FormatStr,P,3),"A\/P") === 0) || ($mod.CompareText(pas.System.Copy(FormatStr,P,4),"AMPM") === 0)) || ($mod.CompareText(pas.System.Copy(FormatStr,P,5),"AM\/PM") === 0)) {
+            Clock12 = true;
+            break;
+          };
+        };
+        P += 1;
+      };
+      Token = "ÿ";
+      lastformattoken = " ";
+      prevlasttoken = "H";
+      while (FormatCurrent <= FormatEnd) {
+        Token = $mod.UpperCase(FormatStr.charAt(FormatCurrent - 1)).charAt(0);
+        Count = 1;
+        P = FormatCurrent + 1;
+        var $tmp2 = Token;
+        if (($tmp2 === "'") || ($tmp2 === '"')) {
+          while ((P < FormatEnd) && (FormatStr.charAt(P - 1) !== Token)) P += 1;
+          P += 1;
+          Count = P - FormatCurrent;
+          StoreStr(FormatCurrent + 1,Count - 2);
+        } else if ($tmp2 === "A") {
+          if ($mod.CompareText(pas.System.Copy(FormatStr,FormatCurrent,4),"AMPM") === 0) {
+            Count = 4;
+            if (Hour < 12) {
+              StoreString($mod.TimeAMString)}
+             else StoreString($mod.TimePMString);
+          } else if ($mod.CompareText(pas.System.Copy(FormatStr,FormatCurrent,5),"AM\/PM") === 0) {
+            Count = 5;
+            if (Hour < 12) {
+              StoreStr(FormatCurrent,2)}
+             else StoreStr(FormatCurrent + 3,2);
+          } else if ($mod.CompareText(pas.System.Copy(FormatStr,FormatCurrent,3),"A\/P") === 0) {
+            Count = 3;
+            if (Hour < 12) {
+              StoreStr(FormatCurrent,1)}
+             else StoreStr(FormatCurrent + 2,1);
+          } else throw $mod.EConvertError.$create("Create$1",["Illegal character in format string"]);
+        } else if ($tmp2 === "\/") {
+          StoreString($mod.DateSeparator);
+        } else if ($tmp2 === ":") {
+          StoreString($mod.TimeSeparator)}
+         else if ((((((((((($tmp2 === " ") || ($tmp2 === "C")) || ($tmp2 === "D")) || ($tmp2 === "H")) || ($tmp2 === "M")) || ($tmp2 === "N")) || ($tmp2 === "S")) || ($tmp2 === "T")) || ($tmp2 === "Y")) || ($tmp2 === "Z")) || ($tmp2 === "F")) {
+          while ((P <= FormatEnd) && ($mod.UpperCase(FormatStr.charAt(P - 1)) === Token)) P += 1;
+          Count = P - FormatCurrent;
+          var $tmp3 = Token;
+          if ($tmp3 === " ") {
+            StoreStr(FormatCurrent,Count)}
+           else if ($tmp3 === "Y") {
+            if (Count > 2) {
+              StoreInt(Year,4)}
+             else StoreInt(Year % 100,2);
+          } else if ($tmp3 === "M") {
+            if (isInterval && ((prevlasttoken === "H") || TimeFlag)) {
+              StoreInt(Minute + ((Hour + (pas.System.Trunc(Math.abs(DateTime)) * 24)) * 60),0)}
+             else if ((lastformattoken === "H") || TimeFlag) {
+              if (Count === 1) {
+                StoreInt(Minute,0)}
+               else StoreInt(Minute,2);
+            } else {
+              var $tmp4 = Count;
+              if ($tmp4 === 1) {
+                StoreInt(Month,0)}
+               else if ($tmp4 === 2) {
+                StoreInt(Month,2)}
+               else if ($tmp4 === 3) {
+                StoreString($mod.ShortMonthNames[Month - 1])}
+               else {
+                StoreString($mod.LongMonthNames[Month - 1]);
+              };
+            };
+          } else if ($tmp3 === "D") {
+            var $tmp5 = Count;
+            if ($tmp5 === 1) {
+              StoreInt(Day,0)}
+             else if ($tmp5 === 2) {
+              StoreInt(Day,2)}
+             else if ($tmp5 === 3) {
+              StoreString($mod.ShortDayNames[DayOfWeek])}
+             else if ($tmp5 === 4) {
+              StoreString($mod.LongDayNames[DayOfWeek])}
+             else if ($tmp5 === 5) {
+              StoreFormat($mod.ShortDateFormat,Nesting + 1,false)}
+             else {
+              StoreFormat($mod.LongDateFormat,Nesting + 1,false);
+            };
+          } else if ($tmp3 === "H") {
+            if (isInterval) {
+              StoreInt(Hour + (pas.System.Trunc(Math.abs(DateTime)) * 24),0)}
+             else if (Clock12) {
+              tmp = Hour % 12;
+              if (tmp === 0) tmp = 12;
+              if (Count === 1) {
+                StoreInt(tmp,0)}
+               else StoreInt(tmp,2);
+            } else {
+              if (Count === 1) {
+                StoreInt(Hour,0)}
+               else StoreInt(Hour,2);
+            }}
+           else if ($tmp3 === "N") {
+            if (isInterval) {
+              StoreInt(Minute + ((Hour + (pas.System.Trunc(Math.abs(DateTime)) * 24)) * 60),0)}
+             else if (Count === 1) {
+              StoreInt(Minute,0)}
+             else StoreInt(Minute,2)}
+           else if ($tmp3 === "S") {
+            if (isInterval) {
+              StoreInt(Second + ((Minute + ((Hour + (pas.System.Trunc(Math.abs(DateTime)) * 24)) * 60)) * 60),0)}
+             else if (Count === 1) {
+              StoreInt(Second,0)}
+             else StoreInt(Second,2)}
+           else if ($tmp3 === "Z") {
+            if (Count === 1) {
+              StoreInt(MilliSecond,0)}
+             else StoreInt(MilliSecond,3)}
+           else if ($tmp3 === "T") {
+            if (Count === 1) {
+              StoreFormat($mod.ShortTimeFormat,Nesting + 1,true)}
+             else StoreFormat($mod.LongTimeFormat,Nesting + 1,true)}
+           else if ($tmp3 === "C") {
+            StoreFormat($mod.ShortDateFormat,Nesting + 1,false);
+            if (((Hour !== 0) || (Minute !== 0)) || (Second !== 0)) {
+              StoreString(" ");
+              StoreFormat($mod.LongTimeFormat,Nesting + 1,true);
+            };
+          } else if ($tmp3 === "F") {
+            StoreFormat($mod.ShortDateFormat,Nesting + 1,false);
+            StoreString(" ");
+            StoreFormat($mod.LongTimeFormat,Nesting + 1,true);
+          };
+          prevlasttoken = lastformattoken;
+          lastformattoken = Token;
+        } else {
+          StoreString(Token);
+        };
+        FormatCurrent += Count;
+      };
+    };
+    $mod.DecodeDateFully(DateTime,{get: function () {
+        return Year;
+      }, set: function (v) {
+        Year = v;
+      }},{get: function () {
+        return Month;
+      }, set: function (v) {
+        Month = v;
+      }},{get: function () {
+        return Day;
+      }, set: function (v) {
+        Day = v;
+      }},{get: function () {
+        return DayOfWeek;
+      }, set: function (v) {
+        DayOfWeek = v;
+      }});
+    $mod.DecodeTime(DateTime,{get: function () {
+        return Hour;
+      }, set: function (v) {
+        Hour = v;
+      }},{get: function () {
+        return Minute;
+      }, set: function (v) {
+        Minute = v;
+      }},{get: function () {
+        return Second;
+      }, set: function (v) {
+        Second = v;
+      }},{get: function () {
+        return MilliSecond;
+      }, set: function (v) {
+        MilliSecond = v;
+      }});
+    if (FormatStr !== "") {
+      StoreFormat(FormatStr,0,false)}
+     else StoreFormat("C",0,false);
+    return Result;
+  };
+  this.TryStrToDate = function (S, Value) {
+    var Result = false;
+    Result = $mod.TryStrToDate$2(S,Value,$mod.ShortDateFormat,"\x00");
+    return Result;
+  };
+  this.TryStrToDate$1 = function (S, Value, separator) {
+    var Result = false;
+    Result = $mod.TryStrToDate$2(S,Value,$mod.ShortDateFormat,separator);
+    return Result;
+  };
+  this.TryStrToDate$2 = function (S, Value, useformat, separator) {
+    var Result = false;
+    var Msg = "";
+    Result = S.length !== 0;
+    if (Result) {
+      Value.set($impl.IntStrToDate({get: function () {
+          return Msg;
+        }, set: function (v) {
+          Msg = v;
+        }},S,useformat,separator));
+      Result = Msg === "";
+    };
+    return Result;
+  };
+  this.TryStrToTime = function (S, Value) {
+    var Result = false;
+    Result = $mod.TryStrToTime$1(S,Value,"\x00");
+    return Result;
+  };
+  this.TryStrToTime$1 = function (S, Value, separator) {
+    var Result = false;
+    var Msg = "";
+    Result = S.length !== 0;
+    if (Result) {
+      Value.set($impl.IntStrToTime({get: function () {
+          return Msg;
+        }, set: function (v) {
+          Msg = v;
+        }},S,S.length,separator));
+      Result = Msg === "";
+    };
+    return Result;
+  };
+  this.TryStrToDateTime = function (S, Value) {
+    var Result = false;
+    var I = 0;
+    var dtdate = 0.0;
+    var dttime = 0.0;
+    Result = false;
+    I = pas.System.Pos($mod.TimeSeparator,S);
+    if (I > 0) {
+      while ((I > 0) && (S.charAt(I - 1) !== " ")) I -= 1;
+      if (I > 0) {
+        if (!$mod.TryStrToDate(pas.System.Copy(S,1,I - 1),{get: function () {
+            return dtdate;
+          }, set: function (v) {
+            dtdate = v;
+          }})) return Result;
+        if (!$mod.TryStrToTime(pas.System.Copy(S,I + 1,S.length - I),{get: function () {
+            return dttime;
+          }, set: function (v) {
+            dttime = v;
+          }})) return Result;
+        Value.set($mod.ComposeDateTime(dtdate,dttime));
+        Result = true;
+      } else Result = $mod.TryStrToTime(S,Value);
+    } else Result = $mod.TryStrToDate(S,Value);
+    return Result;
+  };
+  this.StrToDateDef = function (S, Defvalue) {
+    var Result = 0.0;
+    Result = $mod.StrToDateDef$1(S,Defvalue,"\x00");
+    return Result;
+  };
+  this.StrToDateDef$1 = function (S, Defvalue, separator) {
+    var Result = 0.0;
+    if (!$mod.TryStrToDate$1(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},separator)) Result = Defvalue;
+    return Result;
+  };
+  this.StrToTimeDef = function (S, Defvalue) {
+    var Result = 0.0;
+    Result = $mod.StrToTimeDef$1(S,Defvalue,"\x00");
+    return Result;
+  };
+  this.StrToTimeDef$1 = function (S, Defvalue, separator) {
+    var Result = 0.0;
+    if (!$mod.TryStrToTime$1(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},separator)) Result = Defvalue;
+    return Result;
+  };
+  this.StrToDateTimeDef = function (S, Defvalue) {
+    var Result = 0.0;
+    if (!$mod.TryStrToDateTime(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = Defvalue;
+    return Result;
+  };
+  this.CurrentYear = function () {
+    var Result = 0;
+    Result = (new Date()).getFullYear();
+    return Result;
+  };
+  this.ReplaceTime = function (dati, NewTime) {
+    dati.set($mod.ComposeDateTime(dati.get(),NewTime));
+  };
+  this.ReplaceDate = function (DateTime, NewDate) {
+    var tmp = 0.0;
+    tmp = NewDate;
+    $mod.ReplaceTime({get: function () {
+        return tmp;
+      }, set: function (v) {
+        tmp = v;
+      }},DateTime.get());
+    DateTime.set(tmp);
+  };
+  this.FloatToDateTime = function (Value) {
+    var Result = 0.0;
+    if ((Value < $mod.MinDateTime) || (Value > $mod.MaxDateTime)) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidDateTime,[$mod.FloatToStr(Value)]]);
+    Result = Value;
+    return Result;
+  };
+  this.CurrencyFormat = 0;
+  this.NegCurrFormat = 0;
+  this.CurrencyDecimals = 2;
+  this.CurrencyString = "$";
+  this.FloattoCurr = function (Value) {
+    var Result = 0;
+    if (!$mod.TryFloatToCurr(Value,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidCurrency,[$mod.FloatToStr(Value)]]);
+    return Result;
+  };
+  this.TryFloatToCurr = function (Value, AResult) {
+    var Result = false;
+    Result = ((Value * 10000) >= $mod.MinCurrency) && ((Value * 10000) <= $mod.MaxCurrency);
+    if (Result) AResult.set(Math.floor(Value * 10000));
+    return Result;
+  };
+  this.CurrToStr = function (Value) {
+    var Result = "";
+    Result = $mod.FloatToStrF(Value / 10000,$mod.TFloatFormat.ffGeneral,-1,0);
+    return Result;
+  };
+  this.StrToCurr = function (S) {
+    var Result = 0;
+    if (!$mod.TryStrToCurr(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidCurrency,[S]]);
+    return Result;
+  };
+  this.TryStrToCurr = function (S, Value) {
+    var Result = false;
+    var D = 0.0;
+    Result = $mod.TryStrToFloat(S,{get: function () {
+        return D;
+      }, set: function (v) {
+        D = v;
+      }});
+    if (Result) Value.set(Math.floor(D * 10000));
+    return Result;
+  };
+  this.StrToCurrDef = function (S, Default) {
+    var Result = 0;
+    var R = 0;
+    if ($mod.TryStrToCurr(S,{get: function () {
+        return R;
+      }, set: function (v) {
+        R = v;
+      }})) {
+      Result = R}
+     else Result = Default;
+    return Result;
+  };
+  this.GUID_NULL = new pas.System.TGuid({D1: 0x00000000, D2: 0x0000, D3: 0x0000, D4: [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]});
+  this.Supports = function (Instance, AClass, Obj) {
+    var Result = false;
+    Result = ((Instance !== null) && (Instance.QueryInterface(pas.System.IObjectInstance,Obj) === 0)) && Obj.get().$class.InheritsFrom(AClass);
+    return Result;
+  };
+  this.Supports$1 = function (Instance, IID, Intf) {
+    var Result = false;
+    Result = (Instance !== null) && (Instance.QueryInterface(IID,Intf) === 0);
+    return Result;
+  };
+  this.Supports$2 = function (Instance, IID, Intf) {
+    var Result = false;
+    Result = (Instance !== null) && Instance.GetInterface(IID,Intf);
+    return Result;
+  };
+  this.Supports$3 = function (Instance, IID, Intf) {
+    var Result = false;
+    Result = (Instance !== null) && Instance.GetInterfaceByStr(IID,Intf);
+    return Result;
+  };
+  this.Supports$4 = function (Instance, AClass) {
+    var Result = false;
+    var Temp = null;
+    Result = $mod.Supports(Instance,AClass,{get: function () {
+        return Temp;
+      }, set: function (v) {
+        Temp = v;
+      }});
+    return Result;
+  };
+  this.Supports$5 = function (Instance, IID) {
+    var Result = false;
+    var Temp = null;
+    try {
+      Result = $mod.Supports$1(Instance,IID,{get: function () {
+          return Temp;
+        }, set: function (v) {
+          Temp = v;
+        }});
+    } finally {
+      rtl._Release(Temp);
+    };
+    return Result;
+  };
+  this.Supports$6 = function (Instance, IID) {
+    var Result = false;
+    var Temp = null;
+    Result = $mod.Supports$2(Instance,IID,{get: function () {
+        return Temp;
+      }, set: function (v) {
+        Temp = v;
+      }});
+    if (Temp && Temp.$kind==='com') Temp._Release();
+    return Result;
+  };
+  this.Supports$7 = function (Instance, IID) {
+    var Result = false;
+    var Temp = null;
+    Result = $mod.Supports$3(Instance,IID,{get: function () {
+        return Temp;
+      }, set: function (v) {
+        Temp = v;
+      }});
+    if (Temp && Temp.$kind==='com') Temp._Release();
+    return Result;
+  };
+  this.Supports$8 = function (AClass, IID) {
+    var Result = false;
+    var maps = undefined;
+    if (AClass === null) return false;
+    maps = AClass["$intfmaps"];
+    if (!maps) return false;
+    if (rtl.getObject(maps)[$mod.GUIDToString(IID)]) return true;
+    Result = false;
+    return Result;
+  };
+  this.Supports$9 = function (AClass, IID) {
+    var Result = false;
+    var maps = undefined;
+    if (AClass === null) return false;
+    maps = AClass["$intfmaps"];
+    if (!maps) return false;
+    if (rtl.getObject(maps)[$mod.UpperCase(IID)]) return true;
+    Result = false;
+    return Result;
+  };
+  this.TryStringToGUID = function (s, Guid) {
+    var Result = false;
+    var re = null;
+    if (s.length !== 38) return false;
+    re = new RegExp("^\\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\}$");
+    Result = re.test(s);
+    if (!Result) {
+      Guid.get().D1 = 0;
+      return Result;
+    };
+    rtl.strToGUIDR(s,Guid.get());
+    Result = true;
+    return Result;
+  };
+  this.StringToGUID = function (S) {
+    var Result = new pas.System.TGuid();
+    if (!$mod.TryStringToGUID(S,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidGUID,[S]]);
+    return Result;
+  };
+  this.GUIDToString = function (guid) {
+    var Result = "";
+    Result = rtl.guidrToStr(guid);
+    return Result;
+  };
+  this.IsEqualGUID = function (guid1, guid2) {
+    var Result = false;
+    var i = 0;
+    if (((guid1.D1 !== guid2.D1) || (guid1.D2 !== guid2.D2)) || (guid1.D3 !== guid2.D3)) return false;
+    for (i = 0; i <= 7; i++) if (guid1.D4[i] !== guid2.D4[i]) return false;
+    Result = true;
+    return Result;
+  };
+  this.GuidCase = function (guid, List) {
+    var Result = 0;
+    for (var $l1 = rtl.length(List) - 1; $l1 >= 0; $l1--) {
+      Result = $l1;
+      if ($mod.IsEqualGUID(guid,List[Result])) return Result;
+    };
+    Result = -1;
+    return Result;
+  };
+  $mod.$init = function () {
+    $mod.FormatSettings = $mod.TFormatSettings.$create("Create");
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SAbortError = "Operation aborted";
+  $impl.CheckBoolStrs = function () {
+    if (rtl.length($mod.TrueBoolStrs) === 0) {
+      $mod.TrueBoolStrs = rtl.arraySetLength($mod.TrueBoolStrs,"",1);
+      $mod.TrueBoolStrs[0] = "True";
+    };
+    if (rtl.length($mod.FalseBoolStrs) === 0) {
+      $mod.FalseBoolStrs = rtl.arraySetLength($mod.FalseBoolStrs,"",1);
+      $mod.FalseBoolStrs[0] = "False";
+    };
+  };
+  $impl.feInvalidFormat = 1;
+  $impl.feMissingArgument = 2;
+  $impl.feInvalidArgIndex = 3;
+  $impl.DoFormatError = function (ErrCode, fmt) {
+    var $tmp1 = ErrCode;
+    if ($tmp1 === 1) {
+      throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidFormat,[fmt]])}
+     else if ($tmp1 === 2) {
+      throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SArgumentMissing,[fmt]])}
+     else if ($tmp1 === 3) throw $mod.EConvertError.$create("CreateFmt",[pas.RTLConsts.SInvalidArgIndex,[fmt]]);
+  };
+  $impl.maxdigits = 15;
+  $impl.ReplaceDecimalSep = function (S, DS) {
+    var Result = "";
+    var P = 0;
+    P = pas.System.Pos(".",S);
+    if (P > 0) {
+      Result = (pas.System.Copy(S,1,P - 1) + DS) + pas.System.Copy(S,P + 1,S.length - P)}
+     else Result = S;
+    return Result;
+  };
+  $impl.FormatGeneralFloat = function (Value, Precision, DS) {
+    var Result = "";
+    var P = 0;
+    var PE = 0;
+    var Q = 0;
+    var Exponent = 0;
+    if ((Precision === -1) || (Precision > 15)) Precision = 15;
+    Result = rtl.floatToStr(Value,Precision + 7);
+    Result = $mod.TrimLeft(Result);
+    P = pas.System.Pos(".",Result);
+    if (P === 0) return Result;
+    PE = pas.System.Pos("E",Result);
+    if (PE === 0) {
+      Result = $impl.ReplaceDecimalSep(Result,DS);
+      return Result;
+    };
+    Q = PE + 2;
+    Exponent = 0;
+    while (Q <= Result.length) {
+      Exponent = ((Exponent * 10) + Result.charCodeAt(Q - 1)) - "0".charCodeAt();
+      Q += 1;
+    };
+    if (Result.charAt((PE + 1) - 1) === "-") Exponent = -Exponent;
+    if (((P + Exponent) < PE) && (Exponent > -6)) {
+      Result = rtl.strSetLength(Result,PE - 1);
+      if (Exponent >= 0) {
+        for (var $l1 = 0, $end2 = Exponent - 1; $l1 <= $end2; $l1++) {
+          Q = $l1;
+          Result = rtl.setCharAt(Result,P - 1,Result.charAt((P + 1) - 1));
+          P += 1;
+        };
+        Result = rtl.setCharAt(Result,P - 1,".");
+        P = 1;
+        if (Result.charAt(P - 1) === "-") P += 1;
+        while (((Result.charAt(P - 1) === "0") && (P < Result.length)) && (pas.System.Copy(Result,P + 1,DS.length) !== DS)) pas.System.Delete({get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},P,1);
+      } else {
+        pas.System.Insert(pas.System.Copy("00000",1,-Exponent),{get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},P - 1);
+        Result = rtl.setCharAt(Result,(P - Exponent) - 1,Result.charAt(((P - Exponent) - 1) - 1));
+        Result = rtl.setCharAt(Result,P - 1,".");
+        if (Exponent !== -1) Result = rtl.setCharAt(Result,((P - Exponent) - 1) - 1,"0");
+      };
+      Q = Result.length;
+      while ((Q > 0) && (Result.charAt(Q - 1) === "0")) Q -= 1;
+      if (Result.charAt(Q - 1) === ".") Q -= 1;
+      if ((Q === 0) || ((Q === 1) && (Result.charAt(0) === "-"))) {
+        Result = "0"}
+       else Result = rtl.strSetLength(Result,Q);
+    } else {
+      while (Result.charAt((PE - 1) - 1) === "0") {
+        pas.System.Delete({get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},PE - 1,1);
+        PE -= 1;
+      };
+      if (Result.charAt((PE - 1) - 1) === DS) {
+        pas.System.Delete({get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},PE - 1,1);
+        PE -= 1;
+      };
+      if (Result.charAt((PE + 1) - 1) === "+") {
+        pas.System.Delete({get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},PE + 1,1)}
+       else PE += 1;
+      while (Result.charAt((PE + 1) - 1) === "0") pas.System.Delete({get: function () {
+          return Result;
+        }, set: function (v) {
+          Result = v;
+        }},PE + 1,1);
+    };
+    Result = $impl.ReplaceDecimalSep(Result,DS);
+    return Result;
+  };
+  $impl.FormatExponentFloat = function (Value, Precision, Digits, DS) {
+    var Result = "";
+    var P = 0;
+    DS = $mod.DecimalSeparator;
+    if ((Precision === -1) || (Precision > 15)) Precision = 15;
+    Result = rtl.floatToStr(Value,Precision + 7);
+    while (Result.charAt(0) === " ") pas.System.Delete({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},1,1);
+    P = pas.System.Pos("E",Result);
+    if (P === 0) {
+      Result = $impl.ReplaceDecimalSep(Result,DS);
+      return Result;
+    };
+    P += 2;
+    if (Digits > 4) Digits = 4;
+    Digits = ((Result.length - P) - Digits) + 1;
+    if (Digits < 0) {
+      pas.System.Insert(pas.System.Copy("0000",1,-Digits),{get: function () {
+          return Result;
+        }, set: function (v) {
+          Result = v;
+        }},P)}
+     else while ((Digits > 0) && (Result.charAt(P - 1) === "0")) {
+      pas.System.Delete({get: function () {
+          return Result;
+        }, set: function (v) {
+          Result = v;
+        }},P,1);
+      if (P > Result.length) {
+        pas.System.Delete({get: function () {
+            return Result;
+          }, set: function (v) {
+            Result = v;
+          }},P - 2,2);
+        break;
+      };
+      Digits -= 1;
+    };
+    Result = $impl.ReplaceDecimalSep(Result,DS);
+    return Result;
+  };
+  $impl.FormatFixedFloat = function (Value, Digits, DS) {
+    var Result = "";
+    if (Digits === -1) {
+      Digits = 2}
+     else if (Digits > 18) Digits = 18;
+    Result = rtl.floatToStr(Value,0,Digits);
+    if ((Result !== "") && (Result.charAt(0) === " ")) pas.System.Delete({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},1,1);
+    Result = $impl.ReplaceDecimalSep(Result,DS);
+    return Result;
+  };
+  $impl.FormatNumberFloat = function (Value, Digits, DS, TS) {
+    var Result = "";
+    var P = 0;
+    if (Digits === -1) {
+      Digits = 2}
+     else if (Digits > 15) Digits = 15;
+    Result = rtl.floatToStr(Value,0,Digits);
+    if ((Result !== "") && (Result.charAt(0) === " ")) pas.System.Delete({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},1,1);
+    P = pas.System.Pos(".",Result);
+    Result = $impl.ReplaceDecimalSep(Result,DS);
+    P -= 3;
+    if ((TS !== "") && (TS !== "\x00")) while (P > 1) {
+      if (Result.charAt((P - 1) - 1) !== "-") pas.System.Insert(TS,{get: function () {
+          return Result;
+        }, set: function (v) {
+          Result = v;
+        }},P);
+      P -= 3;
+    };
+    return Result;
+  };
+  $impl.RemoveLeadingNegativeSign = function (AValue, DS) {
+    var Result = false;
+    var i = 0;
+    var TS = "";
+    var StartPos = 0;
+    Result = false;
+    StartPos = 2;
+    TS = $mod.ThousandSeparator;
+    for (var $l1 = StartPos, $end2 = AValue.get().length; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = (AValue.get().charCodeAt(i - 1) in rtl.createSet(48,DS.charCodeAt(),69,43)) || (AValue.get() === TS);
+      if (!Result) break;
+    };
+    if (Result) pas.System.Delete(AValue,1,1);
+    return Result;
+  };
+  $impl.FormatNumberCurrency = function (Value, Digits, DS, TS) {
+    var Result = "";
+    var Negative = false;
+    var P = 0;
+    if (Digits === -1) {
+      Digits = $mod.CurrencyDecimals}
+     else if (Digits > 18) Digits = 18;
+    Result = rtl.spaceLeft("" + Value,0);
+    Negative = Result.charAt(0) === "-";
+    if (Negative) pas.System.Delete({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},1,1);
+    P = pas.System.Pos(".",Result);
+    if (P !== 0) {
+      Result = $impl.ReplaceDecimalSep(Result,DS)}
+     else P = Result.length + 1;
+    P -= 3;
+    while (P > 1) {
+      if ($mod.ThousandSeparator !== "\x00") pas.System.Insert($mod.FormatSettings.GetThousandSeparator(),{get: function () {
+          return Result;
+        }, set: function (v) {
+          Result = v;
+        }},P);
+      P -= 3;
+    };
+    if ((Result.length > 1) && Negative) Negative = !$impl.RemoveLeadingNegativeSign({get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }},DS);
+    if (!Negative) {
+      var $tmp1 = $mod.CurrencyFormat;
+      if ($tmp1 === 0) {
+        Result = $mod.CurrencyString + Result}
+       else if ($tmp1 === 1) {
+        Result = Result + $mod.CurrencyString}
+       else if ($tmp1 === 2) {
+        Result = ($mod.CurrencyString + " ") + Result}
+       else if ($tmp1 === 3) Result = (Result + " ") + $mod.CurrencyString;
+    } else {
+      var $tmp2 = $mod.NegCurrFormat;
+      if ($tmp2 === 0) {
+        Result = (("(" + $mod.CurrencyString) + Result) + ")"}
+       else if ($tmp2 === 1) {
+        Result = ("-" + $mod.CurrencyString) + Result}
+       else if ($tmp2 === 2) {
+        Result = ($mod.CurrencyString + "-") + Result}
+       else if ($tmp2 === 3) {
+        Result = ($mod.CurrencyString + Result) + "-"}
+       else if ($tmp2 === 4) {
+        Result = (("(" + Result) + $mod.CurrencyString) + ")"}
+       else if ($tmp2 === 5) {
+        Result = ("-" + Result) + $mod.CurrencyString}
+       else if ($tmp2 === 6) {
+        Result = (Result + "-") + $mod.CurrencyString}
+       else if ($tmp2 === 7) {
+        Result = (Result + $mod.CurrencyString) + "-"}
+       else if ($tmp2 === 8) {
+        Result = (("-" + Result) + " ") + $mod.CurrencyString}
+       else if ($tmp2 === 9) {
+        Result = (("-" + $mod.CurrencyString) + " ") + Result}
+       else if ($tmp2 === 10) {
+        Result = ((Result + " ") + $mod.CurrencyString) + "-"}
+       else if ($tmp2 === 11) {
+        Result = (($mod.CurrencyString + " ") + Result) + "-"}
+       else if ($tmp2 === 12) {
+        Result = (($mod.CurrencyString + " ") + "-") + Result}
+       else if ($tmp2 === 13) {
+        Result = ((Result + "-") + " ") + $mod.CurrencyString}
+       else if ($tmp2 === 14) {
+        Result = ((("(" + $mod.CurrencyString) + " ") + Result) + ")"}
+       else if ($tmp2 === 15) Result = ((("(" + Result) + " ") + $mod.CurrencyString) + ")";
+    };
+    if (TS === "") ;
+    return Result;
+  };
+  $impl.RESpecials = "([\\[\\]\\(\\)\\\\\\.\\*])";
+  $impl.DoEncodeDate = function (Year, Month, Day) {
+    var Result = 0;
+    var D = 0.0;
+    if ($mod.TryEncodeDate(Year,Month,Day,{get: function () {
+        return D;
+      }, set: function (v) {
+        D = v;
+      }})) {
+      Result = pas.System.Trunc(D)}
+     else Result = 0;
+    return Result;
+  };
+  $impl.DoEncodeTime = function (Hour, Minute, Second, MilliSecond) {
+    var Result = 0.0;
+    if (!$mod.TryEncodeTime(Hour,Minute,Second,MilliSecond,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) Result = 0;
+    return Result;
+  };
+  $impl.DateTimeToStrFormat = ["c","f"];
+  var WhiteSpace = " \b\t\n\f\r";
+  var Digits = "0123456789";
+  $impl.IntStrToDate = function (ErrorMsg, S, useformat, separator) {
+    var Result = 0.0;
+    function FixErrorMsg(errmarg) {
+      ErrorMsg.set($mod.Format(pas.RTLConsts.SInvalidDateFormat,[errmarg]));
+    };
+    var df = "";
+    var d = 0;
+    var m = 0;
+    var y = 0;
+    var ly = 0;
+    var ld = 0;
+    var lm = 0;
+    var n = 0;
+    var i = 0;
+    var len = 0;
+    var c = 0;
+    var dp = 0;
+    var mp = 0;
+    var yp = 0;
+    var which = 0;
+    var s1 = "";
+    var values = [];
+    var YearMoreThenTwoDigits = false;
+    values = rtl.arraySetLength(values,0,4);
+    Result = 0;
+    len = S.length;
+    ErrorMsg.set("");
+    while ((len > 0) && (pas.System.Pos(S.charAt(len - 1),WhiteSpace) > 0)) len -= 1;
+    if (len === 0) {
+      FixErrorMsg(S);
+      return Result;
+    };
+    YearMoreThenTwoDigits = false;
+    if (separator === "\x00") if ($mod.DateSeparator !== "\x00") {
+      separator = $mod.DateSeparator}
+     else separator = "-";
+    df = $mod.UpperCase(useformat);
+    yp = 0;
+    mp = 0;
+    dp = 0;
+    which = 0;
+    i = 0;
+    while ((i < df.length) && (which < 3)) {
+      i += 1;
+      var $tmp1 = df.charAt(i - 1);
+      if ($tmp1 === "Y") {
+        if (yp === 0) {
+          which += 1;
+          yp = which;
+        }}
+       else if ($tmp1 === "M") {
+        if (mp === 0) {
+          which += 1;
+          mp = which;
+        }}
+       else if ($tmp1 === "D") if (dp === 0) {
+        which += 1;
+        dp = which;
+      };
+    };
+    for (i = 1; i <= 3; i++) values[i] = 0;
+    s1 = "";
+    n = 0;
+    for (var $l2 = 1, $end3 = len; $l2 <= $end3; $l2++) {
+      i = $l2;
+      if (pas.System.Pos(S.charAt(i - 1),Digits) > 0) s1 = s1 + S.charAt(i - 1);
+      if ((separator !== " ") && (S.charAt(i - 1) === " ")) continue;
+      if ((S.charAt(i - 1) === separator) || ((i === len) && (pas.System.Pos(S.charAt(i - 1),Digits) > 0))) {
+        n += 1;
+        if (n > 3) {
+          FixErrorMsg(S);
+          return Result;
+        };
+        if ((n === yp) && (s1.length > 2)) YearMoreThenTwoDigits = true;
+        pas.System.val$5(s1,{a: n, p: values, get: function () {
+            return this.p[this.a];
+          }, set: function (v) {
+            this.p[this.a] = v;
+          }},{get: function () {
+            return c;
+          }, set: function (v) {
+            c = v;
+          }});
+        if (c !== 0) {
+          FixErrorMsg(S);
+          return Result;
+        };
+        s1 = "";
+      } else if (pas.System.Pos(S.charAt(i - 1),Digits) === 0) {
+        FixErrorMsg(S);
+        return Result;
+      };
+    };
+    if ((which < 3) && (n > which)) {
+      FixErrorMsg(S);
+      return Result;
+    };
+    $mod.DecodeDate($mod.date(),{get: function () {
+        return ly;
+      }, set: function (v) {
+        ly = v;
+      }},{get: function () {
+        return lm;
+      }, set: function (v) {
+        lm = v;
+      }},{get: function () {
+        return ld;
+      }, set: function (v) {
+        ld = v;
+      }});
+    if (n === 3) {
+      y = values[yp];
+      m = values[mp];
+      d = values[dp];
+    } else {
+      y = ly;
+      if (n < 2) {
+        d = values[1];
+        m = lm;
+      } else if (dp < mp) {
+        d = values[1];
+        m = values[2];
+      } else {
+        d = values[2];
+        m = values[1];
+      };
+    };
+    if (((y >= 0) && (y < 100)) && !YearMoreThenTwoDigits) {
+      ly = ly - $mod.TwoDigitYearCenturyWindow;
+      y += Math.floor(ly / 100) * 100;
+      if (($mod.TwoDigitYearCenturyWindow > 0) && (y < ly)) y += 100;
+    };
+    if (!$mod.TryEncodeDate(y,m,d,{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) ErrorMsg.set(pas.RTLConsts.SErrInvalidDate);
+    return Result;
+  };
+  var AMPM_None = 0;
+  var AMPM_AM = 1;
+  var AMPM_PM = 2;
+  var tiHour = 0;
+  var tiMin = 1;
+  var tiSec = 2;
+  var tiMSec = 3;
+  var Digits$1 = "0123456789";
+  $impl.IntStrToTime = function (ErrorMsg, S, Len, separator) {
+    var Result = 0.0;
+    var AmPm = 0;
+    var TimeValues = [];
+    function SplitElements(TimeValues, AmPm) {
+      var Result = false;
+      var Cur = 0;
+      var Offset = 0;
+      var ElemLen = 0;
+      var Err = 0;
+      var TimeIndex = 0;
+      var FirstSignificantDigit = 0;
+      var Value = 0;
+      var DigitPending = false;
+      var MSecPending = false;
+      var AmPmStr = "";
+      var CurChar = "";
+      var I = 0;
+      var allowedchars = "";
+      Result = false;
+      AmPm.set(0);
+      MSecPending = false;
+      TimeIndex = 0;
+      for (I = 0; I <= 3; I++) TimeValues.get()[I] = 0;
+      Cur = 1;
+      while ((Cur < Len) && (S.charAt(Cur - 1) === " ")) Cur += 1;
+      Offset = Cur;
+      if (((Cur > (Len - 1)) || (S.charAt(Cur - 1) === separator)) || (S.charAt(Cur - 1) === $mod.DecimalSeparator)) {
+        return Result;
+      };
+      DigitPending = pas.System.Pos(S.charAt(Cur - 1),Digits$1) > 0;
+      while (Cur <= Len) {
+        CurChar = S.charAt(Cur - 1);
+        if (pas.System.Pos(CurChar,Digits$1) > 0) {
+          if (!DigitPending || (TimeIndex > 3)) {
+            return Result;
+          };
+          Offset = Cur;
+          if (CurChar !== "0") {
+            FirstSignificantDigit = Offset}
+           else FirstSignificantDigit = -1;
+          while ((Cur < Len) && (pas.System.Pos(S.charAt((Cur + 1) - 1),Digits$1) > 0)) {
+            if ((FirstSignificantDigit === -1) && (S.charAt(Cur - 1) !== "0")) FirstSignificantDigit = Cur;
+            Cur += 1;
+          };
+          if (FirstSignificantDigit === -1) FirstSignificantDigit = Cur;
+          ElemLen = (1 + Cur) - FirstSignificantDigit;
+          if ((ElemLen <= 2) || ((ElemLen <= 3) && (TimeIndex === 3))) {
+            pas.System.val$5(pas.System.Copy(S,FirstSignificantDigit,ElemLen),{get: function () {
+                return Value;
+              }, set: function (v) {
+                Value = v;
+              }},{get: function () {
+                return Err;
+              }, set: function (v) {
+                Err = v;
+              }});
+            TimeValues.get()[TimeIndex] = Value;
+            TimeIndex += 1;
+            DigitPending = false;
+          } else {
+            return Result;
+          };
+        } else if (CurChar === " ") {}
+        else if (CurChar === separator) {
+          if (DigitPending || (TimeIndex > 2)) {
+            return Result;
+          };
+          DigitPending = true;
+          MSecPending = false;
+        } else if (CurChar === $mod.DecimalSeparator) {
+          if ((DigitPending || MSecPending) || (TimeIndex !== 3)) {
+            return Result;
+          };
+          DigitPending = true;
+          MSecPending = true;
+        } else {
+          if ((AmPm.get() !== 0) || DigitPending) {
+            return Result;
+          };
+          Offset = Cur;
+          allowedchars = $mod.DecimalSeparator + " ";
+          if (separator !== "\x00") allowedchars = allowedchars + separator;
+          while (((Cur < (Len - 1)) && (pas.System.Pos(S.charAt((Cur + 1) - 1),allowedchars) === 0)) && (pas.System.Pos(S.charAt((Cur + 1) - 1),Digits$1) === 0)) Cur += 1;
+          ElemLen = (1 + Cur) - Offset;
+          AmPmStr = pas.System.Copy(S,1 + Offset,ElemLen);
+          if ($mod.CompareText(AmPmStr,$mod.TimeAMString) === 0) {
+            AmPm.set(1)}
+           else if ($mod.CompareText(AmPmStr,$mod.TimePMString) === 0) {
+            AmPm.set(2)}
+           else if ($mod.CompareText(AmPmStr,"AM") === 0) {
+            AmPm.set(1)}
+           else if ($mod.CompareText(AmPmStr,"PM") === 0) {
+            AmPm.set(2)}
+           else {
+            return Result;
+          };
+          if (TimeIndex === 0) {
+            DigitPending = true;
+          } else {
+            TimeIndex = 3 + 1;
+            DigitPending = false;
+          };
+        };
+        Cur += 1;
+      };
+      if (((TimeIndex === 0) || ((AmPm.get() !== 0) && ((TimeValues.get()[0] > 12) || (TimeValues.get()[0] === 0)))) || DigitPending) return Result;
+      Result = true;
+      return Result;
+    };
+    TimeValues = rtl.arraySetLength(TimeValues,0,4);
+    if (separator === "\x00") if ($mod.TimeSeparator !== "\x00") {
+      separator = $mod.TimeSeparator}
+     else separator = ":";
+    AmPm = 0;
+    if (!SplitElements({get: function () {
+        return TimeValues;
+      }, set: function (v) {
+        TimeValues = v;
+      }},{get: function () {
+        return AmPm;
+      }, set: function (v) {
+        AmPm = v;
+      }})) {
+      ErrorMsg.set($mod.Format(pas.RTLConsts.SErrInvalidTimeFormat,[S]));
+      return Result;
+    };
+    if ((AmPm === 2) && (TimeValues[0] !== 12)) {
+      TimeValues[0] += 12}
+     else if ((AmPm === 1) && (TimeValues[0] === 12)) TimeValues[0] = 0;
+    if (!$mod.TryEncodeTime(TimeValues[0],TimeValues[1],TimeValues[2],TimeValues[3],{get: function () {
+        return Result;
+      }, set: function (v) {
+        Result = v;
+      }})) ErrorMsg.set($mod.Format(pas.RTLConsts.SErrInvalidTimeFormat,[S]));
+    return Result;
+  };
+  var WhiteSpace$1 = "\t\n\r ";
+  $impl.SplitDateTimeStr = function (DateTimeStr, DateStr, TimeStr) {
+    var Result = 0;
+    var p = 0;
+    var DummyDT = 0.0;
+    Result = 0;
+    DateStr.set("");
+    TimeStr.set("");
+    DateTimeStr = $mod.Trim(DateTimeStr);
+    if (DateTimeStr.length === 0) return Result;
+    if ((($mod.DateSeparator === " ") && ($mod.TimeSeparator === " ")) && (pas.System.Pos(" ",DateTimeStr) > 0)) {
+      DateStr.set(DateTimeStr);
+      return 1;
+    };
+    p = 1;
+    if ($mod.DateSeparator !== " ") {
+      while ((p < DateTimeStr.length) && !(pas.System.Pos(DateTimeStr.charAt((p + 1) - 1),WhiteSpace$1) > 0)) p += 1;
+    } else {
+      p = pas.System.Pos($mod.TimeSeparator,DateTimeStr);
+      if (p !== 0) do {
+        p -= 1;
+      } while (!((p === 0) || (pas.System.Pos(DateTimeStr.charAt(p - 1),WhiteSpace$1) > 0)));
+    };
+    if (p === 0) p = DateTimeStr.length;
+    DateStr.set(pas.System.Copy(DateTimeStr,1,p));
+    TimeStr.set($mod.Trim(pas.System.Copy(DateTimeStr,p + 1,100)));
+    if (TimeStr.get().length !== 0) {
+      Result = 2}
+     else {
+      Result = 1;
+      if ((($mod.DateSeparator !== $mod.TimeSeparator) && (pas.System.Pos($mod.TimeSeparator,DateStr.get()) > 0)) || (($mod.DateSeparator === $mod.TimeSeparator) && !$mod.TryStrToDate(DateStr.get(),{get: function () {
+          return DummyDT;
+        }, set: function (v) {
+          DummyDT = v;
+        }}))) {
+        TimeStr.set(DateStr.get());
+        DateStr.set("");
+      };
+    };
+    return Result;
+  };
+});
+rtl.module("math",["System","SysUtils"],function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  this.MinInteger = -0x10000000000000;
+  this.MaxInteger = 0xfffffffffffff;
+  this.MinDouble = 5.0e-324;
+  this.MaxDouble = 1.7e+308;
+  this.InRange = function (AValue, AMin, AMax) {
+    return (AValue >= AMin) && (AValue <= AMax);
+  };
+  this.InRange$1 = function (AValue, AMin, AMax) {
+    return (AValue >= AMin) && (AValue <= AMax);
+  };
+  this.EnsureRange = function (AValue, AMin, AMax) {
+    if (AValue<AMin){ return AMin;
+    } else if (AValue>AMax){ return AMax;
+    } else return AValue;
+  };
+  this.EnsureRange$1 = function (AValue, AMin, AMax) {
+    if (AValue<AMin){ return AMin;
+    } else if (AValue>AMax){ return AMax;
+    } else return AValue;
+  };
+  this.RoundTo = function (AValue, Digits) {
+    var Result = 0.0;
+    var RV = 0.0;
+    RV = $mod.IntPower(10,Digits);
+    Result = Math.round(AValue / RV) * RV;
+    return Result;
+  };
+  this.SimpleRoundTo = function (AValue, Digits) {
+    var Result = 0.0;
+    var RV = 0.0;
+    RV = $mod.IntPower(10,-Digits);
+    if (AValue < 0) {
+      Result = pas.System.Int((AValue * RV) - 0.5) / RV}
+     else Result = pas.System.Int((AValue * RV) + 0.5) / RV;
+    return Result;
+  };
+  this.randg = function (mean, stddev) {
+    var Result = 0.0;
+    var U1 = 0.0;
+    var S2 = 0.0;
+    do {
+      U1 = (2 * Math.random()) - 1;
+      S2 = pas.System.Sqr$1(U1) + pas.System.Sqr$1((2 * Math.random()) - 1);
+    } while (!(S2 < 1));
+    Result = ((Math.sqrt((-2 * Math.log(S2)) / S2) * U1) * stddev) + mean;
+    return Result;
+  };
+  this.RandomRange = function (aFrom, aTo) {
+    var Result = 0;
+    Result = pas.System.Random(Math.abs(aFrom - aTo)) + Math.min(aTo,aFrom);
+    return Result;
+  };
+  this.RandomRange$1 = function (aFrom, aTo) {
+    var Result = 0;
+    var m = 0;
+    if (aFrom < aTo) {
+      m = aFrom}
+     else m = aTo;
+    Result = pas.System.Random(Math.abs(aFrom - aTo)) + m;
+    return Result;
+  };
+  this.NegativeValue = -1;
+  this.ZeroValue = 0;
+  this.PositiveValue = 1;
+  this.IsZero = function (d, Epsilon) {
+    var Result = false;
+    if (Epsilon === 0) Epsilon = 1E-12;
+    Result = Math.abs(d) <= Epsilon;
+    return Result;
+  };
+  this.IsZero$1 = function (d) {
+    var Result = false;
+    Result = Math.abs(d) <= 1E-12;
+    return Result;
+  };
+  this.IsInfinite = function (d) {
+    return (d==Infinite) || (d==-Infinite);
+  };
+  this.SameValue = function (A, B, Epsilon) {
+    var Result = false;
+    if (Epsilon === 0.0) Epsilon = Math.max(Math.min(Math.abs(A),Math.abs(B)) * 1E-12,1E-12);
+    if (A > B) {
+      Result = (A - B) <= Epsilon}
+     else Result = (B - A) <= Epsilon;
+    return Result;
+  };
+  this.LogN = function (A, Base) {
+    var Result = 0.0;
+    Result = Math.log(A) / Math.log(Base);
+    return Result;
+  };
+  this.Ceil = function (A) {
+    var Result = 0;
+    Result = pas.System.Trunc(Math.ceil(A));
+    return Result;
+  };
+  this.Floor = function (A) {
+    var Result = 0;
+    Result = pas.System.Trunc(Math.floor(A));
+    return Result;
+  };
+  this.Ceil64 = function (A) {
+    var Result = 0;
+    Result = pas.System.Trunc(Math.ceil(A));
+    return Result;
+  };
+  this.Floor64 = function (A) {
+    var Result = 0;
+    Result = pas.System.Trunc(Math.ceil(A));
+    return Result;
+  };
+  this.ldexp = function (x, p) {
+    var Result = 0.0;
+    Result = x * $mod.IntPower(2.0,p);
+    return Result;
+  };
+  this.Frexp = function (X, Mantissa, Exponent) {
+    Exponent.set(0);
+    if (X !== 0) if (Math.abs(X) < 0.5) {
+      do {
+        X = X * 2;
+        Exponent.set(Exponent.get() - 1);
+      } while (!(Math.abs(X) >= 0.5))}
+     else while (Math.abs(X) >= 1) {
+      X = X / 2;
+      Exponent.set(Exponent.get() + 1);
+    };
+    Mantissa.set(X);
+  };
+  this.lnxp1 = function (x) {
+    var Result = 0.0;
+    var y = 0.0;
+    if (x >= 4.0) {
+      Result = Math.log(1.0 + x)}
+     else {
+      y = 1.0 + x;
+      if (y === 1.0) {
+        Result = x}
+       else {
+        Result = Math.log(y);
+        if (y > 0.0) Result = Result + ((x - (y - 1.0)) / y);
+      };
+    };
+    return Result;
+  };
+  this.IntPower = function (base, exponent) {
+    var Result = 0.0;
+    var i = 0;
+    if ((base === 0.0) && (exponent === 0)) {
+      Result = 1}
+     else {
+      i = Math.abs(exponent);
+      Result = 1.0;
+      while (i > 0) {
+        while ((i & 1) === 0) {
+          i = i >>> 1;
+          base = pas.System.Sqr$1(base);
+        };
+        i = i - 1;
+        Result = Result * base;
+      };
+      if (exponent < 0) Result = 1.0 / Result;
+    };
+    return Result;
+  };
+  this.DivMod = function (Dividend, Divisor, Result, Remainder) {
+    if (Dividend < 0) {
+      Dividend = -Dividend;
+      Result.set(-Math.floor(Dividend / Divisor));
+      Remainder.set(-(Dividend + (Result.get() * Divisor)));
+    } else {
+      Result.set(Math.floor(Dividend / Divisor));
+      Remainder.set(Dividend - (Result.get() * Divisor));
+    };
+  };
+  this.DivMod$1 = function (Dividend, Divisor, Result, Remainder) {
+    if (Dividend < 0) {
+      Dividend = -Dividend;
+      Result.set(-Math.floor(Dividend / Divisor));
+      Remainder.set(-(Dividend + (Result.get() * Divisor)));
+    } else {
+      Result.set(Math.floor(Dividend / Divisor));
+      Remainder.set(Dividend - (Result.get() * Divisor));
+    };
+  };
+  this.DivMod$2 = function (Dividend, Divisor, Result, Remainder) {
+    Result.set(Math.floor(Dividend / Divisor));
+    Remainder.set(Dividend - (Result.get() * Divisor));
+  };
+  this.DivMod$3 = function (Dividend, Divisor, Result, Remainder) {
+    if (Dividend < 0) {
+      Dividend = -Dividend;
+      Result.set(-Math.floor(Dividend / Divisor));
+      Remainder.set(-(Dividend + (Result.get() * Divisor)));
+    } else {
+      Result.set(Math.floor(Dividend / Divisor));
+      Remainder.set(Dividend - (Result.get() * Divisor));
+    };
+  };
+  this.DegToRad = function (deg) {
+    var Result = 0.0;
+    Result = deg * (Math.PI / 180.0);
+    return Result;
+  };
+  this.RadToDeg = function (rad) {
+    var Result = 0.0;
+    Result = rad * (180.0 / Math.PI);
+    return Result;
+  };
+  this.GradToRad = function (grad) {
+    var Result = 0.0;
+    Result = grad * (Math.PI / 200.0);
+    return Result;
+  };
+  this.RadToGrad = function (rad) {
+    var Result = 0.0;
+    Result = rad * (200.0 / Math.PI);
+    return Result;
+  };
+  this.DegToGrad = function (deg) {
+    var Result = 0.0;
+    Result = deg * (200.0 / 180.0);
+    return Result;
+  };
+  this.GradToDeg = function (grad) {
+    var Result = 0.0;
+    Result = grad * (180.0 / 200.0);
+    return Result;
+  };
+  this.CycleToRad = function (cycle) {
+    var Result = 0.0;
+    Result = (2 * Math.PI) * cycle;
+    return Result;
+  };
+  this.RadToCycle = function (rad) {
+    var Result = 0.0;
+    Result = rad * (1 / (2 * Math.PI));
+    return Result;
+  };
+  this.DegNormalize = function (deg) {
+    var Result = 0.0;
+    Result = deg - (pas.System.Int(deg / 360) * 360);
+    if (Result < 0) Result = Result + 360;
+    return Result;
+  };
+  this.Norm = function (data) {
+    var Result = 0.0;
+    Result = Math.sqrt($impl.sumofsquares(data));
+    return Result;
+  };
+  this.Mean = function (data) {
+    var Result = 0.0;
+    var N = 0;
+    N = rtl.length(data);
+    if (N === 0) {
+      Result = 0}
+     else Result = $mod.Sum(data) / N;
+    return Result;
+  };
+  this.Sum = function (data) {
+    var Result = 0.0;
+    var i = 0;
+    var N = 0;
+    N = rtl.length(data);
+    Result = 0.0;
+    for (var $l1 = 0, $end2 = N - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + data[i];
+    };
+    return Result;
+  };
+  this.SumsAndSquares = function (data, Sum, SumOfSquares) {
+    var i = 0;
+    var n = 0;
+    var t = 0.0;
+    var s = 0.0;
+    var ss = 0.0;
+    n = rtl.length(data);
+    ss = 0.0;
+    s = 0.0;
+    for (var $l1 = 0, $end2 = n - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      t = data[i];
+      ss = ss + pas.System.Sqr$1(t);
+      s = s + t;
+    };
+    Sum.set(s);
+    SumOfSquares.set(ss);
+  };
+  this.StdDev = function (data) {
+    var Result = 0.0;
+    Result = Math.sqrt($mod.Variance(data));
+    return Result;
+  };
+  this.MeanAndStdDev = function (data, Mean, StdDev) {
+    var I = 0;
+    var N = 0;
+    var M = 0.0;
+    var S = 0.0;
+    N = rtl.length(data);
+    M = 0;
+    S = 0;
+    for (var $l1 = 0, $end2 = N - 1; $l1 <= $end2; $l1++) {
+      I = $l1;
+      M = M + data[I];
+      S = S + pas.System.Sqr$1(data[I]);
+    };
+    M = M / N;
+    S = S - (N * pas.System.Sqr$1(M));
+    if (N > 1) {
+      S = Math.sqrt(S / (N - 1))}
+     else S = 0;
+    Mean.set(M);
+    StdDev.set(S);
+  };
+  this.Variance = function (data) {
+    var Result = 0.0;
+    var n = 0;
+    n = rtl.length(data);
+    if (n === 1) {
+      Result = 0}
+     else Result = $mod.TotalVariance(data) / (n - 1);
+    return Result;
+  };
+  this.TotalVariance = function (data) {
+    var Result = 0.0;
+    var S = 0.0;
+    var SS = 0.0;
+    var N = 0;
+    N = rtl.length(data);
+    if (rtl.length(data) === 1) {
+      Result = 0}
+     else {
+      $mod.SumsAndSquares(data,{get: function () {
+          return S;
+        }, set: function (v) {
+          S = v;
+        }},{get: function () {
+          return SS;
+        }, set: function (v) {
+          SS = v;
+        }});
+      Result = SS - (pas.System.Sqr$1(S) / N);
+    };
+    return Result;
+  };
+  this.PopNStdDev = function (data) {
+    var Result = 0.0;
+    Result = Math.sqrt($mod.PopNVariance(data));
+    return Result;
+  };
+  this.PopNVariance = function (data) {
+    var Result = 0.0;
+    var N = 0;
+    N = rtl.length(data);
+    if (N === 0) {
+      Result = 0}
+     else Result = $mod.TotalVariance(data) / N;
+    return Result;
+  };
+  this.MomentSkewKurtosis = function (data, m1, m2, m3, m4, skew, kurtosis) {
+    var i = 0;
+    var N = 0;
+    var deviation = 0.0;
+    var deviation2 = 0.0;
+    var reciprocalN = 0.0;
+    var lm1 = 0.0;
+    var lm2 = 0.0;
+    var lm3 = 0.0;
+    var lm4 = 0.0;
+    var lskew = 0.0;
+    var lkurtosis = 0.0;
+    N = rtl.length(data);
+    lm1 = 0;
+    reciprocalN = 1 / N;
+    for (var $l1 = 0, $end2 = N - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      lm1 = lm1 + data[i];
+    };
+    lm1 = reciprocalN * lm1;
+    lm2 = 0;
+    lm3 = 0;
+    lm4 = 0;
+    for (var $l3 = 0, $end4 = N - 1; $l3 <= $end4; $l3++) {
+      i = $l3;
+      deviation = data[i] - lm1;
+      deviation2 = deviation * deviation;
+      lm2 = lm2 + deviation2;
+      lm3 = lm3 + (deviation2 * deviation);
+      lm4 = lm4 + (deviation2 * deviation2);
+    };
+    lm2 = reciprocalN * lm2;
+    lm3 = reciprocalN * lm3;
+    lm4 = reciprocalN * lm4;
+    lskew = lm3 / (Math.sqrt(lm2) * lm2);
+    lkurtosis = lm4 / (lm2 * lm2);
+    m1.set(lm1);
+    m2.set(lm2);
+    m3.set(lm3);
+    m4.set(lm4);
+    skew.set(lskew);
+    kurtosis.set(lkurtosis);
+  };
+  this.TPaymentTime = {"0": "ptEndOfPeriod", ptEndOfPeriod: 0, "1": "ptStartOfPeriod", ptStartOfPeriod: 1};
+  this.FutureValue = function (ARate, NPeriods, APayment, APresentValue, APaymentTime) {
+    var Result = 0.0;
+    var q = 0.0;
+    var qn = 0.0;
+    var factor = 0.0;
+    if (ARate === 0) {
+      Result = -APresentValue - (APayment * NPeriods)}
+     else {
+      q = 1.0 + ARate;
+      qn = Math.pow(q,NPeriods);
+      factor = (qn - 1) / (q - 1);
+      if (APaymentTime === $mod.TPaymentTime.ptStartOfPeriod) factor = factor * q;
+      Result = -((APresentValue * qn) + (APayment * factor));
+    };
+    return Result;
+  };
+  var DELTA = 0.001;
+  var EPS = 1E-9;
+  var MAXIT = 20;
+  this.InterestRate = function (NPeriods, APayment, APresentValue, AFutureValue, APaymentTime) {
+    var Result = 0.0;
+    var r1 = 0.0;
+    var r2 = 0.0;
+    var dr = 0.0;
+    var fv1 = 0.0;
+    var fv2 = 0.0;
+    var iteration = 0;
+    iteration = 0;
+    r1 = 0.05;
+    do {
+      r2 = r1 + 0.001;
+      fv1 = $mod.FutureValue(r1,NPeriods,APayment,APresentValue,APaymentTime);
+      fv2 = $mod.FutureValue(r2,NPeriods,APayment,APresentValue,APaymentTime);
+      dr = ((AFutureValue - fv1) / (fv2 - fv1)) * 0.001;
+      r1 = r1 + dr;
+      iteration += 1;
+    } while (!((Math.abs(dr) < 1.0E-9) || (iteration >= 20)));
+    Result = r1;
+    return Result;
+  };
+  this.NumberOfPeriods = function (ARate, APayment, APresentValue, AFutureValue, APaymentTime) {
+    var Result = 0.0;
+    var q = 0.0;
+    var x1 = 0.0;
+    var x2 = 0.0;
+    if (ARate === 0) {
+      Result = -(APresentValue + AFutureValue) / APayment}
+     else {
+      q = 1.0 + ARate;
+      if (APaymentTime === $mod.TPaymentTime.ptStartOfPeriod) APayment = APayment * q;
+      x1 = APayment - (AFutureValue * ARate);
+      x2 = APayment + (APresentValue * ARate);
+      if ((x2 === 0) || ((Math.sign(x1) * Math.sign(x2)) < 0)) {
+        Result = Infinity}
+       else {
+        Result = Math.log(x1 / x2) / Math.log(q);
+      };
+    };
+    return Result;
+  };
+  this.Payment = function (ARate, NPeriods, APresentValue, AFutureValue, APaymentTime) {
+    var Result = 0.0;
+    var q = 0.0;
+    var qn = 0.0;
+    var factor = 0.0;
+    if (ARate === 0) {
+      Result = -(AFutureValue + APresentValue) / NPeriods}
+     else {
+      q = 1.0 + ARate;
+      qn = Math.pow(q,NPeriods);
+      factor = (qn - 1) / (q - 1);
+      if (APaymentTime === $mod.TPaymentTime.ptStartOfPeriod) factor = factor * q;
+      Result = -(AFutureValue + (APresentValue * qn)) / factor;
+    };
+    return Result;
+  };
+  this.PresentValue = function (ARate, NPeriods, APayment, AFutureValue, APaymentTime) {
+    var Result = 0.0;
+    var q = 0.0;
+    var qn = 0.0;
+    var factor = 0.0;
+    if (ARate === 0.0) {
+      Result = -AFutureValue - (APayment * NPeriods)}
+     else {
+      q = 1.0 + ARate;
+      qn = Math.pow(q,NPeriods);
+      factor = (qn - 1) / (q - 1);
+      if (APaymentTime === $mod.TPaymentTime.ptStartOfPeriod) factor = factor * q;
+      Result = -(AFutureValue + (APayment * factor)) / qn;
+    };
+    return Result;
+  };
+  this.IfThen = function (val, ifTrue, ifFalse) {
+    var Result = 0;
+    if (val) {
+      Result = ifTrue}
+     else Result = ifFalse;
+    return Result;
+  };
+  this.IfThen$1 = function (val, ifTrue, ifFalse) {
+    var Result = 0.0;
+    if (val) {
+      Result = ifTrue}
+     else Result = ifFalse;
+    return Result;
+  };
+  this.EqualsValue = 0;
+  this.LessThanValue = -1;
+  this.GreaterThanValue = 1;
+  this.CompareValue = function (A, B) {
+    var Result = -1;
+    Result = 1;
+    if (A === B) {
+      Result = 0}
+     else if (A < B) Result = -1;
+    return Result;
+  };
+  this.CompareValue$1 = function (A, B) {
+    var Result = -1;
+    Result = 1;
+    if (A === B) {
+      Result = 0}
+     else if (A < B) Result = -1;
+    return Result;
+  };
+  this.CompareValue$2 = function (A, B) {
+    var Result = -1;
+    Result = 1;
+    if (A === B) {
+      Result = 0}
+     else if (A < B) Result = -1;
+    return Result;
+  };
+  this.CompareValue$3 = function (A, B, delta) {
+    var Result = -1;
+    Result = 1;
+    if (Math.abs(A - B) <= delta) {
+      Result = 0}
+     else if (A < B) Result = -1;
+    return Result;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.DZeroResolution = 1E-12;
+  $impl.sumofsquares = function (data) {
+    var Result = 0.0;
+    var i = 0;
+    var N = 0;
+    N = rtl.length(data);
+    Result = 0.0;
+    for (var $l1 = 0, $end2 = N - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + pas.System.Sqr$1(data[i]);
+    };
+    return Result;
+  };
+});
+rtl.module("Mat4",["System","vectors","browserconsole","JS","webgl"],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.RotateZ = function (Angle) {
+      $mod.SinCos(Angle,{a: 1, p: this.RawComponents[0], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }},{a: 0, p: this.RawComponents[0], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }});
+      this.RawComponents[0][2] = 0.0;
+      this.RawComponents[0][3] = 0.0;
+      this.RawComponents[1][0] = -this.RawComponents[0][1];
+      this.RawComponents[1][1] = this.RawComponents[0][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.RotateY = function (Angle) {
+      $mod.SinCos(Angle,{a: 0, p: this.RawComponents[2], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }},{a: 0, p: this.RawComponents[0], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }});
+      this.RawComponents[0][1] = 0.0;
+      this.RawComponents[0][2] = -this.RawComponents[2][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][1] = 0.0;
+      this.RawComponents[2][2] = this.RawComponents[0][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.RotateX = function (Angle) {
+      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;
+      $mod.SinCos(Angle,{a: 2, p: this.RawComponents[1], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }},{a: 1, p: this.RawComponents[1], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }});
+      this.RawComponents[1][3] = 0.0;
+      this.RawComponents[2][0] = 0.0;
+      this.RawComponents[2][1] = -this.RawComponents[1][2];
+      this.RawComponents[2][2] = this.RawComponents[1][1];
+      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.Scale = function (x, y, z) {
+      this.RawComponents[0][0] = x;
+      this.RawComponents[0][1] = 0;
+      this.RawComponents[0][2] = 0;
+      this.RawComponents[0][3] = 0;
+      this.RawComponents[1][0] = 0;
+      this.RawComponents[1][1] = y;
+      this.RawComponents[1][2] = 0;
+      this.RawComponents[1][3] = 0;
+      this.RawComponents[2][0] = 0;
+      this.RawComponents[2][1] = 0;
+      this.RawComponents[2][2] = z;
+      this.RawComponents[2][3] = 0;
+      this.RawComponents[3][0] = 0;
+      this.RawComponents[3][1] = 0;
+      this.RawComponents[3][2] = 0;
+      this.RawComponents[3][3] = 1;
+    };
+    this.Ortho = function (Left, Right, Bottom, Top, zNear, zFar) {
+      var rml = 0.0;
+      var tmb = 0.0;
+      var fmn = 0.0;
+      rml = Right - Left;
+      tmb = Top - Bottom;
+      fmn = zFar - zNear;
+      this.RawComponents[0][0] = 2.0 / rml;
+      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] = 2.0 / tmb;
+      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] = -2.0 / fmn;
+      this.RawComponents[2][3] = 0.0;
+      this.RawComponents[3][0] = -(Right + Left) / rml;
+      this.RawComponents[3][1] = -(Top + Bottom) / tmb;
+      this.RawComponents[3][2] = -(zFar + zNear) / fmn;
+      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.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;
+    };
+    this.Show = function () {
+      var x = 0;
+      var y = 0;
+      for (y = 0; y <= 3; y++) {
+        pas.System.Write("[");
+        for (x = 0; x <= 3; x++) {
+          if (x < 3) {
+            pas.System.Write(this.RawComponents[x][y],",")}
+           else pas.System.Write(this.RawComponents[x][y]);
+        };
+        pas.System.Write("]");
+      };
+    };
+  });
+  this.SinCos = function (angle, sinus, cosinus) {
+    sinus.set(Math.sin(angle));
+    cosinus.set(Math.cos(angle));
+  };
+  $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;
+});

+ 275 - 0
Mat4.pas

@@ -0,0 +1,275 @@
+{$mode objfpc}
+
+unit Mat4;
+interface
+uses
+	Vectors,
+	BrowserConsole, JS, Math;
+
+type
+	TMat4 = class
+		public
+			constructor Identity;
+			constructor Translate(const tx,ty,tz: TScalar);
+			constructor RotateZ(const Angle:TScalar);
+			constructor RotateY(const Angle:TScalar);
+			constructor RotateX(const Angle:TScalar);
+			constructor Scale (const x, y, z: TScalar);
+
+			constructor Ortho(const Left,Right,Bottom,Top,zNear,zFar:TScalar);
+			constructor Perspective(const fovy,Aspect,zNear,zFar:TScalar);
+
+			function Multiply (m: TMat4): TMat4;
+			function CopyList: TScalarArray;
+
+			procedure Show;
+		public
+			RawComponents:array[0..3, 0..3] of TScalar;
+	end;
+
+procedure SinCos (angle: TScalar; var sinus: TScalar, var cosinus: TScalar);
+
+implementation
+
+const
+	PI = 3.14159265359;
+	DEG2RAD = PI/180.0;
+  RAD2DEG = 180.0/PI;
+  HalfPI = PI*0.5;	
+
+var
+	Matrix4x4Identity: TMat4;
+
+// TODO: this should be in math
+procedure SinCos (angle: TScalar; var sinus: TScalar, var cosinus: TScalar); 
+begin
+	sinus := sin(angle);
+	cosinus := cos(angle);
+end;
+
+// NOTE: [][] doesn't typecast to array of syntax so we need this
+function TMat4.CopyList: TScalarArray; 
+var
+	x, y: integer;
+	list: TJSArray;
+begin
+	list := TJSArray.new;
+	for x := 0 to 3 do
+	for y := 0 to 3 do
+		list.push(RawComponents[x,y]);
+	result := TScalarArray(list);
+end;
+
+function TMat4.Multiply (m: TMat4): TMat4;
+begin
+ result := TMat4.Identity;
+ result.RawComponents[0,0]:=(m.RawComponents[0,0]*self.RawComponents[0,0])+(m.RawComponents[0,1]*self.RawComponents[1,0])+(m.RawComponents[0,2]*self.RawComponents[2,0])+(m.RawComponents[0,3]*self.RawComponents[3,0]);
+ result.RawComponents[0,1]:=(m.RawComponents[0,0]*self.RawComponents[0,1])+(m.RawComponents[0,1]*self.RawComponents[1,1])+(m.RawComponents[0,2]*self.RawComponents[2,1])+(m.RawComponents[0,3]*self.RawComponents[3,1]);
+ result.RawComponents[0,2]:=(m.RawComponents[0,0]*self.RawComponents[0,2])+(m.RawComponents[0,1]*self.RawComponents[1,2])+(m.RawComponents[0,2]*self.RawComponents[2,2])+(m.RawComponents[0,3]*self.RawComponents[3,2]);
+ result.RawComponents[0,3]:=(m.RawComponents[0,0]*self.RawComponents[0,3])+(m.RawComponents[0,1]*self.RawComponents[1,3])+(m.RawComponents[0,2]*self.RawComponents[2,3])+(m.RawComponents[0,3]*self.RawComponents[3,3]);
+ result.RawComponents[1,0]:=(m.RawComponents[1,0]*self.RawComponents[0,0])+(m.RawComponents[1,1]*self.RawComponents[1,0])+(m.RawComponents[1,2]*self.RawComponents[2,0])+(m.RawComponents[1,3]*self.RawComponents[3,0]);
+ result.RawComponents[1,1]:=(m.RawComponents[1,0]*self.RawComponents[0,1])+(m.RawComponents[1,1]*self.RawComponents[1,1])+(m.RawComponents[1,2]*self.RawComponents[2,1])+(m.RawComponents[1,3]*self.RawComponents[3,1]);
+ result.RawComponents[1,2]:=(m.RawComponents[1,0]*self.RawComponents[0,2])+(m.RawComponents[1,1]*self.RawComponents[1,2])+(m.RawComponents[1,2]*self.RawComponents[2,2])+(m.RawComponents[1,3]*self.RawComponents[3,2]);
+ result.RawComponents[1,3]:=(m.RawComponents[1,0]*self.RawComponents[0,3])+(m.RawComponents[1,1]*self.RawComponents[1,3])+(m.RawComponents[1,2]*self.RawComponents[2,3])+(m.RawComponents[1,3]*self.RawComponents[3,3]);
+ result.RawComponents[2,0]:=(m.RawComponents[2,0]*self.RawComponents[0,0])+(m.RawComponents[2,1]*self.RawComponents[1,0])+(m.RawComponents[2,2]*self.RawComponents[2,0])+(m.RawComponents[2,3]*self.RawComponents[3,0]);
+ result.RawComponents[2,1]:=(m.RawComponents[2,0]*self.RawComponents[0,1])+(m.RawComponents[2,1]*self.RawComponents[1,1])+(m.RawComponents[2,2]*self.RawComponents[2,1])+(m.RawComponents[2,3]*self.RawComponents[3,1]);
+ result.RawComponents[2,2]:=(m.RawComponents[2,0]*self.RawComponents[0,2])+(m.RawComponents[2,1]*self.RawComponents[1,2])+(m.RawComponents[2,2]*self.RawComponents[2,2])+(m.RawComponents[2,3]*self.RawComponents[3,2]);
+ result.RawComponents[2,3]:=(m.RawComponents[2,0]*self.RawComponents[0,3])+(m.RawComponents[2,1]*self.RawComponents[1,3])+(m.RawComponents[2,2]*self.RawComponents[2,3])+(m.RawComponents[2,3]*self.RawComponents[3,3]);
+ result.RawComponents[3,0]:=(m.RawComponents[3,0]*self.RawComponents[0,0])+(m.RawComponents[3,1]*self.RawComponents[1,0])+(m.RawComponents[3,2]*self.RawComponents[2,0])+(m.RawComponents[3,3]*self.RawComponents[3,0]);
+ result.RawComponents[3,1]:=(m.RawComponents[3,0]*self.RawComponents[0,1])+(m.RawComponents[3,1]*self.RawComponents[1,1])+(m.RawComponents[3,2]*self.RawComponents[2,1])+(m.RawComponents[3,3]*self.RawComponents[3,1]);
+ result.RawComponents[3,2]:=(m.RawComponents[3,0]*self.RawComponents[0,2])+(m.RawComponents[3,1]*self.RawComponents[1,2])+(m.RawComponents[3,2]*self.RawComponents[2,2])+(m.RawComponents[3,3]*self.RawComponents[3,2]);
+ result.RawComponents[3,3]:=(m.RawComponents[3,0]*self.RawComponents[0,3])+(m.RawComponents[3,1]*self.RawComponents[1,3])+(m.RawComponents[3,2]*self.RawComponents[2,3])+(m.RawComponents[3,3]*self.RawComponents[3,3]);
+end;
+
+constructor TMat4.Scale (const x, y, z: TScalar);
+begin	
+	RawComponents[0,0] := x;
+	RawComponents[0,1] := 0;
+	RawComponents[0,2] := 0;
+	RawComponents[0,3] := 0;
+        
+	RawComponents[1,0] := 0;
+	RawComponents[1,1] := y;
+	RawComponents[1,2] := 0;
+	RawComponents[1,3] := 0;
+ 
+	RawComponents[2,0] := 0;
+	RawComponents[2,1] := 0;
+	RawComponents[2,2] := z;
+	RawComponents[2,3] := 0;
+	       
+	RawComponents[3,0] := 0;
+	RawComponents[3,1] := 0;
+	RawComponents[3,2] := 0;
+	RawComponents[3,3] := 1;
+end;
+
+constructor TMat4.RotateX(const Angle:TScalar);
+begin
+ RawComponents[0,0]:=1.0;
+ RawComponents[0,1]:=0.0;
+ RawComponents[0,2]:=0.0;
+ RawComponents[0,3]:=0.0;
+ RawComponents[1,0]:=0.0;
+ SinCos(Angle,RawComponents[1,2],RawComponents[1,1]);
+ RawComponents[1,3]:=0.0;
+ RawComponents[2,0]:=0.0;
+ RawComponents[2,1]:=-RawComponents[1,2];
+ RawComponents[2,2]:=RawComponents[1,1];
+ RawComponents[2,3]:=0.0;
+ RawComponents[3,0]:=0.0;
+ RawComponents[3,1]:=0.0;
+ RawComponents[3,2]:=0.0;
+ RawComponents[3,3]:=1.0;
+end;
+
+constructor TMat4.RotateY(const Angle:TScalar);
+begin
+ SinCos(Angle,RawComponents[2,0],RawComponents[0,0]);
+ RawComponents[0,1]:=0.0;
+ RawComponents[0,2]:=-RawComponents[2,0];
+ RawComponents[0,3]:=0.0;
+ RawComponents[1,0]:=0.0;
+ RawComponents[1,1]:=1.0;
+ RawComponents[1,2]:=0.0;
+ RawComponents[1,3]:=0.0;
+ RawComponents[2,1]:=0.0;
+ RawComponents[2,2]:=RawComponents[0,0];
+ RawComponents[2,3]:=0.0;
+ RawComponents[3,0]:=0.0;
+ RawComponents[3,1]:=0.0;
+ RawComponents[3,2]:=0.0;
+ RawComponents[3,3]:=1.0;
+end;
+
+constructor TMat4.RotateZ(const Angle:TScalar);
+begin
+ SinCos(Angle,RawComponents[0,1],RawComponents[0,0]);
+ RawComponents[0,2]:=0.0;
+ RawComponents[0,3]:=0.0;
+ RawComponents[1,0]:=-RawComponents[0,1];
+ RawComponents[1,1]:=RawComponents[0,0];
+ RawComponents[1,2]:=0.0;
+ RawComponents[1,3]:=0.0;
+ RawComponents[2,0]:=0.0;
+ RawComponents[2,1]:=0.0;
+ RawComponents[2,2]:=1.0;
+ RawComponents[2,3]:=0.0;
+ RawComponents[3,0]:=0.0;
+ RawComponents[3,1]:=0.0;
+ RawComponents[3,2]:=0.0;
+ RawComponents[3,3]:=1.0;
+end;
+
+constructor TMat4.Translate(const tx,ty,tz: TScalar);
+begin
+ RawComponents[0,0]:=1.0;
+ RawComponents[0,1]:=0.0;
+ RawComponents[0,2]:=0.0;
+ RawComponents[0,3]:=0.0;
+
+ RawComponents[1,0]:=0.0;
+ RawComponents[1,1]:=1.0;
+ RawComponents[1,2]:=0.0;
+ RawComponents[1,3]:=0.0;
+
+ RawComponents[2,0]:=0.0;
+ RawComponents[2,1]:=0.0;
+ RawComponents[2,2]:=1.0;
+ RawComponents[2,3]:=0.0;
+
+ RawComponents[3,0]:=tx;
+ RawComponents[3,1]:=ty;
+ RawComponents[3,2]:=tz;
+ RawComponents[3,3]:=1.0;
+end;
+
+constructor TMat4.Ortho(const Left,Right,Bottom,Top,zNear,zFar:TScalar);
+var
+	rml,tmb,fmn:TScalar;
+begin
+ rml:=Right-Left;
+ tmb:=Top-Bottom;
+ fmn:=zFar-zNear;
+ RawComponents[0,0]:=2.0/rml;
+ RawComponents[0,1]:=0.0;
+ RawComponents[0,2]:=0.0;
+ RawComponents[0,3]:=0.0;
+ RawComponents[1,0]:=0.0;
+ RawComponents[1,1]:=2.0/tmb;
+ RawComponents[1,2]:=0.0;
+ RawComponents[1,3]:=0.0;
+ RawComponents[2,0]:=0.0;
+ RawComponents[2,1]:=0.0;
+ RawComponents[2,2]:=(-2.0)/fmn;
+ RawComponents[2,3]:=0.0;
+ RawComponents[3,0]:=(-(Right+Left))/rml;
+ RawComponents[3,1]:=(-(Top+Bottom))/tmb;
+ RawComponents[3,2]:=(-(zFar+zNear))/fmn;
+ RawComponents[3,3]:=1.0;
+end;
+
+constructor TMat4.Perspective(const fovy,Aspect,zNear,zFar:TScalar);
+var
+	Sine,Cotangent,ZDelta,Radians:TScalar;
+begin
+ Radians:=(fovy*0.5)*DEG2RAD;
+ ZDelta:=zFar-zNear;
+ Sine:=sin(Radians);
+ if not ((ZDelta=0) or (Sine=0) or (aspect=0)) then begin
+  Cotangent:=cos(Radians)/Sine;
+  // NOTE: What happens on copy?
+  RawComponents:=Matrix4x4Identity.RawComponents;
+  RawComponents[0,0]:=Cotangent/aspect;
+  RawComponents[1,1]:=Cotangent;
+  RawComponents[2,2]:=(-(zFar+zNear))/ZDelta;
+  RawComponents[2,3]:=-1-0;
+  RawComponents[3,2]:=(-(2.0*zNear*zFar))/ZDelta;
+  RawComponents[3,3]:=0.0;
+ end;
+end;
+
+constructor TMat4.Identity;
+begin
+	RawComponents[0,0]:=1.0;
+	RawComponents[0,1]:=0.0;
+	RawComponents[0,2]:=0.0;
+	RawComponents[0,3]:=0.0;
+
+	RawComponents[1,0]:=0.0;
+	RawComponents[1,1]:=1.0;
+	RawComponents[1,2]:=0.0;
+	RawComponents[1,3]:=0.0;
+
+	RawComponents[2,0]:=0.0;
+	RawComponents[2,1]:=0.0;
+	RawComponents[2,2]:=1.0;
+	RawComponents[2,3]:=0.0;
+
+	RawComponents[3,0]:=0.0;
+	RawComponents[3,1]:=0.0;
+	RawComponents[3,2]:=0.0;
+	RawComponents[3,3]:=1.0;
+end;
+
+procedure TMat4.Show;
+var
+	x, y: integer;
+begin
+	for y := 0 to 3 do
+		begin
+			write('[');
+			for x := 0 to 3 do
+				begin
+					if x < 3 then
+						write(RawComponents[x, y], ',')
+					else
+						write(RawComponents[x, y]);
+				end;
+			write(']');
+		end;
+end;
+
+begin
+	Matrix4x4Identity := TMat4.Identity;
+end.

+ 1958 - 0
MemoryBuffer.js

@@ -0,0 +1,1958 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{88387EF6-BCEE-3E17-9E85-5D491ED4FC10}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{ECEC7568-4E50-30C9-A2F0-439342DE2ADB}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{9791C368-4E51-3424-A3CE-D4911D54F385}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("MemoryBuffer",["System","JS"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"TMemoryBuffer",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.byteBuffer = null;
+      this.byteOffset = 0;
+      this.floatBuffer = null;
+    };
+    this.$final = function () {
+      this.byteBuffer = undefined;
+      this.floatBuffer = undefined;
+      pas.System.TObject.$final.call(this);
+    };
+    this.Create$1 = function (size) {
+      this.byteBuffer = new Uint8Array(size);
+      pas.System.Writeln("floats ",Math.floor(this.byteBuffer.byteLength / 4));
+    };
+    this.AddBytes = function (count, data) {
+      pas.System.Writeln("AddBytes: @",this.byteOffset," -> ",data);
+      this.byteBuffer.set(data,this.byteOffset);
+      this.byteOffset = this.byteOffset + (count * 1);
+    };
+    this.AddFloats = function (count, data) {
+      var floatOffset = 0;
+      floatOffset = Math.floor(this.byteOffset / 4);
+      pas.System.Writeln("AddFloats: @",this.byteOffset,"\/",floatOffset," -> ",data);
+      if (this.floatBuffer === null) this.floatBuffer = new Float32Array(this.byteBuffer.buffer,0,Math.floor(this.byteBuffer.byteLength / 4));
+      this.floatBuffer.set(data,floatOffset);
+      this.byteOffset = this.byteOffset + (count * 4);
+    };
+  });
+});

+ 51 - 0
MemoryBuffer.pas

@@ -0,0 +1,51 @@
+{$mode objfpc}
+
+unit MemoryBuffer;
+interface
+uses
+	JS;
+
+type
+	TMemoryBuffer = class
+		private
+			byteBuffer: TJSUint8Array;
+		public
+		 constructor Create (size: integer);
+		 procedure AddBytes (count: integer; data: array of byte);
+		 procedure AddFloats (count: integer; data: array of single);
+		 property GetBytes: TJSUint8Array read byteBuffer;
+		private
+			byteOffset: integer;
+			floatBuffer: TJSFloat32Array;
+	end;
+
+implementation
+
+constructor TMemoryBuffer.Create (size: integer);
+begin
+	byteBuffer := TJSUint8Array.New(size);
+end;
+
+procedure TMemoryBuffer.AddBytes (count: integer; data: array of byte);
+begin
+	//writeln('AddBytes: @', byteOffset, ' -> ', data);
+	byteBuffer._set(data, byteOffset);
+	byteOffset := byteOffset + (count * 1);
+end;
+
+procedure TMemoryBuffer.AddFloats (count: integer; data: array of single);
+var
+	floatOffset: integer;
+begin
+	floatOffset := byteOffset div 4;
+	//writeln('AddFloats: @', byteOffset, '/', floatOffset, ' -> ', data);
+
+	if floatBuffer = nil then
+		floatBuffer := TJSFloat32Array.New(byteBuffer.buffer, 0, byteBuffer.byteLength div 4);
+
+	floatBuffer._set(data, floatOffset);
+
+	byteOffset := byteOffset + (count * 4);
+end;
+
+end.

+ 34 - 0
Pas2JS_WebGL.html

@@ -0,0 +1,34 @@
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <script type="application/javascript" src="Pas2JS_WebGL.js"></script>
+  </head>
+  <body>
+  	<script type="application/glsl" id="vertex.glsl">
+			attribute vec2 in_position;
+			attribute vec4 in_color;
+
+			varying vec4 out_color;
+
+      uniform mat4 projTransform;
+      uniform mat4 viewTransform;
+      uniform mat4 modelTransform;
+
+			void main() {
+			  gl_Position = projTransform * viewTransform * modelTransform * vec4(in_position, 1, 1);
+			  out_color = in_color;
+			}
+  	</script>
+  	<script type="application/glsl" id="fragment.glsl">
+			precision mediump float;
+			varying vec4 out_color;
+
+			void main() {
+			  gl_FragColor = out_color;
+			}
+  	</script>
+    <script type="application/javascript">
+     rtl.run();
+    </script>
+  </body>
+</html>

+ 1728 - 0
Pas2JS_WebGL.js

@@ -0,0 +1,1728 @@
+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.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("Web",["System","JS"],function () {
+  "use strict";
+  var $mod = this;
+});
+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("vectors",["System","browserconsole","JS"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("SysUtils",["System","JS"],function () {
+  "use strict";
+  var $mod = this;
+  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.DegToRad = function (deg) {
+    var Result = 0.0;
+    Result = deg * (Math.PI / 180.0);
+    return Result;
+  };
+});
+rtl.module("Mat4",["System","vectors","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.RotateZ = function (Angle) {
+      $mod.SinCos(Angle,{a: 1, p: this.RawComponents[0], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }},{a: 0, p: this.RawComponents[0], get: function () {
+          return this.p[this.a];
+        }, set: function (v) {
+          this.p[this.a] = v;
+        }});
+      this.RawComponents[0][2] = 0.0;
+      this.RawComponents[0][3] = 0.0;
+      this.RawComponents[1][0] = -this.RawComponents[0][1];
+      this.RawComponents[1][1] = this.RawComponents[0][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.Ortho = function (Left, Right, Bottom, Top, zNear, zFar) {
+      var rml = 0.0;
+      var tmb = 0.0;
+      var fmn = 0.0;
+      rml = Right - Left;
+      tmb = Top - Bottom;
+      fmn = zFar - zNear;
+      this.RawComponents[0][0] = 2.0 / rml;
+      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] = 2.0 / tmb;
+      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] = -2.0 / fmn;
+      this.RawComponents[2][3] = 0.0;
+      this.RawComponents[3][0] = -(Right + Left) / rml;
+      this.RawComponents[3][1] = -(Top + Bottom) / tmb;
+      this.RawComponents[3][2] = -(zFar + zNear) / fmn;
+      this.RawComponents[3][3] = 1.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.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;
+    };
+  });
+  this.SinCos = function (angle, sinus, cosinus) {
+    sinus.set(Math.sin(angle));
+    cosinus.set(Math.cos(angle));
+  };
+  $mod.$init = function () {
+    $impl.Matrix4x4Identity = $mod.TMat4.$create("Identity");
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.Matrix4x4Identity = null;
+});
+rtl.module("MemoryBuffer",["System","JS"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"TMemoryBuffer",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.byteBuffer = null;
+      this.byteOffset = 0;
+      this.floatBuffer = null;
+    };
+    this.$final = function () {
+      this.byteBuffer = undefined;
+      this.floatBuffer = undefined;
+      pas.System.TObject.$final.call(this);
+    };
+    this.Create$1 = function (size) {
+      this.byteBuffer = new Uint8Array(size);
+    };
+    this.AddBytes = function (count, data) {
+      this.byteBuffer.set(data,this.byteOffset);
+      this.byteOffset = this.byteOffset + (count * 1);
+    };
+    this.AddFloats = function (count, data) {
+      var floatOffset = 0;
+      floatOffset = Math.floor(this.byteOffset / 4);
+      if (this.floatBuffer === null) this.floatBuffer = new Float32Array(this.byteBuffer.buffer,0,Math.floor(this.byteBuffer.byteLength / 4));
+      this.floatBuffer.set(data,floatOffset);
+      this.byteOffset = this.byteOffset + (count * 4);
+    };
+  });
+});
+rtl.module("webgl",["System","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("GLUtils",["System","browserconsole","Web","webgl","JS"],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 location = null;
+      location = this.gl.getUniformLocation(this.programID,name);
+      $impl.GLFatal(this.gl,"gl.getUniformLocation");
+      this.gl.uniformMatrix4fv(location,false,value);
+      $impl.GLFatal(this.gl,"gl.uniformMatrix4fv");
+    };
+    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;
+    };
+  });
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.Fatal = function (messageString) {
+    pas.System.Writeln("*** FATAL: ",messageString);
+    return;
+  };
+  $impl.Fatal$1 = function (messageString) {
+    pas.System.Writeln("*** FATAL: ",messageString);
+    return;
+  };
+  $impl.GLFatal = function (gl, messageString) {
+    var error = 0;
+    error = gl.getError();
+    if (error !== gl.NO_ERROR) {
+      $impl.Fatal(messageString);
+    };
+  };
+});
+rtl.module("GLTypes",["System","webgl","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TVec2_Sizeof = function () {
+    var Result = 0;
+    Result = 4 * 2;
+    return Result;
+  };
+  this.TRGBAb_Sizeof = function () {
+    var Result = 0;
+    Result = 1 * 4;
+    return Result;
+  };
+  this.V2 = function (x, y) {
+    var Result = [];
+    Result[0] = x;
+    Result[1] = y;
+    return Result;
+  };
+  this.RGBAb = function (r, g, b, a) {
+    var Result = [];
+    Result[0] = r;
+    Result[1] = g;
+    Result[2] = b;
+    Result[3] = a;
+    return Result;
+  };
+});
+rtl.module("program",["System","vectors","Mat4","MemoryBuffer","GLUtils","GLTypes","browserconsole","Web","webgl","JS","math"],function () {
+  "use strict";
+  var $mod = this;
+  this.GLVertex2 = function (s) {
+    if (s) {
+      this.pos = s.pos;
+      this.color = s.color;
+    } else {
+      this.pos = [];
+      this.color = [];
+    };
+    this.$equal = function (b) {
+      return (this.pos === b.pos) && (this.color === b.color);
+    };
+  };
+  this.kSIZEOF_VERTEX = 12;
+  this.GetVertexData = function () {
+    var Result = null;
+    var buffer = null;
+    var verts = null;
+    var v = new $mod.GLVertex2();
+    var i = 0;
+    verts = new Array();
+    v.pos = pas.GLTypes.V2(0,0);
+    v.color = pas.GLTypes.RGBAb(255,0,0,255);
+    verts.push(new $mod.GLVertex2(v));
+    v.pos = pas.GLTypes.V2(0,100);
+    v.color = pas.GLTypes.RGBAb(0,255,0,255);
+    verts.push(new $mod.GLVertex2(v));
+    v.pos = pas.GLTypes.V2(100,100);
+    v.color = pas.GLTypes.RGBAb(0,0,255,255);
+    verts.push(new $mod.GLVertex2(v));
+    buffer = pas.MemoryBuffer.TMemoryBuffer.$create("Create$1",[12 * verts.length]);
+    for (var $l1 = 0, $end2 = verts.length - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      v = new $mod.GLVertex2(rtl.getObject(verts[i]));
+      buffer.AddFloats(2,v.pos);
+      buffer.AddBytes(4,v.color);
+    };
+    Result = buffer.byteBuffer;
+    return Result;
+  };
+  this.nextTime = 0;
+  this.deltaTime = 0;
+  this.gl = null;
+  this.shader = null;
+  this.projTransform = null;
+  this.viewTransform = null;
+  this.modelTransform = null;
+  this.rotateAngle = 0;
+  this.UpdateCanvas = function (time) {
+    var now = 0.0;
+    var list = [];
+    now = time * 0.001;
+    $mod.deltaTime = now - $mod.nextTime;
+    $mod.nextTime = now;
+    $mod.modelTransform = pas.Mat4.TMat4.$create("Identity");
+    $mod.modelTransform = $mod.modelTransform.Multiply(pas.Mat4.TMat4.$create("Translate",[100,100,0]));
+    $mod.modelTransform = $mod.modelTransform.Multiply(pas.Mat4.TMat4.$create("RotateZ",[pas.math.DegToRad($mod.rotateAngle)]));
+    $mod.rotateAngle = $mod.rotateAngle + (20 * $mod.deltaTime);
+    list = $mod.modelTransform.CopyList();
+    $mod.shader.SetUniformMat4("modelTransform",list);
+    $mod.gl.clear($mod.gl.COLOR_BUFFER_BIT);
+    $mod.gl.drawArrays($mod.gl.TRIANGLES,0,3);
+    window.requestAnimationFrame($mod.UpdateCanvas);
+  };
+  this.canvas = null;
+  this.offset = 0;
+  this.stride = 0;
+  this.vertexShaderSource = "";
+  this.fragmentShaderSource = "";
+  this.buffer = null;
+  this.list = [];
+  $mod.$main = function () {
+    $mod.canvas = document.createElement("canvas");
+    $mod.canvas.width = 300;
+    $mod.canvas.height = 300;
+    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_color");
+    $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.clear($mod.gl.COLOR_BUFFER_BIT);
+    $mod.projTransform = pas.Mat4.TMat4.$create("Ortho",[0,$mod.gl.canvas.width,$mod.gl.canvas.height,0,-1,1]);
+    $mod.viewTransform = pas.Mat4.TMat4.$create("Identity");
+    $mod.modelTransform = pas.Mat4.TMat4.$create("Identity");
+    $mod.list = $mod.projTransform.CopyList();
+    $mod.shader.SetUniformMat4("projTransform",$mod.list);
+    $mod.list = $mod.viewTransform.CopyList();
+    $mod.shader.SetUniformMat4("viewTransform",$mod.list);
+    $mod.list = $mod.modelTransform.CopyList();
+    $mod.shader.SetUniformMat4("modelTransform",$mod.list);
+    $mod.buffer = $mod.gl.createBuffer();
+    $mod.gl.bindBuffer($mod.gl.ARRAY_BUFFER,$mod.buffer);
+    $mod.gl.bufferData($mod.gl.ARRAY_BUFFER,$mod.GetVertexData(),$mod.gl.STATIC_DRAW);
+    $mod.offset = 0;
+    $mod.stride = 12;
+    $mod.gl.enableVertexAttribArray(0);
+    $mod.gl.vertexAttribPointer(0,2,$mod.gl.FLOAT,false,$mod.stride,$mod.offset);
+    $mod.offset = $mod.offset + pas.GLTypes.TVec2_Sizeof();
+    $mod.gl.enableVertexAttribArray(1);
+    $mod.gl.vertexAttribPointer(1,4,$mod.gl.UNSIGNED_BYTE,true,$mod.stride,$mod.offset);
+    $mod.offset = $mod.offset + pas.GLTypes.TRGBAb_Sizeof();
+    window.requestAnimationFrame($mod.UpdateCanvas);
+  };
+});

+ 168 - 0
Pas2JS_WebGL.pas

@@ -0,0 +1,168 @@
+program Hello;
+uses
+	Vectors, Mat4, MemoryBuffer, GLUtils, GLTypes,
+	BrowserConsole, Web, WebGL, WebGL2, JS, Math;
+
+type
+	GLVertex2 = record
+		pos: TVec2;
+		color: TRGBAb;
+	end;
+
+const
+	kSIZEOF_VERTEX = 12;
+
+function GetVertexData: TJSUInt8Array;
+var
+	buffer: TMemoryBuffer;
+	verts: TJSArray;
+	v: GLVertex2;
+	i: integer;
+begin
+	verts := TJSArray.new;
+
+	v.pos := V2(0, 0);
+	v.color := RGBAb(255, 0, 0, 255);
+	verts.push(v);
+
+	v.pos := V2(0, 100);
+	v.color := RGBAb(0, 255, 0, 255);
+	verts.push(v);
+
+	v.pos := V2(100, 100);
+	v.color := RGBAb(0, 0, 255, 255);
+	verts.push(v);
+
+	// pack the array of verticies into a byte buffer
+	buffer := TMemoryBuffer.Create(kSIZEOF_VERTEX * verts.length);
+	for i := 0 to verts.length - 1 do
+		begin
+			v := GLVertex2(verts[i]);
+			buffer.AddFloats(2, v.pos);
+			buffer.AddBytes(4, v.color);
+		end;
+
+	result := buffer.GetBytes;
+end;
+
+
+var
+	nextTime: single = 0;
+	deltaTime: single = 0;
+
+var
+	gl: TJSWebGLRenderingContext;
+	shader: TShader;
+  projTransform: TMat4;
+  viewTransform: TMat4;
+  modelTransform: TMat4;
+
+var
+	rotateAngle: double = 0;
+
+procedure UpdateCanvas(time: TDOMHighResTimeStamp);
+var
+	now: single;
+	list: TScalarArray;
+begin
+	now := time * 0.001;
+	deltaTime := now - nextTime;
+	nextTime := now;
+
+	modelTransform := TMat4.Identity;
+	modelTransform := modelTransform.Multiply(TMat4.Translate(100, 100, 0));
+	modelTransform := modelTransform.Multiply(TMat4.RotateZ(DegToRad(rotateAngle)));
+
+	//fullTransform := projTransform.Multiply(viewTransform);
+	//fullTransform := fullTransform.Multiply(modelTransform);
+
+	rotateAngle := rotateAngle + (20 * deltaTime);
+
+	list := modelTransform.CopyList;
+	shader.SetUniformMat4('modelTransform', TJSFloat32List(list));
+
+	//writeln(deltaTime);
+	gl.clear(gl.COLOR_BUFFER_BIT);
+	gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+	window.requestAnimationFrame(@UpdateCanvas);
+end;
+
+var
+  canvas: TJSHTMLCanvasElement;
+  i: integer;
+  offset: integer;
+  stride: integer;
+  vertexShaderSource: string;
+  fragmentShaderSource: string;
+  buffer: TJSWebGLBuffer;
+  list: TScalarArray;
+begin
+
+	// make webgl context
+  canvas := TJSHTMLCanvasElement(document.createElement('canvas'));
+  canvas.width := 300;
+  canvas.height := 300;
+	document.body.appendChild(canvas);
+
+	gl := TJSWebGLRenderingContext(canvas.getContext('webgl'));
+	if gl = nil then
+		begin
+			writeln('failed to load webgl!');
+			exit;
+		end;
+
+	// create shaders from source in html
+	vertexShaderSource := document.getElementById('vertex.glsl').textContent;
+	fragmentShaderSource := document.getElementById('fragment.glsl').textContent;
+
+	shader := TShader.Create(gl, vertexShaderSource, fragmentShaderSource);
+	shader.Compile;
+  shader.BindAttribLocation(0, 'in_position');
+  shader.BindAttribLocation(1, 'in_color');
+	shader.Link;
+	shader.Use;
+
+	// prepare context
+	gl.clearColor(0.9, 0.9, 0.9, 1);
+	gl.viewport(0, 0, canvas.width, canvas.height);
+	gl.clear(gl.COLOR_BUFFER_BIT);
+
+	// setup transform matricies
+	projTransform := TMat4.Ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
+	viewTransform := TMat4.Identity;
+	modelTransform := TMat4.Identity;
+
+	// TODO: parser bug typecasting to TJSFloat32List(projTransform.CopyList)
+	// so we need to assign to a variable first
+	list := projTransform.CopyList;
+	shader.SetUniformMat4('projTransform', TJSFloat32List(list));
+	list := viewTransform.CopyList;
+	shader.SetUniformMat4('viewTransform', TJSFloat32List(list));
+	list := modelTransform.CopyList;
+	shader.SetUniformMat4('modelTransform', TJSFloat32List(list));
+
+	// create buffer
+	buffer := gl.createBuffer;
+	gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+  gl.bufferData(gl.ARRAY_BUFFER, GetVertexData, gl.STATIC_DRAW);
+
+	// TODO: vertex array objects were added in webgl2
+	offset := 0;  
+	stride := kSIZEOF_VERTEX;
+
+	// position
+	gl.enableVertexAttribArray(0);
+	gl.vertexAttribPointer(0, 2, gl.FLOAT, false, stride, offset);
+	offset := offset + TVec2_Sizeof;
+
+	// color (normalized = true since we're using unsigned byte)
+	gl.enableVertexAttribArray(1);
+	gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, stride, offset);
+	offset := offset + TRGBAb_Sizeof;
+
+	//gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+	// fire off the timer to draw
+	window.requestAnimationFrame(@UpdateCanvas);
+end.

+ 2383 - 0
Vectors.js

@@ -0,0 +1,2383 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{88387EF6-BCEE-3E17-9E85-5D491ED4FC10}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{ECEC7568-4E50-30C9-A2F0-439342DE2ADB}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{9791C368-4E51-3424-A3CE-D4911D54F385}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("Web",["System","Types","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TJSClientRect = function (s) {
+    if (s) {
+      this.left = s.left;
+      this.top = s.top;
+      this.right = s.right;
+      this.bottom = s.bottom;
+    } else {
+      this.left = 0.0;
+      this.top = 0.0;
+      this.right = 0.0;
+      this.bottom = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.left === b.left) && ((this.top === b.top) && ((this.right === b.right) && (this.bottom === b.bottom)));
+    };
+  };
+  this.TJSElementCreationOptions = function (s) {
+    if (s) {
+      this.named = s.named;
+    } else {
+      this.named = "";
+    };
+    this.$equal = function (b) {
+      return this.named === b.named;
+    };
+  };
+  this.TJSEventInit = function (s) {
+    if (s) {
+      this.bubbles = s.bubbles;
+      this.cancelable = s.cancelable;
+      this.scoped = s.scoped;
+      this.composed = s.composed;
+    } else {
+      this.bubbles = false;
+      this.cancelable = false;
+      this.scoped = false;
+      this.composed = false;
+    };
+    this.$equal = function (b) {
+      return (this.bubbles === b.bubbles) && ((this.cancelable === b.cancelable) && ((this.scoped === b.scoped) && (this.composed === b.composed)));
+    };
+  };
+  rtl.createClassExt($mod,"TJSAnimationEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJSLoadEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJsPageTransitionEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClass($mod,"TJSIDBTransactionMode",pas.System.TObject,function () {
+    this.readonly = "readonly";
+    this.readwrite = "readwrite";
+    this.versionchange = "versionchange";
+  });
+  this.TJSIDBIndexParameters = function (s) {
+    if (s) {
+      this.unique = s.unique;
+      this.multiEntry = s.multiEntry;
+      this.locale = s.locale;
+    } else {
+      this.unique = false;
+      this.multiEntry = false;
+      this.locale = "";
+    };
+    this.$equal = function (b) {
+      return (this.unique === b.unique) && ((this.multiEntry === b.multiEntry) && (this.locale === b.locale));
+    };
+  };
+  this.TJSCreateObjectStoreOptions = function (s) {
+    if (s) {
+      this.keyPath = s.keyPath;
+      this.autoIncrement = s.autoIncrement;
+    } else {
+      this.keyPath = undefined;
+      this.autoIncrement = false;
+    };
+    this.$equal = function (b) {
+      return (this.keyPath === b.keyPath) && (this.autoIncrement === b.autoIncrement);
+    };
+  };
+  this.TJSPositionError = function (s) {
+    if (s) {
+      this.code = s.code;
+      this.message = s.message;
+    } else {
+      this.code = 0;
+      this.message = "";
+    };
+    this.$equal = function (b) {
+      return (this.code === b.code) && (this.message === b.message);
+    };
+  };
+  this.TJSPositionOptions = function (s) {
+    if (s) {
+      this.enableHighAccuracy = s.enableHighAccuracy;
+      this.timeout = s.timeout;
+      this.maximumAge = s.maximumAge;
+    } else {
+      this.enableHighAccuracy = false;
+      this.timeout = 0;
+      this.maximumAge = 0;
+    };
+    this.$equal = function (b) {
+      return (this.enableHighAccuracy === b.enableHighAccuracy) && ((this.timeout === b.timeout) && (this.maximumAge === b.maximumAge));
+    };
+  };
+  this.TJSCoordinates = function (s) {
+    if (s) {
+      this.latitude = s.latitude;
+      this.longitude = s.longitude;
+      this.altitude = s.altitude;
+      this.accuracy = s.accuracy;
+      this.altitudeAccuracy = s.altitudeAccuracy;
+      this.heading = s.heading;
+      this.speed = s.speed;
+    } else {
+      this.latitude = 0.0;
+      this.longitude = 0.0;
+      this.altitude = 0.0;
+      this.accuracy = 0.0;
+      this.altitudeAccuracy = 0.0;
+      this.heading = 0.0;
+      this.speed = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.latitude === b.latitude) && ((this.longitude === b.longitude) && ((this.altitude === b.altitude) && ((this.accuracy === b.accuracy) && ((this.altitudeAccuracy === b.altitudeAccuracy) && ((this.heading === b.heading) && (this.speed === b.speed))))));
+    };
+  };
+  this.TJSPosition = function (s) {
+    if (s) {
+      this.coords = new $mod.TJSCoordinates(s.coords);
+      this.timestamp = s.timestamp;
+    } else {
+      this.coords = new $mod.TJSCoordinates();
+      this.timestamp = "";
+    };
+    this.$equal = function (b) {
+      return this.coords.$equal(b.coords) && (this.timestamp === b.timestamp);
+    };
+  };
+  this.TJSServiceWorkerContainerOptions = function (s) {
+    if (s) {
+      this.scope = s.scope;
+    } else {
+      this.scope = "";
+    };
+    this.$equal = function (b) {
+      return this.scope === b.scope;
+    };
+  };
+  this.TJSTextMetrics = function (s) {
+    if (s) {
+      this.width = s.width;
+      this.actualBoundingBoxLeft = s.actualBoundingBoxLeft;
+      this.actualBoundingBoxRight = s.actualBoundingBoxRight;
+      this.fontBoundingBoxAscent = s.fontBoundingBoxAscent;
+      this.fontBoundingBoxDescent = s.fontBoundingBoxDescent;
+      this.actualBoundingBoxAscent = s.actualBoundingBoxAscent;
+      this.actualBoundingBoxDescent = s.actualBoundingBoxDescent;
+      this.emHeightAscent = s.emHeightAscent;
+      this.emHeightDescent = s.emHeightDescent;
+      this.hangingBaseline = s.hangingBaseline;
+      this.alphabeticBaseline = s.alphabeticBaseline;
+      this.ideographicBaseline = s.ideographicBaseline;
+    } else {
+      this.width = 0.0;
+      this.actualBoundingBoxLeft = 0.0;
+      this.actualBoundingBoxRight = 0.0;
+      this.fontBoundingBoxAscent = 0.0;
+      this.fontBoundingBoxDescent = 0.0;
+      this.actualBoundingBoxAscent = 0.0;
+      this.actualBoundingBoxDescent = 0.0;
+      this.emHeightAscent = 0.0;
+      this.emHeightDescent = 0.0;
+      this.hangingBaseline = 0.0;
+      this.alphabeticBaseline = 0.0;
+      this.ideographicBaseline = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.width === b.width) && ((this.actualBoundingBoxLeft === b.actualBoundingBoxLeft) && ((this.actualBoundingBoxRight === b.actualBoundingBoxRight) && ((this.fontBoundingBoxAscent === b.fontBoundingBoxAscent) && ((this.fontBoundingBoxDescent === b.fontBoundingBoxDescent) && ((this.actualBoundingBoxAscent === b.actualBoundingBoxAscent) && ((this.actualBoundingBoxDescent === b.actualBoundingBoxDescent) && ((this.emHeightAscent === b.emHeightAscent) && ((this.emHeightDescent === b.emHeightDescent) && ((this.hangingBaseline === b.hangingBaseline) && ((this.alphabeticBaseline === b.alphabeticBaseline) && (this.ideographicBaseline === b.ideographicBaseline)))))))))));
+    };
+  };
+  this.TJSWheelEventInit = function (s) {
+    if (s) {
+      this.deltaX = s.deltaX;
+      this.deltaY = s.deltaY;
+      this.deltaZ = s.deltaZ;
+      this.deltaMode = s.deltaMode;
+    } else {
+      this.deltaX = 0.0;
+      this.deltaY = 0.0;
+      this.deltaZ = 0.0;
+      this.deltaMode = 0;
+    };
+    this.$equal = function (b) {
+      return (this.deltaX === b.deltaX) && ((this.deltaY === b.deltaY) && ((this.deltaZ === b.deltaZ) && (this.deltaMode === b.deltaMode)));
+    };
+  };
+  rtl.createClass($mod,"TJSKeyNames",pas.System.TObject,function () {
+    this.Alt = "Alt";
+    this.AltGraph = "AltGraph";
+    this.CapsLock = "CapsLock";
+    this.Control = "Control";
+    this.Fn = "Fn";
+    this.FnLock = "FnLock";
+    this.Hyper = "Hyper";
+    this.Meta = "Meta";
+    this.NumLock = "NumLock";
+    this.ScrollLock = "ScrollLock";
+    this.Shift = "Shift";
+    this.Super = "Super";
+    this.symbol = "Symbol";
+    this.SymbolLock = "SymbolLock";
+    this.Enter = "Enter";
+    this.Tab = "Tab";
+    this.Space = " ";
+    this.ArrowDown = "ArrowDown";
+    this.ArrowLeft = "ArrowLeft";
+    this.ArrowRight = "ArrowRight";
+    this.ArrowUp = "ArrowUp";
+    this._End = "End";
+    this.Home = "Home";
+    this.PageDown = "PageDown";
+    this.PageUp = "PageUp";
+    this.BackSpace = "Backspace";
+    this.Clear = "Clear";
+    this.Copy = "Copy";
+    this.CrSel = "CrSel";
+    this.Cut = "Cut";
+    this.Delete = "Delete";
+    this.EraseEof = "EraseEof";
+    this.ExSel = "ExSel";
+    this.Insert = "Insert";
+    this.Paste = "Paste";
+    this.Redo = "Redo";
+    this.Undo = "Undo";
+    this.Accept = "Accept";
+    this.Again = "Again";
+    this.Attn = "Attn";
+    this.Cancel = "Cancel";
+    this.ContextMenu = "Contextmenu";
+    this.Escape = "Escape";
+    this.Execute = "Execute";
+    this.Find = "Find";
+    this.Finish = "Finish";
+    this.Help = "Help";
+    this.Pause = "Pause";
+    this.Play = "Play";
+    this.Props = "Props";
+    this.Select = "Select";
+    this.ZoomIn = "ZoomIn";
+    this.ZoomOut = "ZoomOut";
+    this.BrightnessDown = "BrightnessDown";
+    this.BrightnessUp = "BrightnessUp";
+    this.Eject = "Eject";
+    this.LogOff = "LogOff";
+    this.Power = "Power";
+    this.PowerOff = "PowerOff";
+    this.PrintScreen = "PrintScreen";
+    this.Hibernate = "Hibernate";
+    this.Standby = "Standby";
+    this.WakeUp = "WakeUp";
+    this.AllCandidates = "AllCandidates";
+    this.Alphanumeric = "Alphanumeric";
+    this.CodeInput = "CodeInput";
+    this.Compose = "Compose";
+    this.Convert = "Convert";
+    this.Dead = "Dead";
+    this.FinalMode = "FinalMode";
+    this.GroupFirst = "GroupFirst";
+    this.GroupLast = "GroupLast";
+    this.GroupNext = "GroupNext";
+    this.GroupPrevious = "GroupPrevious";
+    this.ModelChange = "ModelChange";
+    this.NextCandidate = "NextCandidate";
+    this.NonConvert = "NonConvert";
+    this.PreviousCandidate = "PreviousCandidate";
+    this.Process = "Process";
+    this.SingleCandidate = "SingleCandidate";
+    this.HangulMode = "HangulMode";
+    this.HanjaMode = "HanjaMode";
+    this.JunjaMode = "JunjaMode";
+    this.Eisu = "Eisu";
+    this.Hankaku = "Hankaku";
+    this.Hiranga = "Hiranga";
+    this.HirangaKatakana = "HirangaKatakana";
+    this.KanaMode = "KanaMode";
+    this.Katakana = "Katakana";
+    this.Romaji = "Romaji";
+    this.Zenkaku = "Zenkaku";
+    this.ZenkakuHanaku = "ZenkakuHanaku";
+    this.F1 = "F1";
+    this.F2 = "F2";
+    this.F3 = "F3";
+    this.F4 = "F4";
+    this.F5 = "F5";
+    this.F6 = "F6";
+    this.F7 = "F7";
+    this.F8 = "F8";
+    this.F9 = "F9";
+    this.F10 = "F10";
+    this.F11 = "F11";
+    this.F12 = "F12";
+    this.F13 = "F13";
+    this.F14 = "F14";
+    this.F15 = "F15";
+    this.F16 = "F16";
+    this.F17 = "F17";
+    this.F18 = "F18";
+    this.F19 = "F19";
+    this.F20 = "F20";
+    this.Soft1 = "Soft1";
+    this.Soft2 = "Soft2";
+    this.Soft3 = "Soft3";
+    this.Soft4 = "Soft4";
+    this.Decimal = "Decimal";
+    this.Key11 = "Key11";
+    this.Key12 = "Key12";
+    this.Multiply = "Multiply";
+    this.Add = "Add";
+    this.NumClear = "Clear";
+    this.Divide = "Divide";
+    this.Subtract = "Subtract";
+    this.Separator = "Separator";
+    this.AppSwitch = "AppSwitch";
+    this.Call = "Call";
+    this.Camera = "Camera";
+    this.CameraFocus = "CameraFocus";
+    this.EndCall = "EndCall";
+    this.GoBack = "GoBack";
+    this.GoHome = "GoHome";
+    this.HeadsetHook = "HeadsetHook";
+    this.LastNumberRedial = "LastNumberRedial";
+    this.Notification = "Notification";
+    this.MannerMode = "MannerMode";
+    this.VoiceDial = "VoiceDial";
+  });
+});
+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.DefaultCRTConsoleStyle = (((((((((((".pasconsole { " + pas.System.sLineBreak) + "font-family: courier;") + pas.System.sLineBreak) + "font-size: 14px;") + pas.System.sLineBreak) + "background: #000;") + pas.System.sLineBreak) + "color: #14fdce;") + 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("vectors",["System","browserconsole","JS"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClassExt($mod,"TVec2",Array,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.SetX = function (newValue) {
+      this[0] = newValue;
+    };
+    this.SetY = function (newValue) {
+      this[1] = newValue;
+    };
+    this.GetX = function () {
+      var Result = 0.0;
+      Result = rtl.getNumber(this[0]);
+      return Result;
+    };
+    this.GetY = function () {
+      var Result = 0.0;
+      Result = rtl.getNumber(this[1]);
+      return Result;
+    };
+    this.Create = function () {
+    };
+  });
+});

+ 54 - 0
Vectors.pas

@@ -0,0 +1,54 @@
+{$mode objfpc}
+
+unit vectors;
+interface
+uses
+	BrowserConsole, JS;
+
+type
+	TScalar = single;
+	TScalarArray = array of TScalar;
+	
+type
+	TVec2 = class (TJSArray)
+		private
+			procedure SetX (newValue: TScalar);
+			procedure SetY (newValue: TScalar);
+
+			function GetX: TScalar;
+			function GetY: TScalar;
+		public
+			constructor Create;
+
+			property x: TScalar read GetX write SetX;
+			property y: TScalar read GetY write SetY;
+	end;
+
+implementation
+
+procedure TVec2.SetX (newValue: TScalar);
+begin
+	//SetElements(0, newValue);
+	Elements[0] := newValue;
+end;
+
+procedure TVec2.SetY (newValue: TScalar);
+begin
+	Elements[1] := newValue;
+end;
+
+function TVec2.GetX: TScalar;
+begin
+	result := TScalar(Elements[0]); // Illegal type conversion: "set" to "Double"
+end;
+
+function TVec2.GetY: TScalar;
+begin
+	result := TScalar(Elements[1]);
+end;
+
+constructor TVec2.Create;
+begin
+end;
+
+end.

+ 2284 - 0
webgl2.js

@@ -0,0 +1,2284 @@
+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;
+  this.MaxSmallint = 32767;
+  this.MinSmallint = -32768;
+  this.MaxShortInt = 127;
+  this.MinShortInt = -128;
+  this.MaxByte = 0xFF;
+  this.MaxWord = 0xFFFF;
+  this.MaxLongint = 0x7fffffff;
+  this.MaxCardinal = 0xffffffff;
+  this.Maxint = 2147483647;
+  this.IsMultiThread = false;
+  this.TTextLineBreakStyle = {"0": "tlbsLF", tlbsLF: 0, "1": "tlbsCRLF", tlbsCRLF: 1, "2": "tlbsCR", tlbsCR: 2};
+  this.TGuid = function (s) {
+    if (s) {
+      this.D1 = s.D1;
+      this.D2 = s.D2;
+      this.D3 = s.D3;
+      this.D4 = s.D4.slice(0);
+    } else {
+      this.D1 = 0;
+      this.D2 = 0;
+      this.D3 = 0;
+      this.D4 = rtl.arraySetLength(null,0,8);
+    };
+    this.$equal = function (b) {
+      return (this.D1 === b.D1) && ((this.D2 === b.D2) && ((this.D3 === b.D3) && rtl.arrayEq(this.D4,b.D4)));
+    };
+  };
+  rtl.createClass($mod,"TObject",null,function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+    this.Create = function () {
+    };
+    this.Destroy = function () {
+    };
+    this.Free = function () {
+      this.$destroy("Destroy");
+    };
+    this.ClassType = function () {
+      return this;
+    };
+    this.ClassNameIs = function (Name) {
+      var Result = false;
+      Result = $impl.SameText(Name,this.$classname);
+      return Result;
+    };
+    this.InheritsFrom = function (aClass) {
+      return (aClass!=null) && ((this==aClass) || aClass.isPrototypeOf(this));
+    };
+    this.AfterConstruction = function () {
+    };
+    this.BeforeDestruction = function () {
+    };
+    this.GetInterface = function (iid, obj) {
+      var Result = false;
+      var i = iid.$intf;
+      if (i){
+        i = rtl.getIntfG(this,i.$guid,2);
+        if (i){
+          obj.set(i);
+          return true;
+        }
+      };
+      Result = this.GetInterfaceByStr(rtl.guidrToStr(iid),obj);
+      return Result;
+    };
+    this.GetInterface$1 = function (iidstr, obj) {
+      var Result = false;
+      Result = this.GetInterfaceByStr(iidstr,obj);
+      return Result;
+    };
+    this.GetInterfaceByStr = function (iidstr, obj) {
+      var Result = false;
+      if ($mod.IObjectInstance.$equal(rtl.createTGUID(iidstr))) {
+        obj.set(this);
+        return true;
+      };
+      var i = rtl.getIntfG(this,iidstr,2);
+      obj.set(i);
+      return i!==null;
+      Result = false;
+      return Result;
+    };
+    this.GetInterfaceWeak = function (iid, obj) {
+      var Result = false;
+      Result = this.GetInterface(iid,obj);
+      if (Result){
+        var o = obj.get();
+        if (o.$kind==='com'){
+          o._Release();
+        }
+      };
+      return Result;
+    };
+    this.Equals = function (Obj) {
+      var Result = false;
+      Result = Obj === this;
+      return Result;
+    };
+    this.ToString = function () {
+      var Result = "";
+      Result = this.$classname;
+      return Result;
+    };
+  });
+  this.S_OK = 0;
+  this.S_FALSE = 1;
+  this.E_NOINTERFACE = -2147467262;
+  this.E_UNEXPECTED = -2147418113;
+  this.E_NOTIMPL = -2147467263;
+  rtl.createInterface($mod,"IUnknown","{00000000-0000-0000-C000-000000000046}",["QueryInterface","_AddRef","_Release"],null,function () {
+    this.$kind = "com";
+    var $r = this.$rtti;
+    $r.addMethod("QueryInterface",1,[["iid",$mod.$rtti["TGuid"],2],["obj",null,4]],rtl.longint);
+    $r.addMethod("_AddRef",1,null,rtl.longint);
+    $r.addMethod("_Release",1,null,rtl.longint);
+  });
+  rtl.createInterface($mod,"IInvokable","{88387EF6-BCEE-3E17-9E85-5D491ED4FC10}",[],$mod.IUnknown,function () {
+  });
+  rtl.createInterface($mod,"IEnumerator","{ECEC7568-4E50-30C9-A2F0-439342DE2ADB}",["GetCurrent","MoveNext","Reset"],$mod.IUnknown);
+  rtl.createInterface($mod,"IEnumerable","{9791C368-4E51-3424-A3CE-D4911D54F385}",["GetEnumerator"],$mod.IUnknown);
+  rtl.createClass($mod,"TInterfacedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fRefCount = 0;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      this.fRefCount += 1;
+      Result = this.fRefCount;
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      this.fRefCount -= 1;
+      Result = this.fRefCount;
+      if (this.fRefCount === 0) this.$destroy("Destroy");
+      return Result;
+    };
+    this.BeforeDestruction = function () {
+      if (this.fRefCount !== 0) rtl.raiseE('EHeapMemoryError');
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  rtl.createClass($mod,"TAggregatedObject",$mod.TObject,function () {
+    this.$init = function () {
+      $mod.TObject.$init.call(this);
+      this.fController = null;
+    };
+    this.GetController = function () {
+      var Result = null;
+      var $ok = false;
+      try {
+        Result = rtl.setIntfL(Result,this.fController);
+        $ok = true;
+      } finally {
+        if (!$ok) rtl._Release(Result);
+      };
+      return Result;
+    };
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      Result = this.fController.QueryInterface(iid,obj);
+      return Result;
+    };
+    this._AddRef = function () {
+      var Result = 0;
+      Result = this.fController._AddRef();
+      return Result;
+    };
+    this._Release = function () {
+      var Result = 0;
+      Result = this.fController._Release();
+      return Result;
+    };
+    this.Create$1 = function (aController) {
+      $mod.TObject.Create.call(this);
+      this.fController = aController;
+    };
+  });
+  rtl.createClass($mod,"TContainedObject",$mod.TAggregatedObject,function () {
+    this.QueryInterface = function (iid, obj) {
+      var Result = 0;
+      if (this.GetInterface(iid,obj)) {
+        Result = 0}
+       else Result = -2147467262;
+      return Result;
+    };
+    this.$intfmaps = {};
+    rtl.addIntf(this,$mod.IUnknown);
+  });
+  this.IObjectInstance = new $mod.TGuid({D1: 0xD91C9AF4, D2: 0x3C93, D3: 0x420F, D4: [0xA3,0x03,0xBF,0x5B,0xA8,0x2B,0xFD,0x23]});
+  this.IsConsole = false;
+  this.OnParamCount = null;
+  this.OnParamStr = null;
+  this.ParamCount = function () {
+    var Result = 0;
+    if ($mod.OnParamCount != null) {
+      Result = $mod.OnParamCount()}
+     else Result = 0;
+    return Result;
+  };
+  this.ParamStr = function (Index) {
+    var Result = "";
+    if ($mod.OnParamStr != null) {
+      Result = $mod.OnParamStr(Index)}
+     else if (Index === 0) {
+      Result = "js"}
+     else Result = "";
+    return Result;
+  };
+  this.Frac = function (A) {
+    return A % 1;
+  };
+  this.Odd = function (A) {
+    return A&1 != 0;
+  };
+  this.Random = function (Range) {
+    return Math.floor(Math.random()*Range);
+  };
+  this.Sqr = function (A) {
+    return A*A;
+  };
+  this.Sqr$1 = function (A) {
+    return A*A;
+  };
+  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.DefaultTextLineBreakStyle = $mod.TTextLineBreakStyle.tlbsLF;
+  this.Int = function (A) {
+    var Result = 0.0;
+    Result = Math.trunc(A);
+    return Result;
+  };
+  this.Copy = function (S, Index, Size) {
+    if (Index<1) Index = 1;
+    return (Size>0) ? S.substring(Index-1,Index+Size-1) : "";
+  };
+  this.Copy$1 = function (S, Index) {
+    if (Index<1) Index = 1;
+    return S.substr(Index-1);
+  };
+  this.Delete = function (S, Index, Size) {
+    var h = "";
+    if (((Index < 1) || (Index > S.get().length)) || (Size <= 0)) return;
+    h = S.get();
+    S.set($mod.Copy(h,1,Index - 1) + $mod.Copy$1(h,Index + Size));
+  };
+  this.Pos = function (Search, InString) {
+    return InString.indexOf(Search)+1;
+  };
+  this.Pos$1 = function (Search, InString, StartAt) {
+    return InString.indexOf(Search,StartAt-1)+1;
+  };
+  this.Insert = function (Insertion, Target, Index) {
+    var t = "";
+    if (Insertion === "") return;
+    t = Target.get();
+    if (Index < 1) {
+      Target.set(Insertion + t)}
+     else if (Index > t.length) {
+      Target.set(t + Insertion)}
+     else Target.set(($mod.Copy(t,1,Index - 1) + Insertion) + $mod.Copy(t,Index,t.length));
+  };
+  this.upcase = function (c) {
+    return c.toUpperCase();
+  };
+  this.val = function (S, NI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else NI.set($mod.Trunc(x));
+  };
+  this.val$1 = function (S, SI, Code) {
+    var X = 0.0;
+    Code.set(0);
+    X = Number(S);
+    if (isNaN(X) || (X !== $mod.Int(X))) {
+      Code.set(1)}
+     else if ((X < -128) || (X > 127)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(X));
+  };
+  this.val$2 = function (S, B, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 255)) {
+      Code.set(2)}
+     else B.set($mod.Trunc(x));
+  };
+  this.val$3 = function (S, SI, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < -32768) || (x > 32767)) {
+      Code.set(2)}
+     else SI.set($mod.Trunc(x));
+  };
+  this.val$4 = function (S, W, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 65535)) {
+      Code.set(2)}
+     else W.set($mod.Trunc(x));
+  };
+  this.val$5 = function (S, I, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else if (x > 2147483647) {
+      Code.set(2)}
+     else I.set($mod.Trunc(x));
+  };
+  this.val$6 = function (S, C, Code) {
+    var x = 0.0;
+    Code.set(0);
+    x = Number(S);
+    if (isNaN(x) || (x !== $mod.Int(x))) {
+      Code.set(1)}
+     else if ((x < 0) || (x > 4294967295)) {
+      Code.set(2)}
+     else C.set($mod.Trunc(x));
+  };
+  this.val$7 = function (S, d, Code) {
+    var x = 0.0;
+    x = Number(S);
+    if (isNaN(x)) {
+      Code.set(1)}
+     else {
+      Code.set(0);
+      d.set(x);
+    };
+  };
+  this.StringOfChar = function (c, l) {
+    var Result = "";
+    var i = 0;
+    Result = "";
+    for (var $l1 = 1, $end2 = l; $l1 <= $end2; $l1++) {
+      i = $l1;
+      Result = Result + c;
+    };
+    return Result;
+  };
+  this.Write = function () {
+    var i = 0;
+    for (var $l1 = 0, $end2 = rtl.length(arguments) - 1; $l1 <= $end2; $l1++) {
+      i = $l1;
+      if ($impl.WriteCallBack != null) {
+        $impl.WriteCallBack(arguments[i],false)}
+       else $impl.WriteBuf = $impl.WriteBuf + ("" + arguments[i]);
+    };
+  };
+  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;
+  };
+  this.Assigned = function (V) {
+    return (V!=undefined) && (V!=null) && (!rtl.isArray(V) || (V.length > 0));
+  };
+  this.StrictEqual = function (A, B) {
+    return A === B;
+  };
+  this.StrictInequal = function (A, B) {
+    return A !== B;
+  };
+  $mod.$init = function () {
+    rtl.exitcode = 0;
+  };
+},null,function () {
+  "use strict";
+  var $mod = this;
+  var $impl = $mod.$impl;
+  $impl.SameText = function (s1, s2) {
+    return s1.toLowerCase() == s2.toLowerCase();
+  };
+  $impl.WriteBuf = "";
+  $impl.WriteCallBack = null;
+});
+rtl.module("Types",["System"],function () {
+  "use strict";
+  var $mod = this;
+  this.TDirection = {"0": "FromBeginning", FromBeginning: 0, "1": "FromEnd", FromEnd: 1};
+  this.TDuplicates = {"0": "dupIgnore", dupIgnore: 0, "1": "dupAccept", dupAccept: 1, "2": "dupError", dupError: 2};
+  this.TSize = function (s) {
+    if (s) {
+      this.cx = s.cx;
+      this.cy = s.cy;
+    } else {
+      this.cx = 0;
+      this.cy = 0;
+    };
+    this.$equal = function (b) {
+      return (this.cx === b.cx) && (this.cy === b.cy);
+    };
+  };
+  this.TPoint = function (s) {
+    if (s) {
+      this.x = s.x;
+      this.y = s.y;
+    } else {
+      this.x = 0;
+      this.y = 0;
+    };
+    this.$equal = function (b) {
+      return (this.x === b.x) && (this.y === b.y);
+    };
+  };
+  this.TRect = function (s) {
+    if (s) {
+      this.Left = s.Left;
+      this.Top = s.Top;
+      this.Right = s.Right;
+      this.Bottom = s.Bottom;
+    } else {
+      this.Left = 0;
+      this.Top = 0;
+      this.Right = 0;
+      this.Bottom = 0;
+    };
+    this.$equal = function (b) {
+      return (this.Left === b.Left) && ((this.Top === b.Top) && ((this.Right === b.Right) && (this.Bottom === b.Bottom)));
+    };
+  };
+  this.EqualRect = function (r1, r2) {
+    var Result = false;
+    Result = (((r1.Left === r2.Left) && (r1.Right === r2.Right)) && (r1.Top === r2.Top)) && (r1.Bottom === r2.Bottom);
+    return Result;
+  };
+  this.Rect = function (Left, Top, Right, Bottom) {
+    var Result = new $mod.TRect();
+    Result.Left = Left;
+    Result.Top = Top;
+    Result.Right = Right;
+    Result.Bottom = Bottom;
+    return Result;
+  };
+  this.Bounds = function (ALeft, ATop, AWidth, AHeight) {
+    var Result = new $mod.TRect();
+    Result.Left = ALeft;
+    Result.Top = ATop;
+    Result.Right = ALeft + AWidth;
+    Result.Bottom = ATop + AHeight;
+    return Result;
+  };
+  this.Point = function (x, y) {
+    var Result = new $mod.TPoint();
+    Result.x = x;
+    Result.y = y;
+    return Result;
+  };
+  this.PtInRect = function (aRect, p) {
+    var Result = false;
+    Result = (((p.y >= aRect.Top) && (p.y < aRect.Bottom)) && (p.x >= aRect.Left)) && (p.x < aRect.Right);
+    return Result;
+  };
+  this.IntersectRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left > R1.Left) lRect.Left = R2.Left;
+    if (R2.Top > R1.Top) lRect.Top = R2.Top;
+    if (R2.Right < R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom < R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      Result = true;
+      aRect.set(new $mod.TRect(lRect));
+    };
+    return Result;
+  };
+  this.UnionRect = function (aRect, R1, R2) {
+    var Result = false;
+    var lRect = new $mod.TRect();
+    lRect = new $mod.TRect(R1);
+    if (R2.Left < R1.Left) lRect.Left = R2.Left;
+    if (R2.Top < R1.Top) lRect.Top = R2.Top;
+    if (R2.Right > R1.Right) lRect.Right = R2.Right;
+    if (R2.Bottom > R1.Bottom) lRect.Bottom = R2.Bottom;
+    if ($mod.IsRectEmpty(lRect)) {
+      aRect.set(new $mod.TRect($mod.Rect(0,0,0,0)));
+      Result = false;
+    } else {
+      aRect.set(new $mod.TRect(lRect));
+      Result = true;
+    };
+    return Result;
+  };
+  this.IsRectEmpty = function (aRect) {
+    var Result = false;
+    Result = (aRect.Right <= aRect.Left) || (aRect.Bottom <= aRect.Top);
+    return Result;
+  };
+  this.OffsetRect = function (aRect, DX, DY) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left += DX;
+    $with1.Top += DY;
+    $with1.Right += DX;
+    $with1.Bottom += DY;
+    Result = true;
+    return Result;
+  };
+  this.CenterPoint = function (aRect) {
+    var Result = new $mod.TPoint();
+    function Avg(a, b) {
+      var Result = 0;
+      if (a < b) {
+        Result = a + ((b - a) >>> 1)}
+       else Result = b + ((a - b) >>> 1);
+      return Result;
+    };
+    Result.x = Avg(aRect.Left,aRect.Right);
+    Result.y = Avg(aRect.Top,aRect.Bottom);
+    return Result;
+  };
+  this.InflateRect = function (aRect, dx, dy) {
+    var Result = false;
+    var $with1 = aRect.get();
+    $with1.Left -= dx;
+    $with1.Top -= dy;
+    $with1.Right += dx;
+    $with1.Bottom += dy;
+    Result = true;
+    return Result;
+  };
+  this.Size = function (AWidth, AHeight) {
+    var Result = new $mod.TSize();
+    Result.cx = AWidth;
+    Result.cy = AHeight;
+    return Result;
+  };
+  this.Size$1 = function (aRect) {
+    var Result = new $mod.TSize();
+    Result.cx = aRect.Right - aRect.Left;
+    Result.cy = aRect.Bottom - aRect.Top;
+    return Result;
+  };
+});
+rtl.module("JS",["System","Types"],function () {
+  "use strict";
+  var $mod = this;
+  rtl.createClass($mod,"EJS",pas.System.TObject,function () {
+    this.$init = function () {
+      pas.System.TObject.$init.call(this);
+      this.FMessage = "";
+    };
+    this.Create$1 = function (Msg) {
+      this.FMessage = Msg;
+    };
+  });
+  this.TLocaleCompareOptions = function (s) {
+    if (s) {
+      this.localematched = s.localematched;
+      this.usage = s.usage;
+      this.sensitivity = s.sensitivity;
+      this.ignorePunctuation = s.ignorePunctuation;
+      this.numeric = s.numeric;
+      this.caseFirst = s.caseFirst;
+    } else {
+      this.localematched = "";
+      this.usage = "";
+      this.sensitivity = "";
+      this.ignorePunctuation = false;
+      this.numeric = false;
+      this.caseFirst = "";
+    };
+    this.$equal = function (b) {
+      return (this.localematched === b.localematched) && ((this.usage === b.usage) && ((this.sensitivity === b.sensitivity) && ((this.ignorePunctuation === b.ignorePunctuation) && ((this.numeric === b.numeric) && (this.caseFirst === b.caseFirst)))));
+    };
+  };
+  this.New = function (aElements) {
+    var Result = null;
+    var L = 0;
+    var I = 0;
+    var S = "";
+    L = rtl.length(aElements);
+    if ((L % 2) === 1) throw $mod.EJS.$create("Create$1",["Number of arguments must be even"]);
+    I = 0;
+    while (I < L) {
+      if (!rtl.isString(aElements[I])) {
+        S = String(I);
+        throw $mod.EJS.$create("Create$1",[("Argument " + S) + " must be a string."]);
+      };
+      I += 2;
+    };
+    I = 0;
+    Result = new Object();
+    while (I < L) {
+      S = "" + aElements[I];
+      Result[S] = aElements[I + 1];
+      I += 2;
+    };
+    return Result;
+  };
+  this.hasValue = function (v) {
+    if(v){ return true; } else { return false; };
+  };
+  this.isBoolean = function (v) {
+    return typeof(v) == 'boolean';
+  };
+  this.isCallback = function (v) {
+    return rtl.isObject(v) && rtl.isObject(v.scope) && (rtl.isString(v.fn) || rtl.isFunction(v.fn));
+  };
+  this.isChar = function (v) {
+    return (typeof(v)!="string") && (v.length==1);
+  };
+  this.isClass = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == v);
+  };
+  this.isClassInstance = function (v) {
+    return (typeof(v)=="object") && (v!=null) && (v.$class == Object.getPrototypeOf(v));
+  };
+  this.isInteger = function (v) {
+    return Math.floor(v)===v;
+  };
+  this.isNull = function (v) {
+    return v === null;
+  };
+  this.isRecord = function (v) {
+    return (typeof(v)=="function") && (typeof(v.$create) == "function");
+  };
+  this.isUndefined = function (v) {
+    return v == undefined;
+  };
+  this.isDefined = function (v) {
+    return !(v == undefined);
+  };
+  this.isUTF16Char = function (v) {
+    if (typeof(v)!="string") return false;
+    if ((v.length==0) || (v.length>2)) return false;
+    var code = v.charCodeAt(0);
+    if (code < 0xD800){
+      if (v.length == 1) return true;
+    } else if (code <= 0xDBFF){
+      if (v.length==2){
+        code = v.charCodeAt(1);
+        if (code >= 0xDC00 && code <= 0xDFFF) return true;
+      };
+    };
+    return false;
+  };
+  this.jsInstanceOf = function (aFunction, aFunctionWithPrototype) {
+    return aFunction instanceof aFunctionWithPrototype;
+  };
+  this.toNumber = function (v) {
+    return v-0;
+  };
+  this.toInteger = function (v) {
+    var Result = 0;
+    if ($mod.isInteger(v)) {
+      Result = Math.floor(v)}
+     else Result = 0;
+    return Result;
+  };
+  this.toObject = function (Value) {
+    var Result = null;
+    if (rtl.isObject(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toArray = function (Value) {
+    var Result = null;
+    if (rtl.isArray(Value)) {
+      Result = rtl.getObject(Value)}
+     else Result = null;
+    return Result;
+  };
+  this.toBoolean = function (Value) {
+    var Result = false;
+    if ($mod.isBoolean(Value)) {
+      Result = !(Value == false)}
+     else Result = false;
+    return Result;
+  };
+  this.ToString = function (Value) {
+    var Result = "";
+    if (rtl.isString(Value)) {
+      Result = "" + Value}
+     else Result = "";
+    return Result;
+  };
+  this.TJSValueType = {"0": "jvtNull", jvtNull: 0, "1": "jvtBoolean", jvtBoolean: 1, "2": "jvtInteger", jvtInteger: 2, "3": "jvtFloat", jvtFloat: 3, "4": "jvtString", jvtString: 4, "5": "jvtObject", jvtObject: 5, "6": "jvtArray", jvtArray: 6};
+  this.GetValueType = function (JS) {
+    var Result = 0;
+    var t = "";
+    if ($mod.isNull(JS)) {
+      Result = $mod.TJSValueType.jvtNull}
+     else {
+      t = typeof(JS);
+      if (t === "string") {
+        Result = $mod.TJSValueType.jvtString}
+       else if (t === "boolean") {
+        Result = $mod.TJSValueType.jvtBoolean}
+       else if (t === "object") {
+        if (rtl.isArray(JS)) {
+          Result = $mod.TJSValueType.jvtArray}
+         else Result = $mod.TJSValueType.jvtObject;
+      } else if (t === "number") if ($mod.isInteger(JS)) {
+        Result = $mod.TJSValueType.jvtInteger}
+       else Result = $mod.TJSValueType.jvtFloat;
+    };
+    return Result;
+  };
+});
+rtl.module("Web",["System","Types","JS"],function () {
+  "use strict";
+  var $mod = this;
+  this.TJSClientRect = function (s) {
+    if (s) {
+      this.left = s.left;
+      this.top = s.top;
+      this.right = s.right;
+      this.bottom = s.bottom;
+    } else {
+      this.left = 0.0;
+      this.top = 0.0;
+      this.right = 0.0;
+      this.bottom = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.left === b.left) && ((this.top === b.top) && ((this.right === b.right) && (this.bottom === b.bottom)));
+    };
+  };
+  this.TJSElementCreationOptions = function (s) {
+    if (s) {
+      this.named = s.named;
+    } else {
+      this.named = "";
+    };
+    this.$equal = function (b) {
+      return this.named === b.named;
+    };
+  };
+  this.TJSEventInit = function (s) {
+    if (s) {
+      this.bubbles = s.bubbles;
+      this.cancelable = s.cancelable;
+      this.scoped = s.scoped;
+      this.composed = s.composed;
+    } else {
+      this.bubbles = false;
+      this.cancelable = false;
+      this.scoped = false;
+      this.composed = false;
+    };
+    this.$equal = function (b) {
+      return (this.bubbles === b.bubbles) && ((this.cancelable === b.cancelable) && ((this.scoped === b.scoped) && (this.composed === b.composed)));
+    };
+  };
+  rtl.createClassExt($mod,"TJSAnimationEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJSLoadEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClassExt($mod,"TJsPageTransitionEvent",Event,"",function () {
+    this.$init = function () {
+    };
+    this.$final = function () {
+    };
+  });
+  rtl.createClass($mod,"TJSIDBTransactionMode",pas.System.TObject,function () {
+    this.readonly = "readonly";
+    this.readwrite = "readwrite";
+    this.versionchange = "versionchange";
+  });
+  this.TJSIDBIndexParameters = function (s) {
+    if (s) {
+      this.unique = s.unique;
+      this.multiEntry = s.multiEntry;
+      this.locale = s.locale;
+    } else {
+      this.unique = false;
+      this.multiEntry = false;
+      this.locale = "";
+    };
+    this.$equal = function (b) {
+      return (this.unique === b.unique) && ((this.multiEntry === b.multiEntry) && (this.locale === b.locale));
+    };
+  };
+  this.TJSCreateObjectStoreOptions = function (s) {
+    if (s) {
+      this.keyPath = s.keyPath;
+      this.autoIncrement = s.autoIncrement;
+    } else {
+      this.keyPath = undefined;
+      this.autoIncrement = false;
+    };
+    this.$equal = function (b) {
+      return (this.keyPath === b.keyPath) && (this.autoIncrement === b.autoIncrement);
+    };
+  };
+  this.TJSPositionError = function (s) {
+    if (s) {
+      this.code = s.code;
+      this.message = s.message;
+    } else {
+      this.code = 0;
+      this.message = "";
+    };
+    this.$equal = function (b) {
+      return (this.code === b.code) && (this.message === b.message);
+    };
+  };
+  this.TJSPositionOptions = function (s) {
+    if (s) {
+      this.enableHighAccuracy = s.enableHighAccuracy;
+      this.timeout = s.timeout;
+      this.maximumAge = s.maximumAge;
+    } else {
+      this.enableHighAccuracy = false;
+      this.timeout = 0;
+      this.maximumAge = 0;
+    };
+    this.$equal = function (b) {
+      return (this.enableHighAccuracy === b.enableHighAccuracy) && ((this.timeout === b.timeout) && (this.maximumAge === b.maximumAge));
+    };
+  };
+  this.TJSCoordinates = function (s) {
+    if (s) {
+      this.latitude = s.latitude;
+      this.longitude = s.longitude;
+      this.altitude = s.altitude;
+      this.accuracy = s.accuracy;
+      this.altitudeAccuracy = s.altitudeAccuracy;
+      this.heading = s.heading;
+      this.speed = s.speed;
+    } else {
+      this.latitude = 0.0;
+      this.longitude = 0.0;
+      this.altitude = 0.0;
+      this.accuracy = 0.0;
+      this.altitudeAccuracy = 0.0;
+      this.heading = 0.0;
+      this.speed = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.latitude === b.latitude) && ((this.longitude === b.longitude) && ((this.altitude === b.altitude) && ((this.accuracy === b.accuracy) && ((this.altitudeAccuracy === b.altitudeAccuracy) && ((this.heading === b.heading) && (this.speed === b.speed))))));
+    };
+  };
+  this.TJSPosition = function (s) {
+    if (s) {
+      this.coords = new $mod.TJSCoordinates(s.coords);
+      this.timestamp = s.timestamp;
+    } else {
+      this.coords = new $mod.TJSCoordinates();
+      this.timestamp = "";
+    };
+    this.$equal = function (b) {
+      return this.coords.$equal(b.coords) && (this.timestamp === b.timestamp);
+    };
+  };
+  this.TJSServiceWorkerContainerOptions = function (s) {
+    if (s) {
+      this.scope = s.scope;
+    } else {
+      this.scope = "";
+    };
+    this.$equal = function (b) {
+      return this.scope === b.scope;
+    };
+  };
+  this.TJSTextMetrics = function (s) {
+    if (s) {
+      this.width = s.width;
+      this.actualBoundingBoxLeft = s.actualBoundingBoxLeft;
+      this.actualBoundingBoxRight = s.actualBoundingBoxRight;
+      this.fontBoundingBoxAscent = s.fontBoundingBoxAscent;
+      this.fontBoundingBoxDescent = s.fontBoundingBoxDescent;
+      this.actualBoundingBoxAscent = s.actualBoundingBoxAscent;
+      this.actualBoundingBoxDescent = s.actualBoundingBoxDescent;
+      this.emHeightAscent = s.emHeightAscent;
+      this.emHeightDescent = s.emHeightDescent;
+      this.hangingBaseline = s.hangingBaseline;
+      this.alphabeticBaseline = s.alphabeticBaseline;
+      this.ideographicBaseline = s.ideographicBaseline;
+    } else {
+      this.width = 0.0;
+      this.actualBoundingBoxLeft = 0.0;
+      this.actualBoundingBoxRight = 0.0;
+      this.fontBoundingBoxAscent = 0.0;
+      this.fontBoundingBoxDescent = 0.0;
+      this.actualBoundingBoxAscent = 0.0;
+      this.actualBoundingBoxDescent = 0.0;
+      this.emHeightAscent = 0.0;
+      this.emHeightDescent = 0.0;
+      this.hangingBaseline = 0.0;
+      this.alphabeticBaseline = 0.0;
+      this.ideographicBaseline = 0.0;
+    };
+    this.$equal = function (b) {
+      return (this.width === b.width) && ((this.actualBoundingBoxLeft === b.actualBoundingBoxLeft) && ((this.actualBoundingBoxRight === b.actualBoundingBoxRight) && ((this.fontBoundingBoxAscent === b.fontBoundingBoxAscent) && ((this.fontBoundingBoxDescent === b.fontBoundingBoxDescent) && ((this.actualBoundingBoxAscent === b.actualBoundingBoxAscent) && ((this.actualBoundingBoxDescent === b.actualBoundingBoxDescent) && ((this.emHeightAscent === b.emHeightAscent) && ((this.emHeightDescent === b.emHeightDescent) && ((this.hangingBaseline === b.hangingBaseline) && ((this.alphabeticBaseline === b.alphabeticBaseline) && (this.ideographicBaseline === b.ideographicBaseline)))))))))));
+    };
+  };
+  this.TJSWheelEventInit = function (s) {
+    if (s) {
+      this.deltaX = s.deltaX;
+      this.deltaY = s.deltaY;
+      this.deltaZ = s.deltaZ;
+      this.deltaMode = s.deltaMode;
+    } else {
+      this.deltaX = 0.0;
+      this.deltaY = 0.0;
+      this.deltaZ = 0.0;
+      this.deltaMode = 0;
+    };
+    this.$equal = function (b) {
+      return (this.deltaX === b.deltaX) && ((this.deltaY === b.deltaY) && ((this.deltaZ === b.deltaZ) && (this.deltaMode === b.deltaMode)));
+    };
+  };
+  rtl.createClass($mod,"TJSKeyNames",pas.System.TObject,function () {
+    this.Alt = "Alt";
+    this.AltGraph = "AltGraph";
+    this.CapsLock = "CapsLock";
+    this.Control = "Control";
+    this.Fn = "Fn";
+    this.FnLock = "FnLock";
+    this.Hyper = "Hyper";
+    this.Meta = "Meta";
+    this.NumLock = "NumLock";
+    this.ScrollLock = "ScrollLock";
+    this.Shift = "Shift";
+    this.Super = "Super";
+    this.symbol = "Symbol";
+    this.SymbolLock = "SymbolLock";
+    this.Enter = "Enter";
+    this.Tab = "Tab";
+    this.Space = " ";
+    this.ArrowDown = "ArrowDown";
+    this.ArrowLeft = "ArrowLeft";
+    this.ArrowRight = "ArrowRight";
+    this.ArrowUp = "ArrowUp";
+    this._End = "End";
+    this.Home = "Home";
+    this.PageDown = "PageDown";
+    this.PageUp = "PageUp";
+    this.BackSpace = "Backspace";
+    this.Clear = "Clear";
+    this.Copy = "Copy";
+    this.CrSel = "CrSel";
+    this.Cut = "Cut";
+    this.Delete = "Delete";
+    this.EraseEof = "EraseEof";
+    this.ExSel = "ExSel";
+    this.Insert = "Insert";
+    this.Paste = "Paste";
+    this.Redo = "Redo";
+    this.Undo = "Undo";
+    this.Accept = "Accept";
+    this.Again = "Again";
+    this.Attn = "Attn";
+    this.Cancel = "Cancel";
+    this.ContextMenu = "Contextmenu";
+    this.Escape = "Escape";
+    this.Execute = "Execute";
+    this.Find = "Find";
+    this.Finish = "Finish";
+    this.Help = "Help";
+    this.Pause = "Pause";
+    this.Play = "Play";
+    this.Props = "Props";
+    this.Select = "Select";
+    this.ZoomIn = "ZoomIn";
+    this.ZoomOut = "ZoomOut";
+    this.BrightnessDown = "BrightnessDown";
+    this.BrightnessUp = "BrightnessUp";
+    this.Eject = "Eject";
+    this.LogOff = "LogOff";
+    this.Power = "Power";
+    this.PowerOff = "PowerOff";
+    this.PrintScreen = "PrintScreen";
+    this.Hibernate = "Hibernate";
+    this.Standby = "Standby";
+    this.WakeUp = "WakeUp";
+    this.AllCandidates = "AllCandidates";
+    this.Alphanumeric = "Alphanumeric";
+    this.CodeInput = "CodeInput";
+    this.Compose = "Compose";
+    this.Convert = "Convert";
+    this.Dead = "Dead";
+    this.FinalMode = "FinalMode";
+    this.GroupFirst = "GroupFirst";
+    this.GroupLast = "GroupLast";
+    this.GroupNext = "GroupNext";
+    this.GroupPrevious = "GroupPrevious";
+    this.ModelChange = "ModelChange";
+    this.NextCandidate = "NextCandidate";
+    this.NonConvert = "NonConvert";
+    this.PreviousCandidate = "PreviousCandidate";
+    this.Process = "Process";
+    this.SingleCandidate = "SingleCandidate";
+    this.HangulMode = "HangulMode";
+    this.HanjaMode = "HanjaMode";
+    this.JunjaMode = "JunjaMode";
+    this.Eisu = "Eisu";
+    this.Hankaku = "Hankaku";
+    this.Hiranga = "Hiranga";
+    this.HirangaKatakana = "HirangaKatakana";
+    this.KanaMode = "KanaMode";
+    this.Katakana = "Katakana";
+    this.Romaji = "Romaji";
+    this.Zenkaku = "Zenkaku";
+    this.ZenkakuHanaku = "ZenkakuHanaku";
+    this.F1 = "F1";
+    this.F2 = "F2";
+    this.F3 = "F3";
+    this.F4 = "F4";
+    this.F5 = "F5";
+    this.F6 = "F6";
+    this.F7 = "F7";
+    this.F8 = "F8";
+    this.F9 = "F9";
+    this.F10 = "F10";
+    this.F11 = "F11";
+    this.F12 = "F12";
+    this.F13 = "F13";
+    this.F14 = "F14";
+    this.F15 = "F15";
+    this.F16 = "F16";
+    this.F17 = "F17";
+    this.F18 = "F18";
+    this.F19 = "F19";
+    this.F20 = "F20";
+    this.Soft1 = "Soft1";
+    this.Soft2 = "Soft2";
+    this.Soft3 = "Soft3";
+    this.Soft4 = "Soft4";
+    this.Decimal = "Decimal";
+    this.Key11 = "Key11";
+    this.Key12 = "Key12";
+    this.Multiply = "Multiply";
+    this.Add = "Add";
+    this.NumClear = "Clear";
+    this.Divide = "Divide";
+    this.Subtract = "Subtract";
+    this.Separator = "Separator";
+    this.AppSwitch = "AppSwitch";
+    this.Call = "Call";
+    this.Camera = "Camera";
+    this.CameraFocus = "CameraFocus";
+    this.EndCall = "EndCall";
+    this.GoBack = "GoBack";
+    this.GoHome = "GoHome";
+    this.HeadsetHook = "HeadsetHook";
+    this.LastNumberRedial = "LastNumberRedial";
+    this.Notification = "Notification";
+    this.MannerMode = "MannerMode";
+    this.VoiceDial = "VoiceDial";
+  });
+});
+rtl.module("webgl",["System","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});
+rtl.module("WebGL2",["System","webgl","JS","Web"],function () {
+  "use strict";
+  var $mod = this;
+});

+ 470 - 0
webgl2.pas

@@ -0,0 +1,470 @@
+unit WebGL2;
+
+{$mode objfpc}
+{$modeswitch externalclass}
+
+interface
+uses
+ WebGL, JS, Web;
+
+type
+  GLenumDynArray = array of GLenum;
+  GLuintDynArray = array of GLuint;
+
+type
+  GLint64 = nativeint; // long long
+  GLuint64 = longword; // unsigned long long
+type
+  TJSWebGLQuery = class external name 'WebGLQuery' (TJSWebGLObject);
+  TJSWebGLSampler = class external name 'WebGLSampler' (TJSWebGLObject);
+  TJSWebGLSync = class external name 'WebGLSync' (TJSWebGLObject);
+  TJSWebGLTransformFeedback = class external name 'WebGLTransformFeedback' (TJSWebGLObject);
+  TJSWebGLVertexArrayObject = class external name 'WebGLVertexArrayObject' (TJSWebGLObject);
+type  
+  TJSWebGL2RenderingContextBase = class external name 'WebGL2RenderingContextBase' (TJSWebGLRenderingContextBase)
+  public
+  const
+    READ_BUFFER: nativeint;
+    UNPACK_ROW_LENGTH: nativeint;
+    UNPACK_SKIP_ROWS: nativeint;
+    UNPACK_SKIP_PIXELS: nativeint;
+    PACK_ROW_LENGTH: nativeint;
+    PACK_SKIP_ROWS: nativeint;
+    PACK_SKIP_PIXELS: nativeint;
+    COLOR: nativeint;
+    DEPTH: nativeint;
+    STENCIL: nativeint;
+    RED: nativeint;
+    RGB8: nativeint;
+    RGBA8: nativeint;
+    RGB10_A2: nativeint;
+    TEXTURE_BINDING_3D: nativeint;
+    UNPACK_SKIP_IMAGES: nativeint;
+    UNPACK_IMAGE_HEIGHT: nativeint;
+    TEXTURE_3D: nativeint;
+    TEXTURE_WRAP_R: nativeint;
+    MAX_3D_TEXTURE_SIZE: nativeint;
+    UNSIGNED_INT_2_10_10_10_REV: nativeint;
+    MAX_ELEMENTS_VERTICES: nativeint;
+    MAX_ELEMENTS_INDICES: nativeint;
+    TEXTURE_MIN_LOD: nativeint;
+    TEXTURE_MAX_LOD: nativeint;
+    TEXTURE_BASE_LEVEL: nativeint;
+    TEXTURE_MAX_LEVEL: nativeint;
+    MIN: nativeint;
+    MAX: nativeint;
+    DEPTH_COMPONENT24: nativeint;
+    MAX_TEXTURE_LOD_BIAS: nativeint;
+    TEXTURE_COMPARE_MODE: nativeint;
+    TEXTURE_COMPARE_FUNC: nativeint;
+    CURRENT_QUERY: nativeint;
+    QUERY_RESULT: nativeint;
+    QUERY_RESULT_AVAILABLE: nativeint;
+    STREAM_READ: nativeint;
+    STREAM_COPY: nativeint;
+    STATIC_READ: nativeint;
+    STATIC_COPY: nativeint;
+    DYNAMIC_READ: nativeint;
+    DYNAMIC_COPY: nativeint;
+    MAX_DRAW_BUFFERS: nativeint;
+    DRAW_BUFFER0: nativeint;
+    DRAW_BUFFER1: nativeint;
+    DRAW_BUFFER2: nativeint;
+    DRAW_BUFFER3: nativeint;
+    DRAW_BUFFER4: nativeint;
+    DRAW_BUFFER5: nativeint;
+    DRAW_BUFFER6: nativeint;
+    DRAW_BUFFER7: nativeint;
+    DRAW_BUFFER8: nativeint;
+    DRAW_BUFFER9: nativeint;
+    DRAW_BUFFER10: nativeint;
+    DRAW_BUFFER11: nativeint;
+    DRAW_BUFFER12: nativeint;
+    DRAW_BUFFER13: nativeint;
+    DRAW_BUFFER14: nativeint;
+    DRAW_BUFFER15: nativeint;
+    MAX_FRAGMENT_UNIFORM_COMPONENTS: nativeint;
+    MAX_VERTEX_UNIFORM_COMPONENTS: nativeint;
+    SAMPLER_3D: nativeint;
+    SAMPLER_2D_SHADOW: nativeint;
+    FRAGMENT_SHADER_DERIVATIVE_HINT: nativeint;
+    PIXEL_PACK_BUFFER: nativeint;
+    PIXEL_UNPACK_BUFFER: nativeint;
+    PIXEL_PACK_BUFFER_BINDING: nativeint;
+    PIXEL_UNPACK_BUFFER_BINDING: nativeint;
+    FLOAT_MAT2x3: nativeint;
+    FLOAT_MAT2x4: nativeint;
+    FLOAT_MAT3x2: nativeint;
+    FLOAT_MAT3x4: nativeint;
+    FLOAT_MAT4x2: nativeint;
+    FLOAT_MAT4x3: nativeint;
+    SRGB: nativeint;
+    SRGB8: nativeint;
+    SRGB8_ALPHA8: nativeint;
+    COMPARE_REF_TO_TEXTURE: nativeint;
+    RGBA32F: nativeint;
+    RGB32F: nativeint;
+    RGBA16F: nativeint;
+    RGB16F: nativeint;
+    VERTEX_ATTRIB_ARRAY_INTEGER: nativeint;
+    MAX_ARRAY_TEXTURE_LAYERS: nativeint;
+    MIN_PROGRAM_TEXEL_OFFSET: nativeint;
+    MAX_PROGRAM_TEXEL_OFFSET: nativeint;
+    MAX_VARYING_COMPONENTS: nativeint;
+    TEXTURE_2D_ARRAY: nativeint;
+    TEXTURE_BINDING_2D_ARRAY: nativeint;
+    R11F_G11F_B10F: nativeint;
+    UNSIGNED_INT_10F_11F_11F_REV: nativeint;
+    RGB9_E5: nativeint;
+    UNSIGNED_INT_5_9_9_9_REV: nativeint;
+    TRANSFORM_FEEDBACK_BUFFER_MODE: nativeint;
+    MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: nativeint;
+    TRANSFORM_FEEDBACK_VARYINGS: nativeint;
+    TRANSFORM_FEEDBACK_BUFFER_START: nativeint;
+    TRANSFORM_FEEDBACK_BUFFER_SIZE: nativeint;
+    TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: nativeint;
+    RASTERIZER_DISCARD: nativeint;
+    MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: nativeint;
+    MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: nativeint;
+    INTERLEAVED_ATTRIBS: nativeint;
+    SEPARATE_ATTRIBS: nativeint;
+    TRANSFORM_FEEDBACK_BUFFER: nativeint;
+    TRANSFORM_FEEDBACK_BUFFER_BINDING: nativeint;
+    RGBA32UI: nativeint;
+    RGB32UI: nativeint;
+    RGBA16UI: nativeint;
+    RGB16UI: nativeint;
+    RGBA8UI: nativeint;
+    RGB8UI: nativeint;
+    RGBA32I: nativeint;
+    RGB32I: nativeint;
+    RGBA16I: nativeint;
+    RGB16I: nativeint;
+    RGBA8I: nativeint;
+    RGB8I: nativeint;
+    RED_INTEGER: nativeint;
+    RGB_INTEGER: nativeint;
+    RGBA_INTEGER: nativeint;
+    SAMPLER_2D_ARRAY: nativeint;
+    SAMPLER_2D_ARRAY_SHADOW: nativeint;
+    SAMPLER_CUBE_SHADOW: nativeint;
+    UNSIGNED_INT_VEC2: nativeint;
+    UNSIGNED_INT_VEC3: nativeint;
+    UNSIGNED_INT_VEC4: nativeint;
+    INT_SAMPLER_2D: nativeint;
+    INT_SAMPLER_3D: nativeint;
+    INT_SAMPLER_CUBE: nativeint;
+    INT_SAMPLER_2D_ARRAY: nativeint;
+    UNSIGNED_INT_SAMPLER_2D: nativeint;
+    UNSIGNED_INT_SAMPLER_3D: nativeint;
+    UNSIGNED_INT_SAMPLER_CUBE: nativeint;
+    UNSIGNED_INT_SAMPLER_2D_ARRAY: nativeint;
+    DEPTH_COMPONENT32F: nativeint;
+    DEPTH32F_STENCIL8: nativeint;
+    FLOAT_32_UNSIGNED_INT_24_8_REV: nativeint;
+    FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: nativeint;
+    FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_RED_SIZE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: nativeint;
+    FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: nativeint;
+    FRAMEBUFFER_DEFAULT: nativeint;
+    //DEPTH_STENCIL_ATTACHMENT: nativeint;
+    //DEPTH_STENCIL: nativeint;
+    UNSIGNED_INT_24_8: nativeint;
+    DEPTH24_STENCIL8: nativeint;
+    UNSIGNED_NORMALIZED: nativeint;
+    DRAW_FRAMEBUFFER_BINDING: nativeint;
+    READ_FRAMEBUFFER: nativeint;
+    DRAW_FRAMEBUFFER: nativeint;
+    READ_FRAMEBUFFER_BINDING: nativeint;
+    RENDERBUFFER_SAMPLES: nativeint;
+    FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: nativeint;
+    MAX_COLOR_ATTACHMENTS: nativeint;
+    COLOR_ATTACHMENT1: nativeint;
+    COLOR_ATTACHMENT2: nativeint;
+    COLOR_ATTACHMENT3: nativeint;
+    COLOR_ATTACHMENT4: nativeint;
+    COLOR_ATTACHMENT5: nativeint;
+    COLOR_ATTACHMENT6: nativeint;
+    COLOR_ATTACHMENT7: nativeint;
+    COLOR_ATTACHMENT8: nativeint;
+    COLOR_ATTACHMENT9: nativeint;
+    COLOR_ATTACHMENT10: nativeint;
+    COLOR_ATTACHMENT11: nativeint;
+    COLOR_ATTACHMENT12: nativeint;
+    COLOR_ATTACHMENT13: nativeint;
+    COLOR_ATTACHMENT14: nativeint;
+    COLOR_ATTACHMENT15: nativeint;
+    FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: nativeint;
+    MAX_SAMPLES: nativeint;
+    HALF_FLOAT: nativeint;
+    RG: nativeint;
+    RG_INTEGER: nativeint;
+    R8: nativeint;
+    RG8: nativeint;
+    R16F: nativeint;
+    R32F: nativeint;
+    RG16F: nativeint;
+    RG32F: nativeint;
+    R8I: nativeint;
+    R8UI: nativeint;
+    R16I: nativeint;
+    R16UI: nativeint;
+    R32I: nativeint;
+    R32UI: nativeint;
+    RG8I: nativeint;
+    RG8UI: nativeint;
+    RG16I: nativeint;
+    RG16UI: nativeint;
+    RG32I: nativeint;
+    RG32UI: nativeint;
+    VERTEX_ARRAY_BINDING: nativeint;
+    R8_SNORM: nativeint;
+    RG8_SNORM: nativeint;
+    RGB8_SNORM: nativeint;
+    RGBA8_SNORM: nativeint;
+    SIGNED_NORMALIZED: nativeint;
+    COPY_READ_BUFFER: nativeint;
+    COPY_WRITE_BUFFER: nativeint;
+    COPY_READ_BUFFER_BINDING: nativeint;
+    COPY_WRITE_BUFFER_BINDING: nativeint;
+    UNIFORM_BUFFER: nativeint;
+    UNIFORM_BUFFER_BINDING: nativeint;
+    UNIFORM_BUFFER_START: nativeint;
+    UNIFORM_BUFFER_SIZE: nativeint;
+    MAX_VERTEX_UNIFORM_BLOCKS: nativeint;
+    MAX_FRAGMENT_UNIFORM_BLOCKS: nativeint;
+    MAX_COMBINED_UNIFORM_BLOCKS: nativeint;
+    MAX_UNIFORM_BUFFER_BINDINGS: nativeint;
+    MAX_UNIFORM_BLOCK_SIZE: nativeint;
+    MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: nativeint;
+    MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: nativeint;
+    UNIFORM_BUFFER_OFFSET_ALIGNMENT: nativeint;
+    ACTIVE_UNIFORM_BLOCKS: nativeint;
+    UNIFORM_TYPE: nativeint;
+    UNIFORM_SIZE: nativeint;
+    UNIFORM_BLOCK_INDEX: nativeint;
+    UNIFORM_OFFSET: nativeint;
+    UNIFORM_ARRAY_STRIDE: nativeint;
+    UNIFORM_MATRIX_STRIDE: nativeint;
+    UNIFORM_IS_ROW_MAJOR: nativeint;
+    UNIFORM_BLOCK_BINDING: nativeint;
+    UNIFORM_BLOCK_DATA_SIZE: nativeint;
+    UNIFORM_BLOCK_ACTIVE_UNIFORMS: nativeint;
+    UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: nativeint;
+    UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: nativeint;
+    UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: nativeint;
+    INVALID_INDEX: nativeint;
+    MAX_VERTEX_OUTPUT_COMPONENTS: nativeint;
+    MAX_FRAGMENT_INPUT_COMPONENTS: nativeint;
+    MAX_SERVER_WAIT_TIMEOUT: nativeint;
+    OBJECT_TYPE: nativeint;
+    SYNC_CONDITION: nativeint;
+    SYNC_STATUS: nativeint;
+    SYNC_FLAGS: nativeint;
+    SYNC_FENCE: nativeint;
+    SYNC_GPU_COMMANDS_COMPLETE: nativeint;
+    UNSIGNALED: nativeint;
+    SIGNALED: nativeint;
+    ALREADY_SIGNALED: nativeint;
+    TIMEOUT_EXPIRED: nativeint;
+    CONDITION_SATISFIED: nativeint;
+    WAIT_FAILED: nativeint;
+    SYNC_FLUSH_COMMANDS_BIT: nativeint;
+    VERTEX_ATTRIB_ARRAY_DIVISOR: nativeint;
+    ANY_SAMPLES_PASSED: nativeint;
+    ANY_SAMPLES_PASSED_CONSERVATIVE: nativeint;
+    SAMPLER_BINDING: nativeint;
+    RGB10_A2UI: nativeint;
+    INT_2_10_10_10_REV: nativeint;
+    TRANSFORM_FEEDBACK: nativeint;
+    TRANSFORM_FEEDBACK_PAUSED: nativeint;
+    TRANSFORM_FEEDBACK_ACTIVE: nativeint;
+    TRANSFORM_FEEDBACK_BINDING: nativeint;
+    TEXTURE_IMMUTABLE_FORMAT: nativeint;
+    MAX_ELEMENT_INDEX: nativeint;
+    TEXTURE_IMMUTABLE_LEVELS: nativeint;
+    MAX_CLIENT_WAIT_TIMEOUT_WEBGL: nativeint;
+  public
+
+    // Buffer objects
+    procedure bufferData (target: GLenum; size: GLsizeiptr; usage: GLenum);
+    procedure bufferData (target: GLenum; srcData: TJSBufferSource; usage: GLenum);
+    procedure bufferSubData (target: GLenum; dstByteOffset: GLintptr; srcData: TJSBufferSource);
+
+    // WebGL2
+    procedure bufferData (target: GLenum; srcData: TJSArrayBufferView; usage: GLenum; srcOffset: GLuint; length: GLuint);
+    procedure bufferSubData (target: GLenum; dstByteOffset: GLintptr; srcData: TJSArrayBufferView; length: GLuint);
+    procedure copyBufferSubData (readTarget: GLenum; writeTarget: GLenum; readOffset: GLintptr; size: GLsizeiptr);
+
+    // MapBufferRange, in particular its read-only and write-only modes,
+    // can not be exposed safely to JavaScript. GetBufferSubData
+    // replaces it for the purpose of fetching data back from the GPU.
+    procedure getBufferSubData (target: GLenum; srcByteOffset: GLintptr; dstBuffer: TJSArrayBufferView; dstOffset: GLuint; length: GLuint);
+    
+    // Framebuffer objects
+    procedure blitFramebuffer (srcX0: GLint; srcY0: GLint; srcX1: GLint; srcY1: GLint; dstX0: GLint; dstY0: GLint; dstY1: GLint; mask: GLbitfield; filter: GLenum);
+    procedure framebufferTextureLayer (target: GLenum; attachment: GLenum; texture: TJSWebGLTexture; level: GLint);
+    procedure invalidateFramebuffer (target: GLenum; attachments: GLenumDynArray);
+    procedure invalidateSubFramebuffer (target: GLenum; attachments: GLenumDynArray; y: GLint; width: GLsizei; height: GLsizei);
+    procedure readBuffer (src: GLenum);
+
+    // Renderbuffer objects
+    function getInternalformatParameter (target: GLenum; internalformat: GLenum; pname: GLenum): JSValue;
+    procedure renderbufferStorageMultisample (target: GLenum; samples: GLsizei; internalformat: GLenum; height: GLsizei);
+
+    // Texture objects
+    procedure texStorage2D (target: GLenum; levels: GLsizei; internalformat: GLenum; width: GLsizei);
+    procedure texStorage3D (target: GLenum; levels: GLsizei; internalformat: GLenum; width: GLsizei; depth: GLsizei);
+
+    // WebGL1 legacy entrypoints:
+    procedure texImage2D (target: GLenum; level: GLint; internalformat: GLint; height: GLsizei; border: GLint; format: GLenum; pixels: TJSArrayBufferView);
+    procedure texImage2D (target: GLenum; level: GLint; internalformat: GLint; _type: GLenum; source: TJSTexImageSource);
+    procedure texSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; height: GLsizei; _type: GLenum; pixels: TJSArrayBufferView);
+    procedure texSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; _type: GLenum; source: TJSTexImageSource);
+
+    // WebGL2 entrypoints:
+    procedure texImage2D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum; pboOffset: GLintptr);
+    procedure texImage2D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum);
+    procedure texImage2D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum; srcData: TJSArrayBufferView);
+    procedure texImage3D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; border: GLint; format: GLenum; _type: GLenum; pboOffset: GLintptr);
+    procedure texImage3D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; border: GLint; format: GLenum; _type: GLenum);
+    procedure texImage3D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; border: GLint; format: GLenum; _type: GLenum; srcData: TJSArrayBufferView);
+    //procedure texImage3D (target: GLenum; level: GLint; internalformat: GLint; width: GLsizei; height: GLsizei; border: GLint; format: GLenum; _type: GLenum; srcData: TJSArrayBufferView);
+    procedure texSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; format: GLenum; _type: GLenum; pboOffset: GLintptr);
+    procedure texSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; format: GLenum; _type: GLenum);
+    procedure texSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; format: GLenum; _type: GLenum; srcData: TJSArrayBufferView);
+    procedure texSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; height: GLsizei; depth: GLsizei; format: GLenum; _type: GLenum);
+    //procedure texSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; height: GLsizei; depth: GLsizei; format: GLenum; _type: GLenum);
+    procedure texSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; height: GLsizei; depth: GLsizei; format: GLenum; _type: GLenum; srcOffset: GLuint);
+    procedure copyTexSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; y: GLint; width: GLsizei; height: GLsizei);
+    procedure compressedTexImage2D (target: GLenum; level: GLint; internalformat: GLenum; width: GLsizei; border: GLint; imageSize: GLsizei; offset: GLintptr);
+    procedure compressedTexImage2D (target: GLenum; level: GLint; internalformat: GLenum; width: GLsizei; border: GLint; srcData: TJSArrayBufferView; srcOffset: GLuint; srcLengthOverride: GLuint);
+    procedure compressedTexImage3D (target: GLenum; level: GLint; internalformat: GLenum; width: GLsizei; depth: GLsizei; border: GLint; imageSize: GLsizei; offset: GLintptr);
+    procedure compressedTexImage3D (target: GLenum; level: GLint; internalformat: GLenum; width: GLsizei; depth: GLsizei; border: GLint; srcData: TJSArrayBufferView; srcOffset: GLuint; srcLengthOverride: GLuint);
+    procedure compressedTexSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; height: GLsizei; format: GLenum; imageSize: GLsizei; offset: GLintptr);
+    procedure compressedTexSubImage2D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; height: GLsizei; format: GLenum; srcOffset: GLuint; srcLengthOverride: GLuint);
+    procedure compressedTexSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; height: GLsizei; depth: GLsizei; imageSize: GLsizei; offset: GLintptr);
+    procedure compressedTexSubImage3D (target: GLenum; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; height: GLsizei; depth: GLsizei; srcData: TJSArrayBufferView; srcOffset: GLuint; srcLengthOverride: GLuint);
+
+    // Programs and shaders
+    function getFragDataLocation (_program: TJSWebGLProgram; name: TJSDOMString): GLint;
+
+    // Uniforms
+    procedure uniform1ui (location: TJSWebGLUniformLocation; v0: GLuint);
+    procedure uniform2ui (location: TJSWebGLUniformLocation; v0: GLuint; v1: GLuint);
+    procedure uniform3ui (location: TJSWebGLUniformLocation; v0: GLuint; v1: GLuint; v2: GLuint);
+    procedure uniform4ui (location: TJSWebGLUniformLocation; v0: GLuint; v1: GLuint; v2: GLuint; v3: GLuint);
+    procedure uniform1fv (location: TJSWebGLUniformLocation; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform2fv (location: TJSWebGLUniformLocation; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform3fv (location: TJSWebGLUniformLocation; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform4fv (location: TJSWebGLUniformLocation; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform1iv (location: TJSWebGLUniformLocation; data: TJSInt32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform2iv (location: TJSWebGLUniformLocation; data: TJSInt32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform3iv (location: TJSWebGLUniformLocation; data: TJSInt32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform4iv (location: TJSWebGLUniformLocation; data: TJSInt32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform1uiv (location: TJSWebGLUniformLocation; data: TJSUint32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform2uiv (location: TJSWebGLUniformLocation; data: TJSUint32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform3uiv (location: TJSWebGLUniformLocation; data: TJSUint32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniform4uiv (location: TJSWebGLUniformLocation; data: TJSUint32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix2fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix3x2fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix4x2fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix2x3fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix3fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix4x3fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix2x4fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix3x4fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+    procedure uniformMatrix4fv (location: TJSWebGLUniformLocation; transpose: GLboolean; data: TJSFloat32List; srcOffset: GLuint; srcLength: GLuint);
+
+    // Vertex attribs
+    procedure vertexAttribI4i (index: GLuint; x: GLint; y: GLint; z: GLint; w: GLint);
+    procedure vertexAttribI4iv (index: GLuint; values: TJSInt32List);
+    procedure vertexAttribI4ui (index: GLuint; x: GLuint; y: GLuint; z: GLuint; w: GLuint);
+    procedure vertexAttribI4uiv (index: GLuint; values: TJSUint32List);
+    procedure vertexAttribIPointer (index: GLuint; size: GLint; _type: GLenum; stride: GLsizei; offset: GLintptr);
+
+    // Writing to the drawing buffer
+    procedure vertexAttribDivisor (index: GLuint; divisor: GLuint);
+    procedure drawArraysInstanced (mode: GLenum; first: GLint; count: GLsizei; instanceCount: GLsizei);
+    procedure drawElementsInstanced (mode: GLenum; count: GLsizei; _type: GLenum; offset: GLintptr; instanceCount: GLsizei);
+    procedure drawRangeElements (mode: GLenum; start: GLuint; _end: GLuint; count: GLsizei; _type: GLenum; offset: GLintptr);
+
+    // Reading back pixels
+
+    // WebGL1:
+    procedure readPixels (x: GLint; y: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum);
+    // WebGL2:
+    //procedure readPixels (x: GLint; y: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum);
+    procedure readPixels (x: GLint; y: GLint; width: GLsizei; height: GLsizei; format: GLenum; _type: GLenum; dstOffset: GLuint);
+
+    // Multiple Render Targets */
+    procedure drawBuffers (buffers: GLenumDynArray);
+    procedure clearBufferfv (buffer: GLenum; drawbuffer: GLint; values: TJSFloat32List; srcOffset: GLuint);
+    procedure clearBufferiv (buffer: GLenum; drawbuffer: GLint; values: TJSInt32List; srcOffset: GLuint);
+    procedure clearBufferuiv (buffer: GLenum; drawbuffer: GLint; values: TJSUint32List; srcOffset: GLuint);
+    procedure clearBufferfi (buffer: GLenum; drawbuffer: GLint; depth: GLfloat; stencil: GLint);
+
+    // Query Objects
+    function createQuery: TJSWebGLQuery;
+    procedure deleteQuery (query: TJSWebGLQuery);
+    function isQuery (query: TJSWebGLQuery): GLboolean;
+    procedure beginQuery (target: GLenum; query: TJSWebGLQuery);
+    procedure endQuery (target: GLenum);
+    function getQuery (target: GLenum; pname: GLenum): TJSWebGLQuery;
+    function getQueryParameter (query: TJSWebGLQuery; pname: GLenum): JSValue;
+
+    // Sampler Objects
+    function createSampler: TJSWebGLSampler;
+    procedure deleteSampler (sampler: TJSWebGLSampler);
+    function isSampler (sampler: TJSWebGLSampler): GLboolean;
+    procedure bindSampler (_unit: GLuint; sampler: TJSWebGLSampler);
+    procedure samplerParameteri (sampler: TJSWebGLSampler; pname: GLenum; param: GLint);
+    procedure samplerParameterf (sampler: TJSWebGLSampler; pname: GLenum; param: GLfloat);
+    function getSamplerParameter (sampler: TJSWebGLSampler; pname: GLenum): JSValue;
+
+    // Sync objects
+    function fenceSync (condition: GLenum; flags: GLbitfield): TJSWebGLSync;
+    function isSync (sync: TJSWebGLSync): GLboolean;
+    procedure deleteSync (sync: TJSWebGLSync);
+    function clientWaitSync (sync: TJSWebGLSync; flags: GLbitfield; timeout: GLuint64): GLenum;
+    procedure waitSync (sync: TJSWebGLSync; flags: GLbitfield; timeout: GLint64);
+    function getSyncParameter (sync: TJSWebGLSync; pname: GLenum): JSValue;
+
+    // Transform Feedback
+    function createTransformFeedback: TJSWebGLTransformFeedback;
+    procedure deleteTransformFeedback (tf: TJSWebGLTransformFeedback);
+    function isTransformFeedback (tf: TJSWebGLTransformFeedback): GLboolean;
+    procedure bindTransformFeedback (target: GLenum; tf: TJSWebGLTransformFeedback);
+    procedure beginTransformFeedback (primitiveMode: GLenum);
+    procedure endTransformFeedback;
+    procedure transformFeedbackVaryings (_program: TJSWebGLProgram; varyings: array of TJSDOMString; bufferMode: GLenum);
+    function getTransformFeedbackVarying (_program: TJSWebGLProgram; index: GLuint): TJSWebGLActiveInfo;
+    procedure pauseTransformFeedback;
+    procedure resumeTransformFeedback;
+
+    // Uniform Buffer Objects and Transform Feedback Buffers
+    procedure bindBufferBase (target: GLenum; index: GLuint; buffer: TJSWebGLBuffer);
+    procedure bindBufferRange (target: GLenum; index: GLuint; buffer: TJSWebGLBuffer; offset: GLintptr; size: GLsizeiptr);
+    function getIndexedParameter (target: GLenum; index: GLuint): JSValue;
+    function getUniformIndices (_program: TJSWebGLProgram; uniformNames: array of TJSDOMString): GLuintDynArray;
+    function getActiveUniforms (_program: TJSWebGLProgram; uniformIndices: GLuintDynArray; pname: GLenum): JSValue;
+    function getUniformBlockIndex (_program: TJSWebGLProgram; uniformBlockName: TJSDOMString): GLuint;
+    function getActiveUniformBlockParameter (_program: TJSWebGLProgram; uniformBlockIndex: GLuint; pname: GLenum): JSValue;
+    function getActiveUniformBlockName (_program: TJSWebGLProgram; uniformBlockIndex: GLuint): TJSDOMString;
+    procedure uniformBlockBinding (_program: TJSWebGLProgram; uniformBlockIndex: GLuint; uniformBlockBinding: GLuint);
+
+    // Vertex Array Objects
+    function createVertexArray: TJSWebGLVertexArrayObject;
+    procedure deleteVertexArray (vertexArray: TJSWebGLVertexArrayObject);
+    function isVertexArray (vertexArray: TJSWebGLVertexArrayObject): GLboolean;
+    procedure bindVertexArray (_array: TJSWebGLVertexArrayObject);
+end;
+
+TJSWebGL2RenderingContext = class external name 'WebGL2RenderingContext' (TJSWebGL2RenderingContextBase);
+
+implementation
+end.