diff --git a/versions/c1pjs/1.50.4/c1p.js b/versions/c1pjs/1.50.4/c1p.js new file mode 100644 index 0000000000..8da4bcd0a6 --- /dev/null +++ b/versions/c1pjs/1.50.4/c1p.js @@ -0,0 +1,203 @@ +(function(){/* + https://www.pcjs.org/modules/shared/lib/dumpapi.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/reportapi.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/strlib.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/weblib.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/embed.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/defines.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/usrlib.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/shared/lib/component.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/defines.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/panel.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/cpu.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/rom.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/ram.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/keyboard.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/video.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/serial.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/disk.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/debugger.js (C) Jeff Parsons 2012-2018 + https://www.pcjs.org/modules/c1pjs/lib/computer.js (C) Jeff Parsons 2012-2018 +*/ +var f,aa="function"==typeof Object.create?Object.create:function(a){function b(){}b.prototype=a;return new b},ba;if("function"==typeof Object.setPrototypeOf)ba=Object.setPrototypeOf;else{var ca;a:{var da={Jb:!0},ea={};try{ea.__proto__=da;ca=ea.Jb;break a}catch(a){}ca=!1}ba=ca?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null}var fa=ba; +function p(a,b){a.prototype=aa(b.prototype);a.prototype.constructor=a;if(fa)fa(a,b);else for(var c in b)if("prototype"!=c)if(Object.defineProperties){var d=Object.getOwnPropertyDescriptor(b,c);d&&Object.defineProperty(a,c,d)}else a[c]=b[c];a.Se=b.prototype} +for(var ha="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)},q="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,ia=["Math","trunc"],ja=0;jaa?-b:b};na!=ma&&null!=na&&ha(q,la,{configurable:!0,writable:!0,value:na}); +function r(a,b,c){b?9=b?4:4294967295>=b?8:9);c=c?"0x":"";var d=void 0===d?0:d;var e="";isNaN(a)?a=null:null!=a&&(0>a&&-1a&&(a+=Math.pow(16,b)),a>=Math.pow(16,b)&&(b=Math.ceil(Math.log(a)/Math.log(16))));for(var g=d||-1;0=h?48:55;e=String.fromCharCode(h)+e;a=Math.trunc(a/16)}g--}return(void 0===c?"":c)+e}function u(a){return r(a,2,!0)}function w(a){return r(a,4,!0)} +function oa(a){var b=a,c=a.lastIndexOf("/");0<=c&&(b=a.substr(c+1));c=b.indexOf("\x26");0"']/g,function(a){return ra[a]})}var ra={"\x26":"\x26amp;","\x3c":"\x26lt;","\x3e":"\x26gt;",'"':"\x26quot;","'":"\x26#039;"},sa=Date.now||function(){return+new Date}; +function x(a,b){var c=null,d=!0;c=void 0===c?"text":c;d=void 0===d?!1:d;var e=0,g=null;if("object"==typeof resources&&(g=resources[a]))b&&b(a,g,e);else if(d&&"function"==typeof resources)resources(a,function(c,d){b&&b(a,c,d)});else{var h=window.XMLHttpRequest?new window.XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP"),l=!1,k="string"===typeof h.responseType,m=function(){if(4!==h.readyState)return null;try{g=l?h.response:h.responseText}catch(t){}if(null==g||200!=h.status&&(h.status||!g.length|| +"file:"!=(window?window.location.protocol:"file:")))e=h.status||-1;b&&b(a,g,e);return[g,e]};d&&(h.onreadystatechange=m);if(c&&"object"==typeof c){k="";for(var n in c)c.hasOwnProperty(n)&&(k&&(k+="\x26"),k+=n+"\x3d"+encodeURIComponent(c[n]));k=k.replace(/%20/g,"+");h.open("POST",a,d);h.setRequestHeader("Content-type","application/x-www-form-urlencoded");h.send(k)}else h.open("GET",a,d),"arraybuffer"==c&&(k?(l=!0,h.responseType=c):h.overrideMimeType("text/plain; charset\x3dx-user-defined")),h.send(); +d||(h.readyState=4,m())}}function y(a){if(window){var b=window?window.navigator.userAgent:"";return"iOS"==a&&!!b.match(/(iPod|iPhone|iPad)/)&&!!b.match(/AppleWebKit/)||"MSIE"==a&&!!b.match(/(MSIE|Trident)/)||0<=b.indexOf(a)}return!1}function ta(a,b,c){function d(){--a;0<=a&&(b()||(a=0));0a||(this.kb=this.id.substr(0,a));this.C={ready:!1,Ga:!1,$a:!1,Re:!1,N:!1,Oe:!1,error:!1};this.bb=null;this.C.error=!1;this.la=c||0;this.u=this.s=this.U=null;E.push(this)}function B(a){window&&window.alert(a)} +function Ca(a,b){a.value+=b;b=a.value;8192d?c+=a+"\n":c=c.substr(0,d)+(a+".")+ +c.substr(d+a.length);8192Sa(a.P,b,c,d,e)&&(a.pa>b&&(a.pa=b),a.qa=a.P[d][0]&&b<=a.P[d][1]&&a.P[d][3].call(a.P[d][2],b,c)}function P(a,b,c,d,e){0>Sa(a.J,b,c,d,e)&&(a.ea>b&&(a.ea=b),a.fa=a.J[d][0]&&b<=a.J[d][1]&&a.J[d][3].call(a.J[d][2],b,c)}function Sa(a,b,c,d,e){for(var g=0;ga.Oa&&a.K&&(d=a.K);d>a.Ea&&2>a.speed&&(d=a.Ea);a.hb=Math.round(1E3/30);a.ja=Math.floor(1E6/c*d);a.sa=Math.floor(1E6/30*d);a.Xa=Math.floor(1E6/a.Ka*d);a.Wa=Math.floor(1E6/a.Ia*d);b||(a.R=a.sa,a.Z=a.Xa,a.X=a.Wa);a.Ha=0} +function Ya(a){var b=sa(),c=a.hb;a.$&&(c=Math.round(c*a.$/a.sa));c-=b-a.Pe;if(b-=a.Ua)a.K=Math.round(a.V/(100*b))/10,864E5<=b&&O(a);0>c?c=0:1==a.speed?a.K<=a.Ea&&(c=0):2==a.speed&&(c=0);a.Ha+=a.$;return c} +f.ua=function(){if(L(this,!0)){this.C.na||(O(this),this.U&&this.U.start(),this.C.na=!0,this.H.run&&(this.H.run.innerHTML="Halt"),this.ca());1E6<=this.Ha&&Va(this,!0);this.$=0;this.Pe=sa();try{do{this.step(this.ja);var a=this.L-this.D;this.V+=a;this.$+=a;this.L=this.D=0;this.Z-=this.ja;0>=this.Z&&(this.Z+=this.Xa,this.ab());this.X-=this.ja;0>=this.X&&(this.X+=this.Wa,Wa(this));this.R-=this.ja;if(0>=this.R){this.R+=this.sa;break}}while(this.C.na)}catch(b){this.aa();this.update();L(this,!1);Ka(this, +b.stack||b.message);return}setTimeout(function(a){return function(){a.ua()}}(this),Ya(this))}else this.update(),this.U&&this.U.stop(this.Ua,this.V)}; +f.step=function(a){var b=!0;this.b=this.g=-1;var c;if(c=a&&this.u)c=this.u,c=0=d.wa.length&&(d.Ja=0));d=!!g}if(d){b=void 0;this.aa();break}this.f++;this.o[a].call(this);if(0<=this.b){this.b>=this.pa&&this.b<=this.qa&&Ta(this,this.b,this.f);if(d=c)d=this.u,e=!1,Za(d,this.b,d.va,"read")&&(e=!0),d=!!e;if(d){b=!1;this.aa(); +break}this.b=-1}else if(0<=this.g){this.g>=this.ea&&this.g<=this.fa&&Ua(this,this.g,this.f);if(d=c){d=this.u;e=this.g;g=this.a[this.g];var h=!1;(g&255)!=g&&(d.l("invalid value at "+w(e)+": "+g),h=!0);Za(d,e,d.xa,"write")&&(h=!0);d=!!h}if(d){b=!1;this.aa();break}this.g=-1}this.D-=this.fb[a]}while(0>1)&128?64:0;b|=a.i&128?128:0;return a.B&60|b}function bb(a){a.B|=8;a.o[97]=a.Tb;a.o[101]=a.Vb;a.o[105]=a.Sb;a.o[109]=a.Pb;a.o[113]=a.Ub;a.o[117]=a.Wb;a.o[121]=a.Rb;a.o[125]=a.Qb;a.o[225]=a.ie;a.o[229]=a.ke;a.o[233]=a.he;a.o[237]=a.ee;a.o[241]=a.je;a.o[245]=a.le;a.o[249]=a.ge;a.o[253]=a.fe} +function cb(a){a.B&=-9;a.o[97]=a.qb;a.o[101]=a.sb;a.o[105]=a.pb;a.o[109]=a.mb;a.o[113]=a.rb;a.o[117]=a.tb;a.o[121]=a.ob;a.o[125]=a.nb;a.o[225]=a.yb;a.o[229]=a.Ab;a.o[233]=a.xb;a.o[237]=a.ub;a.o[241]=a.zb;a.o[245]=a.Bb;a.o[249]=a.wb;a.o[253]=a.vb}function R(a,b,c){var d=a.c&256?1:0,e=(b&15)+(c&15)+d;10<=e&&(e=e+6&15|16);e+=(b&240)+(c&240);a.A=b^c;a.v=e;a.i=e&255;160<=e&&(e+=96);512<=e&&(e-=256);a.c=e;a.j=b+c+d&255;a.D--;return e&255} +function S(a,b,c){var d=a.c&256?0:1,e=(b&15)-(c&15)-d;0>e&&(e=(e-6&15)-16);e+=(b&240)-(c&240);0>e&&(e-=96);a.i=a.j=(a.c=b-c-d)&255;a.A=b^c;a.v=a.c;a.c^=256;a.D--;return e&255}function Pa(a){a.h=0;a.w=0;a.F=0;a.G=256;a.B=0;a.i=0;a.j=0;a.A=0;a.v=0;a.c=0;a.f=0;a.b=-1;a.g=-1;a.K=0;a.V=a.L=a.D=0} +f.rc=function(){this.f++;this.a[this.G--]=this.f>>8;this.G|=256;this.a[this.G--]=this.f&255;this.G|=256;this.B|=16;this.B=Xa(this);this.a[this.G--]=this.B;this.G|=256;this.B&=239;this.b=65534;this.f=this.a[this.b]|this.a[this.b+1]<<8};f.Ld=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.i=this.j=this.h|=this.a[this.b]};f.Nd=function(){this.b=this.a[this.f++];this.i=this.j=this.h|=this.a[this.b]}; +f.hc=function(){this.g=this.a[this.f++];this.c=this.a[this.g]<<1;this.i=this.j=this.a[this.g]=this.c&255};f.Qd=function(){this.B=Xa(this);this.a[this.G--]=this.B;this.G|=256};f.Kd=function(){this.b=this.f++;this.i=this.j=this.h|=this.a[this.b]};f.gc=function(){this.c=this.h<<1;this.i=this.j=this.h=this.c&255};f.Hd=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.h|=this.a[this.b]}; +f.ec=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.a[this.g]<<1;this.i=this.j=this.a[this.g]=this.c&255};f.qc=function(){this.f+=(this.i&128?0:(this.D--,this.a[this.f]<<24>>24))+1};f.Md=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.i=this.j=this.h|=this.a[this.b]};f.Od=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.h|=this.a[this.b]}; +f.ic=function(){this.g=this.a[this.f++]+this.w&255;this.c=this.a[this.g]<<1;this.i=this.j=this.a[this.g]=this.c&255};f.uc=function(){this.c=0};f.Jd=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.h|=this.a[this.b]};f.Id=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.h|=this.a[this.b]};f.fc=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.a[this.g]<<1;this.i=this.j=this.a[this.g]=this.c&255}; +f.hd=function(){this.b=this.f++;this.a[this.G--]=this.f>>8;this.G|=256;this.a[this.G--]=this.f&255;this.G|=256;this.f=this.a[this.b]|this.a[this.b+1]<<8};f.ac=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.i=this.j=this.h&=this.a[this.b]};f.nc=function(){this.b=this.a[this.f++];this.j=this.h&this.a[this.b];this.i=this.i&127|this.a[this.b]&128;this.v=0;this.A=this.a[this.b]&64?128:0};f.cc=function(){this.b=this.a[this.f++];this.i=this.j=this.h&=this.a[this.b]}; +f.Wd=function(){this.g=this.a[this.f++];this.c=this.c&65280|this.a[this.g];this.c<<=1;this.c=this.c&65534|(this.c&512?1:0);this.i=this.j=this.a[this.g]=this.c&255};f.Sd=function(){this.G=this.G+1&255|256;this.B=this.a[this.G];this.c=this.B&1?256:0;this.j=this.B&2?0:1;this.i=this.B&128;this.v=0;this.A=this.B&64?128:0};f.$b=function(){this.b=this.f++;this.i=this.j=this.h&=this.a[this.b]}; +f.Vd=function(){this.c=this.c&65280|this.h;this.c<<=1;this.c=this.c&65534|(this.c&512?1:0);this.i=this.j=this.h=this.c&255};f.mc=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.j=this.h&this.a[this.b];this.i=this.i&127|this.a[this.b]&128;this.v=0;this.A=this.a[this.b]&64?128:0};f.Xb=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.h&=this.a[this.b]}; +f.Td=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.c&65280|this.a[this.g];this.c<<=1;this.c=this.c&65534|(this.c&512?1:0);this.i=this.j=this.a[this.g]=this.c&255};f.oc=function(){this.f+=(this.i&128?(this.D--,this.a[this.f]<<24>>24):0)+1};f.bc=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.i=this.j=this.h&=this.a[this.b]};f.dc=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.h&=this.a[this.b]}; +f.Xd=function(){this.g=this.a[this.f++]+this.w&255;this.c=this.c&65280|this.a[this.g];this.c<<=1;this.c=this.c&65534|(this.c&512?1:0);this.i=this.j=this.a[this.g]=this.c&255};f.me=function(){this.c=256};f.Zb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.h&=this.a[this.b]};f.Yb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.h&=this.a[this.b]}; +f.Ud=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.c&65280|this.a[this.g];this.c<<=1;this.c=this.c&65534|(this.c&512?1:0);this.i=this.j=this.a[this.g]=this.c&255};f.ce=function(){this.G=this.G+1&255|256;this.B=this.a[this.G];this.c=this.B&1?256:0;this.j=this.B&2?0:1;this.i=this.B&128;this.v=0;this.A=this.B&64?128:0;this.G=this.G+2&255|256;this.f=this.a[this.G-1|256]|this.a[this.G]<<8}; +f.Wc=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.i=this.j=this.h^=this.a[this.b]};f.Yc=function(){this.b=this.a[this.f++];this.i=this.j=this.h^=this.a[this.b]};f.Ed=function(){this.g=this.a[this.f++];this.c=this.c&65279|(this.a[this.g]&1?256:0);this.a[this.g]=(this.c=this.c&65280|this.a[this.g]>>1)&255;this.i=this.j=this.c&255};f.Pd=function(){this.a[this.G--]=this.h;this.G|=256};f.Vc=function(){this.b=this.f++;this.i=this.j=this.h^=this.a[this.b]}; +f.Dd=function(){this.c=this.c&65279|(this.h&1?256:0);this.h=(this.c=this.c&65280|this.h>>1)&255;this.i=this.j=this.c&255};f.gd=function(){this.b=this.f;this.f=this.a[this.b]|this.a[this.b+1]<<8};f.Sc=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.h^=this.a[this.b]};f.Bd=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.c&65279|(this.a[this.g]&1?256:0);this.a[this.g]=(this.c=this.c&65280|this.a[this.g]>>1)&255;this.i=this.j=this.c&255}; +f.sc=function(){this.f+=((this.v&255^this.A^this.v>>1)&128?0:(this.D--,this.a[this.f]<<24>>24))+1};f.Xc=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.i=this.j=this.h^=this.a[this.b]};f.Zc=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.h^=this.a[this.b]};f.Fd=function(){this.g=this.a[this.f++]+this.w&255;this.c=this.c&65279|(this.a[this.g]&1?256:0);this.a[this.g]=(this.c=this.c&65280|this.a[this.g]>>1)&255;this.i=this.j=this.c&255}; +f.wc=function(){this.B&=251};f.Uc=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.h^=this.a[this.b]};f.Tc=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.h^=this.a[this.b]};f.Cd=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.c&65279|(this.a[this.g]&1?256:0);this.a[this.g]=(this.c=this.c&65280|this.a[this.g]>>1)&255;this.i=this.j=this.c&255}; +f.de=function(){this.G=this.G+2&255|256;this.f=(this.a[this.G-1|256]|this.a[this.G]<<8)+1};f.qb=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Tb=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.h=R(this,this.h,this.a[this.b])}; +f.sb=function(){this.b=this.a[this.f++];this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Vb=function(){this.b=this.a[this.f++];this.h=R(this,this.h,this.a[this.b])};f.ae=function(){this.g=this.a[this.f++];this.c=this.c&65280|this.a[this.g];this.c=this.c&65023|(this.c&1?512:0);this.c>>=1;this.i=this.j=this.a[this.g]=this.c&255};f.Rd=function(){this.G=this.G+1&255|256;this.i=this.j=this.h=this.a[this.G]}; +f.pb=function(){this.b=this.f++;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Sb=function(){this.b=this.f++;this.h=R(this,this.h,this.a[this.b])};f.$d=function(){this.c=this.c&65280|this.h;this.c=this.c&65023|(this.c&1?512:0);this.c>>=1;this.i=this.j=this.h=this.c&255};f.fd=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.f=this.a[this.b]|this.a[this.b+1]<<8}; +f.mb=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Pb=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.h=R(this,this.h,this.a[this.b])};f.Yd=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.c&65280|this.a[this.g];this.c=this.c&65023|(this.c&1?512:0);this.c>>=1;this.i=this.j=this.a[this.g]=this.c&255}; +f.tc=function(){this.f+=((this.v&255^this.A^this.v>>1)&128?(this.D--,this.a[this.f]<<24>>24):0)+1};f.rb=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Ub=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.h=R(this,this.h,this.a[this.b])}; +f.tb=function(){this.b=this.a[this.f++]+this.w&255;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Wb=function(){this.b=this.a[this.f++]+this.w&255;this.h=R(this,this.h,this.a[this.b])};f.be=function(){this.g=this.a[this.f++]+this.w&255;this.c=this.c&65280|this.a[this.g];this.c=this.c&65023|(this.c&1?512:0);this.c>>=1;this.i=this.j=this.a[this.g]=this.c&255};f.oe=function(){this.B|=4}; +f.ob=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255};f.Rb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.h=R(this,this.h,this.a[this.b])};f.nb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.h+this.a[this.b]+(this.c&256?1:0);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255}; +f.Qb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.h=R(this,this.h,this.a[this.b])};f.Zd=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.c&65280|this.a[this.g];this.c=this.c&65023|(this.c&1?512:0);this.c>>=1;this.i=this.j=this.a[this.g]=this.c&255};f.se=function(){this.g=this.a[this.f++]+this.w&255;this.g=this.a[this.g]|this.a[this.g+1]<<8;this.a[this.g]=this.h};f.Ae=function(){this.g=this.a[this.f++];this.a[this.g]=this.F}; +f.ue=function(){this.g=this.a[this.f++];this.a[this.g]=this.h};f.xe=function(){this.g=this.a[this.f++];this.a[this.g]=this.w};f.Rc=function(){this.i=this.j=this.F=this.F-1&255};f.Ge=function(){this.i=this.j=this.h=this.w};f.ze=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.a[this.g]=this.F};f.pe=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.a[this.g]=this.h};f.we=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.a[this.g]=this.w}; +f.jc=function(){this.f+=(this.c&256?0:(this.D--,this.a[this.f]<<24>>24))+1};f.te=function(){this.g=this.a[this.f++];this.g=(this.a[this.g]|this.a[this.g+1]<<8)+this.F;this.a[this.g]=this.h};f.Be=function(){this.g=this.a[this.f++]+this.w&255;this.a[this.g]=this.F};f.ve=function(){this.g=this.a[this.f++]+this.w&255;this.a[this.g]=this.h};f.ye=function(){this.g=this.a[this.f++]+this.F&255;this.a[this.g]=this.w};f.Ie=function(){this.i=this.j=this.h=this.F}; +f.re=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.a[this.g]=this.h};f.He=function(){this.G=this.w|256};f.qe=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.a[this.g]=this.h};f.yd=function(){this.b=this.f++;this.i=this.j=this.F=this.a[this.b]};f.nd=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.i=this.j=this.h=this.a[this.b]};f.td=function(){this.b=this.f++;this.i=this.j=this.w=this.a[this.b]}; +f.zd=function(){this.b=this.a[this.f++];this.i=this.j=this.F=this.a[this.b]};f.pd=function(){this.b=this.a[this.f++];this.i=this.j=this.h=this.a[this.b]};f.ud=function(){this.b=this.a[this.f++];this.i=this.j=this.w=this.a[this.b]};f.Ee=function(){this.i=this.j=this.F=this.h};f.md=function(){this.b=this.f++;this.i=this.j=this.h=this.a[this.b]};f.De=function(){this.i=this.j=this.w=this.h};f.wd=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.F=this.a[this.b]}; +f.jd=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.h=this.a[this.b]};f.rd=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.w=this.a[this.b]};f.kc=function(){this.f+=(this.c&256?(this.D--,this.a[this.f]<<24>>24):0)+1};f.od=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.i=this.j=this.h=this.a[this.b]};f.Ad=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.F=this.a[this.b]}; +f.qd=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.h=this.a[this.b]};f.vd=function(){this.b=this.a[this.f++]+this.F&255;this.i=this.j=this.w=this.a[this.b]};f.xc=function(){this.A=this.v=0};f.ld=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.h=this.a[this.b]};f.Fe=function(){this.i=this.j=this.w=this.G&255};f.xd=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.F=this.a[this.b]}; +f.kd=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.h=this.a[this.b]};f.sd=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.w=this.a[this.b]};f.Kc=function(){this.b=this.f++;this.i=this.j=this.c=this.F-this.a[this.b];this.c^=256};f.Cc=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256}; +f.Lc=function(){this.b=this.a[this.f++];this.i=this.j=this.c=this.F-this.a[this.b];this.c^=256};f.Ec=function(){this.b=this.a[this.f++];this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Oc=function(){this.g=this.a[this.f++];this.i=this.j=this.a[this.g]=this.a[this.g]-1&255};f.ed=function(){this.i=this.j=this.F=this.F+1&255};f.Bc=function(){this.b=this.f++;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Qc=function(){this.i=this.j=this.w=this.w-1&255}; +f.Jc=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.c=this.F-this.a[this.b];this.c^=256};f.yc=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Mc=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.a[this.g]=this.a[this.g]-1&255};f.pc=function(){this.f+=(this.j&255?(this.D--,this.a[this.f]<<24>>24):0)+1}; +f.Dc=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Fc=function(){this.b=this.a[this.f++]+this.w&255;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Pc=function(){this.g=this.a[this.f++]+this.w&255;this.i=this.j=this.a[this.g]=this.a[this.g]-1&255};f.vc=function(){cb(this)}; +f.Ac=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.zc=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.c=this.h-this.a[this.b];this.c^=256};f.Nc=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.a[this.g]=this.a[this.g]-1&255};f.Hc=function(){this.b=this.f++;this.i=this.j=this.c=this.w-this.a[this.b];this.c^=256}; +f.yb=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.ie=function(){this.b=this.a[this.f++]+this.w&255;this.b=this.a[this.b]|this.a[this.b+1]<<8;this.h=S(this,this.h,this.a[this.b])};f.Ic=function(){this.b=this.a[this.f++];this.i=this.j=this.c=this.w-this.a[this.b];this.c^=256}; +f.Ab=function(){this.b=this.a[this.f++];this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.ke=function(){this.b=this.a[this.f++];this.h=S(this,this.h,this.a[this.b])};f.bd=function(){this.g=this.a[this.f++];this.i=this.j=this.a[this.g]=this.a[this.g]+1&255};f.dd=function(){this.i=this.j=this.w=this.w+1&255}; +f.xb=function(){this.b=this.f++;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.he=function(){this.b=this.f++;this.h=S(this,this.h,this.a[this.b])};f.Gd=function(){};f.Gc=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.c=this.w-this.a[this.b];this.c^=256}; +f.ub=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.ee=function(){this.b=this.a[this.f++]|this.a[this.f++]<<8;this.h=S(this,this.h,this.a[this.b])};f.$c=function(){this.g=this.a[this.f++]|this.a[this.f++]<<8;this.i=this.j=this.a[this.g]=this.a[this.g]+1&255};f.lc=function(){this.f+=(this.j&255?0:(this.D--,this.a[this.f]<<24>>24))+1}; +f.zb=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.je=function(){this.b=this.a[this.f++];this.b=(this.a[this.b]|this.a[this.b+1]<<8)+this.F;this.h=S(this,this.h,this.a[this.b])}; +f.Bb=function(){this.b=this.a[this.f++]+this.w&255;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.le=function(){this.b=this.a[this.f++]+this.w&255;this.h=S(this,this.h,this.a[this.b])};f.cd=function(){this.g=this.a[this.f++]+this.w&255;this.i=this.j=this.a[this.g]=this.a[this.g]+1&255};f.ne=function(){bb(this)}; +f.wb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.ge=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.F;this.h=S(this,this.h,this.a[this.b])}; +f.vb=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.c=this.h-this.a[this.b]-(this.c&256?0:1);this.A=this.h^this.a[this.b];this.v=this.c;this.i=this.j=this.h=this.c&255;this.c^=256};f.fe=function(){this.b=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.h=S(this,this.h,this.a[this.b])};f.ad=function(){this.g=(this.a[this.f++]|this.a[this.f++]<<8)+this.w;this.i=this.j=this.a[this.g]=this.a[this.g]+1&255}; +f.Ce=function(){var a=this.a[this.f++];switch(a){case 0:this.l("HALT");this.aa();break;case 1:a=this.f;for(var b="";a=c&&(c+=32);a.B=a.B.substr(1);jb(a,c)}0=b&&(b+=32),lb(a,b),mb(a,b,!0,0)&&(2==a.s.speed?mb(a,b,!1,1):(c=!1,a.v[b]&&(clearTimeout(a.v[b]),c=!0),c=kb(a,c),a.v[a.A=b]=setTimeout(function(a){return function(){mb(a,b,!1,3)}}(a),c),a.u&&M(a.u,a.u.ya)&&a.u.message("keyPressSimulate("+u(b)+"): setTimeout()")),c=!0));a.u&&M(a.u,a.u.ya)&&a.u.message("keyPressSimulate("+u(b)+"): "+(c?"true":"false"));return c} +function mb(a,b,c,d){var e=!1;c||(a.v[b]=null,a.A==b&&(a.A=0));var g=0,h=a.b[b];void 0===h&&(1<=b&&26>=b&&(b+=64,g=a.K),h=a.b[b]);void 0!==h&&(b=h>>12,e=h>>8&15,g||(g=h&255),c?(a.o[b]|=1<b||8192<=b));b&&(b=a.L.shift(),void 0!==b&&(a.fa=b),a.V=0,a.qa=d);for(b=d=0;8>b;b++)a.ja&1<b[d][0]&&(e=b[d][0]),g=a.Z&&(d-=a.Z,d=--c.Ba&&(0=c.lb&&(c.ma=c.lb),0>c.ma&&(c.ma=0),c.Ba=20, +a.o.update(a.o.I|128),Eb(a))}this.I=b;a.D.I&4&&U(a,2,this)}}(a)};a.D={I:0,read:function(){},update:function(a){return function(b){void 0!==b&&(this.I=b&-193);U(a,3,this);a.A.update();a.L.update()}}(a)};a.P={I:0,read:function(){},update:function(a){return function(b){void 0!==b&&(3==(b&3)&&(a.v.I=14),this.I=b);a.v.update()}}(a)};a.v={I:14,read:function(){},update:function(a){return function(b){void 0===b&&(b=a.v.I);b&=-2;0<=a.b&&0<=a.g[a.b].Aa&&(b|=1);this.I=b;U(a,16,this)}}(a)};a.J={I:0,read:function(a){return function(){Fb(a)}}(a), +update:function(a){return function(b){void 0!==b&&(this.I=b);U(a,17,this)}}(a)};a.R={I:0,read:function(){},update:function(){return function(){}}(a)}} +f.T=function(a,b,c){switch(b){case "listDisk":return this.H[b]=c,!0;case "loadDisk":return this.H[b]=c,c.onclick=function(a){return function(){if(a.H.listDisk){var b=a.H.listDisk.value,c=b;".json"!=b.substr(b.length-5)&&(c="http://"+window.location.host+"/api/v1/dump?disk\x3d"+b);a.l("loading "+oa(b)+"...");x(c,function(b,c,d){Gb(a,b,c,d)})}}}(this),!0}return!1};f.ba=function(a,b,c,d){this.a=a;this.M=b;if(this.s=d)Ra(d,b,c,this,this.S),P(d,b,c,this,this.Fb);this.W()}; +f.ia=function(a,b){a&&!this.C.N&&(this.C.N=!0,this.u=N(b,"debugger"))}; +function Gb(a,b,c,d){if(d)a.l("disk load error ("+d+")");else{d=[];a.l("mounting "+b+"...");try{if(d=eval("("+c+")"),d.length)if(d[0].length){var e=d[0];if(void 0===e[0].trackNum)a.l("data error: "+e[0]);else if(a.g[0]){for(c=0;c>8&255);a.push(b&255)}function Hb(a,b,c){b=b[c];if(void 0===b)throw Error("missing signature: "+c);for(c=0;cb?b&=3:32>b&&(b&=17);switch(b){case 0:a=a.B.I&4?a.o:a.K;break;case 1:a=a.B;break;case 2:a=a.D.I&4?a.A:a.L;break;case 3:a=a.D;break;case 16:a=c?a.P:a.v;break;case 17:a=a.J;break;default:a=a.R}return a}f.S=function(a,b){if(void 0!==b){var c=Jb(this,a-this.M,!1);this.u&&T(this.u,this,a,b,this.u.Da,!1,c.Cb);c.read()}}; +f.Fb=function(a,b){if(void 0!==b){var c=this.s.S(a),d=Jb(this,a-this.M,!0);if(this.u&&M(this.u,this.u.Da|this.u.jb)&&(T(this.u,this,a,b,this.u.Da,!0,d.Cb),d.Kb))for(a=128,b=d.I^c;b&&a;)b&a&&this.u.message(" changed "+d.Cb+"."+d.Kb[a]+" to "+(c&a?"1":"0")),a>>=1;d.update(c)}};function Db(a,b,c){var d=-1;void 0!==b&&void 0!==c&&(d=0,c&32||(d|=2),a.o.I&64||(d|=1));a.b!=d&&(a.b=d,a.v.update())}function Eb(a){0<=a.b&&(a.g[a.b].Aa=-1,a.J.update(255),a.v.update())} +function Fb(a){if(0<=a.b){var b=a.g[a.b];var c=b.Qa[b.ma];void 0!==c&&(0<=b.Aa&&b.Aa>24)} +f.S=function(a){if(a>=this.M&&a=a.Ca?a.l("invalid address: "+w(b)):(a.a[a.M+b]=c&255,Ua(a.s,b),a.s.update())}function Tb(a,b){X(a.oa,b,void 0)||a.oa.push(b);return!0}function X(a,b,c){for(var d=!1,e=0;eg&&(m+=" ;'"+String.fromCharCode(g)+"'")}if(e==a.s.Za&& +(gk?m+=String.fromCharCode(g):16==k&&(m+="\u2026"),k++;m+='"'}m&&(d+=" "+m);c&&(d=(d+" ").substr(0,30),d+=";"+c.toString());a.$=b;return d} +function Y(a,b){var c=a.ta;if(void 0!==b){var d=16;"$"==b.charAt(0)?b=b.substr(1):"0x"==b.substr(0,2)?b=b.substr(2):"."==b.charAt(b.length-1)&&(d=10,b=b.substr(0,b.length-1));c=parseInt(b,d);isNaN(c)&&(a.l("invalid base-"+d+" address: "+b),c=void 0)}void 0!==c&&(c=a.Ca)&&(a.l("address out of range: "+r(c)),c=void 0);return c} +function Wb(a,b){if("?"==b)a.l("\nfrequency commands:"),a.l("clear\tclear all frequency counts");else{var c=0;if(a.B)if("clear"==b){for(b=0;ba.Lb.indexOf(g)&&(k=a.O);k==a.ib&&27!=g&&(k=a.O)}else a.l("unknown operand: "+h),g=-1;if(0<=g){l=-1;for(e=0;el)l=e;else{a.l("too many instruction matches (both "+u(l)+" and "+u(e)+")");l=-2;break}if(0<=l){if(d.push(l),void 0!==k)if(b=a.ga[l][1],h=h.match(/[0-9A-F]+/),null!==h)for(h=parseInt(h[0],16),1==b&&k==a.da&&(h-=c+2,-128>h||127>>=8;else b&&a.l("instruction missing "+b+" bytes")}else a.l("unknown instruction: "+b+" "+h+"")}}k=d;if(k.length){for(c=0;cg&&kl?String.fromCharCode(l):".",k++;a.l(r(e,4)+" "+b+h)}a.ta=k}break;case "e":k=c[1];if(void 0===k)a.l("missing address");else if(k=Y(a,k),void 0!==k)for(d=2;dd.length&&(a.l("note: only "+ +d.length+" available"),b=d.length);void 0!==h&&(a.cb=0,a.l(b+" instructions earlier:"));h=a.cb?a.cb:1;c-=b;for(0>c&&(c=d.length-1);k&&c!=a.Ja;){e=d[c];if(0>e)break;a.l(Vb(a,e,h++));++c==d.length&&(c=0);k--;b--}a.Ob=b;a.cb=h}10==k&&a.l("no history available");break;case "r":Pb(a,c);break;case "s":a.S(a.s.f)==a.s.Na?(Ub(a,a.s.f+3),a.za=!0,a.ua()||a.s.ca()):Xb(a);break;case "t":Xb(a,c[1]);break;case "u":Ob(a,c[1],c[2],8);break;case "?":case "help":a.l("\ncommands:\n?\thelp\na [#]\tassemble\nb [#]\tbreakpoint\nd [#]\tdump memory\ne [#]\tedit memory\nf\tdump frequencies\ng [#]\trun to [#]\nh\thalt\no\toptions\np [#]\tdump history\nr\tdump/edit registers\ns\tstep over instruction\nt [#]\tstep instruction(s)\nu [#]\tunassemble"); +a.l("note: frequency and history commands operate only when breakpoints are set");break;default:a.l("unknown command: "+b)}}}z(function(){for(var a=G(document,"c1pjs","debugger"),b=0;bg.indexOf("/")&&"/"==window.location.pathname.slice(-1)&&(g=window.location.pathname+g),d?"}"==d.slice(-1)?(d=d.slice(0,-1),1]*\sid=)(['"]).*?\2/,"$1$2"+c+"$2"+(d?" parms\x3d'"+d+"'":"")+(g?' url\x3d"'+g+'"':"")));e||(a=a.replace(/().*?(<\/xsl:variable>)/, +"$1C1Pjs$2"),a=a.replace(/().*?(<\/xsl:variable>)/,"$1c1pjs$2"));g=null;if("\x3c"==a.charAt(0))try{e||(a=a.replace(/\s*/g,"")),window.ActiveXObject||"ActiveXObject"in window?(g=new window.ActiveXObject("Microsoft.XMLDOM"),g.async=!1,g.loadXML(a)):g=(new window.DOMParser).parseFromString(a,"text/xml")}catch(n){g=null,a=n.message}else a="unrecognized XML: "+(255/g.exec(a)){var e=d[2];b("Loading "+e+"...");x(e,function(g,h,l){if(l||!h)c(a,"unable to resolve XML reference: "+d[0]+" ("+l+")");else{if(g=d[3])if(l=h.match(new RegExp("\x3c"+d[1]+"[^\x3e]*\x3e"))){for(var k=l[0],m,n=/( [a-z]+=)(['"])(.*?)\2/gi;m=n.exec(g);)k=0>k.toLowerCase().indexOf(m[1].toLowerCase())?k.replace("\x3e",m[0]+"\x3e"):k.replace(new RegExp(m[1]+"(['\"])(.*?)\\1"),m[0]);l[0]!=k&&(h=h.replace(l[0],k))}else{c(a,"missing \x3c"+ +d[1]+"\x3e in "+e);return}h=h.replace(/<\?xml[^>]*>[\r\n]*/,"");a=a.replace(d[0],h);bc(a,b,c)}})}else c(a,null)} +function cc(a,b,c){function d(a){if(void 0===h){var b=g&&G(g,"machine-warning");h=b&&b[0]||g}h&&(h.innerHTML=qa(a))}function e(a){d("Error: "+a);l&&(--Zb||C(!0));l=!1}var g,h,l=!0;b||(b="machine.xml",c||(c="components.xsl"));Zb++;La[a]={};try{if(g=document.getElementById(a)){var k;if("object"==typeof resources&&(k=resources.css)){var m=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css";n.styleSheet?n.styleSheet.cssText=k:n.appendChild(document.createTextNode(k)); +m.appendChild(n)}c||(c="/versions/c1pjs/1.50.4/components.xsl");k=function(h,k){k?$b(c||"",null,null,!1,d,function(h,l){if(l){var m=c||"";La[a]&&m&&(La[a][m]=h);d("Processing "+b+"...");window.ActiveXObject||"ActiveXObject"in window?(l=k.transformNode(l))?(g.outerHTML=l,--Zb||C(!0)):e("transformNodeToObject failed"):document.implementation&&document.implementation.createDocument?(h=new XSLTProcessor,h.importStylesheet(l),(l=h.transformToFragment(k,document))?g.parentNode?(g.parentNode.replaceChild(l, +g),--Zb||C(!0)):e("invalid machine element: "+a):e("transformToFragment failed")):e("unable to transform XML: unsupported browser")}else e(h)}):e(h)};"\x3c"!=b.charAt(0)?$b(b,a,void 0,!0,d,k):ac(b,null,a,void 0,!1,d,k)}else e("missing machine element: "+a)}catch(t){e(t.message)}return l}window.embedC1P=function(a,b,c){C(!1);return cc(a,b,c)}; +window.commandMachine=function(a,b,c,d,e,g){if("script"==e){d=!1;c+=".machine";if(!g)delete I[c],d=!0;else if("string"==typeof g&&!I[c]){d=!0;e=I;for(var h=c,l=g.length,k=[],m=[],n="",t=null,V=0;V\";\n\nvar LICENSE = \"License: GPL version 3 or later \";\n\nvar CSSCLASS = \"pcjs\";\n\n/**\n * @define {string}\n */\nvar SITEHOST = \"localhost:8088\";// this @define is overridden by the Closure Compiler with \"www.pcjs.org\"\n\n/**\n * @define {boolean}\n */\nvar COMPILED = false; // this @define is overridden by the Closure Compiler (to true)\n\n/**\n * @define {boolean}\n */\nvar DEBUG = true; // this @define is overridden by the Closure Compiler (to false) to remove DEBUG-only code\n\n/**\n * @define {boolean}\n */\nvar MAXDEBUG = false; // this @define is overridden by the Closure Compiler (to false) to remove MAXDEBUG-only code\n\n/**\n * @define {boolean}\n */\nvar PRIVATE = false; // this @define is overridden by the Closure Compiler (to false) to enable PRIVATE code\n\n/*\n * RS-232 DB-25 Pin Definitions, mapped to bits 1-25 in a 32-bit status value.\n *\n * SerialPorts in PCjs machines are considered DTE (Data Terminal Equipment), which means they should be \"virtually\"\n * connected to each other via a null-modem cable, which assumes the following cross-wiring:\n *\n * G 1 <-> 1 G (Ground)\n * TD 2 <-> 3 RD (Received Data)\n * RD 3 <-> 2 TD (Transmitted Data)\n * RTS 4 <-> 5 CTS (Clear To Send)\n * CTS 5 <-> 4 RTS (Request To Send)\n * DSR 6+8 <-> 20 DTR (Data Terminal Ready)\n * SG 7 <-> 7 SG (Signal Ground)\n * DTR 20 <-> 6+8 DSR (Data Set Ready + Carrier Detect)\n * RI 22 <-> 22 RI (Ring Indicator)\n *\n * TODO: Move these definitions to a more appropriate shared file at some point.\n */\nvar RS232 = {\n RTS: {\n PIN: 4,\n MASK: 0x00000010\n },\n CTS: {\n PIN: 5,\n MASK: 0x00000020\n },\n DSR: {\n PIN: 6,\n MASK: 0x00000040\n },\n CD: {\n PIN: 8,\n MASK: 0x00000100\n },\n DTR: {\n PIN: 20,\n MASK: 0x00100000\n },\n RI: {\n PIN: 22,\n MASK: 0x00400000\n }\n};\n\n/*\n * NODE should be true if we're running under NodeJS (eg, command-line), false if not (eg, web browser)\n */\nvar NODE = false;\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/dumpapi.js (C) Jeff Parsons 2012-2018\n */\n\n/*\n * Our \"DiskDump API\", such as it was, used to look like:\n *\n * http://jsmachines.net/bin/convdisk.php?disk=/disks/pc/dos/ibm/2.00/PCDOS200-DISK1.json&format=img\n *\n * To make it (a bit) more \"REST-like\", the above request now looks like:\n *\n * http://www.pcjs.org/api/v1/dump?disk=/disks/pc/dos/ibm/2.00/PCDOS200-DISK1.json&format=img\n *\n * Similarly, our \"FileDump API\" used to look like:\n *\n * http://jsmachines.net/bin/convrom.php?rom=/devices/pc/rom/5150/1981-04-24/PCBIOS-REV1.rom&format=json\n *\n * and that request now looks like:\n *\n * http://www.pcjs.org/api/v1/dump?file=/devices/pc/rom/5150/1981-04-24/PCBIOS-REV1.rom&format=json\n *\n * I don't think it makes sense to avoid \"query\" parameters, because blending the path of a disk image with the\n * the rest of the URL would be (a) confusing, and (b) more work to parse.\n */\nvar DumpAPI = {\n ENDPOINT: \"/api/v1/dump\",\n QUERY: {\n DIR: \"dir\", // value is path of a directory (DiskDump only)\n DISK: \"disk\", // value is path of a disk image (DiskDump only)\n FILE: \"file\", // value is path of a ROM image file (FileDump only)\n IMG: \"img\", // alias for DISK\n PATH: \"path\", // value is path of a one or more files (DiskDump only)\n FORMAT: \"format\", // value is one of FORMAT values below\n COMMENTS: \"comments\", // value is either \"true\" or \"false\"\n DECIMAL: \"decimal\", // value is either \"true\" to force all numbers to decimal, \"false\" or undefined otherwise\n MBHD: \"mbhd\", // value is hard drive size in Mb (formerly \"mbsize\") (DiskDump only) (DEPRECATED)\n SIZE: \"size\" // value is target disk size in Kb (supersedes \"mbhd\") (DiskDump only)\n },\n FORMAT: {\n JSON: \"json\", // default\n JSON_GZ: \"gz\", // gzip is currently used ONLY for compressed JSON\n DATA: \"data\", // same as \"json\", but built without JSON.stringify() (DiskDump only)\n HEX: \"hex\", // deprecated\n OCTAL: \"octal\", // displays data as octal words\n BYTES: \"bytes\", // displays data as hex bytes; normally used only when comments are enabled\n WORDS: \"words\", // displays data as hex words; normally used only when comments are enabled\n LONGS: \"longs\", // displays data as dwords\n IMG: \"img\", // returns the raw disk data (ie, using a Buffer object) (DiskDump only)\n ROM: \"rom\" // returns the raw file data (ie, using a Buffer object) (FileDump only)\n }\n};\n\n/*\n * Because we use an overloaded API endpoint (ie, one that's shared with the FileDump module), we must\n * also provide a list of commands which, when combined with the endpoint, define a unique request.\n */\nDumpAPI.asDiskCommands = [DumpAPI.QUERY.DIR, DumpAPI.QUERY.DISK, DumpAPI.QUERY.PATH];\nDumpAPI.asFileCommands = [DumpAPI.QUERY.FILE];\n\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/reportapi.js (C) Jeff Parsons 2012-2018\n */\n\nvar ReportAPI = {\n ENDPOINT: \"/api/v1/report\",\n QUERY: {\n APP: \"app\",\n VER: \"ver\",\n URL: \"url\",\n USER: \"user\",\n TYPE: \"type\",\n DATA: \"data\"\n },\n TYPE: {\n BUG: \"bug\"\n },\n RES: {\n OK: \"Thank you\"\n }\n};\n\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/strlib.js (C) Jeff Parsons 2012-2018\n */\n\nclass Str {\n /**\n * isValidInt(s, base)\n *\n * The built-in parseInt() function has the annoying feature of returning a partial value (ie,\n * up to the point where it encounters an invalid character); eg, parseInt(\"foo\", 16) returns 0xf.\n *\n * So it's best to use our own Str.parseInt() function, which will in turn use this function to\n * validate the entire string.\n *\n * @param {string} s is the string representation of some number\n * @param {number} [base] is the radix to use (default is 10); only 2, 8, 10 and 16 are supported\n * @return {boolean} true if valid, false if invalid (or the specified base isn't supported)\n */\n static isValidInt(s, base)\n {\n if (!base || base == 10) return s.match(/^-?[0-9]+$/) !== null;\n if (base == 16) return s.match(/^-?[0-9a-f]+$/i) !== null;\n if (base == 8) return s.match(/^-?[0-7]+$/) !== null;\n if (base == 2) return s.match(/^-?[01]+$/) !== null;\n return false;\n }\n\n /**\n * parseInt(s, base)\n *\n * This is a wrapper around the built-in parseInt() function. Our wrapper recognizes certain prefixes\n * ('$' or \"0x\" for hex, '#' or \"0o\" for octal) and suffixes ('.' for decimal, 'h' for hex, 'y' for\n * binary), and then calls isValidInt() to ensure we don't convert strings that contain partial values;\n * see isValidInt() for details.\n *\n * The use of multiple prefix/suffix combinations is undefined (although for the record, we process\n * prefixes first). We do NOT support the \"0b\" prefix to indicate binary UNLESS one or more commas are\n * also present (because \"0b\" is also a valid hex sequence), and we do NOT support a single leading zero\n * to indicate octal (because such a number could also be decimal or hex). Any number of commas are\n * allowed; we remove them all before calling the built-in parseInt().\n *\n * More recently, we've added support for \"^D\", \"^O\", and \"^B\" prefixes to accommodate the base overrides\n * that the PDP-10's MACRO-10 assembly language supports (decimal, octal, and binary, respectively).\n * If this support turns out to adversely affect other debuggers, then it will have to be \"conditionalized\".\n * Similarly, we've added support for \"K\", \"M\", and \"G\" MACRO-10-style suffixes that add 3, 6, or 9 zeros\n * to the value to be parsed, respectively.\n *\n * @param {string} s is the string representation of some number\n * @param {number} [base] is the radix to use (default is 10); can be overridden by prefixes/suffixes\n * @return {number|undefined} corresponding value, or undefined if invalid\n */\n static parseInt(s, base)\n {\n var value;\n\n if (s) {\n if (!base) base = 10;\n\n var ch, chPrefix, chSuffix;\n var fCommas = (s.indexOf(',') > 0);\n if (fCommas) s = s.replace(/,/g, '');\n\n ch = chPrefix = s.charAt(0);\n if (chPrefix == '#') {\n base = 8;\n chPrefix = '';\n }\n else if (chPrefix == '$') {\n base = 16;\n chPrefix = '';\n }\n if (ch != chPrefix) {\n s = s.substr(1);\n }\n else {\n ch = chPrefix = s.substr(0, 2);\n if (chPrefix == '0b' && fCommas || chPrefix == '^B') {\n base = 2;\n chPrefix = '';\n }\n else if (chPrefix == '0o' || chPrefix == '^O') {\n base = 8;\n chPrefix = '';\n }\n else if (chPrefix == '^D') {\n base = 10;\n chPrefix = '';\n }\n else if (chPrefix == '0x') {\n base = 16;\n chPrefix = '';\n }\n if (ch != chPrefix) s = s.substr(2);\n }\n ch = chSuffix = s.slice(-1);\n if (chSuffix == 'Y' || chSuffix == 'y') {\n base = 2;\n chSuffix = '';\n }\n else if (chSuffix == '.') {\n base = 10;\n chSuffix = '';\n }\n else if (chSuffix == 'H' || chSuffix == 'h') {\n base = 16;\n chSuffix = '';\n }\n else if (chSuffix == 'K') {\n chSuffix = '000';\n }\n else if (chSuffix == 'M') {\n chSuffix = '000000';\n }\n else if (chSuffix == 'G') {\n chSuffix = '000000000';\n }\n if (ch != chSuffix) s = s.slice(0, -1) + chSuffix;\n /*\n * This adds support for the MACRO-10 binary shifting (Bn) suffix, which must be stripped from the\n * number before parsing, and then applied to the value after parsing. If n is omitted, 35 is assumed,\n * which is a net shift of zero. If n < 35, then a left shift of (35 - n) is required; if n > 35, then\n * a right shift of -(35 - n) is required.\n */\n var v, shift = 0;\n if (base <= 10) {\n var match = s.match(/(-?[0-9]+)B([0-9]*)/);\n if (match) {\n s = match[1];\n shift = 35 - ((match[2] || 35) & 0xff);\n }\n }\n if (Str.isValidInt(s, base) && !isNaN(v = parseInt(s, base))) {\n /*\n * With the need to support larger (eg, 36-bit) integers, truncating to 32 bits is no longer helpful.\n *\n * value = v|0;\n */\n if (shift) {\n /*\n * Since binary shifting is a logical operation, and since shifting by division only works properly\n * with positive numbers, we must convert a negative value to a positive value, by computing the two's\n * complement.\n */\n if (v < 0) v += Math.pow(2, 36);\n if (shift > 0) {\n v *= Math.pow(2, shift);\n } else {\n v = Math.trunc(v / Math.pow(2, -shift));\n }\n }\n value = v;\n }\n }\n return value;\n }\n\n /**\n * toBase(n, radix, cch, sPrefix, nGrouping)\n *\n * Displays the given number as an unsigned integer using the specified radix and number of digits.\n *\n * @param {number|null|undefined} n\n * @param {number} radix (ie, the base)\n * @param {number} cch (the desired number of digits)\n * @param {string} [sPrefix] (default is none)\n * @param {number} [nGrouping]\n * @return {string}\n */\n static toBase(n, radix, cch, sPrefix = \"\", nGrouping = 0)\n {\n /*\n * An initial \"falsey\" check for null takes care of both null and undefined;\n * we can't rely entirely on isNaN(), because isNaN(null) returns false, oddly enough.\n *\n * Alternatively, we could mask and shift n regardless of whether it's null/undefined/NaN,\n * since JavaScript coerces such operands to zero, but I think there's \"value\" in seeing those\n * values displayed differently.\n */\n var s = \"\";\n if (isNaN(n)) {\n n = null;\n } else if (n != null) {\n /*\n * Callers that produced an input by dividing by a power of two rather than shifting (in order\n * to access more than 32 bits) may produce a fractional result, which ordinarily we would simply\n * ignore, but if the integer portion is zero and the sign is negative, we should probably treat\n * this value as a sign-extension.\n */\n if (n < 0 && n > -1) n = -1;\n /*\n * Negative values should be two's complemented according to the number of digits; for example,\n * 12 octal digits implies an upper limit 8^12.\n */\n if (n < 0) {\n n += Math.pow(radix, cch);\n }\n if (n >= Math.pow(radix, cch)) {\n cch = Math.ceil(Math.log(n) / Math.log(radix));\n }\n }\n var g = nGrouping || -1;\n while (cch-- > 0) {\n if (!g) {\n s = ',' + s;\n g = nGrouping;\n }\n if (n == null) {\n s = '?' + s;\n } else {\n var d = n % radix;\n d += (d >= 0 && d <= 9? 0x30 : 0x41 - 10);\n s = String.fromCharCode(d) + s;\n n = Math.trunc(n / radix);\n }\n g--;\n }\n return sPrefix + s;\n }\n\n /**\n * toBin(n, cch, nGrouping)\n *\n * Converts an integer to binary, with the specified number of digits (up to a maximum of 36).\n *\n * @param {number|null|undefined} n (supports integers up to 36 bits now)\n * @param {number} [cch] is the desired number of binary digits (0 or undefined for default of either 8, 18, or 36)\n * @param {number} [nGrouping]\n * @return {string} the binary representation of n\n */\n static toBin(n, cch, nGrouping)\n {\n if (!cch) {\n // cch = Math.ceil(Math.log(Math.abs(n) + 1) / Math.LN2) || 1;\n var v = Math.abs(n);\n if (v <= 0b11111111) {\n cch = 8;\n } else if (v <= 0b111111111111111111) {\n cch = 18;\n } else {\n cch = 36;\n }\n } else if (cch > 36) cch = 36;\n return Str.toBase(n, 2, cch, \"\", nGrouping);\n }\n\n /**\n * toBinBytes(n, cb, fPrefix)\n *\n * Converts an integer to binary, with the specified number of bytes (up to the default of 4).\n *\n * @param {number|null|undefined} n (interpreted as a 32-bit value)\n * @param {number} [cb] is the desired number of binary bytes (4 is both the default and the maximum)\n * @param {boolean} [fPrefix]\n * @return {string} the binary representation of n\n */\n static toBinBytes(n, cb, fPrefix)\n {\n var s = \"\";\n if (!cb || cb > 4) cb = 4;\n for (var i = 0; i < cb; i++) {\n if (s) s = ',' + s;\n s = Str.toBin(n & 0xff, 8) + s;\n n >>= 8;\n }\n return (fPrefix? \"0b\" : \"\") + s;\n }\n\n /**\n * toOct(n, cch, fPrefix)\n *\n * Converts an integer to octal, with the specified number of digits (default of 6; max of 12)\n *\n * You might be tempted to use the built-in n.toString(8) instead, but it doesn't zero-pad and it\n * doesn't properly convert negative values. Moreover, if n is undefined, n.toString() will throw\n * an exception, whereas this function will return '?' characters.\n *\n * @param {number|null|undefined} n (supports integers up to 36 bits now)\n * @param {number} [cch] is the desired number of octal digits (0 or undefined for default of either 6, 8, or 12)\n * @param {boolean} [fPrefix]\n * @return {string} the octal representation of n\n */\n static toOct(n, cch, fPrefix)\n {\n if (!cch) {\n // cch = Math.ceil(Math.log(Math.abs(n) + 1) / Math.log(8)) || 1;\n var v = Math.abs(n);\n if (v <= 0o777777) {\n cch = 6;\n } else if (v <= 0o77777777) {\n cch = 8;\n } else {\n cch = 12;\n }\n } else if (cch > 12) cch = 12;\n return Str.toBase(n, 8, cch, fPrefix? \"0o\" : \"\");\n }\n\n /**\n * toDec(n, cch)\n *\n * Converts an integer to decimal, with the specified number of digits (default of 5; max of 11)\n *\n * You might be tempted to use the built-in n.toString(10) instead, but it doesn't zero-pad and it\n * doesn't properly convert negative values. Moreover, if n is undefined, n.toString() will throw\n * an exception, whereas this function will return '?' characters.\n *\n * @param {number|null|undefined} n (supports integers up to 36 bits now)\n * @param {number} [cch] is the desired number of decimal digits (0 or undefined for default of either 5 or 11)\n * @return {string} the decimal representation of n\n */\n static toDec(n, cch)\n {\n if (!cch) {\n // cch = Math.ceil(Math.log(Math.abs(n) + 1) / Math.LN10) || 1;\n var v = Math.abs(n);\n if (v <= 99999) {\n cch = 5;\n } else {\n cch = 11;\n }\n } else if (cch > 11) cch = 11;\n return Str.toBase(n, 10, cch);\n }\n\n /**\n * toHex(n, cch, fPrefix)\n *\n * Converts an integer to hex, with the specified number of digits (default of 4 or 8, max of 9).\n *\n * You might be tempted to use the built-in n.toString(16) instead, but it doesn't zero-pad and it\n * doesn't properly convert negative values; for example, if n is -2147483647, then n.toString(16)\n * will return \"-7fffffff\" instead of \"80000001\". Moreover, if n is undefined, n.toString() will\n * throw an exception, whereas this function will return '?' characters.\n *\n * NOTE: The following work-around (adapted from code found on StackOverflow) would be another solution,\n * taking care of negative values, zero-padding, and upper-casing, but not null/undefined/NaN values:\n *\n * s = (n < 0? n + 0x100000000 : n).toString(16);\n * s = \"00000000\".substr(0, 8 - s.length) + s;\n * s = s.substr(0, cch).toUpperCase();\n *\n * @param {number|null|undefined} n (supports integers up to 36 bits now)\n * @param {number} [cch] is the desired number of hex digits (0 or undefined for default of either 4, 8, or 9)\n * @param {boolean} [fPrefix]\n * @return {string} the hex representation of n\n */\n static toHex(n, cch, fPrefix)\n {\n if (!cch) {\n // cch = Math.ceil(Math.log(Math.abs(n) + 1) / Math.log(16)) || 1;\n var v = Math.abs(n);\n if (v <= 0xffff) {\n cch = 4;\n } else if (v <= 0xffffffff) {\n cch = 8;\n } else {\n cch = 9;\n }\n } else if (cch > 9) cch = 9;\n return Str.toBase(n, 16, cch, fPrefix? \"0x\" : \"\");\n }\n\n /**\n * toHexByte(b)\n *\n * Alias for Str.toHex(b, 2, true)\n *\n * @param {number|null|undefined} b is a byte value\n * @return {string} the hex representation of b\n */\n static toHexByte(b)\n {\n return Str.toHex(b, 2, true);\n }\n\n /**\n * toHexWord(w)\n *\n * Alias for Str.toHex(w, 4, true)\n *\n * @param {number|null|undefined} w is a word (16-bit) value\n * @return {string} the hex representation of w\n */\n static toHexWord(w)\n {\n return Str.toHex(w, 4, true);\n }\n\n /**\n * toHexLong(l)\n *\n * Alias for Str.toHex(l, 8, true)\n *\n * @param {number|null|undefined} l is a dword (32-bit) value\n * @return {string} the hex representation of w\n */\n static toHexLong(l)\n {\n return Str.toHex(l, 8, true);\n }\n\n /**\n * getBaseName(sFileName, fStripExt)\n *\n * This is a poor-man's version of Node's path.basename(), which Node-only components should use instead.\n *\n * Note that if fStripExt is true, this strips ANY extension, whereas path.basename() strips the extension only\n * if it matches the second parameter (eg, path.basename(\"/foo/bar/baz/asdf/quux.html\", \".html\") returns \"quux\").\n *\n * @param {string} sFileName\n * @param {boolean} [fStripExt]\n * @return {string}\n */\n static getBaseName(sFileName, fStripExt)\n {\n var sBaseName = sFileName;\n\n var i = sFileName.lastIndexOf('/');\n if (i >= 0) sBaseName = sFileName.substr(i + 1);\n\n /*\n * This next bit is a kludge to clean up names that are part of a URL that includes unsightly query parameters.\n */\n i = sBaseName.indexOf('&');\n if (i > 0) sBaseName = sBaseName.substr(0, i);\n\n if (fStripExt) {\n i = sBaseName.lastIndexOf(\".\");\n if (i > 0) {\n sBaseName = sBaseName.substring(0, i);\n }\n }\n return sBaseName;\n }\n\n /**\n * getExtension(sFileName)\n *\n * This is a poor-man's version of Node's path.extname(), which Node-only components should use instead.\n *\n * Note that we EXCLUDE the period from the returned extension, whereas path.extname() includes it.\n *\n * @param {string} sFileName\n * @return {string} the filename's extension (in lower-case and EXCLUDING the \".\"), or an empty string\n */\n static getExtension(sFileName)\n {\n var sExtension = \"\";\n var i = sFileName.lastIndexOf(\".\");\n if (i >= 0) {\n sExtension = sFileName.substr(i + 1).toLowerCase();\n }\n return sExtension;\n }\n\n /**\n * endsWith(s, sSuffix)\n *\n * @param {string} s\n * @param {string} sSuffix\n * @return {boolean} true if s ends with sSuffix, false if not\n */\n static endsWith(s, sSuffix)\n {\n return s.indexOf(sSuffix, s.length - sSuffix.length) !== -1;\n }\n\n /**\n * escapeHTML(sHTML)\n *\n * @param {string} sHTML\n * @return {string} with HTML entities \"escaped\", similar to PHP's htmlspecialchars()\n */\n static escapeHTML(sHTML)\n {\n return sHTML.replace(/[&<>\"']/g, function(m)\n {\n return Str.HTMLEscapeMap[m];\n });\n }\n\n /**\n * replace(sSearch, sReplace, s)\n *\n * The JavaScript replace() function ALWAYS interprets \"$\" specially in replacement strings, even when\n * the search string is NOT a RegExp; specifically:\n *\n * $$ Inserts a \"$\"\n * $& Inserts the matched substring\n * $` Inserts the portion of the string that precedes the matched substring\n * $' Inserts the portion of the string that follows the matched substring\n * $n Where n is a positive integer less than 100, inserts the nth parenthesized sub-match string,\n * provided the first argument was a RegExp object\n *\n * So, if a replacement string containing dollar signs passes through a series of replace() calls, untold\n * problems could result. Hence, this function, which simply uses the replacement string as-is.\n *\n * Similar to the JavaScript replace() method (when sSearch is a string), this replaces only ONE occurrence\n * (ie, the FIRST occurrence); it might be nice to add options to replace the LAST occurrence and/or ALL\n * occurrences, but we'll revisit that later.\n *\n * @param {string} sSearch\n * @param {string} sReplace\n * @param {string} s\n * @return {string}\n */\n static replace(sSearch, sReplace, s)\n {\n var i = s.indexOf(sSearch);\n if (i >= 0) {\n s = s.substr(0, i) + sReplace + s.substr(i + sSearch.length);\n }\n return s;\n }\n\n /**\n * replaceAll(sSearch, sReplace, s)\n *\n * @param {string} sSearch\n * @param {string} sReplace\n * @param {string} s\n * @return {string}\n */\n static replaceAll(sSearch, sReplace, s)\n {\n var a = {};\n a[sSearch] = sReplace;\n return Str.replaceArray(a, s);\n }\n\n /**\n * replaceArray(a, s)\n *\n * @param {Object} a\n * @param {string} s\n * @return {string}\n */\n static replaceArray(a, s)\n {\n var sMatch = \"\";\n for (var k in a) {\n /*\n * As noted in:\n *\n * http://www.regexguru.com/2008/04/escape-characters-only-when-necessary/\n *\n * inside character classes, only backslash, caret, hyphen and the closing bracket need to be\n * escaped. And in fact, if you ensure that the closing bracket is first, the caret is not first,\n * and the hyphen is last, you can avoid escaping those as well.\n */\n k = k.replace(/([\\\\[\\]*{}().+?|$])/g, \"\\\\$1\");\n sMatch += (sMatch? '|' : '') + k;\n }\n return s.replace(new RegExp('(' + sMatch + ')', \"g\"), function(m)\n {\n return a[m];\n });\n }\n\n /**\n * pad(s, cch, fPadLeft)\n *\n * NOTE: the maximum amount of padding currently supported is 40 spaces.\n *\n * @param {string} s is a string\n * @param {number} cch is desired length\n * @param {boolean} [fPadLeft] (default is padding on the right)\n * @return {string} the original string (s) with spaces padding it to the specified length\n */\n static pad(s, cch, fPadLeft)\n {\n var sPadding = \" \";\n return fPadLeft? (sPadding + s).slice(-cch) : (s + sPadding).slice(0, cch);\n }\n\n /**\n * sprintf(format, ...args)\n *\n * Copied from the CCjs project (/ccjs/lib/stdio.js) and extended. Far from complete let alone sprintf-compatible,\n * but it's a start.\n *\n * @param {string} format\n * @param {...} args\n * @return {string}\n */\n static sprintf(format, ...args)\n {\n var parts = format.split(/%([-+ 0#]?)([0-9]*)(\\.?)([0-9]*)([hlL]?)([A-Za-z%])/);\n var buffer = \"\";\n var partIndex = 0;\n for (var i = 0; i < args.length; i++) {\n\n var arg = args[i], d, s;\n buffer += parts[partIndex++];\n var flags = parts[partIndex];\n var minimum = +parts[partIndex+1] || 0;\n var precision = +parts[partIndex+3] || 0;\n var conversion = parts[partIndex+5];\n\n switch(conversion) {\n case 'd':\n case 'f':\n d = Math.trunc(arg);\n s = d + \"\";\n if (precision) {\n minimum -= (precision + 1);\n }\n if (s.length < minimum) {\n if (flags == '0') {\n if (d < 0) minimum--;\n s = (\"0000000000\" + Math.abs(d)).slice(-minimum);\n if (d < 0) s = '-' + s;\n } else {\n s = (\" \" + s).slice(-minimum);\n }\n }\n if (precision) {\n d = Math.trunc((arg - Math.trunc(arg)) * Math.pow(10, precision));\n s += '.' + (\"0000000000\" + Math.abs(d)).slice(-precision);\n }\n buffer += s;\n break;\n case 's':\n buffer += arg;\n break;\n default:\n /*\n * The supported ANSI C set of conversions: \"dioxXucsfeEgGpn%\"\n */\n buffer += \"(unrecognized printf conversion %\" + conversion + \")\";\n break;\n }\n\n partIndex += 6;\n }\n buffer += parts[partIndex];\n return buffer;\n }\n\n /**\n * stripLeadingZeros(s, fPad)\n *\n * @param {string} s\n * @param {boolean} [fPad]\n * @return {string}\n */\n static stripLeadingZeros(s, fPad)\n {\n var cch = s.length;\n s = s.replace(/^0+([0-9A-F]+)$/i, \"$1\");\n if (fPad) s = Str.pad(s, cch, true);\n return s;\n }\n\n /**\n * trim(s)\n *\n * @param {string} s\n * @return {string}\n */\n static trim(s)\n {\n if (String.prototype.trim) {\n return s.trim();\n }\n return s.replace(/^\\s+|\\s+$/g, \"\");\n }\n\n /**\n * toASCIICode(b)\n *\n * @param {number} b\n * @return {string}\n */\n static toASCIICode(b)\n {\n var s;\n if (b != Str.ASCII.CR && b != Str.ASCII.LF) {\n s = Str.ASCIICodeMap[b];\n }\n if (s) {\n s = '<' + s + '>';\n } else {\n s = String.fromCharCode(b);\n }\n return s;\n }\n}\n\n/*\n * Map special characters to their HTML escape sequences.\n */\nStr.HTMLEscapeMap = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n};\n\n/*\n * Map \"unprintable\" ASCII codes to mnemonics, to more clearly see what's being printed.\n */\nStr.ASCIICodeMap = {\n 0x00: \"NUL\",\n 0x01: \"SOH\", // (CTRL_A) Start of Heading\n 0x02: \"STX\", // (CTRL_B) Start of Text\n 0x03: \"ETX\", // (CTRL_C) End of Text\n 0x04: \"EOT\", // (CTRL_D) End of Transmission\n 0x05: \"ENQ\", // (CTRL_E) Enquiry\n 0x06: \"ACK\", // (CTRL_F) Acknowledge\n 0x07: \"BEL\", // (CTRL_G) Bell\n 0x08: \"BS\", // (CTRL_H) Backspace\n 0x09: \"TAB\", // (CTRL_I) Horizontal Tab (aka HT)\n 0x0A: \"LF\", // (CTRL_J) Line Feed (New Line)\n 0x0B: \"VT\", // (CTRL_K) Vertical Tab\n 0x0C: \"FF\", // (CTRL_L) Form Feed (New Page)\n 0x0D: \"CR\", // (CTRL_M) Carriage Return\n 0x0E: \"SO\", // (CTRL_N) Shift Out\n 0x0F: \"SI\", // (CTRL_O) Shift In\n 0x10: \"DLE\", // (CTRL_P) Data Link Escape\n 0x11: \"XON\", // (CTRL_Q) Device Control 1 (aka DC1)\n 0x12: \"DC2\", // (CTRL_R) Device Control 2\n 0x13: \"XOFF\", // (CTRL_S) Device Control 3 (aka DC3)\n 0x14: \"DC4\", // (CTRL_T) Device Control 4\n 0x15: \"NAK\", // (CTRL_U) Negative Acknowledge\n 0x16: \"SYN\", // (CTRL_V) Synchronous Idle\n 0x17: \"ETB\", // (CTRL_W) End of Transmission Block\n 0x18: \"CAN\", // (CTRL_X) Cancel\n 0x19: \"EM\", // (CTRL_Y) End of Medium\n 0x1A: \"SUB\", // (CTRL_Z) Substitute\n 0x1B: \"ESC\", // Escape\n 0x1C: \"FS\", // File Separator\n 0x1D: \"GS\", // Group Separator\n 0x1E: \"RS\", // Record Separator\n 0x1F: \"US\", // Unit Separator\n 0x7F: \"DEL\"\n};\n\n/*\n * Refer to: https://en.wikipedia.org/wiki/Code_page_437\n */\nStr.CP437ToUnicode = [\n '\\u0000', '\\u263A', '\\u263B', '\\u2665', '\\u2666', '\\u2663', '\\u2660', '\\u2022',\n '\\u25D8', '\\u25CB', '\\u25D9', '\\u2642', '\\u2640', '\\u266A', '\\u266B', '\\u263C',\n '\\u25BA', '\\u25C4', '\\u2195', '\\u203C', '\\u00B6', '\\u00A7', '\\u25AC', '\\u21A8',\n '\\u2191', '\\u2193', '\\u2192', '\\u2190', '\\u221F', '\\u2194', '\\u25B2', '\\u25BC',\n '\\u0020', '\\u0021', '\\u0022', '\\u0023', '\\u0024', '\\u0025', '\\u0026', '\\u0027',\n '\\u0028', '\\u0029', '\\u002A', '\\u002B', '\\u002C', '\\u002D', '\\u002E', '\\u002F',\n '\\u0030', '\\u0031', '\\u0032', '\\u0033', '\\u0034', '\\u0035', '\\u0036', '\\u0037',\n '\\u0038', '\\u0039', '\\u003A', '\\u003B', '\\u003C', '\\u003D', '\\u003E', '\\u003F',\n '\\u0040', '\\u0041', '\\u0042', '\\u0043', '\\u0044', '\\u0045', '\\u0046', '\\u0047',\n '\\u0048', '\\u0049', '\\u004A', '\\u004B', '\\u004C', '\\u004D', '\\u004E', '\\u004F',\n '\\u0050', '\\u0051', '\\u0052', '\\u0053', '\\u0054', '\\u0055', '\\u0056', '\\u0057',\n '\\u0058', '\\u0059', '\\u005A', '\\u005B', '\\u005C', '\\u005D', '\\u005E', '\\u005F',\n '\\u0060', '\\u0061', '\\u0062', '\\u0063', '\\u0064', '\\u0065', '\\u0066', '\\u0067',\n '\\u0068', '\\u0069', '\\u006A', '\\u006B', '\\u006C', '\\u006D', '\\u006E', '\\u006F',\n '\\u0070', '\\u0071', '\\u0072', '\\u0073', '\\u0074', '\\u0075', '\\u0076', '\\u0077',\n '\\u0078', '\\u0079', '\\u007A', '\\u007B', '\\u007C', '\\u007D', '\\u007E', '\\u2302',\n '\\u00C7', '\\u00FC', '\\u00E9', '\\u00E2', '\\u00E4', '\\u00E0', '\\u00E5', '\\u00E7',\n '\\u00EA', '\\u00EB', '\\u00E8', '\\u00EF', '\\u00EE', '\\u00EC', '\\u00C4', '\\u00C5',\n '\\u00C9', '\\u00E6', '\\u00C6', '\\u00F4', '\\u00F6', '\\u00F2', '\\u00FB', '\\u00F9',\n '\\u00FF', '\\u00D6', '\\u00DC', '\\u00A2', '\\u00A3', '\\u00A5', '\\u20A7', '\\u0192',\n '\\u00E1', '\\u00ED', '\\u00F3', '\\u00FA', '\\u00F1', '\\u00D1', '\\u00AA', '\\u00BA',\n '\\u00BF', '\\u2310', '\\u00AC', '\\u00BD', '\\u00BC', '\\u00A1', '\\u00AB', '\\u00BB',\n '\\u2591', '\\u2592', '\\u2593', '\\u2502', '\\u2524', '\\u2561', '\\u2562', '\\u2556',\n '\\u2555', '\\u2563', '\\u2551', '\\u2557', '\\u255D', '\\u255C', '\\u255B', '\\u2510',\n '\\u2514', '\\u2534', '\\u252C', '\\u251C', '\\u2500', '\\u253C', '\\u255E', '\\u255F',\n '\\u255A', '\\u2554', '\\u2569', '\\u2566', '\\u2560', '\\u2550', '\\u256C', '\\u2567',\n '\\u2568', '\\u2564', '\\u2565', '\\u2559', '\\u2558', '\\u2552', '\\u2553', '\\u256B',\n '\\u256A', '\\u2518', '\\u250C', '\\u2588', '\\u2584', '\\u258C', '\\u2590', '\\u2580',\n '\\u03B1', '\\u00DF', '\\u0393', '\\u03C0', '\\u03A3', '\\u03C3', '\\u00B5', '\\u03C4',\n '\\u03A6', '\\u0398', '\\u03A9', '\\u03B4', '\\u221E', '\\u03C6', '\\u03B5', '\\u2229',\n '\\u2261', '\\u00B1', '\\u2265', '\\u2264', '\\u2320', '\\u2321', '\\u00F7', '\\u2248',\n '\\u00B0', '\\u2219', '\\u00B7', '\\u221A', '\\u207F', '\\u00B2', '\\u25A0', '\\u00A0'\n];\n\n/*\n * TODO: Future home of a complete ASCII table.\n */\nStr.ASCII = {\n LF: 0x0A,\n CR: 0x0D\n};\n\nStr.TYPES = {\n NULL: 0,\n BYTE: 1,\n WORD: 2,\n DWORD: 3,\n NUMBER: 4,\n STRING: 5,\n BOOLEAN: 6,\n OBJECT: 7,\n ARRAY: 8\n};\n\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/usrlib.js (C) Jeff Parsons 2012-2018\n */\n\n/**\n * @typedef {{\n * mask: number,\n * shift: number\n * }}\n */\nvar BitField;\n\n/**\n * @typedef {Object.}\n */\nvar BitFields;\n\nclass Usr {\n /**\n * binarySearch(a, v, fnCompare)\n *\n * @param {Array} a is an array\n * @param {number|string|Array|Object} v\n * @param {function((number|string|Array|Object), (number|string|Array|Object))} [fnCompare]\n * @return {number} the index of matching entry if non-negative, otherwise the index of the insertion point\n */\n static binarySearch(a, v, fnCompare)\n {\n var left = 0;\n var right = a.length;\n var found = 0;\n if (fnCompare === undefined) {\n fnCompare = function(a, b)\n {\n return a > b ? 1 : a < b ? -1 : 0;\n };\n }\n while (left < right) {\n var middle = (left + right) >> 1;\n var compareResult;\n compareResult = fnCompare(v, a[middle]);\n if (compareResult > 0) {\n left = middle + 1;\n } else {\n right = middle;\n found = !compareResult;\n }\n }\n return found ? left : ~left;\n }\n\n /**\n * binaryInsert(a, v, fnCompare)\n *\n * If element v already exists in array a, the array is unchanged (we don't allow duplicates); otherwise, the\n * element is inserted into the array at the appropriate index.\n *\n * @param {Array} a is an array\n * @param {number|string|Array|Object} v is the value to insert\n * @param {function((number|string|Array|Object), (number|string|Array|Object))} [fnCompare]\n */\n static binaryInsert(a, v, fnCompare)\n {\n var index = Usr.binarySearch(a, v, fnCompare);\n if (index < 0) {\n a.splice(-(index + 1), 0, v);\n }\n }\n\n /**\n * getTimestamp()\n *\n * @return {string} timestamp containing the current date and time (\"yyyy-mm-dd hh:mm:ss\")\n */\n static getTimestamp()\n {\n return Usr.formatDate(\"Y-m-d H:i:s\");\n }\n\n /**\n * getMonthDays(nMonth, nYear)\n *\n * Note that if we're being called on behalf of the RTC, its year is always truncated to two digits (mod 100),\n * so we have no idea what century the year 0 might refer to. When using the normal leap-year formula, 0 fails\n * the mod 100 test but passes the mod 400 test, so as far as the RTC is concerned, every century year is a leap\n * year. Since we're most likely dealing with the year 2000, that's fine, since 2000 was also a leap year.\n *\n * TODO: There IS a separate CMOS byte that's supposed to be set to CMOS_ADDR.CENTURY_DATE; it's always BCD,\n * so theoretically it will contain values like 0x19 or 0x20 (for the 20th and 21st centuries, respectively), and\n * we could add that as another parameter to this function, to improve the accuracy, but that would go beyond what\n * a real RTC actually does.\n *\n * @param {number} nMonth (1-12)\n * @param {number} nYear (normally a 4-digit year, but it may also be mod 100)\n * @return {number} the maximum (1-based) day allowed for the specified month and year\n */\n static getMonthDays(nMonth, nYear)\n {\n var nDays = Usr.aMonthDays[nMonth - 1];\n if (nDays == 28) {\n if ((nYear % 4) === 0 && ((nYear % 100) || (nYear % 400) === 0)) {\n nDays++;\n }\n }\n return nDays;\n }\n\n /**\n * formatDate(sFormat, date)\n *\n * @param {string} sFormat (eg, \"F j, Y\", \"Y-m-d H:i:s\")\n * @param {Date} [date] (default is the current time)\n * @return {string}\n *\n * Supported identifiers in sFormat include:\n *\n * a: lowercase ante meridiem and post meridiem (am or pm)\n * d: day of the month, 2 digits with leading zeros (01,02,...,31)\n * D: 3-letter day of the week (\"Sun\",\"Mon\",...,\"Sat\")\n * F: month (\"January\",\"February\",...,\"December\")\n * g: hour in 12-hour format, without leading zeros (1,2,...,12)\n * h: hour in 24-hour format, without leading zeros (0,1,...,23)\n * H: hour in 24-hour format, with leading zeros (00,01,...,23)\n * i: minutes, with leading zeros (00,01,...,59)\n * j: day of the month, without leading zeros (1,2,...,31)\n * l: day of the week (\"Sunday\",\"Monday\",...,\"Saturday\")\n * m: month, with leading zeros (01,02,...,12)\n * M: 3-letter month (\"Jan\",\"Feb\",...,\"Dec\")\n * n: month, without leading zeros (1,2,...,12)\n * s: seconds, with leading zeros (00,01,...,59)\n * y: 2-digit year (eg, 14)\n * Y: 4-digit year (eg, 2014)\n *\n * For more inspiration, see: http://php.net/manual/en/function.date.php (of which we support ONLY a subset).\n */\n static formatDate(sFormat, date)\n {\n var sDate = \"\";\n if (!date) date = new Date();\n var iHour = date.getHours();\n var iDay = date.getDate();\n var iMonth = date.getMonth() + 1;\n for (var i = 0; i < sFormat.length; i++) {\n var ch;\n switch ((ch = sFormat.charAt(i))) {\n case 'a':\n sDate += (iHour < 12 ? \"am\" : \"pm\");\n break;\n case 'd':\n sDate += ('0' + iDay).slice(-2);\n break;\n case 'D':\n sDate += Usr.asDays[date.getDay()].substr(0, 3);\n break;\n case 'F':\n sDate += Usr.asMonths[iMonth - 1];\n break;\n case 'g':\n sDate += (!iHour ? 12 : (iHour > 12 ? iHour - 12 : iHour));\n break;\n case 'h':\n sDate += iHour;\n break;\n case 'H':\n sDate += ('0' + iHour).slice(-2);\n break;\n case 'i':\n sDate += ('0' + date.getMinutes()).slice(-2);\n break;\n case 'j':\n sDate += iDay;\n break;\n case 'l':\n sDate += Usr.asDays[date.getDay()];\n break;\n case 'm':\n sDate += ('0' + iMonth).slice(-2);\n break;\n case 'M':\n sDate += Usr.asMonths[iMonth - 1].substr(0, 3);\n break;\n case 'n':\n sDate += iMonth;\n break;\n case 's':\n sDate += ('0' + date.getSeconds()).slice(-2);\n break;\n case 'y':\n sDate += (\"\" + date.getFullYear()).slice(-2);\n break;\n case 'Y':\n sDate += date.getFullYear();\n break;\n default:\n sDate += ch;\n break;\n }\n }\n return sDate;\n }\n\n /**\n * defineBitFields(bfs)\n *\n * Prepares a bit field definition for use with getBitField() and setBitField(); eg:\n *\n * var bfs = Usr.defineBitFields({num:20, count:8, btmod:1, type:3});\n *\n * The above defines a set of bit fields containing four fields: num (bits 0-19), count (bits 20-27), btmod (bit 28), and type (bits 29-31).\n *\n * Usr.setBitField(bfs.num, n, 1);\n *\n * The above set bit field \"bfs.num\" in numeric variable \"n\" to the value 1.\n *\n * @param {Object} bfs\n * @return {BitFields}\n */\n static defineBitFields(bfs)\n {\n var bit = 0;\n for (var f in bfs) {\n var width = bfs[f];\n var mask = ((1 << width) - 1) << bit;\n bfs[f] = {mask: mask, shift: bit};\n bit += width;\n }\n return bfs;\n }\n\n /**\n * initBitFields(bfs, ...)\n *\n * @param {BitFields} bfs\n * @param {...number} var_args\n * @return {number} a value containing all supplied bit fields\n */\n static initBitFields(bfs, var_args)\n {\n var v = 0, i = 1;\n for (var f in bfs) {\n if (i >= arguments.length) break;\n v = Usr.setBitField(bfs[f], v, arguments[i++]);\n }\n return v;\n }\n\n /**\n * getBitField(bf, v)\n *\n * @param {BitField} bf\n * @param {number} v is a value containing bit fields\n * @return {number} the value of the bit field in v defined by bf\n */\n static getBitField(bf, v)\n {\n return (v & bf.mask) >> bf.shift;\n }\n\n /**\n * setBitField(bf, v, n)\n *\n * @param {BitField} bf\n * @param {number} v is a value containing bit fields\n * @param {number} n is a value to store in v in the bit field defined by bf\n * @return {number} updated v\n */\n static setBitField(bf, v, n)\n {\n return (v & ~bf.mask) | ((n << bf.shift) & bf.mask);\n }\n\n /**\n * indexOf(a, t, i)\n *\n * Use this instead of Array.prototype.indexOf() if you can't be sure the browser supports it.\n *\n * @param {Array} a\n * @param {*} t\n * @param {number} [i]\n * @returns {number}\n */\n static indexOf(a, t, i)\n {\n if (Array.prototype.indexOf) {\n return a.indexOf(t, i);\n }\n i = i || 0;\n if (i < 0) i += a.length;\n if (i < 0) i = 0;\n for (var n = a.length; i < n; i++) {\n if (i in a && a[i] === t) return i;\n }\n return -1;\n }\n}\n\nUsr.asDays = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nUsr.asMonths = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\nUsr.aMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\n/**\n * getTime()\n *\n * @return {number} the current time, in milliseconds\n */\nUsr.getTime = Date.now || function() { return +new Date(); };\n\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/weblib.js (C) Jeff Parsons 2012-2018\n */\n\n\n/*\n * According to http://www.w3schools.com/jsref/jsref_obj_global.asp, these are the *global* properties\n * and functions of JavaScript-in-the-Browser:\n *\n * Property Description\n * ---\n * Infinity A numeric value that represents positive/negative infinity\n * NaN \"Not-a-Number\" value\n * undefined Indicates that a variable has not been assigned a value\n *\n * Function Description\n * ---\n * decodeURI() Decodes a URI\n * decodeURIComponent() Decodes a URI component\n * encodeURI() Encodes a URI\n * encodeURIComponent() Encodes a URI component\n * escape() Deprecated in version 1.5. Use encodeURI() or encodeURIComponent() instead\n * eval() Evaluates a string and executes it as if it was script code\n * isFinite() Determines whether a value is a finite, legal number\n * isNaN() Determines whether a value is an illegal number\n * Number() Converts an object's value to a number\n * parseFloat() Parses a string and returns a floating point number\n * parseInt() Parses a string and returns an integer\n * String() Converts an object's value to a string\n * unescape() Deprecated in version 1.5. Use decodeURI() or decodeURIComponent() instead\n *\n * And according to http://www.w3schools.com/jsref/obj_window.asp, these are the properties and functions\n * of the *window* object.\n *\n * Property Description\n * ---\n * closed Returns a Boolean value indicating whether a window has been closed or not\n * defaultStatus Sets or returns the default text in the statusbar of a window\n * document Returns the Document object for the window (See Document object)\n * frames Returns an array of all the frames (including iframes) in the current window\n * history Returns the History object for the window (See History object)\n * innerHeight Returns the inner height of a window's content area\n * innerWidth Returns the inner width of a window's content area\n * length Returns the number of frames (including iframes) in a window\n * location Returns the Location object for the window (See Location object)\n * name Sets or returns the name of a window\n * navigator Returns the Navigator object for the window (See Navigator object)\n * opener Returns a reference to the window that created the window\n * outerHeight Returns the outer height of a window, including toolbars/scrollbars\n * outerWidth Returns the outer width of a window, including toolbars/scrollbars\n * pageXOffset Returns the pixels the current document has been scrolled (horizontally) from the upper left corner of the window\n * pageYOffset Returns the pixels the current document has been scrolled (vertically) from the upper left corner of the window\n * parent Returns the parent window of the current window\n * screen Returns the Screen object for the window (See Screen object)\n * screenLeft Returns the x coordinate of the window relative to the screen\n * screenTop Returns the y coordinate of the window relative to the screen\n * screenX Returns the x coordinate of the window relative to the screen\n * screenY Returns the y coordinate of the window relative to the screen\n * self Returns the current window\n * status Sets or returns the text in the statusbar of a window\n * top Returns the topmost browser window\n *\n * Method Description\n * ---\n * alert() Displays an alert box with a message and an OK button\n * atob() Decodes a base-64 encoded string\n * blur() Removes focus from the current window\n * btoa() Encodes a string in base-64\n * clearInterval() Clears a timer set with setInterval()\n * clearTimeout() Clears a timer set with setTimeout()\n * close() Closes the current window\n * confirm() Displays a dialog box with a message and an OK and a Cancel button\n * createPopup() Creates a pop-up window\n * focus() Sets focus to the current window\n * moveBy() Moves a window relative to its current position\n * moveTo() Moves a window to the specified position\n * open() Opens a new browser window\n * print() Prints the content of the current window\n * prompt() Displays a dialog box that prompts the visitor for input\n * resizeBy() Resizes the window by the specified pixels\n * resizeTo() Resizes the window to the specified width and height\n * scroll() This method has been replaced by the scrollTo() method.\n * scrollBy() Scrolls the content by the specified number of pixels\n * scrollTo() Scrolls the content to the specified coordinates\n * setInterval() Calls a function or evaluates an expression at specified intervals (in milliseconds)\n * setTimeout() Calls a function or evaluates an expression after a specified number of milliseconds\n * stop() Stops the window from loading\n */\n\nclass Web {\n /**\n * log(s, type)\n *\n * For diagnostic output only. DEBUG must be true (or \"--debug\" specified via the command-line)\n * for Component.log() to display anything.\n *\n * @param {string} [s] is the message text\n * @param {string} [type] is the message type\n */\n static log(s, type)\n {\n Component.log(s, type);\n }\n\n /**\n * notice(s, fPrintOnly, id)\n *\n * @param {string} s is the message text\n * @param {boolean} [fPrintOnly]\n * @param {string} [id] is the caller's ID, if any\n */\n static notice(s, fPrintOnly, id)\n {\n Component.notice(s, fPrintOnly, id);\n }\n\n /**\n * alertUser(sMessage)\n * \n * NOTE: Legacy function for older modules (eg, DiskDump); see Component.alertUser().\n *\n * @param {string} sMessage\n */\n static alertUser(sMessage)\n {\n if (window) {\n window.alert(sMessage);\n } else {\n Web.log(sMessage);\n }\n }\n\n /**\n * getResource(sURL, type, fAsync, done, progress)\n *\n * Request the specified resource (sURL), and once the request is complete, notify done().\n *\n * If fAsync is true, a done() callback should ALWAYS be supplied; otherwise, you'll have no\n * idea when the request is complete or what the response was. done() is passed three parameters:\n *\n * done(sURL, resource, nErrorCode)\n *\n * If nErrorCode is zero, resource should contain the requested data; otherwise, an error occurred.\n *\n * If type is set to a string, that string can be used to control the response format;\n * by default, the response format is plain text, but you can specify \"arraybuffer\" to request arbitrary\n * binary data, in which case the returned resource will be a ArrayBuffer rather than a string.\n *\n * @param {string} sURL\n * @param {string|Object|null} [type] (object for POST request, otherwise type of GET request)\n * @param {boolean} [fAsync] is true for an asynchronous request; false otherwise (MUST be set for IE)\n * @param {function(string,string,number)} [done]\n * @param {function(number)} [progress]\n * @return {Array|null} Array containing [resource, nErrorCode], or null if no response available (yet)\n */\n static getResource(sURL, type = \"text\", fAsync = false, done, progress)\n {\n var nErrorCode = 0, resource = null, response = null;\n\n if (typeof resources == 'object' && (resource = resources[sURL])) {\n if (done) done(sURL, resource, nErrorCode);\n return [resource, nErrorCode];\n }\n else if (fAsync && typeof resources == 'function') {\n resources(sURL, function(resource, nErrorCode)\n {\n if (done) done(sURL, resource, nErrorCode);\n });\n return response;\n }\n\n if (DEBUG) {\n /*\n * The larger resources we put on archive.pcjs.org should also be available locally.\n *\n * NOTE: \"http://archive.pcjs.org\" is now \"https://s3-us-west-2.amazonaws.com/archive.pcjs.org\"\n */\n sURL = sURL.replace(/^(http:\\/\\/archive\\.pcjs\\.org|https:\\/\\/s3-us-west-2\\.amazonaws\\.com\\/archive\\.pcjs\\.org)(\\/.*)\\/([^\\/]*)$/, \"$2/archive/$3\");\n }\n\n\n var request = (window.XMLHttpRequest? new window.XMLHttpRequest() : new window.ActiveXObject(\"Microsoft.XMLHTTP\"));\n var fArrayBuffer = false, fXHR2 = (typeof request.responseType === 'string');\n \n var callback = function() {\n if (request.readyState !== 4) {\n if (progress) progress(1);\n return null;\n }\n /*\n * The following line was recommended for WebKit, as a work-around to prevent the handler firing multiple\n * times when debugging. Unfortunately, that's not the only XMLHttpRequest problem that occurs when\n * debugging, so I think the WebKit problem is deeper than that. When we have multiple XMLHttpRequests\n * pending, any debugging activity means most of them simply get dropped on floor, so what may actually be\n * happening are mis-notifications rather than redundant notifications.\n *\n * request.onreadystatechange = undefined;\n */\n /*\n * If the request failed due to, say, a CORS policy denial; eg:\n * \n * Failed to load http://www.allbootdisks.com/downloads/Disks/Windows_95_Boot_Disk_Download48/Diskette%20Images/Windows95a.img:\n * Redirect from 'http://www.allbootdisks.com/downloads/Disks/Windows_95_Boot_Disk_Download48/Diskette%20Images/Windows95a.img' to\n * 'http://www.allbootdisks.com/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.\n * Origin 'http://pcjs:8088' is therefore not allowed access.\n * \n * and our request type was \"arraybuffer\", attempting to access responseText may trigger an exception; eg:\n * \n * Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's\n * 'responseType' is '' or 'text' (was 'arraybuffer').\n * \n * We could tiptoe around these potential landmines, but the safest thing to do is wrap this code with try/catch.\n */\n try {\n resource = fArrayBuffer? request.response : request.responseText;\n } catch(err) {\n if (MAXDEBUG) Web.log(\"xmlHTTPRequest(\" + sURL + \") exception: \" + err.message);\n }\n /*\n * The normal \"success\" case is a non-null resource and an HTTP status code of 200, but when loading files from the\n * local file system (ie, when using the \"file:\" protocol), we have to be a bit more flexible.\n */\n if (resource != null && (request.status == 200 || !request.status && resource.length && Web.getHostProtocol() == \"file:\")) {\n if (MAXDEBUG) Web.log(\"xmlHTTPRequest(\" + sURL + \"): returned \" + resource.length + \" bytes\");\n }\n else {\n nErrorCode = request.status || -1;\n Web.log(\"xmlHTTPRequest(\" + sURL + \"): error code \" + nErrorCode);\n }\n if (progress) progress(2);\n if (done) done(sURL, resource, nErrorCode);\n return [resource, nErrorCode];\n };\n \n if (fAsync) {\n request.onreadystatechange = callback;\n }\n\n if (progress) progress(0);\n\n if (type && typeof type == \"object\") {\n var sPost = \"\";\n for (var p in type) {\n if (!type.hasOwnProperty(p)) continue;\n if (sPost) sPost += \"&\";\n sPost += p + '=' + encodeURIComponent(type[p]);\n }\n sPost = sPost.replace(/%20/g, '+');\n if (MAXDEBUG) Web.log(\"Web.getResource(POST \" + sURL + \"): \" + sPost.length + \" bytes\");\n request.open(\"POST\", sURL, fAsync);\n request.setRequestHeader(\"Content-type\", \"application/x-www-form-urlencoded\");\n request.send(sPost);\n } else {\n if (MAXDEBUG) Web.log(\"Web.getResource(GET \" + sURL + \")\");\n request.open(\"GET\", sURL, fAsync);\n if (type == \"arraybuffer\") {\n if (fXHR2) {\n fArrayBuffer = true;\n request.responseType = type;\n } else {\n request.overrideMimeType(\"text/plain; charset=x-user-defined\");\n }\n }\n request.send();\n }\n\n if (!fAsync) {\n request.readyState = 4; // this may already be set for synchronous requests, but I don't want to take any chances \n response = callback();\n }\n return response;\n }\n\n /**\n * parseMemoryResource(sURL, sData)\n *\n * This converts a variety of JSON-style data streams into an Object with the following properties:\n *\n * aBytes\n * aSymbols\n * addrLoad\n * addrExec\n *\n * If the source data contains a 'bytes' array, it's passed through to 'aBytes'; alternatively, if\n * it contains a 'words' array, the values are converted from 16-bit to 8-bit and stored in 'aBytes',\n * and if it contains a 'longs' array, the values are converted from 32-bit longs into bytes and\n * stored in 'aBytes'.\n *\n * Alternatively, if the source data contains a 'data' array, we simply pass that through to the output\n * object as:\n *\n * aData\n *\n * @param {string} sURL\n * @param {string} sData\n * @return {Object|null} (resource)\n */\n static parseMemoryResource(sURL, sData)\n {\n var i;\n var resource = {\n aBytes: null,\n aSymbols: null,\n addrLoad: null,\n addrExec: null\n };\n\n if (sData.charAt(0) == \"[\" || sData.charAt(0) == \"{\") {\n try {\n var a, ib, data;\n\n if (sData.substr(0, 1) == \"<\") { // if the \"data\" begins with a \"<\"...\n /*\n * Early server configs reported an error (via the nErrorCode parameter) if a tape URL was invalid,\n * but more recent server configs now display a somewhat friendlier HTML error page. The downside,\n * however, is that the original error has been buried, and we've received \"data\" that isn't actually\n * tape data. So if the data we've received appears to be \"HTML-like\", we treat it as an error message.\n */\n throw new Error(sData);\n }\n\n /*\n * TODO: IE9 is rather unfriendly and restrictive with regard to how much data it's willing to\n * eval(). In particular, the 10Mb disk image we use for the Windows 1.01 demo config fails in\n * IE9 with an \"Out of memory\" exception. One work-around would be to chop the data into chunks\n * (perhaps one track per chunk, using regular expressions) and then manually re-assemble it.\n *\n * However, it turns out that using JSON.parse(sDiskData) instead of eval(\"(\" + sDiskData + \")\")\n * is a much easier fix. The only drawback is that we must first quote any unquoted property names\n * and remove any comments, because while eval() was cool with them, JSON.parse() is more particular;\n * the following RegExp replacements take care of those requirements.\n *\n * The use of hex values is something else that eval() was OK with, but JSON.parse() is not, and\n * while I've stopped using hex values in DumpAPI responses (at least when \"format=json\" is specified),\n * I can't guarantee they won't show up in \"legacy\" images, and there's no simple RegExp replacement\n * for transforming hex values into decimal values, so I cop out and fall back to eval() if I detect\n * any hex prefixes (\"0x\") in the sequence. Ditto for error messages, which appear like so:\n *\n * [\"unrecognized disk path: test.img\"]\n */\n if (sData.indexOf(\"0x\") < 0 && sData.indexOf(\"0o\") < 0 && sData.substr(0, 2) != '[\"') {\n data = JSON.parse(sData.replace(/([a-z]+):/gm, '\"$1\":').replace(/\\/\\/[^\\n]*/gm, \"\"));\n } else {\n data = eval(\"(\" + sData + \")\");\n }\n\n resource.addrLoad = data['load'];\n resource.addrExec = data['exec'];\n\n if (a = data['bytes']) {\n resource.aBytes = a;\n }\n else if (a = data['words']) {\n /*\n * Convert all words into bytes\n */\n resource.aBytes = new Array(a.length * 2);\n for (i = 0, ib = 0; i < a.length; i++) {\n resource.aBytes[ib++] = a[i] & 0xff;\n resource.aBytes[ib++] = (a[i] >> 8) & 0xff;\n\n }\n }\n else if (a = data['longs']) {\n /*\n * Convert all dwords (longs) into bytes\n */\n resource.aBytes = new Array(a.length * 4);\n for (i = 0, ib = 0; i < a.length; i++) {\n resource.aBytes[ib++] = a[i] & 0xff;\n resource.aBytes[ib++] = (a[i] >> 8) & 0xff;\n resource.aBytes[ib++] = (a[i] >> 16) & 0xff;\n resource.aBytes[ib++] = (a[i] >> 24) & 0xff;\n }\n }\n else if (a = data['data']) {\n resource.aData = a;\n }\n else {\n resource.aBytes = data;\n }\n\n if (resource.aBytes) {\n if (!resource.aBytes.length) {\n Component.error(\"Empty resource: \" + sURL);\n resource = null;\n }\n else if (resource.aBytes.length == 1) {\n Component.error(resource.aBytes[0]);\n resource = null;\n }\n }\n resource.aSymbols = data['symbols'];\n\n } catch (e) {\n Component.error(\"Resource data error (\" + sURL + \"): \" + e.message);\n resource = null;\n }\n }\n else {\n /*\n * Parse the data manually; we assume it's a series of hex byte-values separated by whitespace.\n */\n var ab = [];\n var sHexData = sData.replace(/\\n/gm, \" \").replace(/ +$/, \"\");\n var asHexData = sHexData.split(\" \");\n for (i = 0; i < asHexData.length; i++) {\n var n = parseInt(asHexData[i], 16);\n if (isNaN(n)) {\n Component.error(\"Resource data error (\" + sURL + \"): invalid hex byte (\" + asHexData[i] + \")\");\n break;\n }\n ab.push(n & 0xff);\n }\n if (i == asHexData.length) resource.aBytes = ab;\n }\n return resource;\n }\n\n /**\n * sendReport(sApp, sVer, sURL, sUser, sType, sReport, sHostName)\n *\n * Send a report (eg, bug report) to the server.\n *\n * @param {string} sApp (eg, \"PCjs\")\n * @param {string} sVer (eg, \"1.02\")\n * @param {string} sURL (eg, \"/devices/pc/machine/5150/mda/64kb/machine.xml\")\n * @param {string} sUser (ie, the user key, if any)\n * @param {string} sType (eg, \"bug\"); one of ReportAPI.TYPE.*\n * @param {string} sReport (eg, unparsed state data)\n * @param {string} [sHostName] (default is http://SITEHOST)\n */\n static sendReport(sApp, sVer, sURL, sUser, sType, sReport, sHostName)\n {\n var dataPost = {};\n dataPost[ReportAPI.QUERY.APP] = sApp;\n dataPost[ReportAPI.QUERY.VER] = sVer;\n dataPost[ReportAPI.QUERY.URL] = sURL;\n dataPost[ReportAPI.QUERY.USER] = sUser;\n dataPost[ReportAPI.QUERY.TYPE] = sType;\n dataPost[ReportAPI.QUERY.DATA] = sReport;\n var sReportURL = (sHostName? sHostName : \"http://\" + SITEHOST) + ReportAPI.ENDPOINT;\n Web.getResource(sReportURL, dataPost, true);\n }\n\n /**\n * getHost()\n *\n * @return {string}\n */\n static getHost()\n {\n return (\"http://\" + (window? window.location.host : SITEHOST));\n }\n\n /**\n * getHostURL()\n *\n * @return {string|null}\n */\n static getHostURL()\n {\n return (window? window.location.href : null);\n }\n\n /**\n * getHostProtocol()\n *\n * @return {string}\n */\n static getHostProtocol()\n {\n return (window? window.location.protocol : \"file:\");\n }\n\n /**\n * getUserAgent()\n *\n * @return {string}\n */\n static getUserAgent()\n {\n return (window? window.navigator.userAgent : \"\");\n }\n\n /**\n * hasLocalStorage\n *\n * true if localStorage support exists, is enabled, and works; false otherwise\n *\n * @return {boolean}\n */\n static hasLocalStorage()\n {\n if (Web.fLocalStorage == null) {\n var f = false;\n if (window) {\n try {\n window.localStorage.setItem(Web.sLocalStorageTest, Web.sLocalStorageTest);\n f = (window.localStorage.getItem(Web.sLocalStorageTest) == Web.sLocalStorageTest);\n window.localStorage.removeItem(Web.sLocalStorageTest);\n } catch (e) {\n Web.logLocalStorageError(e);\n f = false;\n }\n }\n Web.fLocalStorage = f;\n }\n return Web.fLocalStorage;\n }\n\n /**\n * logLocalStorageError(e)\n *\n * @param {Error} e is an exception\n */\n static logLocalStorageError(e)\n {\n Web.log(e.message, \"localStorage error\");\n }\n\n /**\n * getLocalStorageItem(sKey)\n *\n * Returns the requested key value, or null if the key does not exist, or undefined if localStorage is not available\n *\n * @param {string} sKey\n * @return {string|null|undefined} sValue\n */\n static getLocalStorageItem(sKey)\n {\n var sValue;\n if (window) {\n try {\n sValue = window.localStorage.getItem(sKey);\n } catch (e) {\n Web.logLocalStorageError(e);\n }\n }\n return sValue;\n }\n\n /**\n * setLocalStorageItem(sKey, sValue)\n *\n * @param {string} sKey\n * @param {string} sValue\n * @return {boolean} true if localStorage is available, false if not\n */\n static setLocalStorageItem(sKey, sValue)\n {\n try {\n window.localStorage.setItem(sKey, sValue);\n return true;\n } catch (e) {\n Web.logLocalStorageError(e);\n }\n return false;\n }\n\n /**\n * removeLocalStorageItem(sKey)\n *\n * @param {string} sKey\n */\n static removeLocalStorageItem(sKey)\n {\n try {\n window.localStorage.removeItem(sKey);\n } catch (e) {\n Web.logLocalStorageError(e);\n }\n }\n\n /**\n * getLocalStorageKeys()\n *\n * @return {Array}\n */\n static getLocalStorageKeys()\n {\n var a = [];\n try {\n for (var i = 0, c = window.localStorage.length; i < c; i++) {\n a.push(window.localStorage.key(i));\n }\n } catch (e) {\n Web.logLocalStorageError(e);\n }\n return a;\n }\n\n /**\n * reloadPage()\n */\n static reloadPage()\n {\n if (window) window.location.reload();\n }\n\n /**\n * isUserAgent(s)\n *\n * Check the browser's user-agent string for the given substring; \"iOS\" and \"MSIE\" are special values you can\n * use that will match any iOS or MSIE browser, respectively (even IE11, in the case of \"MSIE\").\n *\n * 2013-11-06: In a questionable move, MSFT changed the user-agent reported by IE11 on Windows 8.1, eliminating\n * the \"MSIE\" string (which MSDN calls a \"version token\"; see http://msdn.microsoft.com/library/ms537503.aspx);\n * they say \"public websites should rely on feature detection, rather than browser detection, in order to design\n * their sites for browsers that don't support the features used by the website.\" So, in IE11, we get a user-agent\n * that tries to fool apps into thinking the browser is more like WebKit or Gecko:\n *\n * Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko\n *\n * That's a nice idea, but in the meantime, they hosed the XSL transform code in embed.js, which contained\n * some very critical browser-specific code; turning on IE's \"Compatibility Mode\" didn't help either, because\n * that's a sledgehammer solution which restores the old user-agent string but also disables other features like\n * HTML5 canvas support. As an interim solution, I'm treating any \"MSIE\" check as a check for either \"MSIE\" or\n * \"Trident\".\n *\n * UPDATE: I've since found ways to make the code in embed.js more browser-agnostic, so for now, there's isn't\n * any code that cares about \"MSIE\", but I've left the change in place, because I wouldn't be surprised if I'll\n * need more IE-specific code in the future, perhaps for things like copy/paste functionality, or mouse capture.\n *\n * @param {string} s is a substring to search for in the user-agent; as noted above, \"iOS\" and \"MSIE\" are special values\n * @return {boolean} is true if the string was found, false if not\n */\n static isUserAgent(s)\n {\n if (window) {\n var userAgent = Web.getUserAgent();\n /*\n * Here's one case where we have to be careful with Component, because when isUserAgent() is called by\n * the init code below, component.js hasn't been loaded yet. The simple solution for now is to remove the call.\n *\n * Web.log(\"agent: \" + userAgent);\n *\n * And yes, it would be pointless to use the conditional (?) operator below, if not for the Google Closure\n * Compiler (v20130823) failing to detect the entire expression as a boolean.\n */\n return s == \"iOS\" && !!userAgent.match(/(iPod|iPhone|iPad)/) && !!userAgent.match(/AppleWebKit/) || s == \"MSIE\" && !!userAgent.match(/(MSIE|Trident)/) || (userAgent.indexOf(s) >= 0);\n }\n return false;\n }\n\n /**\n * isMobile()\n *\n * Check the browser's user-agent string for the substring \"Mobi\", as per Mozilla recommendation:\n *\n * https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent\n *\n * @return {boolean} is true if the browser appears to be a mobile (ie, non-desktop) web browser, false if not\n */\n static isMobile()\n {\n return Web.isUserAgent(\"Mobi\");\n }\n\n /**\n * findProperty(obj, sProp, sSuffix)\n *\n * If both sProp and sSuffix are set, then any browser-specific prefixes are inserted between sProp and sSuffix,\n * and if a match is found, it is returned without sProp.\n *\n * For example, if findProperty(document, 'on', 'fullscreenchange') discovers that 'onwebkitfullscreenchange' exists,\n * it will return 'webkitfullscreenchange', in preparation for an addEventListener() call.\n *\n * More commonly, sSuffix is not used, so whatever property is found is returned as-is.\n *\n * @param {Object|null|undefined} obj\n * @param {string} sProp\n * @param {string} [sSuffix]\n * @return {string|null}\n */\n static findProperty(obj, sProp, sSuffix)\n {\n if (obj) {\n for (var i = 0; i < Web.asBrowserPrefixes.length; i++) {\n var sName = Web.asBrowserPrefixes[i];\n if (sSuffix) {\n sName += sSuffix;\n var sEvent = sProp + sName;\n if (sEvent in obj) return sName;\n } else {\n if (!sName) {\n sName = sProp[0];\n } else {\n sName += sProp[0].toUpperCase();\n }\n sName += sProp.substr(1);\n if (sName in obj) return sName;\n }\n }\n }\n return null;\n }\n\n /**\n * getURLParm(sParm)\n *\n * First looks for sParm exactly as specified, then looks for the lower-case version.\n *\n * @param {string} sParm\n * @return {string|undefined}\n */\n static getURLParm(sParm)\n {\n if (!Web.parmsURL) {\n Web.parmsURL = Web.parseURLParms();\n }\n return Web.parmsURL[sParm] || Web.parmsURL[sParm.toLowerCase()];\n }\n\n /**\n * parseURLParms(sParms)\n *\n * @param {string} [sParms] containing the parameter portion of a URL (ie, after the '?')\n * @return {Object} containing properties for each parameter found\n */\n static parseURLParms(sParms)\n {\n var aParms = {};\n if (window) { // an alternative to \"if (typeof module === 'undefined')\" if require(\"defines\") was used\n if (!sParms) {\n /*\n * Note that window.location.href returns the entire URL, whereas window.location.search\n * returns only the parameters, if any (starting with the '?', which we skip over with a substr() call).\n */\n sParms = window.location.search.substr(1);\n }\n var match;\n var pl = /\\+/g; // RegExp for replacing addition symbol with a space\n var search = /([^&=]+)=?([^&]*)/g;\n var decode = function(s)\n {\n return decodeURIComponent(s.replace(pl, \" \"));\n };\n\n while ((match = search.exec(sParms))) {\n aParms[decode(match[1])] = decode(match[2]);\n }\n }\n return aParms;\n }\n\n /**\n * downloadFile(sData, sType, fBase64, sFileName)\n *\n * @param {string} sData\n * @param {string} sType\n * @param {boolean} [fBase64]\n * @param {string} [sFileName]\n */\n static downloadFile(sData, sType, fBase64, sFileName)\n {\n var link = null, sAlert;\n var sURI = \"data:application/\" + sType + (fBase64? \";base64\" : \"\") + \",\";\n\n if (!Web.isUserAgent(\"Firefox\")) {\n sURI += (fBase64? sData : encodeURI(sData));\n } else {\n sURI += (fBase64? sData : encodeURIComponent(sData));\n }\n if (sFileName) {\n link = document.createElement('a');\n if (typeof link.download != 'string') link = null;\n }\n if (link) {\n link.href = sURI;\n link.download = sFileName;\n document.body.appendChild(link); // Firefox allegedly requires the link to be in the body\n link.click();\n document.body.removeChild(link);\n sAlert = 'Check your Downloads folder for ' + sFileName + '.';\n } else {\n window.open(sURI);\n sAlert = 'Check your browser for a new window/tab containing the requested data' + (sFileName? (' (' + sFileName + ')') : '') + '.';\n }\n return sAlert;\n }\n\n /**\n * onCountRepeat(n, fnRepeat, fnComplete, msDelay)\n *\n * Call fnRepeat() n times with an msDelay millisecond delay between calls,\n * then call fnComplete() when n has been exhausted OR fnRepeat() returns false.\n *\n * @param {number} n\n * @param {function()} fnRepeat\n * @param {function()} fnComplete\n * @param {number} [msDelay]\n */\n static onCountRepeat(n, fnRepeat, fnComplete, msDelay)\n {\n var fnTimeout = function doCountRepeat()\n {\n n -= 1;\n if (n >= 0) {\n if (!fnRepeat()) n = 0;\n }\n if (n > 0) {\n setTimeout(fnTimeout, msDelay || 0);\n return;\n }\n fnComplete();\n };\n fnTimeout();\n }\n\n /**\n * onClickRepeat(e, msDelay, msRepeat, fn)\n *\n * Repeatedly call fn() with an initial msDelay, and an msRepeat delay thereafter,\n * as long as HTML control Object e has an active \"down\" event and fn() returns true.\n *\n * @param {Object} e\n * @param {number} msDelay\n * @param {number} msRepeat\n * @param {function(boolean)} fn is passed false on the first call, true on all repeated calls\n */\n static onClickRepeat(e, msDelay, msRepeat, fn)\n {\n var ms = 0, timer = null, fIgnoreMouseEvents = false;\n\n var fnRepeat = function doClickRepeat()\n {\n if (fn(ms === msRepeat)) {\n timer = setTimeout(fnRepeat, ms);\n ms = msRepeat;\n }\n };\n e.onmousedown = function()\n {\n // Web.log(\"onMouseDown()\");\n if (!fIgnoreMouseEvents) {\n if (!timer) {\n ms = msDelay;\n fnRepeat();\n }\n }\n };\n e.ontouchstart = function()\n {\n // Web.log(\"onTouchStart()\");\n if (!timer) {\n ms = msDelay;\n fnRepeat();\n }\n };\n e.onmouseup = e.onmouseout = function()\n {\n // Web.log(\"onMouseUp()/onMouseOut()\");\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n e.ontouchend = e.ontouchcancel = function()\n {\n // Web.log(\"onTouchEnd()/onTouchCancel()\");\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n /*\n * Devices that generate ontouch* events ALSO generate onmouse* events,\n * and generally do so immediately after all the touch events are complete,\n * so unless we want double the action, we need to ignore mouse events.\n */\n fIgnoreMouseEvents = true;\n };\n }\n\n /**\n * onPageEvent(sName, fn)\n *\n * For 'onload', 'onunload', and 'onpageshow' events, most callers should NOT use this function, but\n * instead use Web.onInit(), Web.onShow(), and Web.onExit(), respectively.\n *\n * The only components that should still use onPageEvent() are THIS component (see the bottom of this file)\n * and components that need to capture other events (eg, the 'onresize' event in the Video component).\n *\n * This function creates a chain of callbacks, allowing multiple JavaScript modules to define handlers\n * for the same event, which wouldn't be possible if everyone modified window['onload'], window['onunload'],\n * etc, themselves. However, that's less of a concern now, because assuming everyone else is now using\n * onInit(), onExit(), etc, then there really IS only one component setting the window callback: this one.\n *\n * NOTE: It's risky to refer to obscure event handlers with \"dot\" names, because the Closure Compiler may\n * erroneously replace them (eg, window.onpageshow is a good example).\n *\n * @param {string} sFunc\n * @param {function()} fn\n */\n static onPageEvent(sFunc, fn)\n {\n if (window) {\n var fnPrev = window[sFunc];\n if (typeof fnPrev !== 'function') {\n window[sFunc] = fn;\n } else {\n /*\n * TODO: Determine whether there's any value in receiving/sending the Event object that the\n * browser provides when it generates the original event.\n */\n window[sFunc] = function onWindowEvent()\n {\n if (fnPrev) fnPrev();\n fn();\n };\n }\n }\n };\n\n /**\n * onInit(fn)\n *\n * Use this instead of setting window.onload. Allows multiple JavaScript modules to define their own 'onload' event handler.\n *\n * @param {function()} fn\n */\n static onInit(fn)\n {\n Web.aPageEventHandlers['init'].push(fn);\n };\n\n /**\n * onShow(fn)\n *\n * @param {function()} fn\n *\n * Use this instead of setting window.onpageshow. Allows multiple JavaScript modules to define their own 'onpageshow' event handler.\n */\n static onShow(fn)\n {\n Web.aPageEventHandlers['show'].push(fn);\n };\n\n /**\n * onExit(fn)\n *\n * @param {function()} fn\n *\n * Use this instead of setting window.onunload. Allows multiple JavaScript modules to define their own 'onunload' event handler.\n */\n static onExit(fn)\n {\n Web.aPageEventHandlers['exit'].push(fn);\n };\n\n /**\n * doPageEvent(afn)\n *\n * @param {Array.} afn\n */\n static doPageEvent(afn)\n {\n if (Web.fPageEventsEnabled) {\n try {\n for (var i = 0; i < afn.length; i++) {\n afn[i]();\n }\n } catch (e) {\n Web.notice(\"An unexpected error occurred: \" + e.message + \"\\n\\nIf it happens again, please send this information to support@pcjs.org. Thanks.\");\n }\n }\n };\n\n /**\n * enablePageEvents(fEnable)\n *\n * @param {boolean} fEnable is true to enable page events, false to disable (they're enabled by default)\n */\n static enablePageEvents(fEnable)\n {\n if (!Web.fPageEventsEnabled && fEnable) {\n Web.fPageEventsEnabled = true;\n if (Web.fPageLoaded) Web.sendPageEvent('init');\n if (Web.fPageShowed) Web.sendPageEvent('show');\n return;\n }\n Web.fPageEventsEnabled = fEnable;\n }\n\n /**\n * sendPageEvent(sEvent)\n *\n * This allows us to manually trigger page events.\n *\n * @param {string} sEvent (one of 'init', 'show' or 'exit')\n */\n static sendPageEvent(sEvent)\n {\n if (Web.aPageEventHandlers[sEvent]) {\n Web.doPageEvent(Web.aPageEventHandlers[sEvent]);\n }\n }\n}\n\nWeb.parmsURL = null; // initialized on first call to parseURLParms()\n\nWeb.aPageEventHandlers = {\n 'init': [], // list of window 'onload' handlers\n 'show': [], // list of window 'onpageshow' handlers\n 'exit': [] // list of window 'onunload' handlers (although we prefer to use 'onbeforeunload' if possible)\n};\n\nWeb.asBrowserPrefixes = ['', 'moz', 'ms', 'webkit'];\n\nWeb.fPageLoaded = false; // set once the page's first 'onload' event has occurred\nWeb.fPageShowed = false; // set once the page's first 'onpageshow' event has occurred\nWeb.fPageEventsEnabled = true; // default is true, set to false (or true) by enablePageEvents()\n\n/**\n * fLocalStorage\n *\n * true if localStorage support exists, is enabled, and works; \"falsey\" otherwise\n *\n * @type {boolean|null}\n */\nWeb.fLocalStorage = null;\n\n/**\n * TODO: Is there any way to get the Closure Compiler to stop inlining this string? This isn't cutting it.\n *\n * @const {string}\n */\nWeb.sLocalStorageTest = \"PCjs.localStorage\";\n\nWeb.onPageEvent('onload', function onPageLoad() {\n Web.fPageLoaded = true;\n Web.doPageEvent(Web.aPageEventHandlers['init']);\n});\n\nWeb.onPageEvent('onpageshow', function onPageShow() {\n Web.fPageShowed = true;\n Web.doPageEvent(Web.aPageEventHandlers['show']);\n});\n\nWeb.onPageEvent(Web.isUserAgent(\"iOS\")? 'onpagehide' : (Web.isUserAgent(\"Opera\")? 'onunload' : 'onbeforeunload'), function onPageUnload() {\n Web.doPageEvent(Web.aPageEventHandlers['exit']);\n});\n\n\n\n/**\n * @copyright https://www.pcjs.org/modules/shared/lib/component.js (C) Jeff Parsons 2012-2018\n */\n\n/*\n * All PCjs components now use JSDoc types, primarily so that Google's Closure Compiler will compile\n * everything with zero warnings when ADVANCED_OPTIMIZATIONS are enabled. For more information about\n * the JSDoc types supported by the Closure Compiler:\n *\n * https://developers.google.com/closure/compiler/docs/js-for-compiler#types\n *\n * I also attempted to validate this code with JSLint, but it complained too much; eg, it didn't like\n * \"while (true)\", a tried and \"true\" programming convention for decades, and it wanted me to replace\n * all \"++\" and \"--\" operators with \"+= 1\" and \"-= 1\", use \"(s || '')\" instead of \"(s? s : '')\", etc.\n *\n * I prefer sticking with traditional C-style idioms, in part because they are more portable. That\n * does NOT mean I'm trying to write \"portable JavaScript,\" but some of this code was ported from C code\n * I'd written long ago, so portability is good, and I'm not going to throw that away if there's no need.\n *\n * UPDATE: I've since switched from JSLint to JSHint, which seems to have more reasonable defaults.\n * And for new code, I have adopted some popular JavaScript idioms, like \"(s || '')\", although the need\n * for those kinds of expressions will be reduced as I also start adopting some ES6 features, like\n * default parameters.\n */\n\n\n/**\n * Since the Closure Compiler treats ES6 classes as @struct rather than @dict by default,\n * it deters us from defining named properties on our components; eg:\n *\n * this['exports'] = {...}\n *\n * results in an error:\n *\n * Cannot do '[]' access on a struct\n *\n * So, in order to define 'exports', we must override the @struct assumption by annotating\n * the class as @unrestricted (or @dict). Note that this must be done both here and in the\n * subclass (eg, SerialPort), because otherwise the Compiler won't allow us to *reference*\n * the named property either.\n *\n * TODO: Consider marking ALL our classes unrestricted, because otherwise it forces us to\n * define every single property the class uses in its constructor, which results in a fair\n * bit of redundant initialization, since many properties aren't (and don't need to be) fully\n * initialized until the appropriate init(), reset(), restore(), etc. function is called.\n *\n * The upside, however, may be that since the structure of the class is completely defined by\n * the constructor, JavaScript engines may be able to optimize and run more efficiently.\n *\n * @unrestricted\n */\nclass Component {\n /**\n * Component(type, parms, bitsMessage)\n *\n * A Component object requires:\n *\n * type: a user-defined type name (eg, \"CPU\")\n *\n * and accepts any or all of the following (parms) properties:\n *\n * id: component ID (default is \"\")\n * name: component name (default is \"\"; if blank, toString() will use the type name only)\n * comment: component comment string (default is undefined)\n *\n * Component subclasses will usually have additional (parms) properties.\n *\n * @param {string} type\n * @param {Object} [parms]\n * @param {number} [bitsMessage] selects message(s) that the component wants to enable (default is 0)\n */\n constructor(type, parms, bitsMessage)\n {\n this.type = type;\n\n if (!parms) parms = {'id': \"\", 'name': \"\"};\n\n this.id = parms['id'] || \"\";\n this.name = parms['name'];\n this.comment = parms['comment'];\n this.parms = parms;\n\n /*\n * The following Component properties need to be accessible by other machines and/or command scripts;\n * well, OK, or we could have exported some new functions to walk the contents of these properties, as we\n * did with findMachineComponent(), but this works just as well.\n *\n * Also, while the double-assignment looks silly (ie, using both dot and bracket property notation), it\n * resolves a complaint from the Closure Compiler, because if we use ONLY bracket notation here, then the\n * Compiler wants us to change all the other references to bracket notation as well.\n */\n this.exports = this['exports'] = {};\n this.bindings = this['bindings'] = {};\n\n var i = this.id.indexOf('.');\n if (i < 0) {\n this.idComponent = this.id;\n } else {\n this.idMachine = this.id.substr(0, i);\n this.idComponent = this.id.substr(i + 1);\n }\n\n /*\n * Gather all the various component flags (booleans) into a single \"flags\" object, and encourage\n * subclasses to do the same, to reduce the property clutter we have to wade through while debugging.\n */\n this.flags = {\n ready: false,\n busy: false,\n busyCancel: false,\n initDone: false,\n powered: false,\n unloading: false,\n error: false\n };\n\n this.fnReady = null;\n this.clearError();\n this.bitsMessage = bitsMessage || 0;\n\n this.cmp = null;\n this.bus = null;\n this.cpu = null;\n this.dbg = null;\n\n /*\n * TODO: Consider adding another parameter to the Component() constructor that allows components to tell\n * us if they support single or multiple instances per machine. For example, there can be multiple SerialPort\n * components per machine, but only one CPU component (some machines also support an FPU, but that component\n * is considered separate from the CPU).\n *\n * It's not critical, but it would help catch machine configuration errors; for example, a machine that mistakenly\n * includes two CPU components may, aside from wasting memory, end up with odd side-effects, like unresponsive\n * CPU controls.\n */\n Component.add(this);\n }\n\n /**\n * Component.add(component)\n *\n * @param {Component} component\n */\n static add(component)\n {\n /*\n * This just generates a lot of useless noise, handy in the early days, not so much these days....\n *\n * if (DEBUG) Component.log(\"Component.add(\" + component.type + \",\" + component.id + \")\");\n */\n Component.components.push(component);\n }\n\n /**\n * Component.addMachine(idMachine)\n *\n * @param {string} idMachine\n */\n static addMachine(idMachine)\n {\n Component.machines[idMachine] = {};\n }\n\n /**\n * Component.addMachineResource(idMachine, sName, data)\n *\n * @param {string} idMachine\n * @param {string|null} sName (name of the resource)\n * @param {*} data\n */\n static addMachineResource(idMachine, sName, data)\n {\n /*\n * I used to assert(Component.machines[idMachine]), but when we're running as a Node app, embed.js is not used,\n * so addMachine() is never called, so resources do not need to be recorded.\n */\n if (Component.machines[idMachine] && sName) {\n Component.machines[idMachine][sName] = data;\n }\n }\n\n /**\n * Component.getMachineResources(idMachine)\n *\n * @param {string} idMachine\n * @return {Object|undefined}\n */\n static getMachineResources(idMachine)\n {\n return Component.machines[idMachine];\n }\n\n /**\n * Component.getTime()\n *\n * @return {number} the current time, in milliseconds\n */\n static getTime()\n {\n return Date.now() || +new Date();\n }\n\n /**\n * Component.log(s, type)\n *\n * For diagnostic output only.\n *\n * @param {string} [s] is the message text\n * @param {string} [type] is the message type\n */\n static log(s, type)\n {\n if (!COMPILED) {\n if (s) {\n var sElapsed = \"\", sMsg = (type? (type + \": \") : \"\") + s;\n if (typeof Usr != \"undefined\") {\n if (Component.msStart === undefined) {\n Component.msStart = Component.getTime();\n }\n sElapsed = (Component.getTime() - Component.msStart) + \"ms: \";\n }\n sMsg = sMsg.replace(/\\r/g, '\\\\r').replace(/\\n/g, ' ');\n if (window && window.console) console.log(sElapsed + sMsg);\n }\n }\n }\n\n /**\n * Component.assert(f, s)\n *\n * Verifies conditions that must be true (for DEBUG builds only).\n *\n * The Closure Compiler should automatically remove all references to Component.assert() in non-DEBUG builds.\n * TODO: Add a task to the build process that \"asserts\" there are no instances of \"assertion failure\" in RELEASE builds.\n *\n * @param {boolean} f is the expression we are asserting to be true\n * @param {string} [s] is description of the assertion on failure\n */\n static assert(f, s)\n {\n if (DEBUG) {\n if (!f) {\n if (!s) s = \"assertion failure\";\n Component.log(s);\n throw new Error(s);\n }\n }\n }\n\n /**\n * Component.print(s)\n *\n * Components that inherit from this class should use this.print(), rather than Component.print(), because\n * if a Control Panel is loaded, it will override only the instance method, not the class method (overriding the\n * class method would improperly affect any other machines loaded on the same page).\n *\n * @this {Component}\n * @param {string} s\n */\n static print(s)\n {\n if (!COMPILED) {\n var i = s.lastIndexOf('\\n');\n if (i >= 0) {\n Component.println(s.substr(0, i));\n s = s.substr(i + 1);\n }\n Component.printBuffer += s;\n }\n }\n\n /**\n * Component.println(s, type, id)\n *\n * Components that inherit from this class should use this.println(), rather than Component.println(), because\n * if a Control Panel is loaded, it will override only the instance method, not the class method (overriding the\n * class method would improperly affect any other machines loaded on the same page).\n *\n * @param {string} [s] is the message text\n * @param {string} [type] is the message type\n * @param {string} [id] is the caller's ID, if any\n */\n static println(s, type, id)\n {\n if (!COMPILED) {\n s = Component.printBuffer + (s || \"\");\n Component.log((id? (id + \": \") : \"\") + (s? (\"\\\"\" + s + \"\\\"\") : \"\"), type);\n Component.printBuffer = \"\";\n }\n }\n\n /**\n * Component.notice(s, fPrintOnly, id)\n *\n * notice() is like println() but implies a need for user notification, so we alert() as well.\n *\n * @param {string} s is the message text\n * @param {boolean} [fPrintOnly]\n * @param {string} [id] is the caller's ID, if any\n * @return {boolean}\n */\n static notice(s, fPrintOnly, id)\n {\n if (!COMPILED) {\n Component.println(s, Component.PRINT.NOTICE, id);\n }\n if (!fPrintOnly) Component.alertUser((id? (id + \": \") : \"\") + s);\n return true;\n }\n\n /**\n * Component.warning(s)\n *\n * @param {string} s describes the warning\n */\n static warning(s)\n {\n if (!COMPILED) {\n Component.println(s, Component.PRINT.WARNING);\n }\n Component.alertUser(s);\n }\n\n /**\n * Component.error(s)\n *\n * @param {string} s describes the error; an alert() is displayed as well\n */\n static error(s)\n {\n if (!COMPILED) {\n Component.println(s, Component.PRINT.ERROR);\n }\n Component.alertUser(s);\n }\n\n /**\n * Component.alertUser(sMessage)\n *\n * @param {string} sMessage\n */\n static alertUser(sMessage)\n {\n if (window) {\n window.alert(sMessage);\n } else {\n Component.log(sMessage);\n }\n }\n\n /**\n * Component.confirmUser(sPrompt)\n *\n * @param {string} sPrompt\n * @returns {boolean} true if the user clicked OK, false if Cancel/Close\n */\n static confirmUser(sPrompt)\n {\n var fResponse = false;\n if (window) {\n fResponse = window.confirm(sPrompt);\n }\n return fResponse;\n }\n\n /**\n * Component.promptUser()\n *\n * @param {string} sPrompt\n * @param {string} [sDefault]\n * @returns {string|null}\n */\n static promptUser(sPrompt, sDefault)\n {\n var sResponse = null;\n if (window) {\n sResponse = window.prompt(sPrompt, sDefault === undefined? \"\" : sDefault);\n }\n return sResponse;\n }\n\n /**\n * Component.appendControl(control, sText)\n *\n * @param {Object} control\n * @param {string} sText\n */\n static appendControl(control, sText)\n {\n control.value += sText;\n /*\n * Prevent the + + +
+
+ +
+
+ + +
+
+
+ +
+
+ +

+
+ +
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + + ,autoStart: + + + + + + + + + + + + + + + 600 + + + + + keyboard + ,model: + + + + + + + + + + + + + + + false + + + + + serial + ,demo: + + + + + + + + + + + + + + disk + + + + + + + + + + + + + + + 0 + + + + + + + + + + + rom + ,size:,image:'' + + + + + + + + + + + + + + + 0 + + + + + ram + ,size: + + + + + + + + + + + + + + + 600 + + + + + + + 256 + + + + + + + 224 + + + + + + + black + + + + + + 32 + + + + + + 32 + + + + + + + 0 + + + + + + + 0 + + + + + + + + + + + + + + null + + + + + video + ,model:,screenWidth:,screenHeight:,charCols:,charRows:,charWidth:,charHeight:,charSet:'',screenColor:'',smoothing: + + + + + + + + + + + + + + debugger + + + + + + + + + + + + + + panel + + + + + + + + + + + + + [ + {} + , + ] + + + + computer + ,modules: + + + + + type:'',refID:'',start:,end: + + + diff --git a/versions/c1pjs/1.50.4/document.css b/versions/c1pjs/1.50.4/document.css new file mode 100644 index 0000000000..7072b406e4 --- /dev/null +++ b/versions/c1pjs/1.50.4/document.css @@ -0,0 +1,162 @@ +@CHARSET "UTF-8"; + +.page { + margin: 2% 2%; + padding: 2% 2%; + min-width: 30em; + overflow: auto; + font-size: large; + font-family: Helvetica, Arial, sans-serif; + background: #303030; + color: #ccc; + +} +.page-header { +} +.page-header-title { + text-align: center; + +} +.page a { + color: #7fc07f; + text-decoration: none; +} +a.footlink, a.paralink { + text-decoration: none; +} +a.footlink:link, a.paralink:link { + color: blue; +} +a.footlink:visited, a.paralink:visited { + color: blue; +} +.galleryitem { + float: left; + width: 200px; +} +.item { + float: left; + width: 2em; + text-indent: 1em; +} +.list { + margin-left: 3em; + text-indent: 0; + text-align: justify; +} +ul { + list-style: none; +} +div.pnumber { + float: left; + width: 2em; + text-indent: 1em; +} +div.pitem { + margin-left: 10em; +} +p.indent, .justified p { + text-indent: 2em; + text-align: justify; + line-height: 1.5em; +} +p.noindent { + text-indent: 0; + text-align: justify; +} +p.center, .center { + text-align: center; +} +li.para { + margin-top: 1em; + margin-bottom: 1em; +} +.left { + text-align: left; +} +.right { + text-align: right; +} +blockquote.tag { + font-size: small; + font-family: Monaco, Fixed, monospace; + margin-top: 0; + margin-bottom: 0; +} +.blockquote { + padding-left: 1em; + text-indent: 0; + text-align: justify; +} +.italics { + font-style: italic; +} +.medium { + font-size: medium; +} +.small { + font-size: x-small; +} +.smallcaps { + font-variant: small-caps; +} +.strike { + text-decoration: line-through; +} +.summation, .bracelist { + display: inline-block; + position: relative; + vertical-align: middle; + text-align: center; + margin-bottom: 0.5ex; + text-indent: 0; +} +.bracelist-symbol { + font-size: 3em; + vertical-align: -40%; +} +.summation .summation-lower, .summation .summation-upper, .bracelist-item { + display: block; + font-size: 75%; + text-align: center; +} +.summation .summation-upper { + margin-bottom: 0; + margin-left: 0.8ex; + font-style: italic; +} +.summation .summation-lower{ + margin-bottom: -0.6ex; + font-style: italic; +} +.summation .summation-symbol { + font-size: 2em; +} +p sup { + vertical-align: baseline; + position: relative; + bottom: .5em; + font-size: small; +} +p sub { + vertical-align: baseline; + position: relative; + bottom: -.5em; + font-size: small; +} +.footnote { + font-size: medium; + text-indent: 1em; + text-align: justify; + margin-top: .5em; +} +.image-right { + float: right; + margin-left: 1em; + margin-top: 1em; + margin-bottom: 1em; +} +.image-caption { + font-size: small; + text-align: center; +} \ No newline at end of file diff --git a/versions/c1pjs/1.50.4/document.xsl b/versions/c1pjs/1.50.4/document.xsl new file mode 100644 index 0000000000..dd48b2f5ce --- /dev/null +++ b/versions/c1pjs/1.50.4/document.xsl @@ -0,0 +1,452 @@ + + + + + +]> + + + + + + + + + +

+
+ + + + + + + +

+
+ +

+
+
+
+ + + + + + +
+
+ + +
+ +   + + +
+
+ +
+
+ + + + + + + + + + + + + + + + +

+
+ + +

+
+ + +

+
+ + +
+
+ + +
+
+ + + + + + + + + + + + + + +
+
+ + +
+
+ + +
  • +
    + + +
    image
    +
    + + +
    +
    + + + + +
    {.}
    +
    + +
    {.}
    +
    +
    +
    + + + + + + + + + + < + > + + + + × + + ÷ + σ + + + + + + + + + + + + { + + + + + + + + + + [] + + + + +
    + +
    +
    + + + , and + + + + + MDY + + + + + + + + + + + + + + + + + + + + January + February + March + April + May + June + July + August + September + October + November + December + + + , + + + + + +

    + +
    +
    + + +
    + {.}
    +
    +
    +
    + + + +

    Timeline

    +
    + +

    +
    +
    + +
    +
    + + + + + + + + + +

    +
    + +
    +
    +
    + + + +

    People

    +
    + +

    +
    +
    + +
    +
    + + +

    + +
    + + +

    +
      + +
    +
    + + + + + + + + + + +
  • + +
  • +
    + + + +

    +
    +

    + +

    +
    +
    + + + + false + + + + + + [Original] + + + + + + + + + + [] + + +
    by
    + + +
    + [Source: + + + + + + + ] +
    +
    +
    + + + +

    Resources

    +
    + +

    +
    +
    + +
    +
    + + +

    + +
    + + + +

    +
    +
      + +
    +
    + + +
  • +
    + + + +

    +
    +
    + +
    +
    + + + +

    +
    + +
    + + + +

    +
    +
      + +
    +
    + + + + + +
      + +
    +
    + + + + +
  • +
    + +
  • +
    + +
  • +
    +
    +
    + + +
  • +
    + + + + + + + + + + +
    + < ="" + + ></> + ></> + /> + +
    +
    + +
    diff --git a/versions/c1pjs/1.50.4/machine.xsl b/versions/c1pjs/1.50.4/machine.xsl new file mode 100644 index 0000000000..76c120dd72 --- /dev/null +++ b/versions/c1pjs/1.50.4/machine.xsl @@ -0,0 +1,61 @@ + + + + +]> + + + + + + + + + + + + + + + + + js + + + + + + <xsl:value-of select="$SITEHOST"/> + + + + +
    + +
    +

    +
    + + + + + , + +
    +
    + +
    + + + + -dbg + + + + + + +
    + +
    diff --git a/versions/c1pjs/1.50.4/manifest.xsl b/versions/c1pjs/1.50.4/manifest.xsl new file mode 100644 index 0000000000..fd2c3ecadd --- /dev/null +++ b/versions/c1pjs/1.50.4/manifest.xsl @@ -0,0 +1,247 @@ + + + + +]> + + + + + + + + + + + <xsl:value-of select="$SITEHOST"/> + + + + +
    + +
    +

    Document Manifest

    +
    +
      + + + + None + + + + + + + + + + + + + + + + +
    +
    +
    +

    + +
    +
    +
    + + +
    + + + + + + + + + + + <xsl:value-of select="$SITEHOST"/> + + + + +
    + +
    +

    Software Manifest

    +
    +
      + + + + None + + + + + Unknown + + + + + None + + + + + None + + + + + + + + + + + + + UpdatedReleased + + Unknown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + + + + + +

    No default machine specified for '' in manifest.xml

    +
    + +
    +
    +
    + + + + -dbg + + + + + + +
    + + + + + Unknown + +
  • +
      + + + + + + + + +
    • + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
      • + + + + + + +
      • +
        +
      +
      +
    • +
      + + + + + + + + +
    +
  • +
    +
    + +
    diff --git a/versions/c1pjs/1.50.4/outline.xsl b/versions/c1pjs/1.50.4/outline.xsl new file mode 100644 index 0000000000..d381225d5e --- /dev/null +++ b/versions/c1pjs/1.50.4/outline.xsl @@ -0,0 +1,47 @@ + + + + +]> + + + + + + + + + + + + + + + + + + <xsl:value-of select="title"/><xsl:text> | </xsl:text><xsl:value-of select="$SITEHOST"/> + + + + + +
    +
    + +
    +
    + + + + -dbg + + + + + + +
    + +