=r)return this._interim=s,i;var a;56320<=(a=e.charCodeAt(o))&&a<=57343?t[i++]=1024*(s-55296)+a-56320+65536:(t[i++]=s,t[i++]=a)}else 65279!==s&&(t[i++]=s)}return i},e}();t.StringToUtf32=r;var i=function(){function e(){this.interim=new Uint8Array(3)}return e.prototype.clear=function(){this.interim.fill(0)},e.prototype.decode=function(e,t){var r=e.length;if(!r)return 0;var i,n,o,s,a=0,c=0,l=0;if(this.interim[0]){var h=!1,u=this.interim[0];u&=192==(224&u)?31:224==(240&u)?15:7;for(var f=0,_=void 0;(_=63&this.interim[++f])&&f<4;)u<<=6,u|=_;for(var d=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,p=d-f;l=r)return 0;if(128!=(192&(_=e[l++]))){l--,h=!0;break}this.interim[f++]=_,u<<=6,u|=63&_}h||(2===d?u<128?l--:t[a++]=u:3===d?u<2048||u>=55296&&u<=57343||65279===u||(t[a++]=u):u<65536||u>1114111||(t[a++]=u)),this.interim.fill(0)}for(var v=r-4,g=l;g=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if((c=(31&i)<<6|63&n)<128){g--;continue}t[a++]=c}else if(224==(240&i)){if(g>=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,a;if(128!=(192&(o=e[g++]))){g--;continue}if((c=(15&i)<<12|(63&n)<<6|63&o)<2048||c>=55296&&c<=57343||65279===c)continue;t[a++]=c}else if(240==(248&i)){if(g>=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,a;if(128!=(192&(o=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,this.interim[2]=o,a;if(128!=(192&(s=e[g++]))){g--;continue}if((c=(7&i)<<18|(63&n)<<12|(63&o)<<6|63&s)<65536||c>1114111)continue;t[a++]=c}}return a},e}();t.Utf8ToUtf32=i},225:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;var i,n=r(8273),o=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],s=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]],a=function(){function e(){if(this.version="6",!i){i=new Uint8Array(65536),(0,n.fill)(i,1),i[0]=0,(0,n.fill)(i,0,1,32),(0,n.fill)(i,0,127,160),(0,n.fill)(i,2,4352,4448),i[9001]=2,i[9002]=2,(0,n.fill)(i,2,11904,42192),i[12351]=1,(0,n.fill)(i,2,44032,55204),(0,n.fill)(i,2,63744,64256),(0,n.fill)(i,2,65040,65050),(0,n.fill)(i,2,65072,65136),(0,n.fill)(i,2,65280,65377),(0,n.fill)(i,2,65504,65511);for(var e=0;et[n][1])return!1;for(;n>=i;)if(e>t[r=i+n>>1][1])i=r+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1},e}();t.UnicodeV6=a},5981:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;var r="undefined"==typeof queueMicrotask?function(e){Promise.resolve().then(e)}:queueMicrotask,i=function(){function e(e){this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0}return e.prototype.writeSync=function(e,t){if(void 0!==t&&this._syncCalls>t)this._syncCalls=0;else if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,!this._isSyncWriting){var r;for(this._isSyncWriting=!0;r=this._writeBuffer.shift();){this._action(r);var i=this._callbacks.shift();i&&i()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}},e.prototype.write=function(e,t){var r=this;if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");this._writeBuffer.length||(this._bufferOffset=0,setTimeout((function(){return r._innerWrite()}))),this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)},e.prototype._innerWrite=function(e,t){var i=this;void 0===e&&(e=0),void 0===t&&(t=!0);for(var n=e||Date.now();this._writeBuffer.length>this._bufferOffset;){var o=this._writeBuffer[this._bufferOffset],s=this._action(o,t);if(s)return void s.catch((function(e){return r((function(){throw e})),Promise.resolve(!1)})).then((function(e){return Date.now()-n>=12?setTimeout((function(){return i._innerWrite(0,e)})):i._innerWrite(n,e)}));var a=this._callbacks[this._bufferOffset];if(a&&a(),this._bufferOffset++,this._pendingData-=o.length,Date.now()-n>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((function(){return i._innerWrite()}))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0)},e}();t.WriteBuffer=i},5941:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.toRgbString=t.parseColor=void 0;var r=/^([\da-f]{1})\/([\da-f]{1})\/([\da-f]{1})$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,i=/^[\da-f]+$/;function n(e,t){var r=e.toString(16),i=r.length<2?"0"+r:r;switch(t){case 4:return r[0];case 8:return i;case 12:return(i+i).slice(0,3);default:return i+i}}t.parseColor=function(e){if(e){var t=e.toLowerCase();if(0===t.indexOf("rgb:")){t=t.slice(4);var n=r.exec(t);if(n){var o=n[1]?15:n[4]?255:n[7]?4095:65535;return[Math.round(parseInt(n[1]||n[4]||n[7]||n[10],16)/o*255),Math.round(parseInt(n[2]||n[5]||n[8]||n[11],16)/o*255),Math.round(parseInt(n[3]||n[6]||n[9]||n[12],16)/o*255)]}}else if(0===t.indexOf("#")&&(t=t.slice(1),i.exec(t)&&[3,6,9,12].includes(t.length))){for(var s=t.length/3,a=[0,0,0],c=0;c<3;++c){var l=parseInt(t.slice(s*c,s*c+s),16);a[c]=1===s?l<<4:2===s?l:3===s?l>>4:l>>8}return a}}},t.toRgbString=function(e,t){void 0===t&&(t=16);var r=e[0],i=e[1],o=e[2];return"rgb:"+n(r,t)+"/"+n(i,t)+"/"+n(o,t)}},5770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},6351:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;var i=r(482),n=r(8742),o=r(5770),s=[],a=function(){function e(){this._handlers=Object.create(null),this._active=s,this._ident=0,this._handlerFb=function(){},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}return e.prototype.dispose=function(){this._handlers=Object.create(null),this._handlerFb=function(){},this._active=s},e.prototype.registerHandler=function(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);var r=this._handlers[e];return r.push(t),{dispose:function(){var e=r.indexOf(t);-1!==e&&r.splice(e,1)}}},e.prototype.clearHandler=function(e){this._handlers[e]&&delete this._handlers[e]},e.prototype.setHandlerFallback=function(e){this._handlerFb=e},e.prototype.reset=function(){if(this._active.length)for(var e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=s,this._ident=0},e.prototype.hook=function(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||s,this._active.length)for(var r=this._active.length-1;r>=0;r--)this._active[r].hook(t);else this._handlerFb(this._ident,"HOOK",t)},e.prototype.put=function(e,t,r){if(this._active.length)for(var n=this._active.length-1;n>=0;n--)this._active[n].put(e,t,r);else this._handlerFb(this._ident,"PUT",(0,i.utf32ToString)(e,t,r))},e.prototype.unhook=function(e,t){if(void 0===t&&(t=!0),this._active.length){var r=!1,i=this._active.length-1,n=!1;if(this._stack.paused&&(i=this._stack.loopPosition-1,r=t,n=this._stack.fallThrough,this._stack.paused=!1),!n&&!1===r){for(;i>=0&&!0!==(r=this._active[i].unhook(e));i--)if(r instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=i,this._stack.fallThrough=!1,r;i--}for(;i>=0;i--)if((r=this._active[i].unhook(!1))instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=i,this._stack.fallThrough=!0,r}else this._handlerFb(this._ident,"UNHOOK",e);this._active=s,this._ident=0},e}();t.DcsParser=a;var c=new n.Params;c.addParam(0);var l=function(){function e(e){this._handler=e,this._data="",this._params=c,this._hitLimit=!1}return e.prototype.hook=function(e){this._params=e.length>1||e.params[0]?e.clone():c,this._data="",this._hitLimit=!1},e.prototype.put=function(e,t,r){this._hitLimit||(this._data+=(0,i.utf32ToString)(e,t,r),this._data.length>o.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))},e.prototype.unhook=function(e){var t=this,r=!1;if(this._hitLimit)r=!1;else if(e&&(r=this._handler(this._data,this._params))instanceof Promise)return r.then((function(e){return t._params=c,t._data="",t._hitLimit=!1,e}));return this._params=c,this._data="",this._hitLimit=!1,r},e}();t.DcsHandler=l},2015:function(e,t,r){var i,n=this&&this.__extends||(i=function(e,t){return i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},i(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;var o=r(844),s=r(8273),a=r(8742),c=r(6242),l=r(6351),h=function(){function e(e){this.table=new Uint8Array(e)}return e.prototype.setDefault=function(e,t){(0,s.fill)(this.table,e<<4|t)},e.prototype.add=function(e,t,r,i){this.table[t<<8|e]=r<<4|i},e.prototype.addMany=function(e,t,r,i){for(var n=0;n1)throw new Error("only one byte as prefix supported");if((r=e.prefix.charCodeAt(0))&&60>r||r>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(var i=0;in||n>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");r<<=8,r|=n}}if(1!==e.final.length)throw new Error("final must be a single byte");var o=e.final.charCodeAt(0);if(t[0]>o||o>t[1])throw new Error("final must be in range "+t[0]+" .. "+t[1]);return(r<<=8)|o},r.prototype.identToString=function(e){for(var t=[];e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")},r.prototype.dispose=function(){this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null),this._oscParser.dispose(),this._dcsParser.dispose()},r.prototype.setPrintHandler=function(e){this._printHandler=e},r.prototype.clearPrintHandler=function(){this._printHandler=this._printHandlerFb},r.prototype.registerEscHandler=function(e,t){var r=this._identifier(e,[48,126]);void 0===this._escHandlers[r]&&(this._escHandlers[r]=[]);var i=this._escHandlers[r];return i.push(t),{dispose:function(){var e=i.indexOf(t);-1!==e&&i.splice(e,1)}}},r.prototype.clearEscHandler=function(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]},r.prototype.setEscHandlerFallback=function(e){this._escHandlerFb=e},r.prototype.setExecuteHandler=function(e,t){this._executeHandlers[e.charCodeAt(0)]=t},r.prototype.clearExecuteHandler=function(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]},r.prototype.setExecuteHandlerFallback=function(e){this._executeHandlerFb=e},r.prototype.registerCsiHandler=function(e,t){var r=this._identifier(e);void 0===this._csiHandlers[r]&&(this._csiHandlers[r]=[]);var i=this._csiHandlers[r];return i.push(t),{dispose:function(){var e=i.indexOf(t);-1!==e&&i.splice(e,1)}}},r.prototype.clearCsiHandler=function(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]},r.prototype.setCsiHandlerFallback=function(e){this._csiHandlerFb=e},r.prototype.registerDcsHandler=function(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)},r.prototype.clearDcsHandler=function(e){this._dcsParser.clearHandler(this._identifier(e))},r.prototype.setDcsHandlerFallback=function(e){this._dcsParser.setHandlerFallback(e)},r.prototype.registerOscHandler=function(e,t){return this._oscParser.registerHandler(e,t)},r.prototype.clearOscHandler=function(e){this._oscParser.clearHandler(e)},r.prototype.setOscHandlerFallback=function(e){this._oscParser.setHandlerFallback(e)},r.prototype.setErrorHandler=function(e){this._errorHandler=e},r.prototype.clearErrorHandler=function(){this._errorHandler=this._errorHandlerFb},r.prototype.reset=function(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingCodepoint=0,0!==this._parseStack.state&&(this._parseStack.state=2,this._parseStack.handlers=[])},r.prototype._preserveStack=function(e,t,r,i,n){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=r,this._parseStack.transition=i,this._parseStack.chunkPos=n},r.prototype.parse=function(e,t,r){var i,n=0,o=0,s=0;if(this._parseStack.state)if(2===this._parseStack.state)this._parseStack.state=0,s=this._parseStack.chunkPos+1;else{if(void 0===r||1===this._parseStack.state)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");var a=this._parseStack.handlers,c=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===r&&c>-1)for(;c>=0&&!0!==(i=a[c](this._params));c--)if(i instanceof Promise)return this._parseStack.handlerPos=c,i;this._parseStack.handlers=[];break;case 4:if(!1===r&&c>-1)for(;c>=0&&!0!==(i=a[c]());c--)if(i instanceof Promise)return this._parseStack.handlerPos=c,i;this._parseStack.handlers=[];break;case 6:if(n=e[this._parseStack.chunkPos],i=this._dcsParser.unhook(24!==n&&26!==n,r))return i;27===n&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(n=e[this._parseStack.chunkPos],i=this._oscParser.end(24!==n&&26!==n,r))return i;27===n&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,s=this._parseStack.chunkPos+1,this.precedingCodepoint=0,this.currentState=15&this._parseStack.transition}for(var l=s;l>4){case 2:for(var h=l+1;;++h){if(h>=t||(n=e[h])<32||n>126&&n=t||(n=e[h])<32||n>126&&n=t||(n=e[h])<32||n>126&&n=t||(n=e[h])<32||n>126&&n=0&&!0!==(i=a[f](this._params));f--)if(i instanceof Promise)return this._preserveStack(3,a,f,o,l),i;f<0&&this._csiHandlerFb(this._collect<<8|n,this._params),this.precedingCodepoint=0;break;case 8:do{switch(n){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(n-48)}}while(++l47&&n<60);l--;break;case 9:this._collect<<=8,this._collect|=n;break;case 10:for(var _=this._escHandlers[this._collect<<8|n],d=_?_.length-1:-1;d>=0&&!0!==(i=_[d]());d--)if(i instanceof Promise)return this._preserveStack(4,_,d,o,l),i;d<0&&this._escHandlerFb(this._collect<<8|n),this.precedingCodepoint=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|n,this._params);break;case 13:for(var p=l+1;;++p)if(p>=t||24===(n=e[p])||26===n||27===n||n>127&&n=t||(n=e[v])<32||n>127&&n{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;var i=r(5770),n=r(482),o=[],s=function(){function e(){this._state=0,this._active=o,this._id=-1,this._handlers=Object.create(null),this._handlerFb=function(){},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}return e.prototype.registerHandler=function(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);var r=this._handlers[e];return r.push(t),{dispose:function(){var e=r.indexOf(t);-1!==e&&r.splice(e,1)}}},e.prototype.clearHandler=function(e){this._handlers[e]&&delete this._handlers[e]},e.prototype.setHandlerFallback=function(e){this._handlerFb=e},e.prototype.dispose=function(){this._handlers=Object.create(null),this._handlerFb=function(){},this._active=o},e.prototype.reset=function(){if(2===this._state)for(var e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=o,this._id=-1,this._state=0},e.prototype._start=function(){if(this._active=this._handlers[this._id]||o,this._active.length)for(var e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,"START")},e.prototype._put=function(e,t,r){if(this._active.length)for(var i=this._active.length-1;i>=0;i--)this._active[i].put(e,t,r);else this._handlerFb(this._id,"PUT",(0,n.utf32ToString)(e,t,r))},e.prototype.start=function(){this.reset(),this._state=1},e.prototype.put=function(e,t,r){if(3!==this._state){if(1===this._state)for(;t0&&this._put(e,t,r)}},e.prototype.end=function(e,t){if(void 0===t&&(t=!0),0!==this._state){if(3!==this._state)if(1===this._state&&this._start(),this._active.length){var r=!1,i=this._active.length-1,n=!1;if(this._stack.paused&&(i=this._stack.loopPosition-1,r=t,n=this._stack.fallThrough,this._stack.paused=!1),!n&&!1===r){for(;i>=0&&!0!==(r=this._active[i].end(e));i--)if(r instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=i,this._stack.fallThrough=!1,r;i--}for(;i>=0;i--)if((r=this._active[i].end(!1))instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=i,this._stack.fallThrough=!0,r}else this._handlerFb(this._id,"END",e);this._active=o,this._id=-1,this._state=0}},e}();t.OscParser=s;var a=function(){function e(e){this._handler=e,this._data="",this._hitLimit=!1}return e.prototype.start=function(){this._data="",this._hitLimit=!1},e.prototype.put=function(e,t,r){this._hitLimit||(this._data+=(0,n.utf32ToString)(e,t,r),this._data.length>i.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))},e.prototype.end=function(e){var t=this,r=!1;if(this._hitLimit)r=!1;else if(e&&(r=this._handler(this._data))instanceof Promise)return r.then((function(e){return t._data="",t._hitLimit=!1,e}));return this._data="",this._hitLimit=!1,r},e}();t.OscHandler=a},8742:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;var r=2147483647,i=function(){function e(e,t){if(void 0===e&&(e=32),void 0===t&&(t=32),this.maxLength=e,this.maxSubParamsLength=t,t>256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}return e.fromArray=function(t){var r=new e;if(!t.length)return r;for(var i=Array.isArray(t[0])?1:0;i>8,i=255&this._subParamsIdx[t];i-r>0&&e.push(Array.prototype.slice.call(this._subParams,r,i))}return e},e.prototype.reset=function(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1},e.prototype.addParam=function(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>r?r:e}},e.prototype.addSubParam=function(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>r?r:e,this._subParamsIdx[this.length-1]++}},e.prototype.hasSubParams=function(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0},e.prototype.getSubParams=function(e){var t=this._subParamsIdx[e]>>8,r=255&this._subParamsIdx[e];return r-t>0?this._subParams.subarray(t,r):null},e.prototype.getSubParamsAll=function(){for(var e={},t=0;t>8,i=255&this._subParamsIdx[t];i-r>0&&(e[t]=this._subParams.slice(r,i))}return e},e.prototype.addDigit=function(e){var t;if(!(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)){var i=this._digitIsSub?this._subParams:this.params,n=i[t-1];i[t-1]=~n?Math.min(10*n+e,r):e}},e}();t.Params=i},5741:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0;var r=function(){function e(){this._addons=[]}return e.prototype.dispose=function(){for(var e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()},e.prototype.loadAddon=function(e,t){var r=this,i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=function(){return r._wrappedAddonDispose(i)},t.activate(e)},e.prototype._wrappedAddonDispose=function(e){if(!e.isDisposed){for(var t=-1,r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;var i=r(3785),n=r(511),o=function(){function e(e,t){this._buffer=e,this.type=t}return e.prototype.init=function(e){return this._buffer=e,this},Object.defineProperty(e.prototype,"cursorY",{get:function(){return this._buffer.y},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"cursorX",{get:function(){return this._buffer.x},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"viewportY",{get:function(){return this._buffer.ydisp},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"baseY",{get:function(){return this._buffer.ybase},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"length",{get:function(){return this._buffer.lines.length},enumerable:!1,configurable:!0}),e.prototype.getLine=function(e){var t=this._buffer.lines.get(e);if(t)return new i.BufferLineApiView(t)},e.prototype.getNullCell=function(){return new n.CellData},e}();t.BufferApiView=o},3785:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;var i=r(511),n=function(){function e(e){this._line=e}return Object.defineProperty(e.prototype,"isWrapped",{get:function(){return this._line.isWrapped},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"length",{get:function(){return this._line.length},enumerable:!1,configurable:!0}),e.prototype.getCell=function(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new i.CellData)},e.prototype.translateToString=function(e,t,r){return this._line.translateToString(e,t,r)},e}();t.BufferLineApiView=n},8285:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;var i=r(8771),n=r(8460),o=function(){function e(e){var t=this;this._core=e,this._onBufferChange=new n.EventEmitter,this._normal=new i.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new i.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate((function(){return t._onBufferChange.fire(t.active)}))}return Object.defineProperty(e.prototype,"onBufferChange",{get:function(){return this._onBufferChange.event},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"active",{get:function(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"normal",{get:function(){return this._normal.init(this._core.buffers.normal)},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"alternate",{get:function(){return this._alternate.init(this._core.buffers.alt)},enumerable:!1,configurable:!0}),e}();t.BufferNamespaceApi=o},7975:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0;var r=function(){function e(e){this._core=e}return e.prototype.registerCsiHandler=function(e,t){return this._core.registerCsiHandler(e,(function(e){return t(e.toArray())}))},e.prototype.addCsiHandler=function(e,t){return this.registerCsiHandler(e,t)},e.prototype.registerDcsHandler=function(e,t){return this._core.registerDcsHandler(e,(function(e,r){return t(e,r.toArray())}))},e.prototype.addDcsHandler=function(e,t){return this.registerDcsHandler(e,t)},e.prototype.registerEscHandler=function(e,t){return this._core.registerEscHandler(e,t)},e.prototype.addEscHandler=function(e,t){return this.registerEscHandler(e,t)},e.prototype.registerOscHandler=function(e,t){return this._core.registerOscHandler(e,t)},e.prototype.addOscHandler=function(e,t){return this.registerOscHandler(e,t)},e}();t.ParserApi=r},7090:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0;var r=function(){function e(e){this._core=e}return e.prototype.register=function(e){this._core.unicodeService.register(e)},Object.defineProperty(e.prototype,"versions",{get:function(){return this._core.unicodeService.versions},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeVersion",{get:function(){return this._core.unicodeService.activeVersion},set:function(e){this._core.unicodeService.activeVersion=e},enumerable:!1,configurable:!0}),e}();t.UnicodeApi=r},744:function(e,t,r){var i,n=this&&this.__extends||(i=function(e,t){return i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},i(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),o=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;var a=r(2585),c=r(5295),l=r(8460),h=r(844);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;var u=function(e){function r(r){var i=e.call(this)||this;return i._optionsService=r,i.isUserScrolling=!1,i._onResize=new l.EventEmitter,i._onScroll=new l.EventEmitter,i.cols=Math.max(r.options.cols||0,t.MINIMUM_COLS),i.rows=Math.max(r.options.rows||0,t.MINIMUM_ROWS),i.buffers=new c.BufferSet(r,i),i}return n(r,e),Object.defineProperty(r.prototype,"onResize",{get:function(){return this._onResize.event},enumerable:!1,configurable:!0}),Object.defineProperty(r.prototype,"onScroll",{get:function(){return this._onScroll.event},enumerable:!1,configurable:!0}),Object.defineProperty(r.prototype,"buffer",{get:function(){return this.buffers.active},enumerable:!1,configurable:!0}),r.prototype.dispose=function(){e.prototype.dispose.call(this),this.buffers.dispose()},r.prototype.resize=function(e,t){this.cols=e,this.rows=t,this.buffers.resize(e,t),this.buffers.setupTabStops(this.cols),this._onResize.fire({cols:e,rows:t})},r.prototype.reset=function(){this.buffers.reset(),this.isUserScrolling=!1},r.prototype.scroll=function(e,t){void 0===t&&(t=!1);var r,i=this.buffer;(r=this._cachedBlankLine)&&r.length===this.cols&&r.getFg(0)===e.fg&&r.getBg(0)===e.bg||(r=i.getBlankLine(e,t),this._cachedBlankLine=r),r.isWrapped=t;var n=i.ybase+i.scrollTop,o=i.ybase+i.scrollBottom;if(0===i.scrollTop){var s=i.lines.isFull;o===i.lines.length-1?s?i.lines.recycle().copyFrom(r):i.lines.push(r.clone()):i.lines.splice(o+1,0,r.clone()),s?this.isUserScrolling&&(i.ydisp=Math.max(i.ydisp-1,0)):(i.ybase++,this.isUserScrolling||i.ydisp++)}else{var a=o-n+1;i.lines.shiftElements(n+1,a-1,-1),i.lines.set(o,r.clone())}this.isUserScrolling||(i.ydisp=i.ybase),this._onScroll.fire(i.ydisp)},r.prototype.scrollLines=function(e,t,r){var i=this.buffer;if(e<0){if(0===i.ydisp)return;this.isUserScrolling=!0}else e+i.ydisp>=i.ybase&&(this.isUserScrolling=!1);var n=i.ydisp;i.ydisp=Math.max(Math.min(i.ydisp+e,i.ybase),0),n!==i.ydisp&&(t||this._onScroll.fire(i.ydisp))},r.prototype.scrollPages=function(e){this.scrollLines(e*(this.rows-1))},r.prototype.scrollToTop=function(){this.scrollLines(-this.buffer.ydisp)},r.prototype.scrollToBottom=function(){this.scrollLines(this.buffer.ybase-this.buffer.ydisp)},r.prototype.scrollToLine=function(e){var t=e-this.buffer.ydisp;0!==t&&this.scrollLines(t)},o([s(0,a.IOptionsService)],r)}(h.Disposable);t.BufferService=u},7994:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0;var r=function(){function e(){this.glevel=0,this._charsets=[]}return e.prototype.reset=function(){this.charset=void 0,this._charsets=[],this.glevel=0},e.prototype.setgLevel=function(e){this.glevel=e,this.charset=this._charsets[e]},e.prototype.setgCharset=function(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)},e}();t.CharsetService=r},1753:function(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;var o=r(2585),s=r(8460),a={NONE:{events:0,restrict:function(){return!1}},X10:{events:1,restrict:function(e){return 4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)}},VT200:{events:19,restrict:function(e){return 32!==e.action}},DRAG:{events:23,restrict:function(e){return 32!==e.action||3!==e.button}},ANY:{events:31,restrict:function(e){return!0}}};function c(e,t){var r=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(r|=64,r|=e.action):(r|=3&e.button,4&e.button&&(r|=64),8&e.button&&(r|=128),32===e.action?r|=32:0!==e.action||t||(r|=3)),r}var l=String.fromCharCode,h={DEFAULT:function(e){var t=[c(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":"[M"+l(t[0])+l(t[1])+l(t[2])},SGR:function(e){var t=0===e.action&&4!==e.button?"m":"M";return"[<"+c(e,!0)+";"+e.col+";"+e.row+t}},u=function(){function e(e,t){this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._onProtocolChange=new s.EventEmitter,this._lastEvent=null;for(var r=0,i=Object.keys(a);r=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._compareEvents(this._lastEvent,e))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;var t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0},e.prototype.explainEvents=function(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}},e.prototype._compareEvents=function(e,t){return e.col===t.col&&e.row===t.row&&e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift},i([n(0,o.IBufferService),n(1,o.ICoreService)],e)}();t.CoreMouseService=u},6975:function(e,t,r){var i,n=this&&this.__extends||(i=function(e,t){return i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},i(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),o=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;var a=r(2585),c=r(8460),l=r(1439),h=r(844),u=Object.freeze({insertMode:!1}),f=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0}),_=function(e){function t(t,r,i,n){var o=e.call(this)||this;return o._bufferService=r,o._logService=i,o._optionsService=n,o.isCursorInitialized=!1,o.isCursorHidden=!1,o._onData=o.register(new c.EventEmitter),o._onUserInput=o.register(new c.EventEmitter),o._onBinary=o.register(new c.EventEmitter),o._scrollToBottom=t,o.register({dispose:function(){return o._scrollToBottom=void 0}}),o.modes=(0,l.clone)(u),o.decPrivateModes=(0,l.clone)(f),o}return n(t,e),Object.defineProperty(t.prototype,"onData",{get:function(){return this._onData.event},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"onUserInput",{get:function(){return this._onUserInput.event},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"onBinary",{get:function(){return this._onBinary.event},enumerable:!1,configurable:!0}),t.prototype.reset=function(){this.modes=(0,l.clone)(u),this.decPrivateModes=(0,l.clone)(f)},t.prototype.triggerDataEvent=function(e,t){if(void 0===t&&(t=!1),!this._optionsService.options.disableStdin){var r=this._bufferService.buffer;r.ybase!==r.ydisp&&this._scrollToBottom(),t&&this._onUserInput.fire(),this._logService.debug('sending data "'+e+'"',(function(){return e.split("").map((function(e){return e.charCodeAt(0)}))})),this._onData.fire(e)}},t.prototype.triggerBinaryEvent=function(e){this._optionsService.options.disableStdin||(this._logService.debug('sending binary "'+e+'"',(function(){return e.split("").map((function(e){return e.charCodeAt(0)}))})),this._onBinary.fire(e))},o([s(1,a.IBufferService),s(2,a.ILogService),s(3,a.IOptionsService)],t)}(h.Disposable);t.CoreService=_},3730:function(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DirtyRowService=void 0;var o=r(2585),s=function(){function e(e){this._bufferService=e,this.clearRange()}return Object.defineProperty(e.prototype,"start",{get:function(){return this._start},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"end",{get:function(){return this._end},enumerable:!1,configurable:!0}),e.prototype.clearRange=function(){this._start=this._bufferService.buffer.y,this._end=this._bufferService.buffer.y},e.prototype.markDirty=function(e){ethis._end&&(this._end=e)},e.prototype.markRangeDirty=function(e,t){if(e>t){var r=e;e=t,t=r}ethis._end&&(this._end=t)},e.prototype.markAllDirty=function(){this.markRangeDirty(0,this._bufferService.rows-1)},i([n(0,o.IBufferService)],e)}();t.DirtyRowService=s},4348:function(e,t,r){var i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var i,n=0,o=t.length;n0?n[0].index:t.length;if(t.length!==u)throw new Error("[createInstance] First service dependency of "+e.name+" at position "+(u+1)+" conflicts with "+t.length+" static arguments");return new(e.bind.apply(e,i([void 0],i(i([],t,!0),s,!0),!1)))},e}();t.InstantiationService=a},7866:function(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var i,n=0,o=t.length;n{function r(e,t,r){t.di$target===t?t.di$dependencies.push({id:e,index:r}):(t.di$dependencies=[{id:e,index:r}],t.di$target=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.createDecorator=t.getServiceDependencies=t.serviceRegistry=void 0,t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e.di$dependencies||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);var i=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");r(i,e,n)};return i.toString=function(){return e},t.serviceRegistry.set(e,i),i}},2585:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IUnicodeService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.IDirtyRowService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;var i,n=r(8343);t.IBufferService=(0,n.createDecorator)("BufferService"),t.ICoreMouseService=(0,n.createDecorator)("CoreMouseService"),t.ICoreService=(0,n.createDecorator)("CoreService"),t.ICharsetService=(0,n.createDecorator)("CharsetService"),t.IDirtyRowService=(0,n.createDecorator)("DirtyRowService"),t.IInstantiationService=(0,n.createDecorator)("InstantiationService"),(i=t.LogLevelEnum||(t.LogLevelEnum={}))[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.OFF=4]="OFF",t.ILogService=(0,n.createDecorator)("LogService"),t.IOptionsService=(0,n.createDecorator)("OptionsService"),t.IUnicodeService=(0,n.createDecorator)("UnicodeService")},1480:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;var i=r(8460),n=r(225),o=function(){function e(){this._providers=Object.create(null),this._active="",this._onChange=new i.EventEmitter;var e=new n.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}return Object.defineProperty(e.prototype,"onChange",{get:function(){return this._onChange.event},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"versions",{get:function(){return Object.keys(this._providers)},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeVersion",{get:function(){return this._active},set:function(e){if(!this._providers[e])throw new Error('unknown Unicode version "'+e+'"');this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)},enumerable:!1,configurable:!0}),e.prototype.register=function(e){this._providers[e.version]=e},e.prototype.wcwidth=function(e){return this._activeProvider.wcwidth(e)},e.prototype.getStringCellWidth=function(e){for(var t=0,r=e.length,i=0;i=r)return t+this.wcwidth(n);var o=e.charCodeAt(i);56320<=o&&o<=57343?n=1024*(n-55296)+o-56320+65536:t+=this.wcwidth(o)}t+=this.wcwidth(n)}return t},e}();t.UnicodeService=o}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i].call(o.exports,o,o.exports,r),o.exports}var i={};return(()=>{var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;var t=r(3236),n=r(9042),o=r(7975),s=r(7090),a=r(5741),c=r(8285),l=["cols","rows"],h=function(){function e(e){var r=this;this._core=new t.Terminal(e),this._addonManager=new a.AddonManager,this._publicOptions={};var i=function(e){Object.defineProperty(n._publicOptions,e,{get:function(){return r._core.options[e]},set:function(t){r._checkReadonlyOptions(e),r._core.options[e]=t}})},n=this;for(var o in this._core.options)i(o)}return e.prototype._checkReadonlyOptions=function(e){if(l.includes(e))throw new Error('Option "'+e+'" can only be set in the constructor')},e.prototype._checkProposedApi=function(){if(!this._core.optionsService.options.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")},Object.defineProperty(e.prototype,"onBell",{get:function(){return this._core.onBell},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onBinary",{get:function(){return this._core.onBinary},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onCursorMove",{get:function(){return this._core.onCursorMove},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onData",{get:function(){return this._core.onData},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onKey",{get:function(){return this._core.onKey},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onLineFeed",{get:function(){return this._core.onLineFeed},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onRender",{get:function(){return this._core.onRender},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onResize",{get:function(){return this._core.onResize},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onScroll",{get:function(){return this._core.onScroll},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onSelectionChange",{get:function(){return this._core.onSelectionChange},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onTitleChange",{get:function(){return this._core.onTitleChange},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"element",{get:function(){return this._core.element},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"parser",{get:function(){return this._checkProposedApi(),this._parser||(this._parser=new o.ParserApi(this._core)),this._parser},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"unicode",{get:function(){return this._checkProposedApi(),new s.UnicodeApi(this._core)},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"textarea",{get:function(){return this._core.textarea},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"rows",{get:function(){return this._core.rows},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"cols",{get:function(){return this._core.cols},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"buffer",{get:function(){return this._checkProposedApi(),this._buffer||(this._buffer=new c.BufferNamespaceApi(this._core)),this._buffer},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"markers",{get:function(){return this._checkProposedApi(),this._core.markers},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"modes",{get:function(){var e=this._core.coreService.decPrivateModes,t="none";switch(this._core.coreMouseService.activeProtocol){case"X10":t="x10";break;case"VT200":t="vt200";break;case"DRAG":t="drag";break;case"ANY":t="any"}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,wraparoundMode:e.wraparound}},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"options",{get:function(){return this._publicOptions},set:function(e){for(var t in e)this._publicOptions[t]=e[t]},enumerable:!1,configurable:!0}),e.prototype.blur=function(){this._core.blur()},e.prototype.focus=function(){this._core.focus()},e.prototype.resize=function(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)},e.prototype.open=function(e){this._core.open(e)},e.prototype.attachCustomKeyEventHandler=function(e){this._core.attachCustomKeyEventHandler(e)},e.prototype.registerLinkMatcher=function(e,t,r){return this._checkProposedApi(),this._core.registerLinkMatcher(e,t,r)},e.prototype.deregisterLinkMatcher=function(e){this._checkProposedApi(),this._core.deregisterLinkMatcher(e)},e.prototype.registerLinkProvider=function(e){return this._checkProposedApi(),this._core.registerLinkProvider(e)},e.prototype.registerCharacterJoiner=function(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)},e.prototype.deregisterCharacterJoiner=function(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)},e.prototype.registerMarker=function(e){return this._checkProposedApi(),this._verifyIntegers(e),this._core.addMarker(e)},e.prototype.addMarker=function(e){return this.registerMarker(e)},e.prototype.hasSelection=function(){return this._core.hasSelection()},e.prototype.select=function(e,t,r){this._verifyIntegers(e,t,r),this._core.select(e,t,r)},e.prototype.getSelection=function(){return this._core.getSelection()},e.prototype.getSelectionPosition=function(){return this._core.getSelectionPosition()},e.prototype.clearSelection=function(){this._core.clearSelection()},e.prototype.selectAll=function(){this._core.selectAll()},e.prototype.selectLines=function(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)},e.prototype.dispose=function(){this._addonManager.dispose(),this._core.dispose()},e.prototype.scrollLines=function(e){this._verifyIntegers(e),this._core.scrollLines(e)},e.prototype.scrollPages=function(e){this._verifyIntegers(e),this._core.scrollPages(e)},e.prototype.scrollToTop=function(){this._core.scrollToTop()},e.prototype.scrollToBottom=function(){this._core.scrollToBottom()},e.prototype.scrollToLine=function(e){this._verifyIntegers(e),this._core.scrollToLine(e)},e.prototype.clear=function(){this._core.clear()},e.prototype.write=function(e,t){this._core.write(e,t)},e.prototype.writeUtf8=function(e,t){this._core.write(e,t)},e.prototype.writeln=function(e,t){this._core.write(e),this._core.write("\r\n",t)},e.prototype.paste=function(e){this._core.paste(e)},e.prototype.getOption=function(e){return this._core.optionsService.getOption(e)},e.prototype.setOption=function(e,t){this._checkReadonlyOptions(e),this._core.optionsService.setOption(e,t)},e.prototype.refresh=function(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)},e.prototype.reset=function(){this._core.reset()},e.prototype.clearTextureAtlas=function(){this._core.clearTextureAtlas()},e.prototype.loadAddon=function(e){return this._addonManager.loadAddon(this,e)},Object.defineProperty(e,"strings",{get:function(){return n},enumerable:!1,configurable:!0}),e.prototype._verifyIntegers=function(){for(var e=[],t=0;t void;\n private _bottomBoundaryFocusListener: (e: FocusEvent) => void;\n\n /**\n * This queue has a character pushed to it for keys that are pressed, if the\n * next character added to the terminal is equal to the key char then it is\n * not announced (added to live region) because it has already been announced\n * by the textarea event (which cannot be canceled). There are some race\n * condition cases if there is typing while data is streaming, but this covers\n * the main case of typing into the prompt and inputting the answer to a\n * question (Y/N, etc.).\n */\n private _charsToConsume: string[] = [];\n\n private _charsToAnnounce: string = '';\n\n constructor(\n private readonly _terminal: ITerminal,\n private readonly _renderService: IRenderService\n ) {\n super();\n this._accessibilityTreeRoot = document.createElement('div');\n this._accessibilityTreeRoot.setAttribute('role', 'document');\n this._accessibilityTreeRoot.classList.add('xterm-accessibility');\n this._accessibilityTreeRoot.tabIndex = 0;\n\n this._rowContainer = document.createElement('div');\n this._rowContainer.setAttribute('role', 'list');\n this._rowContainer.classList.add('xterm-accessibility-tree');\n this._rowElements = [];\n for (let i = 0; i < this._terminal.rows; i++) {\n this._rowElements[i] = this._createAccessibilityTreeNode();\n this._rowContainer.appendChild(this._rowElements[i]);\n }\n\n this._topBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.TOP);\n this._bottomBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.BOTTOM);\n this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);\n this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);\n\n this._refreshRowsDimensions();\n this._accessibilityTreeRoot.appendChild(this._rowContainer);\n\n this._renderRowsDebouncer = new TimeBasedDebouncer(this._renderRows.bind(this));\n this._refreshRows();\n\n this._liveRegion = document.createElement('div');\n this._liveRegion.classList.add('live-region');\n this._liveRegion.setAttribute('aria-live', 'assertive');\n this._accessibilityTreeRoot.appendChild(this._liveRegion);\n\n if (!this._terminal.element) {\n throw new Error('Cannot enable accessibility before Terminal.open');\n }\n this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot);\n\n this.register(this._renderRowsDebouncer);\n this.register(this._terminal.onResize(e => this._onResize(e.rows)));\n this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));\n this.register(this._terminal.onScroll(() => this._refreshRows()));\n // Line feed is an issue as the prompt won't be read out after a command is run\n this.register(this._terminal.onA11yChar(char => this._onChar(char)));\n this.register(this._terminal.onLineFeed(() => this._onChar('\\n')));\n this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount)));\n this.register(this._terminal.onKey(e => this._onKey(e.key)));\n this.register(this._terminal.onBlur(() => this._clearLiveRegion()));\n this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));\n\n this._screenDprMonitor = new ScreenDprMonitor();\n this.register(this._screenDprMonitor);\n this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());\n // This shouldn't be needed on modern browsers but is present in case the\n // media query that drives the ScreenDprMonitor isn't supported\n this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));\n }\n\n public dispose(): void {\n super.dispose();\n removeElementFromParent(this._accessibilityTreeRoot);\n this._rowElements.length = 0;\n }\n\n private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {\n const boundaryElement = e.target as HTMLElement;\n const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];\n\n // Don't scroll if the buffer top has reached the end in that direction\n const posInSet = boundaryElement.getAttribute('aria-posinset');\n const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`;\n if (posInSet === lastRowPos) {\n return;\n }\n\n // Don't scroll when the last focused item was not the second row (focus is going the other\n // direction)\n if (e.relatedTarget !== beforeBoundaryElement) {\n return;\n }\n\n // Remove old boundary element from array\n let topBoundaryElement: HTMLElement;\n let bottomBoundaryElement: HTMLElement;\n if (position === BoundaryPosition.TOP) {\n topBoundaryElement = boundaryElement;\n bottomBoundaryElement = this._rowElements.pop()!;\n this._rowContainer.removeChild(bottomBoundaryElement);\n } else {\n topBoundaryElement = this._rowElements.shift()!;\n bottomBoundaryElement = boundaryElement;\n this._rowContainer.removeChild(topBoundaryElement);\n }\n\n // Remove listeners from old boundary elements\n topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener);\n bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener);\n\n // Add new element to array/DOM\n if (position === BoundaryPosition.TOP) {\n const newElement = this._createAccessibilityTreeNode();\n this._rowElements.unshift(newElement);\n this._rowContainer.insertAdjacentElement('afterbegin', newElement);\n } else {\n const newElement = this._createAccessibilityTreeNode();\n this._rowElements.push(newElement);\n this._rowContainer.appendChild(newElement);\n }\n\n // Add listeners to new boundary elements\n this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);\n this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);\n\n // Scroll up\n this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1);\n\n // Focus new boundary before element\n this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus();\n\n // Prevent the standard behavior\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n\n private _onResize(rows: number): void {\n // Remove bottom boundary listener\n this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);\n\n // Grow rows as required\n for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) {\n this._rowElements[i] = this._createAccessibilityTreeNode();\n this._rowContainer.appendChild(this._rowElements[i]);\n }\n // Shrink rows as required\n while (this._rowElements.length > rows) {\n this._rowContainer.removeChild(this._rowElements.pop()!);\n }\n\n // Add bottom boundary listener\n this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);\n\n this._refreshRowsDimensions();\n }\n\n private _createAccessibilityTreeNode(): HTMLElement {\n const element = document.createElement('div');\n element.setAttribute('role', 'listitem');\n element.tabIndex = -1;\n this._refreshRowDimensions(element);\n return element;\n }\n\n private _onTab(spaceCount: number): void {\n for (let i = 0; i < spaceCount; i++) {\n this._onChar(' ');\n }\n }\n\n private _onChar(char: string): void {\n if (this._liveRegionLineCount < MAX_ROWS_TO_READ + 1) {\n if (this._charsToConsume.length > 0) {\n // Have the screen reader ignore the char if it was just input\n const shiftedChar = this._charsToConsume.shift();\n if (shiftedChar !== char) {\n this._charsToAnnounce += char;\n }\n } else {\n this._charsToAnnounce += char;\n }\n\n if (char === '\\n') {\n this._liveRegionLineCount++;\n if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {\n this._liveRegion.textContent += Strings.tooMuchOutput;\n }\n }\n\n // Only detach/attach on mac as otherwise messages can go unaccounced\n if (isMac) {\n if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) {\n setTimeout(() => {\n this._accessibilityTreeRoot.appendChild(this._liveRegion);\n }, 0);\n }\n }\n }\n }\n\n private _clearLiveRegion(): void {\n this._liveRegion.textContent = '';\n this._liveRegionLineCount = 0;\n\n // Only detach/attach on mac as otherwise messages can go unaccounced\n if (isMac) {\n removeElementFromParent(this._liveRegion);\n }\n }\n\n private _onKey(keyChar: string): void {\n this._clearLiveRegion();\n this._charsToConsume.push(keyChar);\n }\n\n private _refreshRows(start?: number, end?: number): void {\n this._renderRowsDebouncer.refresh(start, end, this._terminal.rows);\n }\n\n private _renderRows(start: number, end: number): void {\n const buffer: IBuffer = this._terminal.buffer;\n const setSize = buffer.lines.length.toString();\n for (let i = start; i <= end; i++) {\n const lineData = buffer.translateBufferLineToString(buffer.ydisp + i, true);\n const posInSet = (buffer.ydisp + i + 1).toString();\n const element = this._rowElements[i];\n if (element) {\n if (lineData.length === 0) {\n element.innerText = '\\u00a0';\n } else {\n element.textContent = lineData;\n }\n element.setAttribute('aria-posinset', posInSet);\n element.setAttribute('aria-setsize', setSize);\n }\n }\n this._announceCharacters();\n }\n\n private _refreshRowsDimensions(): void {\n if (!this._renderService.dimensions.actualCellHeight) {\n return;\n }\n if (this._rowElements.length !== this._terminal.rows) {\n this._onResize(this._terminal.rows);\n }\n for (let i = 0; i < this._terminal.rows; i++) {\n this._refreshRowDimensions(this._rowElements[i]);\n }\n }\n\n private _refreshRowDimensions(element: HTMLElement): void {\n element.style.height = `${this._renderService.dimensions.actualCellHeight}px`;\n }\n\n private _announceCharacters(): void {\n if (this._charsToAnnounce.length === 0) {\n return;\n }\n this._liveRegion.textContent += this._charsToAnnounce;\n this._charsToAnnounce = '';\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ISelectionService } from 'browser/services/Services';\nimport { ICoreService } from 'common/services/Services';\n\n/**\n * Prepares text to be pasted into the terminal by normalizing the line endings\n * @param text The pasted text that needs processing before inserting into the terminal\n */\nexport function prepareTextForTerminal(text: string): string {\n return text.replace(/\\r?\\n/g, '\\r');\n}\n\n/**\n * Bracket text for paste, if necessary, as per https://cirw.in/blog/bracketed-paste\n * @param text The pasted text to bracket\n */\nexport function bracketTextForPaste(text: string, bracketedPasteMode: boolean): string {\n if (bracketedPasteMode) {\n return '\\x1b[200~' + text + '\\x1b[201~';\n }\n return text;\n}\n\n/**\n * Binds copy functionality to the given terminal.\n * @param ev The original copy event to be handled\n */\nexport function copyHandler(ev: ClipboardEvent, selectionService: ISelectionService): void {\n if (ev.clipboardData) {\n ev.clipboardData.setData('text/plain', selectionService.selectionText);\n }\n // Prevent or the original text will be copied.\n ev.preventDefault();\n}\n\n/**\n * Redirect the clipboard's data to the terminal's input handler.\n * @param ev The original paste event to be handled\n * @param term The terminal on which to apply the handled paste event\n */\nexport function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, coreService: ICoreService): void {\n ev.stopPropagation();\n if (ev.clipboardData) {\n const text = ev.clipboardData.getData('text/plain');\n paste(text, textarea, coreService);\n }\n}\n\nexport function paste(text: string, textarea: HTMLTextAreaElement, coreService: ICoreService): void {\n text = prepareTextForTerminal(text);\n text = bracketTextForPaste(text, coreService.decPrivateModes.bracketedPasteMode);\n coreService.triggerDataEvent(text, true);\n textarea.value = '';\n}\n\n/**\n * Moves the textarea under the mouse cursor and focuses it.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n */\nexport function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement, screenElement: HTMLElement): void {\n\n // Calculate textarea position relative to the screen element\n const pos = screenElement.getBoundingClientRect();\n const left = ev.clientX - pos.left - 10;\n const top = ev.clientY - pos.top - 10;\n\n // Bring textarea at the cursor position\n textarea.style.width = '20px';\n textarea.style.height = '20px';\n textarea.style.left = `${left}px`;\n textarea.style.top = `${top}px`;\n textarea.style.zIndex = '1000';\n\n textarea.focus();\n}\n\n/**\n * Bind to right-click event and allow right-click copy and paste.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n * @param selectionService The terminal's selection manager.\n * @param shouldSelectWord If true and there is no selection the current word will be selected\n */\nexport function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, screenElement: HTMLElement, selectionService: ISelectionService, shouldSelectWord: boolean): void {\n moveTextAreaUnderMouseCursor(ev, textarea, screenElement);\n\n if (shouldSelectWord) {\n selectionService.rightClickSelect(ev);\n }\n\n // Get textarea ready to copy from the context menu\n textarea.value = selectionService.selectionText;\n textarea.select();\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColor } from 'browser/Types';\nimport { IColorRGB } from 'common/Types';\n\n/**\n * Helper functions where the source type is \"channels\" (individual color channels as numbers).\n */\nexport namespace channels {\n export function toCss(r: number, g: number, b: number, a?: number): string {\n if (a !== undefined) {\n return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}${toPaddedHex(a)}`;\n }\n return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`;\n }\n\n export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number {\n // Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32\n // on LE systems, before it can be used for direct 32-bit buffer writes.\n // >>> 0 forces an unsigned int\n return (r << 24 | g << 16 | b << 8 | a) >>> 0;\n }\n}\n\n/**\n * Helper functions where the source type is `IColor`.\n */\nexport namespace color {\n export function blend(bg: IColor, fg: IColor): IColor {\n const a = (fg.rgba & 0xFF) / 255;\n if (a === 1) {\n return {\n css: fg.css,\n rgba: fg.rgba\n };\n }\n const fgR = (fg.rgba >> 24) & 0xFF;\n const fgG = (fg.rgba >> 16) & 0xFF;\n const fgB = (fg.rgba >> 8) & 0xFF;\n const bgR = (bg.rgba >> 24) & 0xFF;\n const bgG = (bg.rgba >> 16) & 0xFF;\n const bgB = (bg.rgba >> 8) & 0xFF;\n const r = bgR + Math.round((fgR - bgR) * a);\n const g = bgG + Math.round((fgG - bgG) * a);\n const b = bgB + Math.round((fgB - bgB) * a);\n const css = channels.toCss(r, g, b);\n const rgba = channels.toRgba(r, g, b);\n return { css, rgba };\n }\n\n export function isOpaque(color: IColor): boolean {\n return (color.rgba & 0xFF) === 0xFF;\n }\n\n export function ensureContrastRatio(bg: IColor, fg: IColor, ratio: number): IColor | undefined {\n const result = rgba.ensureContrastRatio(bg.rgba, fg.rgba, ratio);\n if (!result) {\n return undefined;\n }\n return rgba.toColor(\n (result >> 24 & 0xFF),\n (result >> 16 & 0xFF),\n (result >> 8 & 0xFF)\n );\n }\n\n export function opaque(color: IColor): IColor {\n const rgbaColor = (color.rgba | 0xFF) >>> 0;\n const [r, g, b] = rgba.toChannels(rgbaColor);\n return {\n css: channels.toCss(r, g, b),\n rgba: rgbaColor\n };\n }\n\n export function opacity(color: IColor, opacity: number): IColor {\n const a = Math.round(opacity * 0xFF);\n const [r, g, b] = rgba.toChannels(color.rgba);\n return {\n css: channels.toCss(r, g, b, a),\n rgba: channels.toRgba(r, g, b, a)\n };\n }\n\n export function toColorRGB(color: IColor): IColorRGB {\n return [(color.rgba >> 24) & 0xFF, (color.rgba >> 16) & 0xFF, (color.rgba >> 8) & 0xFF];\n }\n}\n\n/**\n * Helper functions where the source type is \"css\" (string: '#rgb', '#rgba', '#rrggbb', '#rrggbbaa').\n */\nexport namespace css {\n export function toColor(css: string): IColor {\n switch (css.length) {\n case 7: // #rrggbb\n return {\n css,\n rgba: (parseInt(css.slice(1), 16) << 8 | 0xFF) >>> 0\n };\n case 9: // #rrggbbaa\n return {\n css,\n rgba: parseInt(css.slice(1), 16) >>> 0\n };\n }\n throw new Error('css.toColor: Unsupported css format');\n }\n}\n\n/**\n * Helper functions where the source type is \"rgb\" (number: 0xrrggbb).\n */\nexport namespace rgb {\n /**\n * Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio\n * between two colors.\n * @param rgb The color to use.\n * @see https://www.w3.org/TR/WCAG20/#relativeluminancedef\n */\n export function relativeLuminance(rgb: number): number {\n return relativeLuminance2(\n (rgb >> 16) & 0xFF,\n (rgb >> 8 ) & 0xFF,\n (rgb ) & 0xFF);\n }\n\n /**\n * Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio\n * between two colors.\n * @param r The red channel (0x00 to 0xFF).\n * @param g The green channel (0x00 to 0xFF).\n * @param b The blue channel (0x00 to 0xFF).\n * @see https://www.w3.org/TR/WCAG20/#relativeluminancedef\n */\n export function relativeLuminance2(r: number, g: number, b: number): number {\n const rs = r / 255;\n const gs = g / 255;\n const bs = b / 255;\n const rr = rs <= 0.03928 ? rs / 12.92 : Math.pow((rs + 0.055) / 1.055, 2.4);\n const rg = gs <= 0.03928 ? gs / 12.92 : Math.pow((gs + 0.055) / 1.055, 2.4);\n const rb = bs <= 0.03928 ? bs / 12.92 : Math.pow((bs + 0.055) / 1.055, 2.4);\n return rr * 0.2126 + rg * 0.7152 + rb * 0.0722;\n }\n}\n\n/**\n * Helper functions where the source type is \"rgba\" (number: 0xrrggbbaa).\n */\nexport namespace rgba {\n export function ensureContrastRatio(bgRgba: number, fgRgba: number, ratio: number): number | undefined {\n const bgL = rgb.relativeLuminance(bgRgba >> 8);\n const fgL = rgb.relativeLuminance(fgRgba >> 8);\n const cr = contrastRatio(bgL, fgL);\n if (cr < ratio) {\n if (fgL < bgL) {\n return reduceLuminance(bgRgba, fgRgba, ratio);\n }\n return increaseLuminance(bgRgba, fgRgba, ratio);\n }\n return undefined;\n }\n\n export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number {\n // This is a naive but fast approach to reducing luminance as converting to\n // HSL and back is expensive\n const bgR = (bgRgba >> 24) & 0xFF;\n const bgG = (bgRgba >> 16) & 0xFF;\n const bgB = (bgRgba >> 8) & 0xFF;\n let fgR = (fgRgba >> 24) & 0xFF;\n let fgG = (fgRgba >> 16) & 0xFF;\n let fgB = (fgRgba >> 8) & 0xFF;\n let cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));\n while (cr < ratio && (fgR > 0 || fgG > 0 || fgB > 0)) {\n // Reduce by 10% until the ratio is hit\n fgR -= Math.max(0, Math.ceil(fgR * 0.1));\n fgG -= Math.max(0, Math.ceil(fgG * 0.1));\n fgB -= Math.max(0, Math.ceil(fgB * 0.1));\n cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));\n }\n return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;\n }\n\n export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number {\n // This is a naive but fast approach to increasing luminance as converting to\n // HSL and back is expensive\n const bgR = (bgRgba >> 24) & 0xFF;\n const bgG = (bgRgba >> 16) & 0xFF;\n const bgB = (bgRgba >> 8) & 0xFF;\n let fgR = (fgRgba >> 24) & 0xFF;\n let fgG = (fgRgba >> 16) & 0xFF;\n let fgB = (fgRgba >> 8) & 0xFF;\n let cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));\n while (cr < ratio && (fgR < 0xFF || fgG < 0xFF || fgB < 0xFF)) {\n // Increase by 10% until the ratio is hit\n fgR = Math.min(0xFF, fgR + Math.ceil((255 - fgR) * 0.1));\n fgG = Math.min(0xFF, fgG + Math.ceil((255 - fgG) * 0.1));\n fgB = Math.min(0xFF, fgB + Math.ceil((255 - fgB) * 0.1));\n cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));\n }\n return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;\n }\n\n // FIXME: Move this to channels NS?\n export function toChannels(value: number): [number, number, number, number] {\n return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF];\n }\n\n export function toColor(r: number, g: number, b: number): IColor {\n return {\n css: channels.toCss(r, g, b),\n rgba: channels.toRgba(r, g, b)\n };\n }\n}\n\nexport function toPaddedHex(c: number): string {\n const s = c.toString(16);\n return s.length < 2 ? '0' + s : s;\n}\n\n/**\n * Gets the contrast ratio between two relative luminance values.\n * @param l1 The first relative luminance.\n * @param l2 The first relative luminance.\n * @see https://www.w3.org/TR/WCAG20/#contrast-ratiodef\n */\nexport function contrastRatio(l1: number, l2: number): number {\n if (l1 < l2) {\n return (l2 + 0.05) / (l1 + 0.05);\n }\n return (l1 + 0.05) / (l2 + 0.05);\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColor, IColorContrastCache } from 'browser/Types';\n\nexport class ColorContrastCache implements IColorContrastCache {\n private _color: { [bg: number]: { [fg: number]: IColor | null | undefined } | undefined } = {};\n private _rgba: { [bg: number]: { [fg: number]: string | null | undefined } | undefined } = {};\n\n public clear(): void {\n this._color = {};\n this._rgba = {};\n }\n\n public setCss(bg: number, fg: number, value: string | null): void {\n if (!this._rgba[bg]) {\n this._rgba[bg] = {};\n }\n this._rgba[bg]![fg] = value;\n }\n\n public getCss(bg: number, fg: number): string | null | undefined {\n return this._rgba[bg] ? this._rgba[bg]![fg] : undefined;\n }\n\n public setColor(bg: number, fg: number, value: IColor | null): void {\n if (!this._color[bg]) {\n this._color[bg] = {};\n }\n this._color[bg]![fg] = value;\n }\n\n public getColor(bg: number, fg: number): IColor | null | undefined {\n return this._color[bg] ? this._color[bg]![fg] : undefined;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorManager, IColor, IColorSet, IColorContrastCache } from 'browser/Types';\nimport { ITheme } from 'common/services/Services';\nimport { channels, color, css } from 'browser/Color';\nimport { ColorContrastCache } from 'browser/ColorContrastCache';\nimport { ColorIndex } from 'common/Types';\n\n\ninterface IRestoreColorSet {\n foreground: IColor;\n background: IColor;\n cursor: IColor;\n ansi: IColor[];\n}\n\n\nconst DEFAULT_FOREGROUND = css.toColor('#ffffff');\nconst DEFAULT_BACKGROUND = css.toColor('#000000');\nconst DEFAULT_CURSOR = css.toColor('#ffffff');\nconst DEFAULT_CURSOR_ACCENT = css.toColor('#000000');\nconst DEFAULT_SELECTION = {\n css: 'rgba(255, 255, 255, 0.3)',\n rgba: 0xFFFFFF4D\n};\n\n// An IIFE to generate DEFAULT_ANSI_COLORS.\nexport const DEFAULT_ANSI_COLORS = Object.freeze((() => {\n const colors = [\n // dark:\n css.toColor('#2e3436'),\n css.toColor('#cc0000'),\n css.toColor('#4e9a06'),\n css.toColor('#c4a000'),\n css.toColor('#3465a4'),\n css.toColor('#75507b'),\n css.toColor('#06989a'),\n css.toColor('#d3d7cf'),\n // bright:\n css.toColor('#555753'),\n css.toColor('#ef2929'),\n css.toColor('#8ae234'),\n css.toColor('#fce94f'),\n css.toColor('#729fcf'),\n css.toColor('#ad7fa8'),\n css.toColor('#34e2e2'),\n css.toColor('#eeeeec')\n ];\n\n // Fill in the remaining 240 ANSI colors.\n // Generate colors (16-231)\n const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];\n for (let i = 0; i < 216; i++) {\n const r = v[(i / 36) % 6 | 0];\n const g = v[(i / 6) % 6 | 0];\n const b = v[i % 6];\n colors.push({\n css: channels.toCss(r, g, b),\n rgba: channels.toRgba(r, g, b)\n });\n }\n\n // Generate greys (232-255)\n for (let i = 0; i < 24; i++) {\n const c = 8 + i * 10;\n colors.push({\n css: channels.toCss(c, c, c),\n rgba: channels.toRgba(c, c, c)\n });\n }\n\n return colors;\n})());\n\n/**\n * Manages the source of truth for a terminal's colors.\n */\nexport class ColorManager implements IColorManager {\n public colors: IColorSet;\n private _ctx: CanvasRenderingContext2D;\n private _litmusColor: CanvasGradient;\n private _contrastCache: IColorContrastCache;\n private _restoreColors!: IRestoreColorSet;\n\n constructor(document: Document, public allowTransparency: boolean) {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get rendering context');\n }\n this._ctx = ctx;\n this._ctx.globalCompositeOperation = 'copy';\n this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1);\n this._contrastCache = new ColorContrastCache();\n this.colors = {\n foreground: DEFAULT_FOREGROUND,\n background: DEFAULT_BACKGROUND,\n cursor: DEFAULT_CURSOR,\n cursorAccent: DEFAULT_CURSOR_ACCENT,\n selectionTransparent: DEFAULT_SELECTION,\n selectionOpaque: color.blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),\n ansi: DEFAULT_ANSI_COLORS.slice(),\n contrastCache: this._contrastCache\n };\n this._updateRestoreColors();\n }\n\n public onOptionsChange(key: string): void {\n if (key === 'minimumContrastRatio') {\n this._contrastCache.clear();\n }\n }\n\n /**\n * Sets the terminal's theme.\n * @param theme The theme to use. If a partial theme is provided then default\n * colors will be used where colors are not defined.\n */\n public setTheme(theme: ITheme = {}): void {\n this.colors.foreground = this._parseColor(theme.foreground, DEFAULT_FOREGROUND);\n this.colors.background = this._parseColor(theme.background, DEFAULT_BACKGROUND);\n this.colors.cursor = this._parseColor(theme.cursor, DEFAULT_CURSOR, true);\n this.colors.cursorAccent = this._parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT, true);\n this.colors.selectionTransparent = this._parseColor(theme.selection, DEFAULT_SELECTION, true);\n this.colors.selectionOpaque = color.blend(this.colors.background, this.colors.selectionTransparent);\n /**\n * If selection color is opaque, blend it with background with 0.3 opacity\n * Issue #2737\n */\n if (color.isOpaque(this.colors.selectionTransparent)) {\n const opacity = 0.3;\n this.colors.selectionTransparent = color.opacity(this.colors.selectionTransparent, opacity);\n }\n this.colors.ansi[0] = this._parseColor(theme.black, DEFAULT_ANSI_COLORS[0]);\n this.colors.ansi[1] = this._parseColor(theme.red, DEFAULT_ANSI_COLORS[1]);\n this.colors.ansi[2] = this._parseColor(theme.green, DEFAULT_ANSI_COLORS[2]);\n this.colors.ansi[3] = this._parseColor(theme.yellow, DEFAULT_ANSI_COLORS[3]);\n this.colors.ansi[4] = this._parseColor(theme.blue, DEFAULT_ANSI_COLORS[4]);\n this.colors.ansi[5] = this._parseColor(theme.magenta, DEFAULT_ANSI_COLORS[5]);\n this.colors.ansi[6] = this._parseColor(theme.cyan, DEFAULT_ANSI_COLORS[6]);\n this.colors.ansi[7] = this._parseColor(theme.white, DEFAULT_ANSI_COLORS[7]);\n this.colors.ansi[8] = this._parseColor(theme.brightBlack, DEFAULT_ANSI_COLORS[8]);\n this.colors.ansi[9] = this._parseColor(theme.brightRed, DEFAULT_ANSI_COLORS[9]);\n this.colors.ansi[10] = this._parseColor(theme.brightGreen, DEFAULT_ANSI_COLORS[10]);\n this.colors.ansi[11] = this._parseColor(theme.brightYellow, DEFAULT_ANSI_COLORS[11]);\n this.colors.ansi[12] = this._parseColor(theme.brightBlue, DEFAULT_ANSI_COLORS[12]);\n this.colors.ansi[13] = this._parseColor(theme.brightMagenta, DEFAULT_ANSI_COLORS[13]);\n this.colors.ansi[14] = this._parseColor(theme.brightCyan, DEFAULT_ANSI_COLORS[14]);\n this.colors.ansi[15] = this._parseColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]);\n // Clear our the cache\n this._contrastCache.clear();\n this._updateRestoreColors();\n }\n\n public restoreColor(slot?: ColorIndex): void {\n // unset slot restores all ansi colors\n if (slot === undefined) {\n for (let i = 0; i < this._restoreColors.ansi.length; ++i) {\n this.colors.ansi[i] = this._restoreColors.ansi[i];\n }\n return;\n }\n switch (slot) {\n case ColorIndex.FOREGROUND:\n this.colors.foreground = this._restoreColors.foreground;\n break;\n case ColorIndex.BACKGROUND:\n this.colors.background = this._restoreColors.background;\n break;\n case ColorIndex.CURSOR:\n this.colors.cursor = this._restoreColors.cursor;\n break;\n default:\n this.colors.ansi[slot] = this._restoreColors.ansi[slot];\n }\n }\n\n private _updateRestoreColors(): void {\n this._restoreColors = {\n foreground: this.colors.foreground,\n background: this.colors.background,\n cursor: this.colors.cursor,\n ansi: [...this.colors.ansi]\n };\n }\n\n private _parseColor(\n css: string | undefined,\n fallback: IColor,\n allowTransparency: boolean = this.allowTransparency\n ): IColor {\n if (css === undefined) {\n return fallback;\n }\n\n // If parsing the value results in failure, then it must be ignored, and the attribute must\n // retain its previous value.\n // -- https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles\n this._ctx.fillStyle = this._litmusColor;\n this._ctx.fillStyle = css;\n if (typeof this._ctx.fillStyle !== 'string') {\n console.warn(`Color: ${css} is invalid using fallback ${fallback.css}`);\n return fallback;\n }\n\n this._ctx.fillRect(0, 0, 1, 1);\n const data = this._ctx.getImageData(0, 0, 1, 1).data;\n\n // Check if the printed color was transparent\n if (data[3] !== 0xFF) {\n if (!allowTransparency) {\n // Ideally we'd just ignore the alpha channel, but...\n //\n // Browsers may not give back exactly the same RGB values we put in, because most/all\n // convert the color to a pre-multiplied representation. getImageData converts that back to\n // a un-premultipled representation, but the precision loss may make the RGB channels unuable\n // on their own.\n //\n // E.g. In Chrome #12345610 turns into #10305010, and in the extreme case, 0xFFFFFF00 turns\n // into 0x00000000.\n //\n // \"Note: Due to the lossy nature of converting to and from premultiplied alpha color values,\n // pixels that have just been set using putImageData() might be returned to an equivalent\n // getImageData() as different values.\"\n // -- https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation\n //\n // So let's just use the fallback color in this case instead.\n console.warn(\n `Color: ${css} is using transparency, but allowTransparency is false. ` +\n `Using fallback ${fallback.css}.`\n );\n return fallback;\n }\n\n // https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color\n // the color value has alpha less than 1.0, and the string is the color value in the CSS rgba()\n const [r, g, b, a] = this._ctx.fillStyle.substring(5, this._ctx.fillStyle.length - 1).split(',').map(component => Number(component));\n const alpha = Math.round(a * 255);\n const rgba: number = channels.toRgba(r, g, b, alpha);\n return {\n rgba,\n css\n };\n }\n\n return {\n // https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color\n // if it has alpha equal to 1.0, then the string is a lowercase six-digit hex value, prefixed with a \"#\" character\n css: this._ctx.fillStyle,\n rgba: channels.toRgba(data[0], data[1], data[2], data[3])\n };\n }\n}\n","/**\n * Copyright (c) 2020 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport function removeElementFromParent(...elements: (HTMLElement | undefined)[]): void {\n for (const e of elements) {\n e?.parentElement?.removeChild(e);\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IDisposable } from 'common/Types';\n\n/**\n * Adds a disposable listener to a node in the DOM, returning the disposable.\n * @param type The event type.\n * @param handler The handler for the listener.\n */\nexport function addDisposableDomListener(\n node: Element | Window | Document,\n type: string,\n handler: (e: any) => void,\n options?: boolean | AddEventListenerOptions\n): IDisposable {\n node.addEventListener(type, handler, options);\n let disposed = false;\n return {\n dispose: () => {\n if (disposed) {\n return;\n }\n disposed = true;\n node.removeEventListener(type, handler, options);\n }\n };\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types';\nimport { IBufferStringIteratorResult } from 'common/buffer/Types';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { ILogService, IBufferService, IOptionsService, IUnicodeService } from 'common/services/Services';\n\n/**\n * Limit of the unwrapping line expansion (overscan) at the top and bottom\n * of the actual viewport in ASCII characters.\n * A limit of 2000 should match most sane urls.\n */\nconst OVERSCAN_CHAR_LIMIT = 2000;\n\n/**\n * The Linkifier applies links to rows shortly after they have been refreshed.\n */\nexport class Linkifier implements ILinkifier {\n /**\n * The time to wait after a row is changed before it is linkified. This prevents\n * the costly operation of searching every row multiple times, potentially a\n * huge amount of times.\n */\n protected static _timeBeforeLatency = 200;\n\n protected _linkMatchers: IRegisteredLinkMatcher[] = [];\n\n private _mouseZoneManager: IMouseZoneManager | undefined;\n private _element: HTMLElement | undefined;\n\n private _rowsTimeoutId: number | undefined;\n private _nextLinkMatcherId = 0;\n private _rowsToLinkify: { start: number | undefined, end: number | undefined };\n\n private _onShowLinkUnderline = new EventEmitter();\n public get onShowLinkUnderline(): IEvent { return this._onShowLinkUnderline.event; }\n private _onHideLinkUnderline = new EventEmitter();\n public get onHideLinkUnderline(): IEvent { return this._onHideLinkUnderline.event; }\n private _onLinkTooltip = new EventEmitter();\n public get onLinkTooltip(): IEvent { return this._onLinkTooltip.event; }\n\n constructor(\n @IBufferService protected readonly _bufferService: IBufferService,\n @ILogService private readonly _logService: ILogService,\n @IUnicodeService private readonly _unicodeService: IUnicodeService\n ) {\n this._rowsToLinkify = {\n start: undefined,\n end: undefined\n };\n }\n\n /**\n * Attaches the linkifier to the DOM, enabling linkification.\n * @param mouseZoneManager The mouse zone manager to register link zones with.\n */\n public attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void {\n this._element = element;\n this._mouseZoneManager = mouseZoneManager;\n }\n\n /**\n * Queue linkification on a set of rows.\n * @param start The row to linkify from (inclusive).\n * @param end The row to linkify to (inclusive).\n */\n public linkifyRows(start: number, end: number): void {\n // Don't attempt linkify if not yet attached to DOM\n if (!this._mouseZoneManager) {\n return;\n }\n\n // Increase range to linkify\n if (this._rowsToLinkify.start === undefined || this._rowsToLinkify.end === undefined) {\n this._rowsToLinkify.start = start;\n this._rowsToLinkify.end = end;\n } else {\n this._rowsToLinkify.start = Math.min(this._rowsToLinkify.start, start);\n this._rowsToLinkify.end = Math.max(this._rowsToLinkify.end, end);\n }\n\n // Clear out any existing links on this row range\n this._mouseZoneManager.clearAll(start, end);\n\n // Restart timer\n if (this._rowsTimeoutId) {\n clearTimeout(this._rowsTimeoutId);\n }\n\n // Cannot use window.setTimeout since tests need to run in node\n this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier._timeBeforeLatency) as any as number;\n }\n\n /**\n * Linkifies the rows requested.\n */\n private _linkifyRows(): void {\n this._rowsTimeoutId = undefined;\n const buffer = this._bufferService.buffer;\n\n if (this._rowsToLinkify.start === undefined || this._rowsToLinkify.end === undefined) {\n this._logService.debug('_rowToLinkify was unset before _linkifyRows was called');\n return;\n }\n\n // Ensure the start row exists\n const absoluteRowIndexStart = buffer.ydisp + this._rowsToLinkify.start;\n if (absoluteRowIndexStart >= buffer.lines.length) {\n return;\n }\n\n // Invalidate bad end row values (if a resize happened)\n const absoluteRowIndexEnd = buffer.ydisp + Math.min(this._rowsToLinkify.end, this._bufferService.rows) + 1;\n\n // Iterate over the range of unwrapped content strings within start..end\n // (excluding).\n // _doLinkifyRow gets full unwrapped lines with the start row as buffer offset\n // for every matcher.\n // The unwrapping is needed to also match content that got wrapped across\n // several buffer lines. To avoid a worst case scenario where the whole buffer\n // contains just a single unwrapped string we limit this line expansion beyond\n // the viewport to +OVERSCAN_CHAR_LIMIT chars (overscan) at top and bottom.\n // This comes with the tradeoff that matches longer than OVERSCAN_CHAR_LIMIT\n // chars will not match anymore at the viewport borders.\n const overscanLineLimit = Math.ceil(OVERSCAN_CHAR_LIMIT / this._bufferService.cols);\n const iterator = this._bufferService.buffer.iterator(\n false, absoluteRowIndexStart, absoluteRowIndexEnd, overscanLineLimit, overscanLineLimit);\n while (iterator.hasNext()) {\n const lineData: IBufferStringIteratorResult = iterator.next();\n for (let i = 0; i < this._linkMatchers.length; i++) {\n this._doLinkifyRow(lineData.range.first, lineData.content, this._linkMatchers[i]);\n }\n }\n\n this._rowsToLinkify.start = undefined;\n this._rowsToLinkify.end = undefined;\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for. Specifically, this\n * searches the textContent of the rows. You will want to use \\s to match a\n * space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number {\n if (!handler) {\n throw new Error('handler must be defined');\n }\n const matcher: IRegisteredLinkMatcher = {\n id: this._nextLinkMatcherId++,\n regex,\n handler,\n matchIndex: options.matchIndex,\n validationCallback: options.validationCallback,\n hoverTooltipCallback: options.tooltipCallback,\n hoverLeaveCallback: options.leaveCallback,\n willLinkActivate: options.willLinkActivate,\n priority: options.priority || 0\n };\n this._addLinkMatcherToList(matcher);\n return matcher.id;\n }\n\n /**\n * Inserts a link matcher to the list in the correct position based on the\n * priority of each link matcher. New link matchers of equal priority are\n * considered after older link matchers.\n * @param matcher The link matcher to be added.\n */\n private _addLinkMatcherToList(matcher: IRegisteredLinkMatcher): void {\n if (this._linkMatchers.length === 0) {\n this._linkMatchers.push(matcher);\n return;\n }\n\n for (let i = this._linkMatchers.length - 1; i >= 0; i--) {\n if (matcher.priority <= this._linkMatchers[i].priority) {\n this._linkMatchers.splice(i + 1, 0, matcher);\n return;\n }\n }\n\n this._linkMatchers.splice(0, 0, matcher);\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n * @return Whether a link matcher was found and deregistered.\n */\n public deregisterLinkMatcher(matcherId: number): boolean {\n for (let i = 0; i < this._linkMatchers.length; i++) {\n if (this._linkMatchers[i].id === matcherId) {\n this._linkMatchers.splice(i, 1);\n return true;\n }\n }\n return false;\n }\n\n /**\n * Linkifies a row given a specific handler.\n * @param rowIndex The row index to linkify (absolute index).\n * @param text string content of the unwrapped row.\n * @param matcher The link matcher for this line.\n */\n private _doLinkifyRow(rowIndex: number, text: string, matcher: ILinkMatcher): void {\n // clone regex to do a global search on text\n const rex = new RegExp(matcher.regex.source, (matcher.regex.flags || '') + 'g');\n let match;\n let stringIndex = -1;\n while ((match = rex.exec(text)) !== null) {\n const uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];\n if (!uri) {\n // something matched but does not comply with the given matchIndex\n // since this is most likely a bug the regex itself we simply do nothing here\n this._logService.debug('match found without corresponding matchIndex', match, matcher);\n break;\n }\n\n // Get index, match.index is for the outer match which includes negated chars\n // therefore we cannot use match.index directly, instead we search the position\n // of the match group in text again\n // also correct regex and string search offsets for the next loop run\n stringIndex = text.indexOf(uri, stringIndex + 1);\n rex.lastIndex = stringIndex + uri.length;\n if (stringIndex < 0) {\n // invalid stringIndex (should not have happened)\n break;\n }\n\n // get the buffer index as [absolute row, col] for the match\n const bufferIndex = this._bufferService.buffer.stringIndexToBufferIndex(rowIndex, stringIndex);\n if (bufferIndex[0] < 0) {\n // invalid bufferIndex (should not have happened)\n break;\n }\n\n const line = this._bufferService.buffer.lines.get(bufferIndex[0]);\n if (!line) {\n break;\n }\n\n const attr = line.getFg(bufferIndex[1]);\n const fg = attr ? (attr >> 9) & 0x1ff : undefined;\n\n if (matcher.validationCallback) {\n matcher.validationCallback(uri, isValid => {\n // Discard link if the line has already changed\n if (this._rowsTimeoutId) {\n return;\n }\n if (isValid) {\n this._addLink(bufferIndex[1], bufferIndex[0] - this._bufferService.buffer.ydisp, uri, matcher, fg);\n }\n });\n } else {\n this._addLink(bufferIndex[1], bufferIndex[0] - this._bufferService.buffer.ydisp, uri, matcher, fg);\n }\n }\n }\n\n /**\n * Registers a link to the mouse zone manager.\n * @param x The column the link starts.\n * @param y The row the link is on.\n * @param uri The URI of the link.\n * @param matcher The link matcher for the link.\n * @param fg The link color for hover event.\n */\n private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher, fg: number | undefined): void {\n if (!this._mouseZoneManager || !this._element) {\n return;\n }\n // FIXME: get cell length from buffer to avoid mismatch after Unicode version change\n const width = this._unicodeService.getStringCellWidth(uri);\n const x1 = x % this._bufferService.cols;\n const y1 = y + Math.floor(x / this._bufferService.cols);\n let x2 = (x1 + width) % this._bufferService.cols;\n let y2 = y1 + Math.floor((x1 + width) / this._bufferService.cols);\n if (x2 === 0) {\n x2 = this._bufferService.cols;\n y2--;\n }\n\n this._mouseZoneManager.add(new MouseZone(\n x1 + 1,\n y1 + 1,\n x2 + 1,\n y2 + 1,\n e => {\n if (matcher.handler) {\n return matcher.handler(e, uri);\n }\n const newWindow = window.open();\n if (newWindow) {\n newWindow.opener = null;\n newWindow.location.href = uri;\n } else {\n console.warn('Opening link blocked as opener could not be cleared');\n }\n },\n () => {\n this._onShowLinkUnderline.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));\n this._element!.classList.add('xterm-cursor-pointer');\n },\n e => {\n this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));\n if (matcher.hoverTooltipCallback) {\n // Note that IViewportRange use 1-based coordinates to align with escape sequences such\n // as CUP which use 1,1 as the default for row/col\n matcher.hoverTooltipCallback(e, uri, { start: { x: x1, y: y1 }, end: { x: x2, y: y2 } });\n }\n },\n () => {\n this._onHideLinkUnderline.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));\n this._element!.classList.remove('xterm-cursor-pointer');\n if (matcher.hoverLeaveCallback) {\n matcher.hoverLeaveCallback();\n }\n },\n e => {\n if (matcher.willLinkActivate) {\n return matcher.willLinkActivate(e, uri);\n }\n return true;\n }\n ));\n }\n\n private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number | undefined): ILinkifierEvent {\n return { x1, y1, x2, y2, cols: this._bufferService.cols, fg };\n }\n}\n\nexport class MouseZone implements IMouseZone {\n constructor(\n public x1: number,\n public y1: number,\n public x2: number,\n public y2: number,\n public clickCallback: (e: MouseEvent) => any,\n public hoverCallback: (e: MouseEvent) => any,\n public tooltipCallback: (e: MouseEvent) => any,\n public leaveCallback: () => void,\n public willLinkActivate: (e: MouseEvent) => boolean\n ) {\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations, ILinkWithState } from 'browser/Types';\nimport { IDisposable } from 'common/Types';\nimport { IMouseService, IRenderService } from './services/Services';\nimport { IBufferService } from 'common/services/Services';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { Disposable, getDisposeArrayDisposable, disposeArray } from 'common/Lifecycle';\nimport { addDisposableDomListener } from 'browser/Lifecycle';\n\nexport class Linkifier2 extends Disposable implements ILinkifier2 {\n private _element: HTMLElement | undefined;\n private _mouseService: IMouseService | undefined;\n private _renderService: IRenderService | undefined;\n private _linkProviders: ILinkProvider[] = [];\n public get currentLink(): ILinkWithState | undefined { return this._currentLink; }\n protected _currentLink: ILinkWithState | undefined;\n private _lastMouseEvent: MouseEvent | undefined;\n private _linkCacheDisposables: IDisposable[] = [];\n private _lastBufferCell: IBufferCellPosition | undefined;\n private _isMouseOut: boolean = true;\n private _activeProviderReplies: Map | undefined;\n private _activeLine: number = -1;\n\n private _onShowLinkUnderline = this.register(new EventEmitter());\n public get onShowLinkUnderline(): IEvent { return this._onShowLinkUnderline.event; }\n private _onHideLinkUnderline = this.register(new EventEmitter());\n public get onHideLinkUnderline(): IEvent { return this._onHideLinkUnderline.event; }\n\n constructor(\n @IBufferService private readonly _bufferService: IBufferService\n ) {\n super();\n this.register(getDisposeArrayDisposable(this._linkCacheDisposables));\n }\n\n public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {\n this._linkProviders.push(linkProvider);\n return {\n dispose: () => {\n // Remove the link provider from the list\n const providerIndex = this._linkProviders.indexOf(linkProvider);\n\n if (providerIndex !== -1) {\n this._linkProviders.splice(providerIndex, 1);\n }\n }\n };\n }\n\n public attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void {\n this._element = element;\n this._mouseService = mouseService;\n this._renderService = renderService;\n\n this.register(addDisposableDomListener(this._element, 'mouseleave', () => {\n this._isMouseOut = true;\n this._clearCurrentLink();\n }));\n this.register(addDisposableDomListener(this._element, 'mousemove', this._onMouseMove.bind(this)));\n this.register(addDisposableDomListener(this._element, 'click', this._onClick.bind(this)));\n }\n\n private _onMouseMove(event: MouseEvent): void {\n this._lastMouseEvent = event;\n\n if (!this._element || !this._mouseService) {\n return;\n }\n\n const position = this._positionFromMouseEvent(event, this._element, this._mouseService);\n if (!position) {\n return;\n }\n this._isMouseOut = false;\n\n // Ignore the event if it's an embedder created hover widget\n const composedPath = event.composedPath() as HTMLElement[];\n for (let i = 0; i < composedPath.length; i++) {\n const target = composedPath[i];\n // Hit Terminal.element, break and continue\n if (target.classList.contains('xterm')) {\n break;\n }\n // It's a hover, don't respect hover event\n if (target.classList.contains('xterm-hover')) {\n return;\n }\n }\n\n if (!this._lastBufferCell || (position.x !== this._lastBufferCell.x || position.y !== this._lastBufferCell.y)) {\n this._onHover(position);\n this._lastBufferCell = position;\n }\n }\n\n private _onHover(position: IBufferCellPosition): void {\n // TODO: This currently does not cache link provider results across wrapped lines, activeLine should be something like `activeRange: {startY, endY}`\n // Check if we need to clear the link\n if (this._activeLine !== position.y) {\n this._clearCurrentLink();\n this._askForLink(position, false);\n return;\n }\n\n // Check the if the link is in the mouse position\n const isCurrentLinkInPosition = this._currentLink && this._linkAtPosition(this._currentLink.link, position);\n if (!isCurrentLinkInPosition) {\n this._clearCurrentLink();\n this._askForLink(position, true);\n }\n }\n\n private _askForLink(position: IBufferCellPosition, useLineCache: boolean): void {\n if (!this._activeProviderReplies || !useLineCache) {\n this._activeProviderReplies?.forEach(reply => {\n reply?.forEach(linkWithState => {\n if (linkWithState.link.dispose) {\n linkWithState.link.dispose();\n }\n });\n });\n this._activeProviderReplies = new Map();\n this._activeLine = position.y;\n }\n let linkProvided = false;\n\n // There is no link cached, so ask for one\n this._linkProviders.forEach((linkProvider, i) => {\n if (useLineCache) {\n const existingReply = this._activeProviderReplies?.get(i);\n // If there isn't a reply, the provider hasn't responded yet.\n\n // TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring\n // provideLinks isn't triggered again saves ILink.hover firing twice though. This probably\n // needs promises to get fixed\n if (existingReply) {\n linkProvided = this._checkLinkProviderResult(i, position, linkProvided);\n }\n } else {\n linkProvider.provideLinks(position.y, (links: ILink[] | undefined) => {\n if (this._isMouseOut) {\n return;\n }\n const linksWithState: ILinkWithState[] | undefined = links?.map(link => ({ link }));\n this._activeProviderReplies?.set(i, linksWithState);\n linkProvided = this._checkLinkProviderResult(i, position, linkProvided);\n\n // If all providers have responded, remove lower priority links that intersect ranges of\n // higher priority links\n if (this._activeProviderReplies?.size === this._linkProviders.length) {\n this._removeIntersectingLinks(position.y, this._activeProviderReplies);\n }\n });\n }\n });\n }\n\n private _removeIntersectingLinks(y: number, replies: Map): void {\n const occupiedCells = new Set();\n for (let i = 0; i < replies.size; i++) {\n const providerReply = replies.get(i);\n if (!providerReply) {\n continue;\n }\n for (let i = 0; i < providerReply.length; i++) {\n const linkWithState = providerReply[i];\n const startX = linkWithState.link.range.start.y < y ? 0 : linkWithState.link.range.start.x;\n const endX = linkWithState.link.range.end.y > y ? this._bufferService.cols : linkWithState.link.range.end.x;\n for (let x = startX; x <= endX; x++) {\n if (occupiedCells.has(x)) {\n providerReply.splice(i--, 1);\n break;\n }\n occupiedCells.add(x);\n }\n }\n }\n }\n\n private _checkLinkProviderResult(index: number, position: IBufferCellPosition, linkProvided: boolean): boolean {\n if (!this._activeProviderReplies) {\n return linkProvided;\n }\n\n const links = this._activeProviderReplies.get(index);\n\n // Check if every provider before this one has come back undefined\n let hasLinkBefore = false;\n for (let j = 0; j < index; j++) {\n if (!this._activeProviderReplies.has(j) || this._activeProviderReplies.get(j)) {\n hasLinkBefore = true;\n }\n }\n\n // If all providers with higher priority came back undefined, then this provider's link for\n // the position should be used\n if (!hasLinkBefore && links) {\n const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position));\n if (linkAtPosition) {\n linkProvided = true;\n this._handleNewLink(linkAtPosition);\n }\n }\n\n // Check if all the providers have responded\n if (this._activeProviderReplies.size === this._linkProviders.length && !linkProvided) {\n // Respect the order of the link providers\n for (let j = 0; j < this._activeProviderReplies.size; j++) {\n const currentLink = this._activeProviderReplies.get(j)?.find(link => this._linkAtPosition(link.link, position));\n if (currentLink) {\n linkProvided = true;\n this._handleNewLink(currentLink);\n break;\n }\n }\n }\n\n return linkProvided;\n }\n\n private _onClick(event: MouseEvent): void {\n if (!this._element || !this._mouseService || !this._currentLink) {\n return;\n }\n\n const position = this._positionFromMouseEvent(event, this._element, this._mouseService);\n\n if (!position) {\n return;\n }\n\n if (this._linkAtPosition(this._currentLink.link, position)) {\n this._currentLink.link.activate(event, this._currentLink.link.text);\n }\n }\n\n private _clearCurrentLink(startRow?: number, endRow?: number): void {\n if (!this._element || !this._currentLink || !this._lastMouseEvent) {\n return;\n }\n\n // If we have a start and end row, check that the link is within it\n if (!startRow || !endRow || (this._currentLink.link.range.start.y >= startRow && this._currentLink.link.range.end.y <= endRow)) {\n this._linkLeave(this._element, this._currentLink.link, this._lastMouseEvent);\n this._currentLink = undefined;\n disposeArray(this._linkCacheDisposables);\n }\n }\n\n private _handleNewLink(linkWithState: ILinkWithState): void {\n if (!this._element || !this._lastMouseEvent || !this._mouseService) {\n return;\n }\n\n const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element, this._mouseService);\n\n if (!position) {\n return;\n }\n\n // Trigger hover if the we have a link at the position\n if (this._linkAtPosition(linkWithState.link, position)) {\n this._currentLink = linkWithState;\n this._currentLink.state = {\n decorations: {\n underline: linkWithState.link.decorations === undefined ? true : linkWithState.link.decorations.underline,\n pointerCursor: linkWithState.link.decorations === undefined ? true : linkWithState.link.decorations.pointerCursor\n },\n isHovered: true\n };\n this._linkHover(this._element, linkWithState.link, this._lastMouseEvent);\n\n // Add listener for tracking decorations changes\n linkWithState.link.decorations = {} as ILinkDecorations;\n Object.defineProperties(linkWithState.link.decorations, {\n pointerCursor: {\n get: () => this._currentLink?.state?.decorations.pointerCursor,\n set: v => {\n if (this._currentLink?.state && this._currentLink.state.decorations.pointerCursor !== v) {\n this._currentLink.state.decorations.pointerCursor = v;\n if (this._currentLink.state.isHovered) {\n this._element?.classList.toggle('xterm-cursor-pointer', v);\n }\n }\n }\n },\n underline: {\n get: () => this._currentLink?.state?.decorations.underline,\n set: v => {\n if (this._currentLink?.state && this._currentLink?.state?.decorations.underline !== v) {\n this._currentLink.state.decorations.underline = v;\n if (this._currentLink.state.isHovered) {\n this._fireUnderlineEvent(linkWithState.link, v);\n }\n }\n }\n }\n });\n\n // Add listener for rerendering\n if (this._renderService) {\n this._linkCacheDisposables.push(this._renderService.onRenderedBufferChange(e => {\n // When start is 0 a scroll most likely occurred, make sure links above the fold also get\n // cleared.\n const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp;\n this._clearCurrentLink(start, e.end + 1 + this._bufferService.buffer.ydisp);\n }));\n }\n }\n }\n\n protected _linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void {\n if (this._currentLink?.state) {\n this._currentLink.state.isHovered = true;\n if (this._currentLink.state.decorations.underline) {\n this._fireUnderlineEvent(link, true);\n }\n if (this._currentLink.state.decorations.pointerCursor) {\n element.classList.add('xterm-cursor-pointer');\n }\n }\n\n if (link.hover) {\n link.hover(event, link.text);\n }\n }\n\n private _fireUnderlineEvent(link: ILink, showEvent: boolean): void {\n const range = link.range;\n const scrollOffset = this._bufferService.buffer.ydisp;\n const event = this._createLinkUnderlineEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined);\n const emitter = showEvent ? this._onShowLinkUnderline : this._onHideLinkUnderline;\n emitter.fire(event);\n }\n\n protected _linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void {\n if (this._currentLink?.state) {\n this._currentLink.state.isHovered = false;\n if (this._currentLink.state.decorations.underline) {\n this._fireUnderlineEvent(link, false);\n }\n if (this._currentLink.state.decorations.pointerCursor) {\n element.classList.remove('xterm-cursor-pointer');\n }\n }\n\n if (link.leave) {\n link.leave(event, link.text);\n }\n }\n\n /**\n * Check if the buffer position is within the link\n * @param link\n * @param position\n */\n private _linkAtPosition(link: ILink, position: IBufferCellPosition): boolean {\n const sameLine = link.range.start.y === link.range.end.y;\n const wrappedFromLeft = link.range.start.y < position.y;\n const wrappedToRight = link.range.end.y > position.y;\n\n // If the start and end have the same y, then the position must be between start and end x\n // If not, then handle each case seperately, depending on which way it wraps\n return ((sameLine && link.range.start.x <= position.x && link.range.end.x >= position.x) ||\n (wrappedFromLeft && link.range.end.x >= position.x) ||\n (wrappedToRight && link.range.start.x <= position.x) ||\n (wrappedFromLeft && wrappedToRight)) &&\n link.range.start.y <= position.y &&\n link.range.end.y >= position.y;\n }\n\n /**\n * Get the buffer position from a mouse event\n * @param event\n */\n private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement, mouseService: IMouseService): IBufferCellPosition | undefined {\n const coords = mouseService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);\n if (!coords) {\n return;\n }\n\n return { x: coords[0], y: coords[1] + this._bufferService.buffer.ydisp };\n }\n\n private _createLinkUnderlineEvent(x1: number, y1: number, x2: number, y2: number, fg: number | undefined): ILinkifierEvent {\n return { x1, y1, x2, y2, cols: this._bufferService.cols, fg };\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n// eslint-disable-next-line prefer-const\nexport let promptLabel = 'Terminal input';\n\n// eslint-disable-next-line prefer-const\nexport let tooMuchOutput = 'Too much output to announce, navigate to rows manually to read';\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Disposable } from 'common/Lifecycle';\nimport { addDisposableDomListener } from 'browser/Lifecycle';\nimport { IMouseService, ISelectionService } from 'browser/services/Services';\nimport { IMouseZoneManager, IMouseZone } from 'browser/Types';\nimport { IBufferService, IOptionsService } from 'common/services/Services';\n\n/**\n * The MouseZoneManager allows components to register zones within the terminal\n * that trigger hover and click callbacks.\n *\n * This class was intentionally made not so robust initially as the only case it\n * needed to support was single-line links which never overlap. Improvements can\n * be made in the future.\n */\nexport class MouseZoneManager extends Disposable implements IMouseZoneManager {\n private _zones: IMouseZone[] = [];\n\n private _areZonesActive: boolean = false;\n private _mouseMoveListener: (e: MouseEvent) => any;\n private _mouseLeaveListener: (e: MouseEvent) => any;\n private _clickListener: (e: MouseEvent) => any;\n\n private _tooltipTimeout: number | undefined;\n private _currentZone: IMouseZone | undefined;\n private _lastHoverCoords: [number | undefined, number | undefined] = [undefined, undefined];\n private _initialSelectionLength: number = 0;\n\n constructor(\n private readonly _element: HTMLElement,\n private readonly _screenElement: HTMLElement,\n @IBufferService private readonly _bufferService: IBufferService,\n @IMouseService private readonly _mouseService: IMouseService,\n @ISelectionService private readonly _selectionService: ISelectionService,\n @IOptionsService private readonly _optionsService: IOptionsService\n ) {\n super();\n\n this.register(addDisposableDomListener(this._element, 'mousedown', e => this._onMouseDown(e)));\n\n // These events are expensive, only listen to it when mouse zones are active\n this._mouseMoveListener = e => this._onMouseMove(e);\n this._mouseLeaveListener = e => this._onMouseLeave(e);\n this._clickListener = e => this._onClick(e);\n }\n\n public dispose(): void {\n super.dispose();\n this._deactivate();\n }\n\n public add(zone: IMouseZone): void {\n this._zones.push(zone);\n if (this._zones.length === 1) {\n this._activate();\n }\n }\n\n public clearAll(start?: number, end?: number): void {\n // Exit if there's nothing to clear\n if (this._zones.length === 0) {\n return;\n }\n\n // Clear all if start/end weren't set\n if (!start || !end) {\n start = 0;\n end = this._bufferService.rows - 1;\n }\n\n // Iterate through zones and clear them out if they're within the range\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if ((zone.y1 > start && zone.y1 <= end + 1) ||\n (zone.y2 > start && zone.y2 <= end + 1) ||\n (zone.y1 < start && zone.y2 > end + 1)) {\n if (this._currentZone && this._currentZone === zone) {\n this._currentZone.leaveCallback();\n this._currentZone = undefined;\n }\n this._zones.splice(i--, 1);\n }\n }\n\n // Deactivate the mouse zone manager if all the zones have been removed\n if (this._zones.length === 0) {\n this._deactivate();\n }\n }\n\n private _activate(): void {\n if (!this._areZonesActive) {\n this._areZonesActive = true;\n this._element.addEventListener('mousemove', this._mouseMoveListener);\n this._element.addEventListener('mouseleave', this._mouseLeaveListener);\n this._element.addEventListener('click', this._clickListener);\n }\n }\n\n private _deactivate(): void {\n if (this._areZonesActive) {\n this._areZonesActive = false;\n this._element.removeEventListener('mousemove', this._mouseMoveListener);\n this._element.removeEventListener('mouseleave', this._mouseLeaveListener);\n this._element.removeEventListener('click', this._clickListener);\n }\n }\n\n private _onMouseMove(e: MouseEvent): void {\n // TODO: Ideally this would only clear the hover state when the mouse moves\n // outside of the mouse zone\n if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) {\n this._onHover(e);\n // Record the current coordinates\n this._lastHoverCoords = [e.pageX, e.pageY];\n }\n }\n\n private _onHover(e: MouseEvent): void {\n const zone = this._findZoneEventAt(e);\n\n // Do nothing if the zone is the same\n if (zone === this._currentZone) {\n return;\n }\n\n // Fire the hover end callback and cancel any existing timer if a new zone\n // is being hovered\n if (this._currentZone) {\n this._currentZone.leaveCallback();\n this._currentZone = undefined;\n if (this._tooltipTimeout) {\n clearTimeout(this._tooltipTimeout);\n }\n }\n\n // Exit if there is not zone\n if (!zone) {\n return;\n }\n this._currentZone = zone;\n\n // Trigger the hover callback\n if (zone.hoverCallback) {\n zone.hoverCallback(e);\n }\n\n // Restart the tooltip timeout\n this._tooltipTimeout = window.setTimeout(() => this._onTooltip(e), this._optionsService.options.linkTooltipHoverDuration);\n }\n\n private _onTooltip(e: MouseEvent): void {\n this._tooltipTimeout = undefined;\n const zone = this._findZoneEventAt(e);\n zone?.tooltipCallback(e);\n }\n\n private _onMouseDown(e: MouseEvent): void {\n // Store current terminal selection length, to check if we're performing\n // a selection operation\n this._initialSelectionLength = this._getSelectionLength();\n\n // Ignore the event if there are no zones active\n if (!this._areZonesActive) {\n return;\n }\n\n // Find the active zone, prevent event propagation if found to prevent other\n // components from handling the mouse event.\n const zone = this._findZoneEventAt(e);\n if (zone?.willLinkActivate(e)) {\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _onMouseLeave(e: MouseEvent): void {\n // Fire the hover end callback and cancel any existing timer if the mouse\n // leaves the terminal element\n if (this._currentZone) {\n this._currentZone.leaveCallback();\n this._currentZone = undefined;\n if (this._tooltipTimeout) {\n clearTimeout(this._tooltipTimeout);\n }\n }\n }\n\n private _onClick(e: MouseEvent): void {\n // Find the active zone and click it if found and no selection was\n // being performed\n const zone = this._findZoneEventAt(e);\n const currentSelectionLength = this._getSelectionLength();\n\n if (zone && currentSelectionLength === this._initialSelectionLength) {\n zone.clickCallback(e);\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _getSelectionLength(): number {\n const selectionText = this._selectionService.selectionText;\n return selectionText ? selectionText.length : 0;\n }\n\n private _findZoneEventAt(e: MouseEvent): IMouseZone | undefined {\n const coords = this._mouseService.getCoords(e, this._screenElement, this._bufferService.cols, this._bufferService.rows);\n if (!coords) {\n return undefined;\n }\n const x = coords[0];\n const y = coords[1];\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if (zone.y1 === zone.y2) {\n // Single line link\n if (y === zone.y1 && x >= zone.x1 && x < zone.x2) {\n return zone;\n }\n } else {\n // Multi-line link\n if ((y === zone.y1 && x >= zone.x1) ||\n (y === zone.y2 && x < zone.x2) ||\n (y > zone.y1 && y < zone.y2)) {\n return zone;\n }\n }\n }\n return undefined;\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDebouncer } from 'browser/Types';\n\n/**\n * Debounces calls to render terminal rows using animation frames.\n */\nexport class RenderDebouncer implements IRenderDebouncer {\n private _rowStart: number | undefined;\n private _rowEnd: number | undefined;\n private _rowCount: number | undefined;\n private _animationFrame: number | undefined;\n\n constructor(\n private _renderCallback: (start: number, end: number) => void\n ) {\n }\n\n public dispose(): void {\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = undefined;\n }\n }\n\n public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {\n this._rowCount = rowCount;\n // Get the min/max row start/end for the arg values\n rowStart = rowStart !== undefined ? rowStart : 0;\n rowEnd = rowEnd !== undefined ? rowEnd : this._rowCount - 1;\n // Set the properties to the updated values\n this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;\n this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;\n\n if (this._animationFrame) {\n return;\n }\n\n this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());\n }\n\n private _innerRefresh(): void {\n // Make sure values are set\n if (this._rowStart === undefined || this._rowEnd === undefined || this._rowCount === undefined) {\n return;\n }\n\n // Clamp values\n const start = Math.max(this._rowStart, 0);\n const end = Math.min(this._rowEnd, this._rowCount - 1);\n\n // Reset debouncer (this happens before render callback as the render could trigger it again)\n this._rowStart = undefined;\n this._rowEnd = undefined;\n this._animationFrame = undefined;\n\n // Run render callback\n this._renderCallback(start, end);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Disposable } from 'common/Lifecycle';\n\nexport type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void;\n\n/**\n * The screen device pixel ratio monitor allows listening for when the\n * window.devicePixelRatio value changes. This is done not with polling but with\n * the use of window.matchMedia to watch media queries. When the event fires,\n * the listener will be reattached using a different media query to ensure that\n * any further changes will register.\n *\n * The listener should fire on both window zoom changes and switching to a\n * monitor with a different DPI.\n */\nexport class ScreenDprMonitor extends Disposable {\n private _currentDevicePixelRatio: number = window.devicePixelRatio;\n private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;\n private _listener: ScreenDprListener | undefined;\n private _resolutionMediaMatchList: MediaQueryList | undefined;\n\n public setListener(listener: ScreenDprListener): void {\n if (this._listener) {\n this.clearListener();\n }\n this._listener = listener;\n this._outerListener = () => {\n if (!this._listener) {\n return;\n }\n this._listener(window.devicePixelRatio, this._currentDevicePixelRatio);\n this._updateDpr();\n };\n this._updateDpr();\n }\n\n public dispose(): void {\n super.dispose();\n this.clearListener();\n }\n\n private _updateDpr(): void {\n if (!this._outerListener) {\n return;\n }\n\n // Clear listeners for old DPR\n this._resolutionMediaMatchList?.removeListener(this._outerListener);\n\n // Add listeners for new DPR\n this._currentDevicePixelRatio = window.devicePixelRatio;\n this._resolutionMediaMatchList = window.matchMedia(`screen and (resolution: ${window.devicePixelRatio}dppx)`);\n this._resolutionMediaMatchList.addListener(this._outerListener);\n }\n\n public clearListener(): void {\n if (!this._resolutionMediaMatchList || !this._listener || !this._outerListener) {\n return;\n }\n this._resolutionMediaMatchList.removeListener(this._outerListener);\n this._resolutionMediaMatchList = undefined;\n this._listener = undefined;\n this._outerListener = undefined;\n }\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n *\n * Originally forked from (with the author's permission):\n * Fabrice Bellard's javascript vt100 for jslinux:\n * http://bellard.org/jslinux/\n * Copyright (c) 2011 Fabrice Bellard\n * The original design remains. The terminal itself\n * has been extended to include xterm CSI codes, among\n * other features.\n *\n * Terminal Emulation References:\n * http://vt100.net/\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * http://invisible-island.net/vttest/\n * http://www.inwap.com/pdp10/ansicode.txt\n * http://linux.die.net/man/4/console_codes\n * http://linux.die.net/man/7/urxvt\n */\n\nimport { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2, CharacterJoinerHandler } from 'browser/Types';\nimport { IRenderer } from 'browser/renderer/Types';\nimport { CompositionHelper } from 'browser/input/CompositionHelper';\nimport { Viewport } from 'browser/Viewport';\nimport { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard';\nimport { C0 } from 'common/data/EscapeSequences';\nimport { WindowsOptionsReportType } from '../common/InputHandler';\nimport { Renderer } from 'browser/renderer/Renderer';\nimport { Linkifier } from 'browser/Linkifier';\nimport { SelectionService } from 'browser/services/SelectionService';\nimport * as Browser from 'common/Platform';\nimport { addDisposableDomListener } from 'browser/Lifecycle';\nimport * as Strings from 'browser/LocalizableStrings';\nimport { SoundService } from 'browser/services/SoundService';\nimport { MouseZoneManager } from 'browser/MouseZoneManager';\nimport { AccessibilityManager } from './AccessibilityManager';\nimport { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';\nimport { DomRenderer } from 'browser/renderer/dom/DomRenderer';\nimport { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';\nimport { evaluateKeyboardEvent } from 'common/input/Keyboard';\nimport { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';\nimport { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';\nimport { ColorManager } from 'browser/ColorManager';\nimport { RenderService } from 'browser/services/RenderService';\nimport { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService } from 'browser/services/Services';\nimport { CharSizeService } from 'browser/services/CharSizeService';\nimport { IBuffer } from 'common/buffer/Types';\nimport { MouseService } from 'browser/services/MouseService';\nimport { Linkifier2 } from 'browser/Linkifier2';\nimport { CoreBrowserService } from 'browser/services/CoreBrowserService';\nimport { CoreTerminal } from 'common/CoreTerminal';\nimport { color, rgba } from 'browser/Color';\nimport { CharacterJoinerService } from 'browser/services/CharacterJoinerService';\nimport { toRgbString } from 'common/input/XParseColor';\n\n// Let it work inside Node.js for automated testing purposes.\nconst document: Document = (typeof window !== 'undefined') ? window.document : null as any;\n\nexport class Terminal extends CoreTerminal implements ITerminal {\n public textarea: HTMLTextAreaElement | undefined;\n public element: HTMLElement | undefined;\n public screenElement: HTMLElement | undefined;\n\n private _document: Document | undefined;\n private _viewportScrollArea: HTMLElement | undefined;\n private _viewportElement: HTMLElement | undefined;\n private _helperContainer: HTMLElement | undefined;\n private _compositionView: HTMLElement | undefined;\n\n // private _visualBellTimer: number;\n\n public browser: IBrowser = Browser as any;\n\n private _customKeyEventHandler: CustomKeyEventHandler | undefined;\n\n // browser services\n private _charSizeService: ICharSizeService | undefined;\n private _mouseService: IMouseService | undefined;\n private _renderService: IRenderService | undefined;\n private _characterJoinerService: ICharacterJoinerService | undefined;\n private _selectionService: ISelectionService | undefined;\n private _soundService: ISoundService | undefined;\n\n /**\n * Records whether the keydown event has already been handled and triggered a data event, if so\n * the keypress event should not trigger a data event but should still print to the textarea so\n * screen readers will announce it.\n */\n private _keyDownHandled: boolean = false;\n\n /**\n * Records whether the keypress event has already been handled and triggered a data event, if so\n * the input event should not trigger a data event but should still print to the textarea so\n * screen readers will announce it.\n */\n private _keyPressHandled: boolean = false;\n\n /**\n * Records whether there has been a keydown event for a dead key without a corresponding keydown\n * event for the composed/alternative character. If we cancel the keydown event for the dead key,\n * no events will be emitted for the final character.\n */\n private _unprocessedDeadKey: boolean = false;\n\n public linkifier: ILinkifier;\n public linkifier2: ILinkifier2;\n public viewport: IViewport | undefined;\n private _compositionHelper: ICompositionHelper | undefined;\n private _mouseZoneManager: IMouseZoneManager | undefined;\n private _accessibilityManager: AccessibilityManager | undefined;\n private _colorManager: ColorManager | undefined;\n private _theme: ITheme | undefined;\n\n private _onCursorMove = new EventEmitter();\n public get onCursorMove(): IEvent { return this._onCursorMove.event; }\n private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>();\n public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; }\n private _onRender = new EventEmitter<{ start: number, end: number }>();\n public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; }\n private _onSelectionChange = new EventEmitter();\n public get onSelectionChange(): IEvent { return this._onSelectionChange.event; }\n private _onTitleChange = new EventEmitter();\n public get onTitleChange(): IEvent { return this._onTitleChange.event; }\n private _onBell = new EventEmitter();\n public get onBell(): IEvent { return this._onBell.event; }\n\n private _onFocus = new EventEmitter();\n public get onFocus(): IEvent { return this._onFocus.event; }\n private _onBlur = new EventEmitter();\n public get onBlur(): IEvent { return this._onBlur.event; }\n private _onA11yCharEmitter = new EventEmitter();\n public get onA11yChar(): IEvent { return this._onA11yCharEmitter.event; }\n private _onA11yTabEmitter = new EventEmitter();\n public get onA11yTab(): IEvent { return this._onA11yTabEmitter.event; }\n\n /**\n * Creates a new `Terminal` object.\n *\n * @param options An object containing a set of options, the available options are:\n * - `cursorBlink` (boolean): Whether the terminal cursor blinks\n * - `cols` (number): The number of columns of the terminal (horizontal size)\n * - `rows` (number): The number of rows of the terminal (vertical size)\n *\n * @public\n * @class Xterm Xterm\n * @alias module:xterm/src/xterm\n */\n constructor(\n options: Partial = {}\n ) {\n super(options);\n\n this._setup();\n\n this.linkifier = this._instantiationService.createInstance(Linkifier);\n this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));\n\n // Setup InputHandler listeners\n this.register(this._inputHandler.onRequestBell(() => this.bell()));\n this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)));\n this.register(this._inputHandler.onRequestSendFocus(() => this._reportFocus()));\n this.register(this._inputHandler.onRequestReset(() => this.reset()));\n this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));\n this.register(this._inputHandler.onColor((event) => this._handleColorEvent(event)));\n this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));\n this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));\n this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));\n this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter));\n\n // Setup listeners\n this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)));\n }\n\n /**\n * Handle color event from inputhandler for OSC 4|104 | 10|110 | 11|111 | 12|112.\n * An event from OSC 4|104 may contain multiple set or report requests, and multiple\n * or none restore requests (resetting all),\n * while an event from OSC 10|110 | 11|111 | 12|112 always contains a single request.\n */\n private _handleColorEvent(event: IColorEvent): void {\n if (!this._colorManager) return;\n for (const req of event) {\n let acc: 'foreground' | 'background' | 'cursor' | 'ansi' | undefined = undefined;\n let ident = '';\n switch (req.index) {\n case ColorIndex.FOREGROUND: // OSC 10 | 110\n acc = 'foreground';\n ident = '10';\n break;\n case ColorIndex.BACKGROUND: // OSC 11 | 111\n acc = 'background';\n ident = '11';\n break;\n case ColorIndex.CURSOR: // OSC 12 | 112\n acc = 'cursor';\n ident = '12';\n break;\n default: // OSC 4 | 104\n // we can skip the [0..255] range check here (already done in inputhandler)\n acc = 'ansi';\n ident = '4;' + req.index;\n }\n if (acc) {\n switch (req.type) {\n case ColorRequestType.REPORT:\n const channels = color.toColorRGB(acc === 'ansi'\n ? this._colorManager.colors.ansi[req.index]\n : this._colorManager.colors[acc]);\n this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(channels)}${C0.BEL}`);\n break;\n case ColorRequestType.SET:\n if (acc === 'ansi') this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color);\n else this._colorManager.colors[acc] = rgba.toColor(...req.color);\n break;\n case ColorRequestType.RESTORE:\n this._colorManager.restoreColor(req.index);\n break;\n }\n }\n }\n this._renderService?.setColors(this._colorManager.colors);\n this.viewport?.onThemeChange(this._colorManager.colors);\n }\n\n public dispose(): void {\n if (this._isDisposed) {\n return;\n }\n super.dispose();\n this._renderService?.dispose();\n this._customKeyEventHandler = undefined;\n this.write = () => { };\n this.element?.parentNode?.removeChild(this.element);\n }\n\n protected _setup(): void {\n super._setup();\n\n this._customKeyEventHandler = undefined;\n }\n\n /**\n * Convenience property to active buffer.\n */\n public get buffer(): IBuffer {\n return this.buffers.active;\n }\n\n /**\n * Focus the terminal. Delegates focus handling to the terminal's DOM element.\n */\n public focus(): void {\n if (this.textarea) {\n this.textarea.focus({ preventScroll: true });\n }\n }\n\n protected _updateOptions(key: string): void {\n super._updateOptions(key);\n\n // TODO: These listeners should be owned by individual components\n switch (key) {\n case 'fontFamily':\n case 'fontSize':\n // When the font changes the size of the cells may change which requires a renderer clear\n this._renderService?.clear();\n this._charSizeService?.measure();\n break;\n case 'cursorBlink':\n case 'cursorStyle':\n // The DOM renderer needs a row refresh to update the cursor styles\n this.refresh(this.buffer.y, this.buffer.y);\n break;\n case 'customGlyphs':\n case 'drawBoldTextInBrightColors':\n case 'letterSpacing':\n case 'lineHeight':\n case 'fontWeight':\n case 'fontWeightBold':\n case 'minimumContrastRatio':\n // When the font changes the size of the cells may change which requires a renderer clear\n if (this._renderService) {\n this._renderService.clear();\n this._renderService.onResize(this.cols, this.rows);\n this.refresh(0, this.rows - 1);\n }\n break;\n case 'rendererType':\n if (this._renderService) {\n this._renderService.setRenderer(this._createRenderer());\n this._renderService.onResize(this.cols, this.rows);\n }\n break;\n case 'scrollback':\n this.viewport?.syncScrollArea();\n break;\n case 'screenReaderMode':\n if (this.optionsService.options.screenReaderMode) {\n if (!this._accessibilityManager && this._renderService) {\n this._accessibilityManager = new AccessibilityManager(this, this._renderService);\n }\n } else {\n this._accessibilityManager?.dispose();\n this._accessibilityManager = undefined;\n }\n break;\n case 'tabStopWidth': this.buffers.setupTabStops(); break;\n case 'theme':\n this._setTheme(this.optionsService.options.theme);\n break;\n }\n }\n\n /**\n * Binds the desired focus behavior on a given terminal object.\n */\n private _onTextAreaFocus(ev: KeyboardEvent): void {\n if (this.coreService.decPrivateModes.sendFocus) {\n this.coreService.triggerDataEvent(C0.ESC + '[I');\n }\n this.updateCursorStyle(ev);\n this.element!.classList.add('focus');\n this._showCursor();\n this._onFocus.fire();\n }\n\n /**\n * Blur the terminal, calling the blur function on the terminal's underlying\n * textarea.\n */\n public blur(): void {\n return this.textarea?.blur();\n }\n\n /**\n * Binds the desired blur behavior on a given terminal object.\n */\n private _onTextAreaBlur(): void {\n // Text can safely be removed on blur. Doing it earlier could interfere with\n // screen readers reading it out.\n this.textarea!.value = '';\n this.refresh(this.buffer.y, this.buffer.y);\n if (this.coreService.decPrivateModes.sendFocus) {\n this.coreService.triggerDataEvent(C0.ESC + '[O');\n }\n this.element!.classList.remove('focus');\n this._onBlur.fire();\n }\n\n private _syncTextArea(): void {\n if (!this.textarea || !this.buffer.isCursorInViewport || this._compositionHelper!.isComposing || !this._renderService) {\n return;\n }\n const cursorY = this.buffer.ybase + this.buffer.y;\n const bufferLine = this.buffer.lines.get(cursorY);\n if (!bufferLine) {\n return;\n }\n const cursorX = Math.min(this.buffer.x, this.cols - 1);\n const cellHeight = this._renderService.dimensions.actualCellHeight;\n const width = bufferLine.getWidth(cursorX);\n const cellWidth = this._renderService.dimensions.actualCellWidth * width;\n const cursorTop = this.buffer.y * this._renderService.dimensions.actualCellHeight;\n const cursorLeft = cursorX * this._renderService.dimensions.actualCellWidth;\n\n // Sync the textarea to the exact position of the composition view so the IME knows where the\n // text is.\n this.textarea.style.left = cursorLeft + 'px';\n this.textarea.style.top = cursorTop + 'px';\n this.textarea.style.width = cellWidth + 'px';\n this.textarea.style.height = cellHeight + 'px';\n this.textarea.style.lineHeight = cellHeight + 'px';\n this.textarea.style.zIndex = '-5';\n }\n\n /**\n * Initialize default behavior\n */\n private _initGlobal(): void {\n this._bindKeys();\n\n // Bind clipboard functionality\n this.register(addDisposableDomListener(this.element!, 'copy', (event: ClipboardEvent) => {\n // If mouse events are active it means the selection manager is disabled and\n // copy should be handled by the host program.\n if (!this.hasSelection()) {\n return;\n }\n copyHandler(event, this._selectionService!);\n }));\n const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea!, this.coreService);\n this.register(addDisposableDomListener(this.textarea!, 'paste', pasteHandlerWrapper));\n this.register(addDisposableDomListener(this.element!, 'paste', pasteHandlerWrapper));\n\n // Handle right click context menus\n if (Browser.isFirefox) {\n // Firefox doesn't appear to fire the contextmenu event on right click\n this.register(addDisposableDomListener(this.element!, 'mousedown', (event: MouseEvent) => {\n if (event.button === 2) {\n rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);\n }\n }));\n } else {\n this.register(addDisposableDomListener(this.element!, 'contextmenu', (event: MouseEvent) => {\n rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);\n }));\n }\n\n // Move the textarea under the cursor when middle clicking on Linux to ensure\n // middle click to paste selection works. This only appears to work in Chrome\n // at the time is writing.\n if (Browser.isLinux) {\n // Use auxclick event over mousedown the latter doesn't seem to work. Note\n // that the regular click event doesn't fire for the middle mouse button.\n this.register(addDisposableDomListener(this.element!, 'auxclick', (event: MouseEvent) => {\n if (event.button === 1) {\n moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!);\n }\n }));\n }\n }\n\n /**\n * Apply key handling to the terminal\n */\n private _bindKeys(): void {\n this.register(addDisposableDomListener(this.textarea!, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));\n this.register(addDisposableDomListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));\n this.register(addDisposableDomListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));\n this.register(addDisposableDomListener(this.textarea!, 'compositionstart', () => this._compositionHelper!.compositionstart()));\n this.register(addDisposableDomListener(this.textarea!, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper!.compositionupdate(e)));\n this.register(addDisposableDomListener(this.textarea!, 'compositionend', () => this._compositionHelper!.compositionend()));\n this.register(addDisposableDomListener(this.textarea!, 'input', (ev: InputEvent) => this._inputEvent(ev), true));\n this.register(this.onRender(() => this._compositionHelper!.updateCompositionElements()));\n this.register(this.onRender(e => this._queueLinkification(e.start, e.end)));\n }\n\n /**\n * Opens the terminal within an element.\n *\n * @param parent The element to create the terminal within.\n */\n public open(parent: HTMLElement): void {\n if (!parent) {\n throw new Error('Terminal requires a parent element.');\n }\n\n if (!parent.isConnected) {\n this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');\n }\n\n this._document = parent.ownerDocument!;\n\n // Create main element container\n this.element = this._document.createElement('div');\n this.element.dir = 'ltr'; // xterm.css assumes LTR\n this.element.classList.add('terminal');\n this.element.classList.add('xterm');\n this.element.setAttribute('tabindex', '0');\n parent.appendChild(this.element);\n\n // Performance: Use a document fragment to build the terminal\n // viewport and helper elements detached from the DOM\n const fragment = document.createDocumentFragment();\n this._viewportElement = document.createElement('div');\n this._viewportElement.classList.add('xterm-viewport');\n fragment.appendChild(this._viewportElement);\n this._viewportScrollArea = document.createElement('div');\n this._viewportScrollArea.classList.add('xterm-scroll-area');\n this._viewportElement.appendChild(this._viewportScrollArea);\n\n this.screenElement = document.createElement('div');\n this.screenElement.classList.add('xterm-screen');\n // Create the container that will hold helpers like the textarea for\n // capturing DOM Events. Then produce the helpers.\n this._helperContainer = document.createElement('div');\n this._helperContainer.classList.add('xterm-helpers');\n this.screenElement.appendChild(this._helperContainer);\n fragment.appendChild(this.screenElement);\n\n this.textarea = document.createElement('textarea');\n this.textarea.classList.add('xterm-helper-textarea');\n this.textarea.setAttribute('aria-label', Strings.promptLabel);\n this.textarea.setAttribute('aria-multiline', 'false');\n this.textarea.setAttribute('autocorrect', 'off');\n this.textarea.setAttribute('autocapitalize', 'off');\n this.textarea.setAttribute('spellcheck', 'false');\n this.textarea.tabIndex = 0;\n this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._onTextAreaFocus(ev)));\n this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));\n this._helperContainer.appendChild(this.textarea);\n\n const coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea);\n this._instantiationService.setService(ICoreBrowserService, coreBrowserService);\n\n this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);\n this._instantiationService.setService(ICharSizeService, this._charSizeService);\n\n this._theme = this.options.theme || this._theme;\n this._colorManager = new ColorManager(document, this.options.allowTransparency);\n this.register(this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e)));\n this._colorManager.setTheme(this._theme);\n\n this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService);\n this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService);\n\n const renderer = this._createRenderer();\n this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement));\n this._instantiationService.setService(IRenderService, this._renderService);\n this.register(this._renderService.onRenderedBufferChange(e => this._onRender.fire(e)));\n this.onResize(e => this._renderService!.resize(e.cols, e.rows));\n\n this._compositionView = document.createElement('div');\n this._compositionView.classList.add('composition-view');\n this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);\n this._helperContainer.appendChild(this._compositionView);\n\n // Performance: Add viewport and helper elements from the fragment\n this.element.appendChild(fragment);\n\n this._soundService = this._instantiationService.createInstance(SoundService);\n this._instantiationService.setService(ISoundService, this._soundService);\n this._mouseService = this._instantiationService.createInstance(MouseService);\n this._instantiationService.setService(IMouseService, this._mouseService);\n\n this.viewport = this._instantiationService.createInstance(Viewport,\n (amount: number) => this.scrollLines(amount, true, ScrollSource.VIEWPORT),\n this._viewportElement,\n this._viewportScrollArea,\n this.element\n );\n this.viewport.onThemeChange(this._colorManager.colors);\n this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));\n this.register(this.viewport);\n\n this.register(this.onCursorMove(() => {\n this._renderService!.onCursorMove();\n this._syncTextArea();\n }));\n this.register(this.onResize(() => this._renderService!.onResize(this.cols, this.rows)));\n this.register(this.onBlur(() => this._renderService!.onBlur()));\n this.register(this.onFocus(() => this._renderService!.onFocus()));\n this.register(this._renderService.onDimensionsChange(() => this.viewport!.syncScrollArea()));\n\n this._selectionService = this.register(this._instantiationService.createInstance(SelectionService,\n this.element,\n this.screenElement,\n this.linkifier2\n ));\n this._instantiationService.setService(ISelectionService, this._selectionService);\n this.register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));\n this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));\n this.register(this._selectionService.onRequestRedraw(e => this._renderService!.onSelectionChanged(e.start, e.end, e.columnSelectMode)));\n this.register(this._selectionService.onLinuxMouseSelection(text => {\n // If there's a new selection, put it into the textarea, focus and select it\n // in order to register it as a selection on the OS. This event is fired\n // only on Linux to enable middle click to paste selection.\n this.textarea!.value = text;\n this.textarea!.focus();\n this.textarea!.select();\n }));\n this.register(this._onScroll.event(ev => {\n this.viewport!.syncScrollArea();\n this._selectionService!.refresh();\n }));\n this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService!.refresh()));\n\n this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement);\n this.register(this._mouseZoneManager);\n this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));\n this.linkifier.attachToDom(this.element, this._mouseZoneManager);\n this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);\n\n // This event listener must be registered aftre MouseZoneManager is created\n this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));\n\n // apply mouse event classes set by escape codes before terminal was attached\n if (this.coreMouseService.areMouseEventsActive) {\n this._selectionService.disable();\n this.element.classList.add('enable-mouse-events');\n } else {\n this._selectionService.enable();\n }\n\n if (this.options.screenReaderMode) {\n // Note that this must be done *after* the renderer is created in order to\n // ensure the correct order of the dprchange event\n this._accessibilityManager = new AccessibilityManager(this, this._renderService);\n }\n\n // Measure the character size\n this._charSizeService.measure();\n\n // Setup loop that draws to screen\n this.refresh(0, this.rows - 1);\n\n // Initialize global actions that need to be taken on the document.\n this._initGlobal();\n\n // Listen for mouse events and translate\n // them into terminal mouse protocols.\n this.bindMouse();\n }\n\n private _createRenderer(): IRenderer {\n switch (this.options.rendererType) {\n case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2);\n case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2);\n default: throw new Error(`Unrecognized rendererType \"${this.options.rendererType}\"`);\n }\n }\n\n /**\n * Sets the theme on the renderer. The renderer must have been initialized.\n * @param theme The theme to set.\n */\n private _setTheme(theme: ITheme): void {\n this._theme = theme;\n this._colorManager?.setTheme(theme);\n this._renderService?.setColors(this._colorManager!.colors);\n this.viewport?.onThemeChange(this._colorManager!.colors);\n }\n\n /**\n * Bind certain mouse events to the terminal.\n * By default only 3 button + wheel up/down is ativated. For higher buttons\n * no mouse report will be created. Typically the standard actions will be active.\n *\n * There are several reasons not to enable support for higher buttons/wheel:\n * - Button 4 and 5 are typically used for history back and forward navigation,\n * there is no straight forward way to supress/intercept those standard actions.\n * - Support for higher buttons does not work in some platform/browser combinations.\n * - Left/right wheel was not tested.\n * - Emulators vary in mouse button support, typically only 3 buttons and\n * wheel up/down work reliable.\n *\n * TODO: Move mouse event code into its own file.\n */\n public bindMouse(): void {\n const self = this;\n const el = this.element!;\n\n // send event to CoreMouseService\n function sendEvent(ev: MouseEvent | WheelEvent): boolean {\n // get mouse coordinates\n const pos = self._mouseService!.getRawByteCoords(ev, self.screenElement!, self.cols, self.rows);\n if (!pos) {\n return false;\n }\n\n let but: CoreMouseButton;\n let action: CoreMouseAction | undefined;\n switch ((ev as any).overrideType || ev.type) {\n case 'mousemove':\n action = CoreMouseAction.MOVE;\n if (ev.buttons === undefined) {\n // buttons is not supported on macOS, try to get a value from button instead\n but = CoreMouseButton.NONE;\n if (ev.button !== undefined) {\n but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;\n }\n } else {\n // according to MDN buttons only reports up to button 5 (AUX2)\n but = ev.buttons & 1 ? CoreMouseButton.LEFT :\n ev.buttons & 4 ? CoreMouseButton.MIDDLE :\n ev.buttons & 2 ? CoreMouseButton.RIGHT :\n CoreMouseButton.NONE; // fallback to NONE\n }\n break;\n case 'mouseup':\n action = CoreMouseAction.UP;\n but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;\n break;\n case 'mousedown':\n action = CoreMouseAction.DOWN;\n but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;\n break;\n case 'wheel':\n // only UP/DOWN wheel events are respected\n if ((ev as WheelEvent).deltaY !== 0) {\n action = (ev as WheelEvent).deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;\n }\n but = CoreMouseButton.WHEEL;\n break;\n default:\n // dont handle other event types by accident\n return false;\n }\n\n // exit if we cannot determine valid button/action values\n // do nothing for higher buttons than wheel\n if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) {\n return false;\n }\n\n return self.coreMouseService.triggerMouseEvent({\n col: pos.x - 33, // FIXME: why -33 here?\n row: pos.y - 33,\n button: but,\n action,\n ctrl: ev.ctrlKey,\n alt: ev.altKey,\n shift: ev.shiftKey\n });\n }\n\n /**\n * Event listener state handling.\n * We listen to the onProtocolChange event of CoreMouseService and put\n * requested listeners in `requestedEvents`. With this the listeners\n * have all bits to do the event listener juggling.\n * Note: 'mousedown' currently is \"always on\" and not managed\n * by onProtocolChange.\n */\n const requestedEvents: { [key: string]: ((ev: Event) => void) | null } = {\n mouseup: null,\n wheel: null,\n mousedrag: null,\n mousemove: null\n };\n const eventListeners: { [key: string]: (ev: any) => void | boolean } = {\n mouseup: (ev: MouseEvent) => {\n sendEvent(ev);\n if (!ev.buttons) {\n // if no other button is held remove global handlers\n this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);\n if (requestedEvents.mousedrag) {\n this._document!.removeEventListener('mousemove', requestedEvents.mousedrag);\n }\n }\n return this.cancel(ev);\n },\n wheel: (ev: WheelEvent) => {\n sendEvent(ev);\n return this.cancel(ev, true);\n },\n mousedrag: (ev: MouseEvent) => {\n // deal only with move while a button is held\n if (ev.buttons) {\n sendEvent(ev);\n }\n },\n mousemove: (ev: MouseEvent) => {\n // deal only with move without any button\n if (!ev.buttons) {\n sendEvent(ev);\n }\n }\n };\n this.register(this.coreMouseService.onProtocolChange(events => {\n // apply global changes on events\n if (events) {\n if (this.optionsService.options.logLevel === 'debug') {\n this._logService.debug('Binding to mouse events:', this.coreMouseService.explainEvents(events));\n }\n this.element!.classList.add('enable-mouse-events');\n this._selectionService!.disable();\n } else {\n this._logService.debug('Unbinding from mouse events.');\n this.element!.classList.remove('enable-mouse-events');\n this._selectionService!.enable();\n }\n\n // add/remove handlers from requestedEvents\n\n if (!(events & CoreMouseEventType.MOVE)) {\n el.removeEventListener('mousemove', requestedEvents.mousemove!);\n requestedEvents.mousemove = null;\n } else if (!requestedEvents.mousemove) {\n el.addEventListener('mousemove', eventListeners.mousemove);\n requestedEvents.mousemove = eventListeners.mousemove;\n }\n\n if (!(events & CoreMouseEventType.WHEEL)) {\n el.removeEventListener('wheel', requestedEvents.wheel!);\n requestedEvents.wheel = null;\n } else if (!requestedEvents.wheel) {\n el.addEventListener('wheel', eventListeners.wheel, { passive: false });\n requestedEvents.wheel = eventListeners.wheel;\n }\n\n if (!(events & CoreMouseEventType.UP)) {\n this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);\n requestedEvents.mouseup = null;\n } else if (!requestedEvents.mouseup) {\n requestedEvents.mouseup = eventListeners.mouseup;\n }\n\n if (!(events & CoreMouseEventType.DRAG)) {\n this._document!.removeEventListener('mousemove', requestedEvents.mousedrag!);\n requestedEvents.mousedrag = null;\n } else if (!requestedEvents.mousedrag) {\n requestedEvents.mousedrag = eventListeners.mousedrag;\n }\n }));\n // force initial onProtocolChange so we dont miss early mouse requests\n this.coreMouseService.activeProtocol = this.coreMouseService.activeProtocol;\n\n /**\n * \"Always on\" event listeners.\n */\n this.register(addDisposableDomListener(el, 'mousedown', (ev: MouseEvent) => {\n ev.preventDefault();\n this.focus();\n\n // Don't send the mouse button to the pty if mouse events are disabled or\n // if the selection manager is having selection forced (ie. a modifier is\n // held).\n if (!this.coreMouseService.areMouseEventsActive || this._selectionService!.shouldForceSelection(ev)) {\n return;\n }\n\n sendEvent(ev);\n\n // Register additional global handlers which should keep reporting outside\n // of the terminal element.\n // Note: Other emulators also do this for 'mousedown' while a button\n // is held, we currently limit 'mousedown' to the terminal only.\n if (requestedEvents.mouseup) {\n this._document!.addEventListener('mouseup', requestedEvents.mouseup);\n }\n if (requestedEvents.mousedrag) {\n this._document!.addEventListener('mousemove', requestedEvents.mousedrag);\n }\n\n return this.cancel(ev);\n }));\n\n this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {\n // do nothing, if app side handles wheel itself\n if (requestedEvents.wheel) return;\n\n if (!this.buffer.hasScrollback) {\n // Convert wheel events into up/down events when the buffer does not have scrollback, this\n // enables scrolling in apps hosted in the alt buffer such as vim or tmux.\n const amount = this.viewport!.getLinesScrolled(ev);\n\n // Do nothing if there's no vertical scroll\n if (amount === 0) {\n return;\n }\n\n // Construct and send sequences\n const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');\n let data = '';\n for (let i = 0; i < Math.abs(amount); i++) {\n data += sequence;\n }\n this.coreService.triggerDataEvent(data, true);\n return this.cancel(ev, true);\n }\n\n // normal viewport scrolling\n // conditionally stop event, if the viewport still had rows to scroll within\n if (this.viewport!.onWheel(ev)) {\n return this.cancel(ev);\n }\n }, { passive: false }));\n\n this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => {\n if (this.coreMouseService.areMouseEventsActive) return;\n this.viewport!.onTouchStart(ev);\n return this.cancel(ev);\n }, { passive: true }));\n\n this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => {\n if (this.coreMouseService.areMouseEventsActive) return;\n if (!this.viewport!.onTouchMove(ev)) {\n return this.cancel(ev);\n }\n }, { passive: false }));\n }\n\n\n /**\n * Tells the renderer to refresh terminal content between two rows (inclusive) at the next\n * opportunity.\n * @param start The row to start from (between 0 and this.rows - 1).\n * @param end The row to end at (between start and this.rows - 1).\n */\n public refresh(start: number, end: number): void {\n this._renderService?.refreshRows(start, end);\n }\n\n /**\n * Queues linkification for the specified rows.\n * @param start The row to start from (between 0 and this.rows - 1).\n * @param end The row to end at (between start and this.rows - 1).\n */\n private _queueLinkification(start: number, end: number): void {\n this.linkifier?.linkifyRows(start, end);\n }\n\n /**\n * Change the cursor style for different selection modes\n */\n public updateCursorStyle(ev: KeyboardEvent): void {\n if (this._selectionService?.shouldColumnSelect(ev)) {\n this.element!.classList.add('column-select');\n } else {\n this.element!.classList.remove('column-select');\n }\n }\n\n /**\n * Display the cursor element\n */\n private _showCursor(): void {\n if (!this.coreService.isCursorInitialized) {\n this.coreService.isCursorInitialized = true;\n this.refresh(this.buffer.y, this.buffer.y);\n }\n }\n\n public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {\n super.scrollLines(disp, suppressScrollEvent, source);\n this.refresh(0, this.rows - 1);\n }\n\n public paste(data: string): void {\n paste(data, this.textarea!, this.coreService);\n }\n\n /**\n * Attaches a custom key event handler which is run before keys are processed,\n * giving consumers of xterm.js ultimate control as to what keys should be\n * processed by the terminal and what keys should not.\n * @param customKeyEventHandler The custom KeyboardEvent handler to attach.\n * This is a function that takes a KeyboardEvent, allowing consumers to stop\n * propagation and/or prevent the default action. The function returns whether\n * the event should be processed by xterm.js.\n */\n public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {\n this._customKeyEventHandler = customKeyEventHandler;\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for, specifically\n * this searches the textContent of the rows. You will want to use \\s to match\n * a space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number {\n const matcherId = this.linkifier.registerLinkMatcher(regex, handler, options);\n this.refresh(0, this.rows - 1);\n return matcherId;\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n */\n public deregisterLinkMatcher(matcherId: number): void {\n if (this.linkifier.deregisterLinkMatcher(matcherId)) {\n this.refresh(0, this.rows - 1);\n }\n }\n\n public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {\n return this.linkifier2.registerLinkProvider(linkProvider);\n }\n\n public registerCharacterJoiner(handler: CharacterJoinerHandler): number {\n if (!this._characterJoinerService) {\n throw new Error('Terminal must be opened first');\n }\n const joinerId = this._characterJoinerService.register(handler);\n this.refresh(0, this.rows - 1);\n return joinerId;\n }\n\n public deregisterCharacterJoiner(joinerId: number): void {\n if (!this._characterJoinerService) {\n throw new Error('Terminal must be opened first');\n }\n if (this._characterJoinerService.deregister(joinerId)) {\n this.refresh(0, this.rows - 1);\n }\n }\n\n public get markers(): IMarker[] {\n return this.buffer.markers;\n }\n\n public addMarker(cursorYOffset: number): IMarker | undefined {\n // Disallow markers on the alt buffer\n if (this.buffer !== this.buffers.normal) {\n return;\n }\n\n return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);\n }\n\n /**\n * Gets whether the terminal has an active selection.\n */\n public hasSelection(): boolean {\n return this._selectionService ? this._selectionService.hasSelection : false;\n }\n\n /**\n * Selects text within the terminal.\n * @param column The column the selection starts at..\n * @param row The row the selection starts at.\n * @param length The length of the selection.\n */\n public select(column: number, row: number, length: number): void {\n this._selectionService!.setSelection(column, row, length);\n }\n\n /**\n * Gets the terminal's current selection, this is useful for implementing copy\n * behavior outside of xterm.js.\n */\n public getSelection(): string {\n return this._selectionService ? this._selectionService.selectionText : '';\n }\n\n public getSelectionPosition(): ISelectionPosition | undefined {\n if (!this._selectionService || !this._selectionService.hasSelection) {\n return undefined;\n }\n\n return {\n startColumn: this._selectionService.selectionStart![0],\n startRow: this._selectionService.selectionStart![1],\n endColumn: this._selectionService.selectionEnd![0],\n endRow: this._selectionService.selectionEnd![1]\n };\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n this._selectionService?.clearSelection();\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n this._selectionService?.selectAll();\n }\n\n public selectLines(start: number, end: number): void {\n this._selectionService?.selectLines(start, end);\n }\n\n /**\n * Handle a keydown event\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param ev The keydown event to be handled.\n */\n protected _keyDown(event: KeyboardEvent): boolean | undefined {\n this._keyDownHandled = false;\n\n if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {\n return false;\n }\n\n if (!this._compositionHelper!.keydown(event)) {\n if (this.buffer.ybase !== this.buffer.ydisp) {\n this._bufferService.scrollToBottom();\n }\n return false;\n }\n\n if (event.key === 'Dead' || event.key === 'AltGraph') {\n this._unprocessedDeadKey = true;\n }\n\n const result = evaluateKeyboardEvent(event, this.coreService.decPrivateModes.applicationCursorKeys, this.browser.isMac, this.options.macOptionIsMeta);\n\n this.updateCursorStyle(event);\n\n if (result.type === KeyboardResultType.PAGE_DOWN || result.type === KeyboardResultType.PAGE_UP) {\n const scrollCount = this.rows - 1;\n this.scrollLines(result.type === KeyboardResultType.PAGE_UP ? -scrollCount : scrollCount);\n return this.cancel(event, true);\n }\n\n if (result.type === KeyboardResultType.SELECT_ALL) {\n this.selectAll();\n }\n\n if (this._isThirdLevelShift(this.browser, event)) {\n return true;\n }\n\n if (result.cancel) {\n // The event is canceled at the end already, is this necessary?\n this.cancel(event, true);\n }\n\n if (!result.key) {\n return true;\n }\n\n if (this._unprocessedDeadKey) {\n this._unprocessedDeadKey = false;\n return true;\n }\n\n // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers\n // will announce deleted characters. This will not work 100% of the time but it should cover\n // most scenarios.\n if (result.key === C0.ETX || result.key === C0.CR) {\n this.textarea!.value = '';\n }\n\n this._onKey.fire({ key: result.key, domEvent: event });\n this._showCursor();\n this.coreService.triggerDataEvent(result.key, true);\n\n // Cancel events when not in screen reader mode so events don't get bubbled up and handled by\n // other listeners. When screen reader mode is enabled, this could cause issues if the event\n // is handled at a higher level, this is a compromise in order to echo keys to the screen\n // reader.\n if (!this.optionsService.options.screenReaderMode) {\n return this.cancel(event, true);\n }\n\n this._keyDownHandled = true;\n }\n\n private _isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {\n const thirdLevelKey =\n (browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||\n (browser.isWindows && ev.altKey && ev.ctrlKey && !ev.metaKey) ||\n (browser.isWindows && ev.getModifierState('AltGraph'));\n\n if (ev.type === 'keypress') {\n return thirdLevelKey;\n }\n\n // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)\n return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);\n }\n\n protected _keyUp(ev: KeyboardEvent): void {\n if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {\n return;\n }\n\n if (!wasModifierKeyOnlyEvent(ev)) {\n this.focus();\n }\n\n this.updateCursorStyle(ev);\n this._keyPressHandled = false;\n }\n\n /**\n * Handle a keypress event.\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param ev The keypress event to be handled.\n */\n protected _keyPress(ev: KeyboardEvent): boolean {\n let key;\n\n this._keyPressHandled = false;\n\n if (this._keyDownHandled) {\n return false;\n }\n\n if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {\n return false;\n }\n\n this.cancel(ev);\n\n if (ev.charCode) {\n key = ev.charCode;\n } else if (ev.which === null || ev.which === undefined) {\n key = ev.keyCode;\n } else if (ev.which !== 0 && ev.charCode !== 0) {\n key = ev.which;\n } else {\n return false;\n }\n\n if (!key || (\n (ev.altKey || ev.ctrlKey || ev.metaKey) && !this._isThirdLevelShift(this.browser, ev)\n )) {\n return false;\n }\n\n key = String.fromCharCode(key);\n\n this._onKey.fire({ key, domEvent: ev });\n this._showCursor();\n this.coreService.triggerDataEvent(key, true);\n\n this._keyPressHandled = true;\n\n // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow\n // keys could be ignored\n this._unprocessedDeadKey = false;\n\n return true;\n }\n\n /**\n * Handle an input event.\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent\n * @param ev The input event to be handled.\n */\n protected _inputEvent(ev: InputEvent): boolean {\n // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to\n // support reading out character input which can doubling up input characters\n if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.options.screenReaderMode) {\n if (this._keyPressHandled) {\n return false;\n }\n\n // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow\n // keys could be ignored\n this._unprocessedDeadKey = false;\n\n const text = ev.data;\n this.coreService.triggerDataEvent(text, true);\n\n this.cancel(ev);\n return true;\n }\n\n return false;\n }\n\n /**\n * Ring the bell.\n * Note: We could do sweet things with webaudio here\n */\n public bell(): void {\n if (this._soundBell()) {\n this._soundService?.playBellSound();\n }\n\n this._onBell.fire();\n\n // if (this._visualBell()) {\n // this.element.classList.add('visual-bell-active');\n // clearTimeout(this._visualBellTimer);\n // this._visualBellTimer = window.setTimeout(() => {\n // this.element.classList.remove('visual-bell-active');\n // }, 200);\n // }\n }\n\n /**\n * Resizes the terminal.\n *\n * @param x The number of columns to resize to.\n * @param y The number of rows to resize to.\n */\n public resize(x: number, y: number): void {\n if (x === this.cols && y === this.rows) {\n // Check if we still need to measure the char size (fixes #785).\n if (this._charSizeService && !this._charSizeService.hasValidSize) {\n this._charSizeService.measure();\n }\n return;\n }\n\n super.resize(x, y);\n }\n\n private _afterResize(x: number, y: number): void {\n this._charSizeService?.measure();\n\n // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an\n // invalid location\n this.viewport?.syncScrollArea(true);\n }\n\n /**\n * Clear the entire buffer, making the prompt line the new first line.\n */\n public clear(): void {\n if (this.buffer.ybase === 0 && this.buffer.y === 0) {\n // Don't clear if it's already clear\n return;\n }\n this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);\n this.buffer.lines.length = 1;\n this.buffer.ydisp = 0;\n this.buffer.ybase = 0;\n this.buffer.y = 0;\n for (let i = 1; i < this.rows; i++) {\n this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA));\n }\n this.refresh(0, this.rows - 1);\n this._onScroll.fire({ position: this.buffer.ydisp, source: ScrollSource.TERMINAL });\n }\n\n /**\n * Reset terminal.\n * Note: Calling this directly from JS is synchronous but does not clear\n * input buffers and does not reset the parser, thus the terminal will\n * continue to apply pending input data.\n * If you need in band reset (synchronous with input data) consider\n * using DECSTR (soft reset, CSI ! p) or RIS instead (hard reset, ESC c).\n */\n public reset(): void {\n /**\n * Since _setup handles a full terminal creation, we have to carry forward\n * a few things that should not reset.\n */\n this.options.rows = this.rows;\n this.options.cols = this.cols;\n const customKeyEventHandler = this._customKeyEventHandler;\n\n this._setup();\n super.reset();\n this._selectionService?.reset();\n\n // reattach\n this._customKeyEventHandler = customKeyEventHandler;\n\n // do a full screen refresh\n this.refresh(0, this.rows - 1);\n this.viewport?.syncScrollArea();\n }\n\n public clearTextureAtlas(): void {\n this._renderService?.clearTextureAtlas();\n }\n\n private _reportFocus(): void {\n if (this.element?.classList.contains('focus')) {\n this.coreService.triggerDataEvent(C0.ESC + '[I');\n } else {\n this.coreService.triggerDataEvent(C0.ESC + '[O');\n }\n }\n\n private _reportWindowsOptions(type: WindowsOptionsReportType): void {\n if (!this._renderService) {\n return;\n }\n\n switch (type) {\n case WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:\n const canvasWidth = this._renderService.dimensions.scaledCanvasWidth.toFixed(0);\n const canvasHeight = this._renderService.dimensions.scaledCanvasHeight.toFixed(0);\n this.coreService.triggerDataEvent(`${C0.ESC}[4;${canvasHeight};${canvasWidth}t`);\n break;\n case WindowsOptionsReportType.GET_CELL_SIZE_PIXELS:\n const cellWidth = this._renderService.dimensions.scaledCellWidth.toFixed(0);\n const cellHeight = this._renderService.dimensions.scaledCellHeight.toFixed(0);\n this.coreService.triggerDataEvent(`${C0.ESC}[6;${cellHeight};${cellWidth}t`);\n break;\n }\n }\n\n // TODO: Remove cancel function and cancelEvents option\n public cancel(ev: Event, force?: boolean): boolean | undefined {\n if (!this.options.cancelEvents && !force) {\n return;\n }\n ev.preventDefault();\n ev.stopPropagation();\n return false;\n }\n\n private _visualBell(): boolean {\n return false;\n // return this.options.bellStyle === 'visual' ||\n // this.options.bellStyle === 'both';\n }\n\n private _soundBell(): boolean {\n return this.options.bellStyle === 'sound';\n // return this.options.bellStyle === 'sound' ||\n // this.options.bellStyle === 'both';\n }\n}\n\n/**\n * Helpers\n */\n\nfunction wasModifierKeyOnlyEvent(ev: KeyboardEvent): boolean {\n return ev.keyCode === 16 || // Shift\n ev.keyCode === 17 || // Ctrl\n ev.keyCode === 18; // Alt\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nconst RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second\n\nimport { IRenderDebouncer } from 'browser/Types';\n\n/**\n * Debounces calls to update screen readers to update at most once configurable interval of time.\n */\nexport class TimeBasedDebouncer implements IRenderDebouncer {\n private _rowStart: number | undefined;\n private _rowEnd: number | undefined;\n private _rowCount: number | undefined;\n\n // The last moment that the Terminal was refreshed at\n private _lastRefreshMs = 0;\n // Whether a trailing refresh should be triggered due to a refresh request that was throttled\n private _additionalRefreshRequested = false;\n\n private _refreshTimeoutID: number | undefined;\n\n constructor(\n private _renderCallback: (start: number, end: number) => void,\n private readonly _debounceThresholdMS = RENDER_DEBOUNCE_THRESHOLD_MS\n ) {\n }\n\n public dispose(): void {\n if (this._refreshTimeoutID) {\n clearTimeout(this._refreshTimeoutID);\n }\n }\n\n public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {\n this._rowCount = rowCount;\n // Get the min/max row start/end for the arg values\n rowStart = rowStart !== undefined ? rowStart : 0;\n rowEnd = rowEnd !== undefined ? rowEnd : this._rowCount - 1;\n // Set the properties to the updated values\n this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;\n this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;\n\n // Only refresh if the time since last refresh is above a threshold, otherwise wait for\n // enough time to pass before refreshing again.\n const refreshRequestTime: number = Date.now();\n if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) {\n // Enough time has lapsed since the last refresh; refresh immediately\n this._lastRefreshMs = refreshRequestTime;\n this._innerRefresh();\n } else if (!this._additionalRefreshRequested) {\n // This is the first additional request throttled; set up trailing refresh\n const elapsed = refreshRequestTime - this._lastRefreshMs;\n const waitPeriodBeforeTrailingRefresh = this._debounceThresholdMS - elapsed;\n this._additionalRefreshRequested = true;\n\n this._refreshTimeoutID = window.setTimeout(() => {\n this._lastRefreshMs = Date.now();\n this._innerRefresh();\n this._additionalRefreshRequested = false;\n this._refreshTimeoutID = undefined; // No longer need to clear the timeout\n }, waitPeriodBeforeTrailingRefresh);\n }\n }\n\n private _innerRefresh(): void {\n // Make sure values are set\n if (this._rowStart === undefined || this._rowEnd === undefined || this._rowCount === undefined) {\n return;\n }\n\n // Clamp values\n const start = Math.max(this._rowStart, 0);\n const end = Math.min(this._rowEnd, this._rowCount - 1);\n\n // Reset debouncer (this happens before render callback as the render could trigger it again)\n this._rowStart = undefined;\n this._rowEnd = undefined;\n\n // Run render callback\n this._renderCallback(start, end);\n }\n}\n\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Disposable } from 'common/Lifecycle';\nimport { addDisposableDomListener } from 'browser/Lifecycle';\nimport { IColorSet, IViewport } from 'browser/Types';\nimport { ICharSizeService, IRenderService } from 'browser/services/Services';\nimport { IBufferService, IOptionsService } from 'common/services/Services';\nimport { IBuffer } from 'common/buffer/Types';\nimport { IRenderDimensions } from 'browser/renderer/Types';\n\nconst FALLBACK_SCROLL_BAR_WIDTH = 15;\n\n/**\n * Represents the viewport of a terminal, the visible area within the larger buffer of output.\n * Logic for the virtual scroll bar is included in this object.\n */\nexport class Viewport extends Disposable implements IViewport {\n public scrollBarWidth: number = 0;\n private _currentRowHeight: number = 0;\n private _currentScaledCellHeight: number = 0;\n private _lastRecordedBufferLength: number = 0;\n private _lastRecordedViewportHeight: number = 0;\n private _lastRecordedBufferHeight: number = 0;\n private _lastTouchY: number = 0;\n private _lastScrollTop: number = 0;\n private _lastHadScrollBar: boolean = false;\n private _activeBuffer: IBuffer;\n private _renderDimensions: IRenderDimensions;\n\n // Stores a partial line amount when scrolling, this is used to keep track of how much of a line\n // is scrolled so we can \"scroll\" over partial lines and feel natural on touchpads. This is a\n // quick fix and could have a more robust solution in place that reset the value when needed.\n private _wheelPartialScroll: number = 0;\n\n private _refreshAnimationFrame: number | null = null;\n private _ignoreNextScrollEvent: boolean = false;\n\n constructor(\n private readonly _scrollLines: (amount: number) => void,\n private readonly _viewportElement: HTMLElement,\n private readonly _scrollArea: HTMLElement,\n private readonly _element: HTMLElement,\n @IBufferService private readonly _bufferService: IBufferService,\n @IOptionsService private readonly _optionsService: IOptionsService,\n @ICharSizeService private readonly _charSizeService: ICharSizeService,\n @IRenderService private readonly _renderService: IRenderService\n ) {\n super();\n\n // Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.\n // Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case,\n // therefore we account for a standard amount to make it visible\n this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;\n this._lastHadScrollBar = true;\n this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._onScroll.bind(this)));\n\n // Track properties used in performance critical code manually to avoid using slow getters\n this._activeBuffer = this._bufferService.buffer;\n this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));\n this._renderDimensions = this._renderService.dimensions;\n this.register(this._renderService.onDimensionsChange(e => this._renderDimensions = e));\n\n // Perform this async to ensure the ICharSizeService is ready.\n setTimeout(() => this.syncScrollArea(), 0);\n }\n\n public onThemeChange(colors: IColorSet): void {\n this._viewportElement.style.backgroundColor = colors.background.css;\n }\n\n /**\n * Refreshes row height, setting line-height, viewport height and scroll area height if\n * necessary.\n */\n private _refresh(immediate: boolean): void {\n if (immediate) {\n this._innerRefresh();\n if (this._refreshAnimationFrame !== null) {\n cancelAnimationFrame(this._refreshAnimationFrame);\n }\n return;\n }\n if (this._refreshAnimationFrame === null) {\n this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());\n }\n }\n\n private _innerRefresh(): void {\n if (this._charSizeService.height > 0) {\n this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio;\n this._currentScaledCellHeight = this._renderService.dimensions.scaledCellHeight;\n this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;\n const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.canvasHeight);\n if (this._lastRecordedBufferHeight !== newBufferHeight) {\n this._lastRecordedBufferHeight = newBufferHeight;\n this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px';\n }\n }\n\n // Sync scrollTop\n const scrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;\n if (this._viewportElement.scrollTop !== scrollTop) {\n // Ignore the next scroll event which will be triggered by setting the scrollTop as we do not\n // want this event to scroll the terminal\n this._ignoreNextScrollEvent = true;\n this._viewportElement.scrollTop = scrollTop;\n }\n\n // Update scroll bar width\n if (this._optionsService.options.scrollback === 0) {\n this.scrollBarWidth = 0;\n } else {\n this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;\n }\n this._lastHadScrollBar = this.scrollBarWidth > 0;\n\n const elementStyle = window.getComputedStyle(this._element);\n const elementPadding = parseInt(elementStyle.paddingLeft) + parseInt(elementStyle.paddingRight);\n this._viewportElement.style.width = (this._renderService.dimensions.actualCellWidth * (this._bufferService.cols) + this.scrollBarWidth + (this._lastHadScrollBar ? elementPadding : 0)).toString() + 'px';\n this._refreshAnimationFrame = null;\n }\n\n /**\n * Updates dimensions and synchronizes the scroll area if necessary.\n */\n public syncScrollArea(immediate: boolean = false): void {\n // If buffer height changed\n if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {\n this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;\n this._refresh(immediate);\n return;\n }\n\n // If viewport height changed\n if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) {\n this._refresh(immediate);\n return;\n }\n\n // If the buffer position doesn't match last scroll top\n if (this._lastScrollTop !== this._activeBuffer.ydisp * this._currentRowHeight) {\n this._refresh(immediate);\n return;\n }\n\n // If row height changed\n if (this._renderDimensions.scaledCellHeight !== this._currentScaledCellHeight) {\n this._refresh(immediate);\n return;\n }\n\n // If the scroll bar visibility changed\n if (this._lastHadScrollBar !== (this._optionsService.options.scrollback > 0)) {\n this._refresh(immediate);\n }\n }\n\n /**\n * Handles scroll events on the viewport, calculating the new viewport and requesting the\n * terminal to scroll to it.\n * @param ev The scroll event.\n */\n private _onScroll(ev: Event): void {\n // Record current scroll top position\n this._lastScrollTop = this._viewportElement.scrollTop;\n\n // Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt\n // which causes the terminal to scroll the buffer to the top\n if (!this._viewportElement.offsetParent) {\n return;\n }\n\n // Ignore the event if it was flagged to ignore (when the source of the event is from Viewport)\n if (this._ignoreNextScrollEvent) {\n this._ignoreNextScrollEvent = false;\n // Still trigger the scroll so lines get refreshed\n this._scrollLines(0);\n return;\n }\n\n const newRow = Math.round(this._lastScrollTop / this._currentRowHeight);\n const diff = newRow - this._bufferService.buffer.ydisp;\n this._scrollLines(diff);\n }\n\n /**\n * Handles bubbling of scroll event in case the viewport has reached top or bottom\n * @param ev The scroll event.\n * @param amount The amount scrolled\n */\n private _bubbleScroll(ev: Event, amount: number): boolean {\n const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight;\n if ((amount < 0 && this._viewportElement.scrollTop !== 0) ||\n (amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {\n if (ev.cancelable) {\n ev.preventDefault();\n }\n return false;\n }\n return true;\n }\n\n /**\n * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual\n * scrolling to `onScroll`, this event needs to be attached manually by the consumer of\n * `Viewport`.\n * @param ev The mouse wheel event.\n */\n public onWheel(ev: WheelEvent): boolean {\n const amount = this._getPixelsScrolled(ev);\n if (amount === 0) {\n return false;\n }\n this._viewportElement.scrollTop += amount;\n return this._bubbleScroll(ev, amount);\n }\n\n private _getPixelsScrolled(ev: WheelEvent): number {\n // Do nothing if it's not a vertical scroll event\n if (ev.deltaY === 0 || ev.shiftKey) {\n return 0;\n }\n\n // Fallback to WheelEvent.DOM_DELTA_PIXEL\n let amount = this._applyScrollModifier(ev.deltaY, ev);\n if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {\n amount *= this._currentRowHeight;\n } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n amount *= this._currentRowHeight * this._bufferService.rows;\n }\n return amount;\n }\n\n /**\n * Gets the number of pixels scrolled by the mouse event taking into account what type of delta\n * is being used.\n * @param ev The mouse wheel event.\n */\n public getLinesScrolled(ev: WheelEvent): number {\n // Do nothing if it's not a vertical scroll event\n if (ev.deltaY === 0 || ev.shiftKey) {\n return 0;\n }\n\n // Fallback to WheelEvent.DOM_DELTA_LINE\n let amount = this._applyScrollModifier(ev.deltaY, ev);\n if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {\n amount /= this._currentRowHeight + 0.0; // Prevent integer division\n this._wheelPartialScroll += amount;\n amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);\n this._wheelPartialScroll %= 1;\n } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n amount *= this._bufferService.rows;\n }\n return amount;\n }\n\n private _applyScrollModifier(amount: number, ev: WheelEvent): number {\n const modifier = this._optionsService.options.fastScrollModifier;\n // Multiply the scroll speed when the modifier is down\n if ((modifier === 'alt' && ev.altKey) ||\n (modifier === 'ctrl' && ev.ctrlKey) ||\n (modifier === 'shift' && ev.shiftKey)) {\n return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;\n }\n\n return amount * this._optionsService.options.scrollSensitivity;\n }\n\n /**\n * Handles the touchstart event, recording the touch occurred.\n * @param ev The touch event.\n */\n public onTouchStart(ev: TouchEvent): void {\n this._lastTouchY = ev.touches[0].pageY;\n }\n\n /**\n * Handles the touchmove event, scrolling the viewport if the position shifted.\n * @param ev The touch event.\n */\n public onTouchMove(ev: TouchEvent): boolean {\n const deltaY = this._lastTouchY - ev.touches[0].pageY;\n this._lastTouchY = ev.touches[0].pageY;\n if (deltaY === 0) {\n return false;\n }\n this._viewportElement.scrollTop += deltaY;\n return this._bubbleScroll(ev, deltaY);\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderService } from 'browser/services/Services';\nimport { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';\n\ninterface IPosition {\n start: number;\n end: number;\n}\n\n/**\n * Encapsulates the logic for handling compositionstart, compositionupdate and compositionend\n * events, displaying the in-progress composition to the UI and forwarding the final composition\n * to the handler.\n */\nexport class CompositionHelper {\n /**\n * Whether input composition is currently happening, eg. via a mobile keyboard, speech input or\n * IME. This variable determines whether the compositionText should be displayed on the UI.\n */\n private _isComposing: boolean;\n public get isComposing(): boolean { return this._isComposing; }\n\n /**\n * The position within the input textarea's value of the current composition.\n */\n private _compositionPosition: IPosition;\n\n /**\n * Whether a composition is in the process of being sent, setting this to false will cancel any\n * in-progress composition.\n */\n private _isSendingComposition: boolean;\n\n /**\n * Data already sent due to keydown event.\n */\n private _dataAlreadySent: string;\n\n constructor(\n private readonly _textarea: HTMLTextAreaElement,\n private readonly _compositionView: HTMLElement,\n @IBufferService private readonly _bufferService: IBufferService,\n @IOptionsService private readonly _optionsService: IOptionsService,\n @ICoreService private readonly _coreService: ICoreService,\n @IRenderService private readonly _renderService: IRenderService\n ) {\n this._isComposing = false;\n this._isSendingComposition = false;\n this._compositionPosition = { start: 0, end: 0 };\n this._dataAlreadySent = '';\n }\n\n /**\n * Handles the compositionstart event, activating the composition view.\n */\n public compositionstart(): void {\n this._isComposing = true;\n this._compositionPosition.start = this._textarea.value.length;\n this._compositionView.textContent = '';\n this._dataAlreadySent = '';\n this._compositionView.classList.add('active');\n }\n\n /**\n * Handles the compositionupdate event, updating the composition view.\n * @param ev The event.\n */\n public compositionupdate(ev: Pick): void {\n this._compositionView.textContent = ev.data;\n this.updateCompositionElements();\n setTimeout(() => {\n this._compositionPosition.end = this._textarea.value.length;\n }, 0);\n }\n\n /**\n * Handles the compositionend event, hiding the composition view and sending the composition to\n * the handler.\n */\n public compositionend(): void {\n this._finalizeComposition(true);\n }\n\n /**\n * Handles the keydown event, routing any necessary events to the CompositionHelper functions.\n * @param ev The keydown event.\n * @return Whether the Terminal should continue processing the keydown event.\n */\n public keydown(ev: KeyboardEvent): boolean {\n if (this._isComposing || this._isSendingComposition) {\n if (ev.keyCode === 229) {\n // Continue composing if the keyCode is the \"composition character\"\n return false;\n }\n if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {\n // Continue composing if the keyCode is a modifier key\n return false;\n }\n // Finish composition immediately. This is mainly here for the case where enter is\n // pressed and the handler needs to be triggered before the command is executed.\n this._finalizeComposition(false);\n }\n\n if (ev.keyCode === 229) {\n // If the \"composition character\" is used but gets to this point it means a non-composition\n // character (eg. numbers and punctuation) was pressed when the IME was active.\n this._handleAnyTextareaChanges();\n return false;\n }\n\n return true;\n }\n\n /**\n * Finalizes the composition, resuming regular input actions. This is called when a composition\n * is ending.\n * @param waitForPropagation Whether to wait for events to propagate before sending\n * the input. This should be false if a non-composition keystroke is entered before the\n * compositionend event is triggered, such as enter, so that the composition is sent before\n * the command is executed.\n */\n private _finalizeComposition(waitForPropagation: boolean): void {\n this._compositionView.classList.remove('active');\n this._isComposing = false;\n\n if (!waitForPropagation) {\n // Cancel any delayed composition send requests and send the input immediately.\n this._isSendingComposition = false;\n const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end);\n this._coreService.triggerDataEvent(input, true);\n } else {\n // Make a deep copy of the composition position here as a new compositionstart event may\n // fire before the setTimeout executes.\n const currentCompositionPosition = {\n start: this._compositionPosition.start,\n end: this._compositionPosition.end\n };\n\n // Since composition* events happen before the changes take place in the textarea on most\n // browsers, use a setTimeout with 0ms time to allow the native compositionend event to\n // complete. This ensures the correct character is retrieved.\n // This solution was used because:\n // - The compositionend event's data property is unreliable, at least on Chromium\n // - The last compositionupdate event's data property does not always accurately describe\n // the character, a counter example being Korean where an ending consonsant can move to\n // the following character if the following input is a vowel.\n this._isSendingComposition = true;\n setTimeout(() => {\n // Ensure that the input has not already been sent\n if (this._isSendingComposition) {\n this._isSendingComposition = false;\n let input;\n // Add length of data already sent due to keydown event,\n // otherwise input characters can be duplicated. (Issue #3191)\n currentCompositionPosition.start += this._dataAlreadySent.length;\n if (this._isComposing) {\n // Use the end position to get the string if a new composition has started.\n input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);\n } else {\n // Don't use the end position here in order to pick up any characters after the\n // composition has finished, for example when typing a non-composition character\n // (eg. 2) after a composition character.\n input = this._textarea.value.substring(currentCompositionPosition.start);\n }\n if (input.length > 0) {\n this._coreService.triggerDataEvent(input, true);\n }\n }\n }, 0);\n }\n }\n\n /**\n * Apply any changes made to the textarea after the current event chain is allowed to complete.\n * This should be called when not currently composing but a keydown event with the \"composition\n * character\" (229) is triggered, in order to allow non-composition text to be entered when an\n * IME is active.\n */\n private _handleAnyTextareaChanges(): void {\n const oldValue = this._textarea.value;\n setTimeout(() => {\n // Ignore if a composition has started since the timeout\n if (!this._isComposing) {\n const newValue = this._textarea.value;\n const diff = newValue.replace(oldValue, '');\n if (diff.length > 0) {\n this._dataAlreadySent = diff;\n this._coreService.triggerDataEvent(diff, true);\n }\n }\n }, 0);\n }\n\n /**\n * Positions the composition view on top of the cursor and the textarea just below it (so the\n * IME helper dialog is positioned correctly).\n * @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is\n * necessary as the IME events across browsers are not consistently triggered.\n */\n public updateCompositionElements(dontRecurse?: boolean): void {\n if (!this._isComposing) {\n return;\n }\n\n if (this._bufferService.buffer.isCursorInViewport) {\n const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);\n\n const cellHeight = this._renderService.dimensions.actualCellHeight;\n const cursorTop = this._bufferService.buffer.y * this._renderService.dimensions.actualCellHeight;\n const cursorLeft = cursorX * this._renderService.dimensions.actualCellWidth;\n\n this._compositionView.style.left = cursorLeft + 'px';\n this._compositionView.style.top = cursorTop + 'px';\n this._compositionView.style.height = cellHeight + 'px';\n this._compositionView.style.lineHeight = cellHeight + 'px';\n this._compositionView.style.fontFamily = this._optionsService.options.fontFamily;\n this._compositionView.style.fontSize = this._optionsService.options.fontSize + 'px';\n // Sync the textarea to the exact position of the composition view so the IME knows where the\n // text is.\n const compositionViewBounds = this._compositionView.getBoundingClientRect();\n this._textarea.style.left = cursorLeft + 'px';\n this._textarea.style.top = cursorTop + 'px';\n // Ensure the text area is at least 1x1, otherwise certain IMEs may break\n this._textarea.style.width = Math.max(compositionViewBounds.width, 1) + 'px';\n this._textarea.style.height = Math.max(compositionViewBounds.height, 1) + 'px';\n this._textarea.style.lineHeight = compositionViewBounds.height + 'px';\n }\n\n if (!dontRecurse) {\n setTimeout(() => this.updateCompositionElements(true), 0);\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport function getCoordsRelativeToElement(event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] {\n const rect = element.getBoundingClientRect();\n return [event.clientX - rect.left, event.clientY - rect.top];\n}\n\n/**\n * Gets coordinates within the terminal for a particular mouse event. The result\n * is returned as an array in the form [x, y] instead of an object as it's a\n * little faster and this function is used in some low level code.\n * @param event The mouse event.\n * @param element The terminal's container element.\n * @param colCount The number of columns in the terminal.\n * @param rowCount The number of rows n the terminal.\n * @param isSelection Whether the request is for the selection or not. This will\n * apply an offset to the x value such that the left half of the cell will\n * select that cell and the right half will select the next cell.\n */\nexport function getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, actualCellWidth: number, actualCellHeight: number, isSelection?: boolean): [number, number] | undefined {\n // Coordinates cannot be measured if there are no valid\n if (!hasValidCharSize) {\n return undefined;\n }\n\n const coords = getCoordsRelativeToElement(event, element);\n if (!coords) {\n return undefined;\n }\n\n coords[0] = Math.ceil((coords[0] + (isSelection ? actualCellWidth / 2 : 0)) / actualCellWidth);\n coords[1] = Math.ceil(coords[1] / actualCellHeight);\n\n // Ensure coordinates are within the terminal viewport. Note that selections\n // need an addition point of precision to cover the end point (as characters\n // cover half of one char and half of the next).\n coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));\n coords[1] = Math.min(Math.max(coords[1], 1), rowCount);\n\n return coords;\n}\n\n/**\n * Gets coordinates within the terminal for a particular mouse event, wrapping\n * them to the bounds of the terminal and adding 32 to both the x and y values\n * as expected by xterm.\n */\nexport function getRawByteCoords(coords: [number, number] | undefined): { x: number, y: number } | undefined {\n if (!coords) {\n return undefined;\n }\n\n // xterm sends raw bytes and starts at 32 (SP) for each.\n return { x: coords[0] + 32, y: coords[1] + 32 };\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { C0 } from 'common/data/EscapeSequences';\nimport { IBufferService } from 'common/services/Services';\n\nconst enum Direction {\n UP = 'A',\n DOWN = 'B',\n RIGHT = 'C',\n LEFT = 'D'\n}\n\n/**\n * Concatenates all the arrow sequences together.\n * Resets the starting row to an unwrapped row, moves to the requested row,\n * then moves to requested col.\n */\nexport function moveToCellSequence(targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): string {\n const startX = bufferService.buffer.x;\n const startY = bufferService.buffer.y;\n\n // The alt buffer should try to navigate between rows\n if (!bufferService.buffer.hasScrollback) {\n return resetStartingRow(startX, startY, targetX, targetY, bufferService, applicationCursor) +\n moveToRequestedRow(startY, targetY, bufferService, applicationCursor) +\n moveToRequestedCol(startX, startY, targetX, targetY, bufferService, applicationCursor);\n }\n\n // Only move horizontally for the normal buffer\n let direction;\n if (startY === targetY) {\n direction = startX > targetX ? Direction.LEFT : Direction.RIGHT;\n return repeat(Math.abs(startX - targetX), sequence(direction, applicationCursor));\n }\n direction = startY > targetY ? Direction.LEFT : Direction.RIGHT;\n const rowDifference = Math.abs(startY - targetY);\n const cellsToMove = colsFromRowEnd(startY > targetY ? targetX : startX, bufferService) +\n (rowDifference - 1) * bufferService.cols + 1 /* wrap around 1 row */ +\n colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService);\n return repeat(cellsToMove, sequence(direction, applicationCursor));\n}\n\n/**\n * Find the number of cols from a row beginning to a col.\n */\nfunction colsFromRowBeginning(currX: number, bufferService: IBufferService): number {\n return currX - 1;\n}\n\n/**\n * Find the number of cols from a col to row end.\n */\nfunction colsFromRowEnd(currX: number, bufferService: IBufferService): number {\n return bufferService.cols - currX;\n}\n\n/**\n * If the initial position of the cursor is on a row that is wrapped, move the\n * cursor up to the first row that is not wrapped to have accurate vertical\n * positioning.\n */\nfunction resetStartingRow(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): string {\n if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length === 0) {\n return '';\n }\n return repeat(bufferLine(\n startX, startY, startX,\n startY - wrappedRowsForRow(bufferService, startY), false, bufferService\n ).length, sequence(Direction.LEFT, applicationCursor));\n}\n\n/**\n * Using the reset starting and ending row, move to the requested row,\n * ignoring wrapped rows\n */\nfunction moveToRequestedRow(startY: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): string {\n const startRow = startY - wrappedRowsForRow(bufferService, startY);\n const endRow = targetY - wrappedRowsForRow(bufferService, targetY);\n\n const rowsToMove = Math.abs(startRow - endRow) - wrappedRowsCount(startY, targetY, bufferService);\n\n return repeat(rowsToMove, sequence(verticalDirection(startY, targetY), applicationCursor));\n}\n\n/**\n * Move to the requested col on the ending row\n */\nfunction moveToRequestedCol(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): string {\n let startRow;\n if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length > 0) {\n startRow = targetY - wrappedRowsForRow(bufferService, targetY);\n } else {\n startRow = startY;\n }\n\n const endRow = targetY;\n const direction = horizontalDirection(startX, startY, targetX, targetY, bufferService, applicationCursor);\n\n return repeat(bufferLine(\n startX, startRow, targetX, endRow,\n direction === Direction.RIGHT, bufferService\n ).length, sequence(direction, applicationCursor));\n}\n\n/**\n * Utility functions\n */\n\n/**\n * Calculates the number of wrapped rows between the unwrapped starting and\n * ending rows. These rows need to ignored since the cursor skips over them.\n */\nfunction wrappedRowsCount(startY: number, targetY: number, bufferService: IBufferService): number {\n let wrappedRows = 0;\n const startRow = startY - wrappedRowsForRow(bufferService, startY);\n const endRow = targetY - wrappedRowsForRow(bufferService, targetY);\n\n for (let i = 0; i < Math.abs(startRow - endRow); i++) {\n const direction = verticalDirection(startY, targetY) === Direction.UP ? -1 : 1;\n const line = bufferService.buffer.lines.get(startRow + (direction * i));\n if (line?.isWrapped) {\n wrappedRows++;\n }\n }\n\n return wrappedRows;\n}\n\n/**\n * Calculates the number of wrapped rows that make up a given row.\n * @param currentRow The row to determine how many wrapped rows make it up\n */\nfunction wrappedRowsForRow(bufferService: IBufferService, currentRow: number): number {\n let rowCount = 0;\n let line = bufferService.buffer.lines.get(currentRow);\n let lineWraps = line?.isWrapped;\n\n while (lineWraps && currentRow >= 0 && currentRow < bufferService.rows) {\n rowCount++;\n line = bufferService.buffer.lines.get(--currentRow);\n lineWraps = line?.isWrapped;\n }\n\n return rowCount;\n}\n\n/**\n * Direction determiners\n */\n\n/**\n * Determines if the right or left arrow is needed\n */\nfunction horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction {\n let startRow;\n if (moveToRequestedRow(targetX, targetY, bufferService, applicationCursor).length > 0) {\n startRow = targetY - wrappedRowsForRow(bufferService, targetY);\n } else {\n startRow = startY;\n }\n\n if ((startX < targetX &&\n startRow <= targetY) || // down/right or same y/right\n (startX >= targetX &&\n startRow < targetY)) { // down/left or same y/left\n return Direction.RIGHT;\n }\n return Direction.LEFT;\n}\n\n/**\n * Determines if the up or down arrow is needed\n */\nfunction verticalDirection(startY: number, targetY: number): Direction {\n return startY > targetY ? Direction.UP : Direction.DOWN;\n}\n\n/**\n * Constructs the string of chars in the buffer from a starting row and col\n * to an ending row and col\n * @param startCol The starting column position\n * @param startRow The starting row position\n * @param endCol The ending column position\n * @param endRow The ending row position\n * @param forward Direction to move\n */\nfunction bufferLine(\n startCol: number,\n startRow: number,\n endCol: number,\n endRow: number,\n forward: boolean,\n bufferService: IBufferService\n): string {\n let currentCol = startCol;\n let currentRow = startRow;\n let bufferStr = '';\n\n while (currentCol !== endCol || currentRow !== endRow) {\n currentCol += forward ? 1 : -1;\n\n if (forward && currentCol > bufferService.cols - 1) {\n bufferStr += bufferService.buffer.translateBufferLineToString(\n currentRow, false, startCol, currentCol\n );\n currentCol = 0;\n startCol = 0;\n currentRow++;\n } else if (!forward && currentCol < 0) {\n bufferStr += bufferService.buffer.translateBufferLineToString(\n currentRow, false, 0, startCol + 1\n );\n currentCol = bufferService.cols - 1;\n startCol = currentCol;\n currentRow--;\n }\n }\n\n return bufferStr + bufferService.buffer.translateBufferLineToString(\n currentRow, false, startCol, currentCol\n );\n}\n\n/**\n * Constructs the escape sequence for clicking an arrow\n * @param direction The direction to move\n */\nfunction sequence(direction: Direction, applicationCursor: boolean): string {\n const mod = applicationCursor ? 'O' : '[';\n return C0.ESC + mod + direction;\n}\n\n/**\n * Returns a string repeated a given number of times\n * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat\n * @param count The number of times to repeat the string\n * @param string The string that is to be repeated\n */\nfunction repeat(count: number, str: string): string {\n count = Math.floor(count);\n let rpt = '';\n for (let i = 0; i < count; i++) {\n rpt += str;\n }\n return rpt;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDimensions, IRenderLayer } from 'browser/renderer/Types';\nimport { ICellData } from 'common/Types';\nimport { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants';\nimport { IGlyphIdentifier } from 'browser/renderer/atlas/Types';\nimport { DIM_OPACITY, INVERTED_DEFAULT_COLOR, TEXT_BASELINE } from 'browser/renderer/atlas/Constants';\nimport { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';\nimport { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache';\nimport { AttributeData } from 'common/buffer/AttributeData';\nimport { IColorSet, IColor } from 'browser/Types';\nimport { CellData } from 'common/buffer/CellData';\nimport { IBufferService, IOptionsService } from 'common/services/Services';\nimport { throwIfFalsy } from 'browser/renderer/RendererUtils';\nimport { channels, color, rgba } from 'browser/Color';\nimport { removeElementFromParent } from 'browser/Dom';\nimport { tryDrawCustomChar } from 'browser/renderer/CustomGlyphs';\n\nexport abstract class BaseRenderLayer implements IRenderLayer {\n private _canvas: HTMLCanvasElement;\n protected _ctx!: CanvasRenderingContext2D;\n private _scaledCharWidth: number = 0;\n private _scaledCharHeight: number = 0;\n private _scaledCellWidth: number = 0;\n private _scaledCellHeight: number = 0;\n private _scaledCharLeft: number = 0;\n private _scaledCharTop: number = 0;\n\n protected _charAtlas: BaseCharAtlas | undefined;\n\n /**\n * An object that's reused when drawing glyphs in order to reduce GC.\n */\n private _currentGlyphIdentifier: IGlyphIdentifier = {\n chars: '',\n code: 0,\n bg: 0,\n fg: 0,\n bold: false,\n dim: false,\n italic: false\n };\n\n constructor(\n private _container: HTMLElement,\n id: string,\n zIndex: number,\n private _alpha: boolean,\n protected _colors: IColorSet,\n private _rendererId: number,\n protected readonly _bufferService: IBufferService,\n protected readonly _optionsService: IOptionsService\n ) {\n this._canvas = document.createElement('canvas');\n this._canvas.classList.add(`xterm-${id}-layer`);\n this._canvas.style.zIndex = zIndex.toString();\n this._initCanvas();\n this._container.appendChild(this._canvas);\n }\n\n public dispose(): void {\n removeElementFromParent(this._canvas);\n this._charAtlas?.dispose();\n }\n\n private _initCanvas(): void {\n this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha }));\n // Draw the background if this is an opaque layer\n if (!this._alpha) {\n this._clearAll();\n }\n }\n\n public onOptionsChanged(): void {}\n public onBlur(): void {}\n public onFocus(): void {}\n public onCursorMove(): void {}\n public onGridChanged(startRow: number, endRow: number): void {}\n public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {}\n\n public setColors(colorSet: IColorSet): void {\n this._refreshCharAtlas(colorSet);\n }\n\n protected _setTransparency(alpha: boolean): void {\n // Do nothing when alpha doesn't change\n if (alpha === this._alpha) {\n return;\n }\n\n // Create new canvas and replace old one\n const oldCanvas = this._canvas;\n this._alpha = alpha;\n // Cloning preserves properties\n this._canvas = this._canvas.cloneNode() as HTMLCanvasElement;\n this._initCanvas();\n this._container.replaceChild(this._canvas, oldCanvas);\n\n // Regenerate char atlas and force a full redraw\n this._refreshCharAtlas(this._colors);\n this.onGridChanged(0, this._bufferService.rows - 1);\n }\n\n /**\n * Refreshes the char atlas, aquiring a new one if necessary.\n * @param colorSet The color set to use for the char atlas.\n */\n private _refreshCharAtlas(colorSet: IColorSet): void {\n if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {\n return;\n }\n this._charAtlas = acquireCharAtlas(this._optionsService.options, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight);\n this._charAtlas.warmUp();\n }\n\n public resize(dim: IRenderDimensions): void {\n this._scaledCellWidth = dim.scaledCellWidth;\n this._scaledCellHeight = dim.scaledCellHeight;\n this._scaledCharWidth = dim.scaledCharWidth;\n this._scaledCharHeight = dim.scaledCharHeight;\n this._scaledCharLeft = dim.scaledCharLeft;\n this._scaledCharTop = dim.scaledCharTop;\n this._canvas.width = dim.scaledCanvasWidth;\n this._canvas.height = dim.scaledCanvasHeight;\n this._canvas.style.width = `${dim.canvasWidth}px`;\n this._canvas.style.height = `${dim.canvasHeight}px`;\n\n // Draw the background if this is an opaque layer\n if (!this._alpha) {\n this._clearAll();\n }\n\n this._refreshCharAtlas(this._colors);\n }\n\n public abstract reset(): void;\n\n public clearTextureAtlas(): void {\n this._charAtlas?.clear();\n }\n\n /**\n * Fills 1+ cells completely. This uses the existing fillStyle on the context.\n * @param x The column to start at.\n * @param y The row to start at\n * @param width The number of columns to fill.\n * @param height The number of rows to fill.\n */\n protected _fillCells(x: number, y: number, width: number, height: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the middle of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected _fillMiddleLineAtCells(x: number, y: number, width: number = 1): void {\n const cellOffset = Math.ceil(this._scaledCellHeight * 0.5);\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n (y + 1) * this._scaledCellHeight - cellOffset - window.devicePixelRatio,\n width * this._scaledCellWidth,\n window.devicePixelRatio);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,\n width * this._scaledCellWidth,\n window.devicePixelRatio);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected _fillLeftLineAtCell(x: number, y: number, width: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n window.devicePixelRatio * width,\n this._scaledCellHeight);\n }\n\n /**\n * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing\n * strokeStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void {\n this._ctx.lineWidth = window.devicePixelRatio;\n this._ctx.strokeRect(\n x * this._scaledCellWidth + window.devicePixelRatio / 2,\n y * this._scaledCellHeight + (window.devicePixelRatio / 2),\n width * this._scaledCellWidth - window.devicePixelRatio,\n (height * this._scaledCellHeight) - window.devicePixelRatio);\n }\n\n /**\n * Clears the entire canvas.\n */\n protected _clearAll(): void {\n if (this._alpha) {\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n } else {\n this._ctx.fillStyle = this._colors.background.css;\n this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);\n }\n }\n\n /**\n * Clears 1+ cells completely.\n * @param x The column to start at.\n * @param y The row to start at.\n * @param width The number of columns to clear.\n * @param height The number of rows to clear.\n */\n protected _clearCells(x: number, y: number, width: number, height: number): void {\n if (this._alpha) {\n this._ctx.clearRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n } else {\n this._ctx.fillStyle = this._colors.background.css;\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n }\n\n /**\n * Draws a truecolor character at the cell. The character will be clipped to\n * ensure that it fits with the cell, including the cell to the right if it's\n * a wide character. This uses the existing fillStyle on the context.\n * @param cell The cell data for the character to draw.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param color The color of the character.\n */\n protected _fillCharTrueColor(cell: CellData, x: number, y: number): void {\n this._ctx.font = this._getFont(false, false);\n this._ctx.textBaseline = TEXT_BASELINE;\n this._clipRow(y);\n\n // Draw custom characters if applicable\n let drawSuccess = false;\n if (this._optionsService.options.customGlyphs !== false) {\n drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);\n }\n\n // Draw the character\n if (!drawSuccess) {\n this._ctx.fillText(\n cell.getChars(),\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight);\n }\n }\n\n /**\n * Draws one or more characters at a cell. If possible this will draw using\n * the character atlas to reduce draw time.\n * @param chars The character or characters.\n * @param code The character code.\n * @param width The width of the characters.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param bg The background color, in the format stored within the attributes.\n * This is used to validate whether a cached image can be used.\n * @param bold Whether the text is bold.\n */\n protected _drawChars(cell: ICellData, x: number, y: number): void {\n const contrastColor = this._getContrastColor(cell);\n\n // skip cache right away if we draw in RGB\n // Note: to avoid bad runtime JoinedCellData will be skipped\n // in the cache handler itself (atlasDidDraw == false) and\n // fall through to uncached later down below\n if (contrastColor || cell.isFgRGB() || cell.isBgRGB()) {\n this._drawUncachedChars(cell, x, y, contrastColor);\n return;\n }\n\n let fg;\n let bg;\n if (cell.isInverse()) {\n fg = (cell.isBgDefault()) ? INVERTED_DEFAULT_COLOR : cell.getBgColor();\n bg = (cell.isFgDefault()) ? INVERTED_DEFAULT_COLOR : cell.getFgColor();\n } else {\n bg = (cell.isBgDefault()) ? DEFAULT_COLOR : cell.getBgColor();\n fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor();\n }\n\n const drawInBrightColor = this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8;\n\n fg += drawInBrightColor ? 8 : 0;\n this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR;\n this._currentGlyphIdentifier.code = cell.getCode() || WHITESPACE_CELL_CODE;\n this._currentGlyphIdentifier.bg = bg;\n this._currentGlyphIdentifier.fg = fg;\n this._currentGlyphIdentifier.bold = !!cell.isBold();\n this._currentGlyphIdentifier.dim = !!cell.isDim();\n this._currentGlyphIdentifier.italic = !!cell.isItalic();\n const atlasDidDraw = this._charAtlas?.draw(this._ctx, this._currentGlyphIdentifier, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop);\n\n if (!atlasDidDraw) {\n this._drawUncachedChars(cell, x, y);\n }\n }\n\n /**\n * Draws one or more characters at one or more cells. The character(s) will be\n * clipped to ensure that they fit with the cell(s), including the cell to the\n * right if the last character is a wide character.\n * @param chars The character.\n * @param width The width of the character.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param x The column to draw at.\n * @param y The row to draw at.\n */\n private _drawUncachedChars(cell: ICellData, x: number, y: number, fgOverride?: IColor): void {\n this._ctx.save();\n this._ctx.font = this._getFont(!!cell.isBold(), !!cell.isItalic());\n this._ctx.textBaseline = TEXT_BASELINE;\n\n if (cell.isInverse()) {\n if (fgOverride) {\n this._ctx.fillStyle = fgOverride.css;\n } else if (cell.isBgDefault()) {\n this._ctx.fillStyle = color.opaque(this._colors.background).css;\n } else if (cell.isBgRGB()) {\n this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;\n } else {\n let bg = cell.getBgColor();\n if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {\n bg += 8;\n }\n this._ctx.fillStyle = this._colors.ansi[bg].css;\n }\n } else {\n if (fgOverride) {\n this._ctx.fillStyle = fgOverride.css;\n } else if (cell.isFgDefault()) {\n this._ctx.fillStyle = this._colors.foreground.css;\n } else if (cell.isFgRGB()) {\n this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;\n } else {\n let fg = cell.getFgColor();\n if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {\n fg += 8;\n }\n this._ctx.fillStyle = this._colors.ansi[fg].css;\n }\n }\n\n this._clipRow(y);\n\n // Apply alpha to dim the character\n if (cell.isDim()) {\n this._ctx.globalAlpha = DIM_OPACITY;\n }\n\n // Draw custom characters if applicable\n let drawSuccess = false;\n if (this._optionsService.options.customGlyphs !== false) {\n drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);\n }\n\n // Draw the character\n if (!drawSuccess) {\n this._ctx.fillText(\n cell.getChars(),\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight);\n }\n\n this._ctx.restore();\n }\n\n\n /**\n * Clips a row to ensure no pixels will be drawn outside the cells in the row.\n * @param y The row to clip.\n */\n private _clipRow(y: number): void {\n this._ctx.beginPath();\n this._ctx.rect(\n 0,\n y * this._scaledCellHeight,\n this._bufferService.cols * this._scaledCellWidth,\n this._scaledCellHeight);\n this._ctx.clip();\n }\n\n /**\n * Gets the current font.\n * @param isBold If we should use the bold fontWeight.\n */\n protected _getFont(isBold: boolean, isItalic: boolean): string {\n const fontWeight = isBold ? this._optionsService.options.fontWeightBold : this._optionsService.options.fontWeight;\n const fontStyle = isItalic ? 'italic' : '';\n\n return `${fontStyle} ${fontWeight} ${this._optionsService.options.fontSize * window.devicePixelRatio}px ${this._optionsService.options.fontFamily}`;\n }\n\n private _getContrastColor(cell: CellData): IColor | undefined {\n if (this._optionsService.options.minimumContrastRatio === 1) {\n return undefined;\n }\n\n // Try get from cache first\n const adjustedColor = this._colors.contrastCache.getColor(cell.bg, cell.fg);\n if (adjustedColor !== undefined) {\n return adjustedColor || undefined;\n }\n\n let fgColor = cell.getFgColor();\n let fgColorMode = cell.getFgColorMode();\n let bgColor = cell.getBgColor();\n let bgColorMode = cell.getBgColorMode();\n const isInverse = !!cell.isInverse();\n const isBold = !!cell.isInverse();\n if (isInverse) {\n const temp = fgColor;\n fgColor = bgColor;\n bgColor = temp;\n const temp2 = fgColorMode;\n fgColorMode = bgColorMode;\n bgColorMode = temp2;\n }\n\n const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, isInverse);\n const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, isInverse, isBold);\n const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._optionsService.options.minimumContrastRatio);\n\n if (!result) {\n this._colors.contrastCache.setColor(cell.bg, cell.fg, null);\n return undefined;\n }\n\n const color: IColor = {\n css: channels.toCss(\n (result >> 24) & 0xFF,\n (result >> 16) & 0xFF,\n (result >> 8) & 0xFF\n ),\n rgba: result\n };\n this._colors.contrastCache.setColor(cell.bg, cell.fg, color);\n\n return color;\n }\n\n private _resolveBackgroundRgba(bgColorMode: number, bgColor: number, inverse: boolean): number {\n switch (bgColorMode) {\n case Attributes.CM_P16:\n case Attributes.CM_P256:\n return this._colors.ansi[bgColor].rgba;\n case Attributes.CM_RGB:\n return bgColor << 8;\n case Attributes.CM_DEFAULT:\n default:\n if (inverse) {\n return this._colors.foreground.rgba;\n }\n return this._colors.background.rgba;\n }\n }\n\n private _resolveForegroundRgba(fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean): number {\n switch (fgColorMode) {\n case Attributes.CM_P16:\n case Attributes.CM_P256:\n if (this._optionsService.options.drawBoldTextInBrightColors && bold && fgColor < 8) {\n fgColor += 8;\n }\n return this._colors.ansi[fgColor].rgba;\n case Attributes.CM_RGB:\n return fgColor << 8;\n case Attributes.CM_DEFAULT:\n default:\n if (inverse) {\n return this._colors.background.rgba;\n }\n return this._colors.foreground.rgba;\n }\n }\n}\n\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types';\nimport { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';\nimport { ICellData } from 'common/Types';\nimport { CellData } from 'common/buffer/CellData';\nimport { IColorSet } from 'browser/Types';\nimport { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';\nimport { IEventEmitter } from 'common/EventEmitter';\nimport { ICoreBrowserService } from 'browser/services/Services';\n\ninterface ICursorState {\n x: number;\n y: number;\n isFocused: boolean;\n style: string;\n width: number;\n}\n\n/**\n * The time between cursor blinks.\n */\nconst BLINK_INTERVAL = 600;\n\nexport class CursorRenderLayer extends BaseRenderLayer {\n private _state: ICursorState;\n private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void};\n private _cursorBlinkStateManager: CursorBlinkStateManager | undefined;\n private _cell: ICellData = new CellData();\n\n constructor(\n container: HTMLElement,\n zIndex: number,\n colors: IColorSet,\n rendererId: number,\n private _onRequestRedraw: IEventEmitter,\n @IBufferService bufferService: IBufferService,\n @IOptionsService optionsService: IOptionsService,\n @ICoreService private readonly _coreService: ICoreService,\n @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService\n ) {\n super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService);\n this._state = {\n x: 0,\n y: 0,\n isFocused: false,\n style: '',\n width: 0\n };\n this._cursorRenderers = {\n 'bar': this._renderBarCursor.bind(this),\n 'block': this._renderBlockCursor.bind(this),\n 'underline': this._renderUnderlineCursor.bind(this)\n };\n }\n\n public dispose(): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.dispose();\n this._cursorBlinkStateManager = undefined;\n }\n super.dispose();\n }\n\n public resize(dim: IRenderDimensions): void {\n super.resize(dim);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = {\n x: 0,\n y: 0,\n isFocused: false,\n style: '',\n width: 0\n };\n }\n\n public reset(): void {\n this._clearCursor();\n this._cursorBlinkStateManager?.restartBlinkAnimation();\n this.onOptionsChanged();\n }\n\n public onBlur(): void {\n this._cursorBlinkStateManager?.pause();\n this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });\n }\n\n public onFocus(): void {\n this._cursorBlinkStateManager?.resume();\n this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });\n }\n\n public onOptionsChanged(): void {\n if (this._optionsService.options.cursorBlink) {\n if (!this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => {\n this._render(true);\n });\n }\n } else {\n this._cursorBlinkStateManager?.dispose();\n this._cursorBlinkStateManager = undefined;\n }\n // Request a refresh from the terminal as management of rendering is being\n // moved back to the terminal\n this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });\n }\n\n public onCursorMove(): void {\n this._cursorBlinkStateManager?.restartBlinkAnimation();\n }\n\n public onGridChanged(startRow: number, endRow: number): void {\n if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {\n this._render(false);\n } else {\n this._cursorBlinkStateManager.restartBlinkAnimation();\n }\n }\n\n private _render(triggeredByAnimationFrame: boolean): void {\n // Don't draw the cursor if it's hidden\n if (!this._coreService.isCursorInitialized || this._coreService.isCursorHidden) {\n this._clearCursor();\n return;\n }\n\n const cursorY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;\n const viewportRelativeCursorY = cursorY - this._bufferService.buffer.ydisp;\n\n // Don't draw the cursor if it's off-screen\n if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) {\n this._clearCursor();\n return;\n }\n\n // in case cursor.x == cols adjust visual cursor to cols - 1\n const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);\n this._bufferService.buffer.lines.get(cursorY)!.loadCell(cursorX, this._cell);\n if (this._cell.content === undefined) {\n return;\n }\n\n if (!this._coreBrowserService.isFocused) {\n this._clearCursor();\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor.css;\n const cursorStyle = this._optionsService.options.cursorStyle;\n if (cursorStyle && cursorStyle !== 'block') {\n this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell);\n } else {\n this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell);\n }\n this._ctx.restore();\n this._state.x = cursorX;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = cursorStyle;\n this._state.width = this._cell.getWidth();\n return;\n }\n\n // Don't draw the cursor if it's blinking\n if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) {\n this._clearCursor();\n return;\n }\n\n if (this._state) {\n // The cursor is already in the correct spot, don't redraw\n if (this._state.x === cursorX &&\n this._state.y === viewportRelativeCursorY &&\n this._state.isFocused === this._coreBrowserService.isFocused &&\n this._state.style === this._optionsService.options.cursorStyle &&\n this._state.width === this._cell.getWidth()) {\n return;\n }\n this._clearCursor();\n }\n\n this._ctx.save();\n this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell);\n this._ctx.restore();\n\n this._state.x = cursorX;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = this._optionsService.options.cursorStyle;\n this._state.width = this._cell.getWidth();\n }\n\n private _clearCursor(): void {\n if (this._state) {\n // Avoid potential rounding errors when device pixel ratio is less than 1\n if (window.devicePixelRatio < 1) {\n this._clearAll();\n } else {\n this._clearCells(this._state.x, this._state.y, this._state.width, 1);\n }\n this._state = {\n x: 0,\n y: 0,\n isFocused: false,\n style: '',\n width: 0\n };\n }\n }\n\n private _renderBarCursor(x: number, y: number, cell: ICellData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor.css;\n this._fillLeftLineAtCell(x, y, this._optionsService.options.cursorWidth);\n this._ctx.restore();\n }\n\n private _renderBlockCursor(x: number, y: number, cell: ICellData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor.css;\n this._fillCells(x, y, cell.getWidth(), 1);\n this._ctx.fillStyle = this._colors.cursorAccent.css;\n this._fillCharTrueColor(cell, x, y);\n this._ctx.restore();\n }\n\n private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor.css;\n this._fillBottomLineAtCells(x, y);\n this._ctx.restore();\n }\n\n private _renderBlurCursor(x: number, y: number, cell: ICellData): void {\n this._ctx.save();\n this._ctx.strokeStyle = this._colors.cursor.css;\n this._strokeRectAtCell(x, y, cell.getWidth(), 1);\n this._ctx.restore();\n }\n}\n\nclass CursorBlinkStateManager {\n public isCursorVisible: boolean;\n\n private _animationFrame: number | undefined;\n private _blinkStartTimeout: number | undefined;\n private _blinkInterval: number | undefined;\n\n /**\n * The time at which the animation frame was restarted, this is used on the\n * next render to restart the timers so they don't need to restart the timers\n * multiple times over a short period.\n */\n private _animationTimeRestarted: number | undefined;\n\n constructor(\n isFocused: boolean,\n private _renderCallback: () => void\n ) {\n this.isCursorVisible = true;\n if (isFocused) {\n this._restartInterval();\n }\n }\n\n public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); }\n\n public dispose(): void {\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = undefined;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = undefined;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = undefined;\n }\n }\n\n public restartBlinkAnimation(): void {\n if (this.isPaused) {\n return;\n }\n // Save a timestamp so that the restart can be done on the next interval\n this._animationTimeRestarted = Date.now();\n // Force a cursor render to ensure it's visible and in the correct position\n this.isCursorVisible = true;\n if (!this._animationFrame) {\n this._animationFrame = window.requestAnimationFrame(() => {\n this._renderCallback();\n this._animationFrame = undefined;\n });\n }\n }\n\n private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {\n // Clear any existing interval\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = undefined;\n }\n\n // Setup the initial timeout which will hide the cursor, this is done before\n // the regular interval is setup in order to support restarting the blink\n // animation in a lightweight way (without thrashing clearInterval and\n // setInterval).\n this._blinkStartTimeout = window.setTimeout(() => {\n // Check if another animation restart was requested while this was being\n // started\n if (this._animationTimeRestarted) {\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = undefined;\n if (time > 0) {\n this._restartInterval(time);\n return;\n }\n }\n\n // Hide the cursor\n this.isCursorVisible = false;\n this._animationFrame = window.requestAnimationFrame(() => {\n this._renderCallback();\n this._animationFrame = undefined;\n });\n\n // Setup the blink interval\n this._blinkInterval = window.setInterval(() => {\n // Adjust the animation time if it was restarted\n if (this._animationTimeRestarted) {\n // calc time diff\n // Make restart interval do a setTimeout initially?\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = undefined;\n this._restartInterval(time);\n return;\n }\n\n // Invert visibility and render\n this.isCursorVisible = !this.isCursorVisible;\n this._animationFrame = window.requestAnimationFrame(() => {\n this._renderCallback();\n this._animationFrame = undefined;\n });\n }, BLINK_INTERVAL);\n }, timeToStart);\n }\n\n public pause(): void {\n this.isCursorVisible = true;\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = undefined;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = undefined;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = undefined;\n }\n }\n\n public resume(): void {\n // Clear out any existing timers just in case\n this.pause();\n\n this._animationTimeRestarted = undefined;\n this._restartInterval();\n this.restartBlinkAnimation();\n }\n}\n","/**\n * Copyright (c) 2021 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { throwIfFalsy } from 'browser/renderer/RendererUtils';\n\ninterface IBlockVector {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport const blockElementDefinitions: { [index: string]: IBlockVector[] | undefined } = {\n // Block elements (0x2580-0x2590)\n '▀': [{ x: 0, y: 0, w: 8, h: 4 }], // UPPER HALF BLOCK\n '▁': [{ x: 0, y: 7, w: 8, h: 1 }], // LOWER ONE EIGHTH BLOCK\n '▂': [{ x: 0, y: 6, w: 8, h: 2 }], // LOWER ONE QUARTER BLOCK\n '▃': [{ x: 0, y: 5, w: 8, h: 3 }], // LOWER THREE EIGHTHS BLOCK\n '▄': [{ x: 0, y: 4, w: 8, h: 4 }], // LOWER HALF BLOCK\n '▅': [{ x: 0, y: 3, w: 8, h: 5 }], // LOWER FIVE EIGHTHS BLOCK\n '▆': [{ x: 0, y: 2, w: 8, h: 6 }], // LOWER THREE QUARTERS BLOCK\n '▇': [{ x: 0, y: 1, w: 8, h: 7 }], // LOWER SEVEN EIGHTHS BLOCK\n '█': [{ x: 0, y: 0, w: 8, h: 8 }], // FULL BLOCK\n '▉': [{ x: 0, y: 0, w: 7, h: 8 }], // LEFT SEVEN EIGHTHS BLOCK\n '▊': [{ x: 0, y: 0, w: 6, h: 8 }], // LEFT THREE QUARTERS BLOCK\n '▋': [{ x: 0, y: 0, w: 5, h: 8 }], // LEFT FIVE EIGHTHS BLOCK\n '▌': [{ x: 0, y: 0, w: 4, h: 8 }], // LEFT HALF BLOCK\n '▍': [{ x: 0, y: 0, w: 3, h: 8 }], // LEFT THREE EIGHTHS BLOCK\n '▎': [{ x: 0, y: 0, w: 2, h: 8 }], // LEFT ONE QUARTER BLOCK\n '▏': [{ x: 0, y: 0, w: 1, h: 8 }], // LEFT ONE EIGHTH BLOCK\n '▐': [{ x: 4, y: 0, w: 4, h: 8 }], // RIGHT HALF BLOCK\n\n // Block elements (0x2594-0x2595)\n '▔': [{ x: 0, y: 0, w: 9, h: 1 }], // UPPER ONE EIGHTH BLOCK\n '▕': [{ x: 7, y: 0, w: 1, h: 8 }], // RIGHT ONE EIGHTH BLOCK\n\n // Terminal graphic characters (0x2596-0x259F)\n '▖': [{ x: 0, y: 4, w: 4, h: 4 }], // QUADRANT LOWER LEFT\n '▗': [{ x: 4, y: 4, w: 4, h: 4 }], // QUADRANT LOWER RIGHT\n '▘': [{ x: 0, y: 0, w: 4, h: 4 }], // QUADRANT UPPER LEFT\n '▙': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT\n '▚': [{ x: 0, y: 0, w: 4, h: 4 }, { x: 4, y: 4, w: 4, h: 4 }], // QUADRANT UPPER LEFT AND LOWER RIGHT\n '▛': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 0, w: 4, h: 8 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT\n '▜': [{ x: 0, y: 0, w: 8, h: 4 }, { x: 4, y: 0, w: 4, h: 8 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT\n '▝': [{ x: 4, y: 0, w: 4, h: 4 }], // QUADRANT UPPER RIGHT\n '▞': [{ x: 4, y: 0, w: 4, h: 4 }, { x: 0, y: 4, w: 4, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT\n '▟': [{ x: 4, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT\n\n // VERTICAL ONE EIGHTH BLOCK-2 through VERTICAL ONE EIGHTH BLOCK-7\n '\\u{1FB70}': [{ x: 1, y: 0, w: 1, h: 8 }],\n '\\u{1FB71}': [{ x: 2, y: 0, w: 1, h: 8 }],\n '\\u{1FB72}': [{ x: 3, y: 0, w: 1, h: 8 }],\n '\\u{1FB73}': [{ x: 4, y: 0, w: 1, h: 8 }],\n '\\u{1FB74}': [{ x: 5, y: 0, w: 1, h: 8 }],\n '\\u{1FB75}': [{ x: 6, y: 0, w: 1, h: 8 }],\n\n // HORIZONTAL ONE EIGHTH BLOCK-2 through HORIZONTAL ONE EIGHTH BLOCK-7\n '\\u{1FB76}': [{ x: 0, y: 1, w: 8, h: 1 }],\n '\\u{1FB77}': [{ x: 0, y: 2, w: 8, h: 1 }],\n '\\u{1FB78}': [{ x: 0, y: 3, w: 8, h: 1 }],\n '\\u{1FB79}': [{ x: 0, y: 4, w: 8, h: 1 }],\n '\\u{1FB7A}': [{ x: 0, y: 5, w: 8, h: 1 }],\n '\\u{1FB7B}': [{ x: 0, y: 6, w: 8, h: 1 }],\n\n // LEFT AND LOWER ONE EIGHTH BLOCK\n '\\u{1FB7C}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }],\n // LEFT AND UPPER ONE EIGHTH BLOCK\n '\\u{1FB7D}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }],\n // RIGHT AND UPPER ONE EIGHTH BLOCK\n '\\u{1FB7E}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }],\n // RIGHT AND LOWER ONE EIGHTH BLOCK\n '\\u{1FB7F}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }],\n // UPPER AND LOWER ONE EIGHTH BLOCK\n '\\u{1FB80}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }],\n // HORIZONTAL ONE EIGHTH BLOCK-1358\n '\\u{1FB81}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 2, w: 8, h: 1 }, { x: 0, y: 4, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }],\n\n // UPPER ONE QUARTER BLOCK\n '\\u{1FB82}': [{ x: 0, y: 0, w: 8, h: 2 }],\n // UPPER THREE EIGHTHS BLOCK\n '\\u{1FB83}': [{ x: 0, y: 0, w: 8, h: 3 }],\n // UPPER FIVE EIGHTHS BLOCK\n '\\u{1FB84}': [{ x: 0, y: 0, w: 8, h: 5 }],\n // UPPER THREE QUARTERS BLOCK\n '\\u{1FB85}': [{ x: 0, y: 0, w: 8, h: 6 }],\n // UPPER SEVEN EIGHTHS BLOCK\n '\\u{1FB86}': [{ x: 0, y: 0, w: 8, h: 7 }],\n\n // RIGHT ONE QUARTER BLOCK\n '\\u{1FB87}': [{ x: 6, y: 0, w: 2, h: 8 }],\n // RIGHT THREE EIGHTHS B0OCK\n '\\u{1FB88}': [{ x: 5, y: 0, w: 3, h: 8 }],\n // RIGHT FIVE EIGHTHS BL0CK\n '\\u{1FB89}': [{ x: 3, y: 0, w: 5, h: 8 }],\n // RIGHT THREE QUARTERS 0LOCK\n '\\u{1FB8A}': [{ x: 2, y: 0, w: 6, h: 8 }],\n // RIGHT SEVEN EIGHTHS B0OCK\n '\\u{1FB8B}': [{ x: 1, y: 0, w: 7, h: 8 }],\n\n // CHECKER BOARD FILL\n '\\u{1FB95}': [\n { x: 0, y: 0, w: 2, h: 2 }, { x: 4, y: 0, w: 2, h: 2 },\n { x: 2, y: 2, w: 2, h: 2 }, { x: 6, y: 2, w: 2, h: 2 },\n { x: 0, y: 4, w: 2, h: 2 }, { x: 4, y: 4, w: 2, h: 2 },\n { x: 2, y: 6, w: 2, h: 2 }, { x: 6, y: 6, w: 2, h: 2 }\n ],\n // INVERSE CHECKER BOARD FILL\n '\\u{1FB96}': [\n { x: 2, y: 0, w: 2, h: 2 }, { x: 6, y: 0, w: 2, h: 2 },\n { x: 0, y: 2, w: 2, h: 2 }, { x: 4, y: 2, w: 2, h: 2 },\n { x: 2, y: 4, w: 2, h: 2 }, { x: 6, y: 4, w: 2, h: 2 },\n { x: 0, y: 6, w: 2, h: 2 }, { x: 4, y: 6, w: 2, h: 2 }\n ],\n // HEAVY HORIZONTAL FILL (upper middle and lower one quarter block)\n '\\u{1FB97}': [{ x: 0, y: 2, w: 8, h: 2 }, { x: 0, y: 6, w: 8, h: 2 }]\n};\n\ntype PatternDefinition = number[][];\n\n/**\n * Defines the repeating pattern used by special characters, the pattern is made up of a 2d array of\n * pixel values to be filled (1) or not filled (0).\n */\nconst patternCharacterDefinitions: { [key: string]: PatternDefinition | undefined } = {\n // Shade characters (0x2591-0x2593)\n '░': [ // LIGHT SHADE (25%)\n [1, 0, 0, 0],\n [0, 0, 0, 0],\n [0, 0, 1, 0],\n [0, 0, 0, 0]\n ],\n '▒': [ // MEDIUM SHADE (50%)\n [1, 0],\n [0, 0],\n [0, 1],\n [0, 0]\n ],\n '▓': [ // DARK SHADE (75%)\n [0, 1],\n [1, 1],\n [1, 0],\n [1, 1]\n ]\n};\n\nconst enum Shapes {\n /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',\n /** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5',\n\n /** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5',\n /** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5',\n /** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1',\n /** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5',\n\n /** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0',\n /** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5',\n /** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5',\n /** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1',\n\n /** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0',\n /** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5',\n /** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5',\n /** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1',\n\n /** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1',\n\n /** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled\n /** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled\n /** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled\n /** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9',\n /** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333',\n /** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95',\n}\n\nconst enum Style {\n NORMAL = 1,\n BOLD = 3\n}\n\n/**\n * This contains the definitions of all box drawing characters in the format of SVG paths (ie. the\n * svg d attribute).\n */\nexport const boxDrawingDefinitions: { [character: string]: { [fontWeight: number]: string | ((xp: number, yp: number) => string) } | undefined } = {\n // Uniform normal and bold\n '─': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT },\n '━': { [Style.BOLD]: Shapes.LEFT_TO_RIGHT },\n '│': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM },\n '┃': { [Style.BOLD]: Shapes.TOP_TO_BOTTOM },\n '┌': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM },\n '┏': { [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },\n '┐': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM },\n '┓': { [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },\n '└': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT },\n '┗': { [Style.BOLD]: Shapes.TOP_TO_RIGHT },\n '┘': { [Style.NORMAL]: Shapes.TOP_TO_LEFT },\n '┛': { [Style.BOLD]: Shapes.TOP_TO_LEFT },\n '├': { [Style.NORMAL]: Shapes.T_RIGHT },\n '┣': { [Style.BOLD]: Shapes.T_RIGHT },\n '┤': { [Style.NORMAL]: Shapes.T_LEFT },\n '┫': { [Style.BOLD]: Shapes.T_LEFT },\n '┬': { [Style.NORMAL]: Shapes.T_BOTTOM },\n '┳': { [Style.BOLD]: Shapes.T_BOTTOM },\n '┴': { [Style.NORMAL]: Shapes.T_TOP },\n '┻': { [Style.BOLD]: Shapes.T_TOP },\n '┼': { [Style.NORMAL]: Shapes.CROSS },\n '╋': { [Style.BOLD]: Shapes.CROSS },\n '╴': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT },\n '╸': { [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '╵': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP },\n '╹': { [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '╶': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT },\n '╺': { [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '╷': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM },\n '╻': { [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n\n // Double border\n '═': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },\n '║': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },\n '╒': { [Style.NORMAL]: (xp, yp) => `M.5,1 L.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` },\n '╓': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},1 L${.5 - xp},.5 L1,.5 M${.5 + xp},.5 L${.5 + xp},1` },\n '╔': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` },\n '╕': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L.5,${.5 - yp} L.5,1 M0,${.5 + yp} L.5,${.5 + yp}` },\n '╖': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},1 L${.5 + xp},.5 L0,.5 M${.5 - xp},.5 L${.5 - xp},1` },\n '╗': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},1` },\n '╘': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 + yp} L1,${.5 + yp} M.5,${.5 - yp} L1,${.5 - yp}` },\n '╙': { [Style.NORMAL]: (xp, yp) => `M1,.5 L${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` },\n '╚': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0 M1,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},0` },\n '╛': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L.5,${.5 + yp} L.5,0 M0,${.5 - yp} L.5,${.5 - yp}` },\n '╜': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 + xp},.5 L${.5 + xp},0 M${.5 - xp},.5 L${.5 - xp},0` },\n '╝': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M0,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},0` },\n '╞': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` },\n '╟': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1 M${.5 + xp},.5 L1,.5` },\n '╠': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },\n '╡': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L.5,${.5 - yp} M0,${.5 + yp} L.5,${.5 + yp}` },\n '╢': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 - xp},.5 M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },\n '╣': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},0 L${.5 + xp},1 M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0` },\n '╤': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp} M.5,${.5 + yp} L.5,1` },\n '╥': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},1 M${.5 + xp},.5 L${.5 + xp},1` },\n '╦': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` },\n '╧': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 - yp} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },\n '╨': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` },\n '╩': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L1,${.5 + yp} M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },\n '╪': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },\n '╫': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },\n '╬': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },\n\n // Diagonal\n '╱': { [Style.NORMAL]: 'M1,0 L0,1' },\n '╲': { [Style.NORMAL]: 'M0,0 L1,1' },\n '╳': { [Style.NORMAL]: 'M1,0 L0,1 M0,0 L1,1' },\n\n // Mixed weight\n '╼': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '╽': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '╾': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '╿': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┍': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┎': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '┑': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┒': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '┕': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┖': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┙': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┚': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┝': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┞': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┟': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '┠': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },\n '┡': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT },\n '┢': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },\n '┥': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┦': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┧': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '┨': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },\n '┩': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT },\n '┪': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },\n '┭': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┮': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┯': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },\n '┰': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '┱': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },\n '┲': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },\n '┵': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┶': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┷': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },\n '┸': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '┹': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_LEFT },\n '┺': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_RIGHT },\n '┽': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },\n '┾': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}`, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },\n '┿': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },\n '╀': { [Style.NORMAL]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}`, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },\n '╁': { [Style.NORMAL]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },\n '╂': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },\n '╃': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT },\n '╄': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT },\n '╅': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },\n '╆': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },\n '╇': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}` },\n '╈': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}` },\n '╉': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}` },\n '╊': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}` },\n\n // Dashed\n '╌': { [Style.NORMAL]: Shapes.TWO_DASHES_HORIZONTAL },\n '╍': { [Style.BOLD]: Shapes.TWO_DASHES_HORIZONTAL },\n '┄': { [Style.NORMAL]: Shapes.THREE_DASHES_HORIZONTAL },\n '┅': { [Style.BOLD]: Shapes.THREE_DASHES_HORIZONTAL },\n '┈': { [Style.NORMAL]: Shapes.FOUR_DASHES_HORIZONTAL },\n '┉': { [Style.BOLD]: Shapes.FOUR_DASHES_HORIZONTAL },\n '╎': { [Style.NORMAL]: Shapes.TWO_DASHES_VERTICAL },\n '╏': { [Style.BOLD]: Shapes.TWO_DASHES_VERTICAL },\n '┆': { [Style.NORMAL]: Shapes.THREE_DASHES_VERTICAL },\n '┇': { [Style.BOLD]: Shapes.THREE_DASHES_VERTICAL },\n '┊': { [Style.NORMAL]: Shapes.FOUR_DASHES_VERTICAL },\n '┋': { [Style.BOLD]: Shapes.FOUR_DASHES_VERTICAL },\n\n // Curved\n '╭': { [Style.NORMAL]: 'C.5,1,.5,.5,1,.5' },\n '╮': { [Style.NORMAL]: 'C.5,1,.5,.5,0,.5' },\n '╯': { [Style.NORMAL]: 'C.5,0,.5,.5,0,.5' },\n '╰': { [Style.NORMAL]: 'C.5,0,.5,.5,1,.5' }\n};\n\n/**\n * Try drawing a custom block element or box drawing character, returning whether it was\n * successfully drawn.\n */\nexport function tryDrawCustomChar(\n ctx: CanvasRenderingContext2D,\n c: string,\n xOffset: number,\n yOffset: number,\n scaledCellWidth: number,\n scaledCellHeight: number\n): boolean {\n const blockElementDefinition = blockElementDefinitions[c];\n if (blockElementDefinition) {\n drawBlockElementChar(ctx, blockElementDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight);\n return true;\n }\n\n const patternDefinition = patternCharacterDefinitions[c];\n if (patternDefinition) {\n drawPatternChar(ctx, patternDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight);\n return true;\n }\n\n const boxDrawingDefinition = boxDrawingDefinitions[c];\n if (boxDrawingDefinition) {\n drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight);\n return true;\n }\n\n return false;\n}\n\nfunction drawBlockElementChar(\n ctx: CanvasRenderingContext2D,\n charDefinition: IBlockVector[],\n xOffset: number,\n yOffset: number,\n scaledCellWidth: number,\n scaledCellHeight: number\n): void {\n for (let i = 0; i < charDefinition.length; i++) {\n const box = charDefinition[i];\n const xEighth = scaledCellWidth / 8;\n const yEighth = scaledCellHeight / 8;\n ctx.fillRect(\n xOffset + box.x * xEighth,\n yOffset + box.y * yEighth,\n box.w * xEighth,\n box.h * yEighth\n );\n }\n}\n\nconst cachedPatterns: Map> = new Map();\n\nfunction drawPatternChar(\n ctx: CanvasRenderingContext2D,\n charDefinition: number[][],\n xOffset: number,\n yOffset: number,\n scaledCellWidth: number,\n scaledCellHeight: number\n): void {\n let patternSet = cachedPatterns.get(charDefinition);\n if (!patternSet) {\n patternSet = new Map();\n cachedPatterns.set(charDefinition, patternSet);\n }\n const fillStyle = ctx.fillStyle;\n if (typeof fillStyle !== 'string') {\n throw new Error(`Unexpected fillStyle type \"${fillStyle}\"`);\n }\n let pattern = patternSet.get(fillStyle);\n if (!pattern) {\n const width = charDefinition[0].length;\n const height = charDefinition.length;\n const tmpCanvas = document.createElement('canvas');\n tmpCanvas.width = width;\n tmpCanvas.height = height;\n const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));\n const imageData = new ImageData(width, height);\n\n // Extract rgba from fillStyle\n let r: number;\n let g: number;\n let b: number;\n let a: number;\n if (fillStyle.startsWith('#')) {\n r = parseInt(fillStyle.substr(1, 2), 16);\n g = parseInt(fillStyle.substr(3, 2), 16);\n b = parseInt(fillStyle.substr(5, 2), 16);\n a = fillStyle.length > 7 && parseInt(fillStyle.substr(7, 2), 16) || 1;\n } else if (fillStyle.startsWith('rgba')) {\n ([r, g, b, a] = fillStyle.substring(5, fillStyle.length - 1).split(',').map(e => parseFloat(e)));\n } else {\n throw new Error(`Unexpected fillStyle color format \"${fillStyle}\" when drawing pattern glyph`);\n }\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n imageData.data[(y * width + x) * 4 ] = r;\n imageData.data[(y * width + x) * 4 + 1] = g;\n imageData.data[(y * width + x) * 4 + 2] = b;\n imageData.data[(y * width + x) * 4 + 3] = charDefinition[y][x] * (a * 255);\n }\n }\n tmpCtx.putImageData(imageData, 0, 0);\n pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));\n patternSet.set(fillStyle, pattern);\n }\n ctx.fillStyle = pattern;\n ctx.fillRect(xOffset, yOffset, scaledCellWidth, scaledCellHeight);\n}\n\n/**\n * Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to\n * canvas draw calls.\n *\n * Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐\n * ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤\n * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘\n * ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐\n * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤\n * └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘\n *\n * Other:\n * ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈\n * │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉\n * ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋\n *\n * All box drawing characters:\n * ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏\n * ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟\n * ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯\n * ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿\n * ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏\n * ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟\n * ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯\n * ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿\n *\n * ---\n *\n * Box drawing alignment tests: █\n * ▉\n * ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳\n * ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳\n * ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳\n * ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\n * ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎\n * ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n * ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n *\n * Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html\n */\nfunction drawBoxDrawingChar(\n ctx: CanvasRenderingContext2D,\n charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) },\n xOffset: number,\n yOffset: number,\n scaledCellWidth: number,\n scaledCellHeight: number\n): void {\n ctx.strokeStyle = ctx.fillStyle;\n for (const [fontWeight, instructions] of Object.entries(charDefinition)) {\n ctx.beginPath();\n ctx.lineWidth = window.devicePixelRatio * Number.parseInt(fontWeight);\n let actualInstructions: string;\n if (typeof instructions === 'function') {\n const xp = .15;\n const yp = .15 / scaledCellHeight * scaledCellWidth;\n actualInstructions = instructions(xp, yp);\n } else {\n actualInstructions = instructions;\n }\n for (const instruction of actualInstructions.split(' ')) {\n const type = instruction[0];\n const f = svgToCanvasInstructionMap[type];\n if (!f) {\n console.error(`Could not find drawing instructions for \"${type}\"`);\n continue;\n }\n const args: string[] = instruction.substring(1).split(',');\n if (!args[0] || !args[1]) {\n continue;\n }\n f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset));\n }\n ctx.stroke();\n ctx.closePath();\n }\n}\n\nfunction clamp(value: number, max: number, min: number = 0): number {\n return Math.max(Math.min(value, max), min);\n}\n\nconst svgToCanvasInstructionMap: { [index: string]: any } = {\n 'C': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]),\n 'L': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.lineTo(args[0], args[1]),\n 'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])\n};\n\nfunction translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number): number[] {\n const result = args.map(e => parseFloat(e) || parseInt(e));\n\n if (result.length < 2) {\n throw new Error('Too few arguments for instruction');\n }\n\n for (let x = 0; x < result.length; x += 2) {\n // Translate from 0-1 to 0-cellWidth\n result[x] *= cellWidth;\n // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp\n // line at 100% devicePixelRatio\n if (result[x] !== 0) {\n result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);\n }\n // Apply the cell's offset (ie. x*cellWidth)\n result[x] += xOffset;\n }\n\n for (let y = 1; y < result.length; y += 2) {\n // Translate from 0-1 to 0-cellHeight\n result[y] *= cellHeight;\n // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp\n // line at 100% devicePixelRatio\n if (result[y] !== 0) {\n result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);\n }\n // Apply the cell's offset (ie. x*cellHeight)\n result[y] += yOffset;\n }\n\n return result;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport class GridCache {\n public cache: (T | undefined)[][];\n\n public constructor() {\n this.cache = [];\n }\n\n public resize(width: number, height: number): void {\n for (let x = 0; x < width; x++) {\n if (this.cache.length <= x) {\n this.cache.push([]);\n }\n for (let y = this.cache[x].length; y < height; y++) {\n this.cache[x].push(undefined);\n }\n this.cache[x].length = height;\n }\n this.cache.length = width;\n }\n\n public clear(): void {\n for (let x = 0; x < this.cache.length; x++) {\n for (let y = 0; y < this.cache[x].length; y++) {\n this.cache[x][y] = undefined;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDimensions } from 'browser/renderer/Types';\nimport { BaseRenderLayer } from './BaseRenderLayer';\nimport { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';\nimport { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';\nimport { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';\nimport { IBufferService, IOptionsService } from 'common/services/Services';\n\nexport class LinkRenderLayer extends BaseRenderLayer {\n private _state: ILinkifierEvent | undefined;\n\n constructor(\n container: HTMLElement,\n zIndex: number,\n colors: IColorSet,\n rendererId: number,\n linkifier: ILinkifier,\n linkifier2: ILinkifier2,\n @IBufferService bufferService: IBufferService,\n @IOptionsService optionsService: IOptionsService\n ) {\n super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);\n linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e));\n linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e));\n\n linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));\n linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));\n }\n\n public resize(dim: IRenderDimensions): void {\n super.resize(dim);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = undefined;\n }\n\n public reset(): void {\n this._clearCurrentLink();\n }\n\n private _clearCurrentLink(): void {\n if (this._state) {\n this._clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1);\n const middleRowCount = this._state.y2 - this._state.y1 - 1;\n if (middleRowCount > 0) {\n this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount);\n }\n this._clearCells(0, this._state.y2, this._state.x2, 1);\n this._state = undefined;\n }\n }\n\n private _onShowLinkUnderline(e: ILinkifierEvent): void {\n if (e.fg === INVERTED_DEFAULT_COLOR) {\n this._ctx.fillStyle = this._colors.background.css;\n } else if (e.fg && is256Color(e.fg)) {\n // 256 color support\n this._ctx.fillStyle = this._colors.ansi[e.fg].css;\n } else {\n this._ctx.fillStyle = this._colors.foreground.css;\n }\n\n if (e.y1 === e.y2) {\n // Single line link\n this._fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1);\n } else {\n // Multi-line link\n this._fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1);\n for (let y = e.y1 + 1; y < e.y2; y++) {\n this._fillBottomLineAtCells(0, y, e.cols);\n }\n this._fillBottomLineAtCells(0, e.y2, e.x2);\n }\n this._state = e;\n }\n\n private _onHideLinkUnderline(e: ILinkifierEvent): void {\n this._clearCurrentLink();\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { TextRenderLayer } from 'browser/renderer/TextRenderLayer';\nimport { SelectionRenderLayer } from 'browser/renderer/SelectionRenderLayer';\nimport { CursorRenderLayer } from 'browser/renderer/CursorRenderLayer';\nimport { IRenderLayer, IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types';\nimport { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer';\nimport { Disposable } from 'common/Lifecycle';\nimport { IColorSet, ILinkifier, ILinkifier2 } from 'browser/Types';\nimport { ICharSizeService, ICoreBrowserService } from 'browser/services/Services';\nimport { IBufferService, IOptionsService, ICoreService, IInstantiationService } from 'common/services/Services';\nimport { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\n\nlet nextRendererId = 1;\n\nexport class Renderer extends Disposable implements IRenderer {\n private _id = nextRendererId++;\n\n private _renderLayers: IRenderLayer[];\n private _devicePixelRatio: number;\n\n public dimensions: IRenderDimensions;\n\n private _onRequestRedraw = new EventEmitter();\n public get onRequestRedraw(): IEvent { return this._onRequestRedraw.event; }\n\n constructor(\n private _colors: IColorSet,\n private readonly _screenElement: HTMLElement,\n linkifier: ILinkifier,\n linkifier2: ILinkifier2,\n @IInstantiationService instantiationService: IInstantiationService,\n @IBufferService private readonly _bufferService: IBufferService,\n @ICharSizeService private readonly _charSizeService: ICharSizeService,\n @IOptionsService private readonly _optionsService: IOptionsService\n ) {\n super();\n const allowTransparency = this._optionsService.options.allowTransparency;\n this._renderLayers = [\n instantiationService.createInstance(TextRenderLayer, this._screenElement, 0, this._colors, allowTransparency, this._id),\n instantiationService.createInstance(SelectionRenderLayer, this._screenElement, 1, this._colors, this._id),\n instantiationService.createInstance(LinkRenderLayer, this._screenElement, 2, this._colors, this._id, linkifier, linkifier2),\n instantiationService.createInstance(CursorRenderLayer, this._screenElement, 3, this._colors, this._id, this._onRequestRedraw)\n ];\n this.dimensions = {\n scaledCharWidth: 0,\n scaledCharHeight: 0,\n scaledCellWidth: 0,\n scaledCellHeight: 0,\n scaledCharLeft: 0,\n scaledCharTop: 0,\n scaledCanvasWidth: 0,\n scaledCanvasHeight: 0,\n canvasWidth: 0,\n canvasHeight: 0,\n actualCellWidth: 0,\n actualCellHeight: 0\n };\n this._devicePixelRatio = window.devicePixelRatio;\n this._updateDimensions();\n this.onOptionsChanged();\n }\n\n public dispose(): void {\n for (const l of this._renderLayers) {\n l.dispose();\n }\n super.dispose();\n removeTerminalFromCache(this._id);\n }\n\n public onDevicePixelRatioChange(): void {\n // If the device pixel ratio changed, the char atlas needs to be regenerated\n // and the terminal needs to refreshed\n if (this._devicePixelRatio !== window.devicePixelRatio) {\n this._devicePixelRatio = window.devicePixelRatio;\n this.onResize(this._bufferService.cols, this._bufferService.rows);\n }\n }\n\n public setColors(colors: IColorSet): void {\n this._colors = colors;\n // Clear layers and force a full render\n for (const l of this._renderLayers) {\n l.setColors(this._colors);\n l.reset();\n }\n }\n\n public onResize(cols: number, rows: number): void {\n // Update character and canvas dimensions\n this._updateDimensions();\n\n // Resize all render layers\n for (const l of this._renderLayers) {\n l.resize(this.dimensions);\n }\n\n // Resize the screen\n this._screenElement.style.width = `${this.dimensions.canvasWidth}px`;\n this._screenElement.style.height = `${this.dimensions.canvasHeight}px`;\n }\n\n public onCharSizeChanged(): void {\n this.onResize(this._bufferService.cols, this._bufferService.rows);\n }\n\n public onBlur(): void {\n this._runOperation(l => l.onBlur());\n }\n\n public onFocus(): void {\n this._runOperation(l => l.onFocus());\n }\n\n public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {\n this._runOperation(l => l.onSelectionChanged(start, end, columnSelectMode));\n }\n\n public onCursorMove(): void {\n this._runOperation(l => l.onCursorMove());\n }\n\n public onOptionsChanged(): void {\n this._runOperation(l => l.onOptionsChanged());\n }\n\n public clear(): void {\n this._runOperation(l => l.reset());\n }\n\n private _runOperation(operation: (layer: IRenderLayer) => void): void {\n for (const l of this._renderLayers) {\n operation(l);\n }\n }\n\n /**\n * Performs the refresh loop callback, calling refresh only if a refresh is\n * necessary before queueing up the next one.\n */\n public renderRows(start: number, end: number): void {\n for (const l of this._renderLayers) {\n l.onGridChanged(start, end);\n }\n }\n\n public clearTextureAtlas(): void {\n for (const layer of this._renderLayers) {\n layer.clearTextureAtlas();\n }\n }\n\n /**\n * Recalculates the character and canvas dimensions.\n */\n private _updateDimensions(): void {\n if (!this._charSizeService.hasValidSize) {\n return;\n }\n\n // Calculate the scaled character width. Width is floored as it must be\n // drawn to an integer grid in order for the CharAtlas \"stamps\" to not be\n // blurry. When text is drawn to the grid not using the CharAtlas, it is\n // clipped to ensure there is no overlap with the next cell.\n this.dimensions.scaledCharWidth = Math.floor(this._charSizeService.width * window.devicePixelRatio);\n\n // Calculate the scaled character height. Height is ceiled in case\n // devicePixelRatio is a floating point number in order to ensure there is\n // enough space to draw the character to the cell.\n this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);\n\n // Calculate the scaled cell height, if lineHeight is not 1 then the value\n // will be floored because since lineHeight can never be lower then 1, there\n // is a guarentee that the scaled line height will always be larger than\n // scaled char height.\n this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight);\n\n // Calculate the y coordinate within a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharTop = this._optionsService.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);\n\n // Calculate the scaled cell width, taking the letterSpacing into account.\n this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing);\n\n // Calculate the x coordinate with a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharLeft = Math.floor(this._optionsService.options.letterSpacing / 2);\n\n // Recalculate the canvas dimensions; scaled* define the actual number of\n // pixel in the canvas\n this.dimensions.scaledCanvasHeight = this._bufferService.rows * this.dimensions.scaledCellHeight;\n this.dimensions.scaledCanvasWidth = this._bufferService.cols * this.dimensions.scaledCellWidth;\n\n // The the size of the canvas on the page. It's very important that this\n // rounds to nearest integer and not ceils as browsers often set\n // window.devicePixelRatio as something like 1.100000023841858, when it's\n // actually 1.1. Ceiling causes blurriness as the backing canvas image is 1\n // pixel too large for the canvas element size.\n this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);\n this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);\n\n // Get the _actual_ dimensions of an individual cell. This needs to be\n // derived from the canvasWidth/Height calculated above which takes into\n // account window.devicePixelRatio. ICharSizeService.width/height by itself\n // is insufficient when the page is not at 100% zoom level as it's measured\n // in CSS pixels, but the actual char size on the canvas can differ.\n this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows;\n this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols;\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport function throwIfFalsy(value: T | undefined | null): T {\n if (!value) {\n throw new Error('value must not be falsy');\n }\n return value;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDimensions } from 'browser/renderer/Types';\nimport { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';\nimport { IColorSet } from 'browser/Types';\nimport { IBufferService, IOptionsService } from 'common/services/Services';\n\ninterface ISelectionState {\n start?: [number, number];\n end?: [number, number];\n columnSelectMode?: boolean;\n ydisp?: number;\n}\n\nexport class SelectionRenderLayer extends BaseRenderLayer {\n private _state!: ISelectionState;\n\n constructor(\n container: HTMLElement,\n zIndex: number,\n colors: IColorSet,\n rendererId: number,\n @IBufferService bufferService: IBufferService,\n @IOptionsService optionsService: IOptionsService\n ) {\n super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService);\n this._clearState();\n }\n\n private _clearState(): void {\n this._state = {\n start: undefined,\n end: undefined,\n columnSelectMode: undefined,\n ydisp: undefined\n };\n }\n\n public resize(dim: IRenderDimensions): void {\n super.resize(dim);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._clearState();\n }\n\n public reset(): void {\n if (this._state.start && this._state.end) {\n this._clearState();\n this._clearAll();\n }\n }\n\n public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {\n // Selection has not changed\n if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {\n return;\n }\n\n // Remove all selections\n this._clearAll();\n\n // Selection does not exist\n if (!start || !end) {\n this._clearState();\n return;\n }\n\n // Translate from buffer position to viewport position\n const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;\n const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;\n const viewportCappedStartRow = Math.max(viewportStartRow, 0);\n const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);\n\n // No need to draw the selection\n if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {\n this._state.ydisp = this._bufferService.buffer.ydisp;\n return;\n }\n\n this._ctx.fillStyle = this._colors.selectionTransparent.css;\n\n if (columnSelectMode) {\n const startCol = start[0];\n const width = end[0] - startCol;\n const height = viewportCappedEndRow - viewportCappedStartRow + 1;\n this._fillCells(startCol, viewportCappedStartRow, width, height);\n } else {\n // Draw first row\n const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;\n const startRowEndCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;\n this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);\n\n // Draw middle rows\n const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);\n this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount);\n\n // Draw final row\n if (viewportCappedStartRow !== viewportCappedEndRow) {\n // Only draw viewportEndRow if it's not the same as viewportStartRow\n const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;\n this._fillCells(0, viewportCappedEndRow, endCol, 1);\n }\n }\n\n // Save state for next render\n this._state.start = [start[0], start[1]];\n this._state.end = [end[0], end[1]];\n this._state.columnSelectMode = columnSelectMode;\n this._state.ydisp = this._bufferService.buffer.ydisp;\n }\n\n private _didStateChange(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean, ydisp: number): boolean {\n return !this._areCoordinatesEqual(start, this._state.start) ||\n !this._areCoordinatesEqual(end, this._state.end) ||\n columnSelectMode !== this._state.columnSelectMode ||\n ydisp !== this._state.ydisp;\n }\n\n private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean {\n if (!coord1 || !coord2) {\n return false;\n }\n\n return coord1[0] === coord2[0] && coord1[1] === coord2[1];\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderDimensions } from 'browser/renderer/Types';\nimport { CharData, ICellData } from 'common/Types';\nimport { GridCache } from 'browser/renderer/GridCache';\nimport { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';\nimport { AttributeData } from 'common/buffer/AttributeData';\nimport { NULL_CELL_CODE, Content } from 'common/buffer/Constants';\nimport { IColorSet } from 'browser/Types';\nimport { CellData } from 'common/buffer/CellData';\nimport { IOptionsService, IBufferService } from 'common/services/Services';\nimport { ICharacterJoinerService } from 'browser/services/Services';\nimport { JoinedCellData } from 'browser/services/CharacterJoinerService';\n\n/**\n * This CharData looks like a null character, which will forc a clear and render\n * when the character changes (a regular space ' ' character may not as it's\n * drawn state is a cleared cell).\n */\n// const OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1];\n\nexport class TextRenderLayer extends BaseRenderLayer {\n private _state: GridCache;\n private _characterWidth: number = 0;\n private _characterFont: string = '';\n private _characterOverlapCache: { [key: string]: boolean } = {};\n private _workCell = new CellData();\n\n constructor(\n container: HTMLElement,\n zIndex: number,\n colors: IColorSet,\n alpha: boolean,\n rendererId: number,\n @IBufferService bufferService: IBufferService,\n @IOptionsService optionsService: IOptionsService,\n @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService\n ) {\n super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService);\n this._state = new GridCache();\n }\n\n public resize(dim: IRenderDimensions): void {\n super.resize(dim);\n\n // Clear the character width cache if the font or width has changed\n const terminalFont = this._getFont(false, false);\n if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {\n this._characterWidth = dim.scaledCharWidth;\n this._characterFont = terminalFont;\n this._characterOverlapCache = {};\n }\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state.clear();\n this._state.resize(this._bufferService.cols, this._bufferService.rows);\n }\n\n public reset(): void {\n this._state.clear();\n this._clearAll();\n }\n\n private _forEachCell(\n firstRow: number,\n lastRow: number,\n callback: (\n cell: ICellData,\n x: number,\n y: number\n ) => void\n ): void {\n for (let y = firstRow; y <= lastRow; y++) {\n const row = y + this._bufferService.buffer.ydisp;\n const line = this._bufferService.buffer.lines.get(row);\n const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);\n for (let x = 0; x < this._bufferService.cols; x++) {\n line!.loadCell(x, this._workCell);\n let cell = this._workCell;\n\n // If true, indicates that the current character(s) to draw were joined.\n let isJoined = false;\n let lastCharX = x;\n\n // The character to the left is a wide character, drawing is owned by\n // the char at x-1\n if (cell.getWidth() === 0) {\n continue;\n }\n\n // Process any joined character ranges as needed. Because of how the\n // ranges are produced, we know that they are valid for the characters\n // and attributes of our input.\n if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {\n isJoined = true;\n const range = joinedRanges.shift()!;\n\n // We already know the exact start and end column of the joined range,\n // so we get the string and width representing it directly\n cell = new JoinedCellData(\n this._workCell,\n line!.translateToString(true, range[0], range[1]),\n range[1] - range[0]\n );\n\n // Skip over the cells occupied by this range in the loop\n lastCharX = range[1] - 1;\n }\n\n // If the character is an overlapping char and the character to the\n // right is a space, take ownership of the cell to the right. We skip\n // this check for joined characters because their rendering likely won't\n // yield the same result as rendering the last character individually.\n if (!isJoined && this._isOverlapping(cell)) {\n // If the character is overlapping, we want to force a re-render on every\n // frame. This is specifically to work around the case where two\n // overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a\n // space is added. Without this, the first half of `b` would never\n // get removed, and `a` would not re-render because it thinks it's\n // already in the correct state.\n // this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;\n if (lastCharX < line!.length - 1 && line!.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {\n // patch width to 2\n cell.content &= ~Content.WIDTH_MASK;\n cell.content |= 2 << Content.WIDTH_SHIFT;\n // this._clearChar(x + 1, y);\n // The overlapping char's char data will force a clear and render when the\n // overlapping char is no longer to the left of the character and also when\n // the space changes to another character.\n // this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;\n }\n }\n\n callback(\n cell,\n x,\n y\n );\n\n x = lastCharX;\n }\n }\n }\n\n /**\n * Draws the background for a specified range of columns. Tries to batch adjacent cells of the\n * same color together to reduce draw calls.\n */\n private _drawBackground(firstRow: number, lastRow: number): void {\n const ctx = this._ctx;\n const cols = this._bufferService.cols;\n let startX: number = 0;\n let startY: number = 0;\n let prevFillStyle: string | null = null;\n\n ctx.save();\n\n this._forEachCell(firstRow, lastRow, (cell, x, y) => {\n // libvte and xterm both draw the background (but not foreground) of invisible characters,\n // so we should too.\n let nextFillStyle = null; // null represents default background color\n\n if (cell.isInverse()) {\n if (cell.isFgDefault()) {\n nextFillStyle = this._colors.foreground.css;\n } else if (cell.isFgRGB()) {\n nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;\n } else {\n nextFillStyle = this._colors.ansi[cell.getFgColor()].css;\n }\n } else if (cell.isBgRGB()) {\n nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;\n } else if (cell.isBgPalette()) {\n nextFillStyle = this._colors.ansi[cell.getBgColor()].css;\n }\n\n if (prevFillStyle === null) {\n // This is either the first iteration, or the default background was set. Either way, we\n // don't need to draw anything.\n startX = x;\n startY = y;\n }\n\n if (y !== startY) {\n // our row changed, draw the previous row\n ctx.fillStyle = prevFillStyle || '';\n this._fillCells(startX, startY, cols - startX, 1);\n startX = x;\n startY = y;\n } else if (prevFillStyle !== nextFillStyle) {\n // our color changed, draw the previous characters in this row\n ctx.fillStyle = prevFillStyle || '';\n this._fillCells(startX, startY, x - startX, 1);\n startX = x;\n startY = y;\n }\n\n prevFillStyle = nextFillStyle;\n });\n\n // flush the last color we encountered\n if (prevFillStyle !== null) {\n ctx.fillStyle = prevFillStyle;\n this._fillCells(startX, startY, cols - startX, 1);\n }\n\n ctx.restore();\n }\n\n private _drawForeground(firstRow: number, lastRow: number): void {\n this._forEachCell(firstRow, lastRow, (cell, x, y) => {\n if (cell.isInvisible()) {\n return;\n }\n this._drawChars(cell, x, y);\n if (cell.isUnderline() || cell.isStrikethrough()) {\n this._ctx.save();\n\n if (cell.isInverse()) {\n if (cell.isBgDefault()) {\n this._ctx.fillStyle = this._colors.background.css;\n } else if (cell.isBgRGB()) {\n this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;\n } else {\n let bg = cell.getBgColor();\n if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {\n bg += 8;\n }\n this._ctx.fillStyle = this._colors.ansi[bg].css;\n }\n } else {\n if (cell.isFgDefault()) {\n this._ctx.fillStyle = this._colors.foreground.css;\n } else if (cell.isFgRGB()) {\n this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;\n } else {\n let fg = cell.getFgColor();\n if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {\n fg += 8;\n }\n this._ctx.fillStyle = this._colors.ansi[fg].css;\n }\n }\n\n if (cell.isStrikethrough()) {\n this._fillMiddleLineAtCells(x, y, cell.getWidth());\n }\n if (cell.isUnderline()) {\n this._fillBottomLineAtCells(x, y, cell.getWidth());\n }\n this._ctx.restore();\n }\n });\n }\n\n public onGridChanged(firstRow: number, lastRow: number): void {\n // Resize has not been called yet\n if (this._state.cache.length === 0) {\n return;\n }\n\n if (this._charAtlas) {\n this._charAtlas.beginFrame();\n }\n\n this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1);\n this._drawBackground(firstRow, lastRow);\n this._drawForeground(firstRow, lastRow);\n }\n\n public onOptionsChanged(): void {\n this._setTransparency(this._optionsService.options.allowTransparency);\n }\n\n /**\n * Whether a character is overlapping to the next cell.\n */\n private _isOverlapping(cell: ICellData): boolean {\n // Only single cell characters can be overlapping, rendering issues can\n // occur without this check\n if (cell.getWidth() !== 1) {\n return false;\n }\n\n // We assume that any ascii character will not overlap\n if (cell.getCode() < 256) {\n return false;\n }\n\n const chars = cell.getChars();\n\n // Deliver from cache if available\n if (this._characterOverlapCache.hasOwnProperty(chars)) {\n return this._characterOverlapCache[chars];\n }\n\n // Setup the font\n this._ctx.save();\n this._ctx.font = this._characterFont;\n\n // Measure the width of the character, but Math.floor it\n // because that is what the renderer does when it calculates\n // the character dimensions we are comparing against\n const overlaps = Math.floor(this._ctx.measureText(chars).width) > this._characterWidth;\n\n // Restore the original context\n this._ctx.restore();\n\n // Cache and return\n this._characterOverlapCache[chars] = overlaps;\n return overlaps;\n }\n\n /**\n * Clear the charcater at the cell specified.\n * @param x The column of the char.\n * @param y The row of the char.\n */\n // private _clearChar(x: number, y: number): void {\n // let colsToClear = 1;\n // // Clear the adjacent character if it was wide\n // const state = this._state.cache[x][y];\n // if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) {\n // colsToClear = 2;\n // }\n // this.clearCells(x, y, colsToClear, 1);\n // }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IGlyphIdentifier } from 'browser/renderer/atlas/Types';\nimport { IDisposable } from 'common/Types';\n\nexport abstract class BaseCharAtlas implements IDisposable {\n private _didWarmUp: boolean = false;\n\n public dispose(): void { }\n\n /**\n * Perform any work needed to warm the cache before it can be used. May be called multiple times.\n * Implement _doWarmUp instead if you only want to get called once.\n */\n public warmUp(): void {\n if (!this._didWarmUp) {\n this._doWarmUp();\n this._didWarmUp = true;\n }\n }\n\n /**\n * Perform any work needed to warm the cache before it can be used. Used by the default\n * implementation of warmUp(), and will only be called once.\n */\n private _doWarmUp(): void { }\n\n public clear(): void { }\n\n /**\n * Called when we start drawing a new frame.\n *\n * TODO: We rely on this getting called by TextRenderLayer. This should really be called by\n * Renderer instead, but we need to make Renderer the source-of-truth for the char atlas, instead\n * of BaseRenderLayer.\n */\n public beginFrame(): void { }\n\n /**\n * May be called before warmUp finishes, however it is okay for the implementation to\n * do nothing and return false in that case.\n *\n * @param ctx Where to draw the character onto.\n * @param glyph Information about what to draw\n * @param x The position on the context to start drawing at\n * @param y The position on the context to start drawing at\n * @returns The success state. True if we drew the character.\n */\n public abstract draw(\n ctx: CanvasRenderingContext2D,\n glyph: IGlyphIdentifier,\n x: number,\n y: number\n ): boolean;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils';\nimport { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';\nimport { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas';\nimport { ICharAtlasConfig } from 'browser/renderer/atlas/Types';\nimport { IColorSet } from 'browser/Types';\nimport { ITerminalOptions } from 'common/services/Services';\n\ninterface ICharAtlasCacheEntry {\n atlas: BaseCharAtlas;\n config: ICharAtlasConfig;\n // N.B. This implementation potentially holds onto copies of the terminal forever, so\n // this may cause memory leaks.\n ownedBy: number[];\n}\n\nconst charAtlasCache: ICharAtlasCacheEntry[] = [];\n\n/**\n * Acquires a char atlas, either generating a new one or returning an existing\n * one that is in use by another terminal.\n */\nexport function acquireCharAtlas(\n options: ITerminalOptions,\n rendererId: number,\n colors: IColorSet,\n scaledCharWidth: number,\n scaledCharHeight: number\n): BaseCharAtlas {\n const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors);\n\n // Check to see if the renderer already owns this config\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n const ownedByIndex = entry.ownedBy.indexOf(rendererId);\n if (ownedByIndex >= 0) {\n if (configEquals(entry.config, newConfig)) {\n return entry.atlas;\n }\n // The configs differ, release the renderer from the entry\n if (entry.ownedBy.length === 1) {\n entry.atlas.dispose();\n charAtlasCache.splice(i, 1);\n } else {\n entry.ownedBy.splice(ownedByIndex, 1);\n }\n break;\n }\n }\n\n // Try match a char atlas from the cache\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n if (configEquals(entry.config, newConfig)) {\n // Add the renderer to the cache entry and return\n entry.ownedBy.push(rendererId);\n return entry.atlas;\n }\n }\n\n const newEntry: ICharAtlasCacheEntry = {\n atlas: new DynamicCharAtlas(\n document,\n newConfig\n ),\n config: newConfig,\n ownedBy: [rendererId]\n };\n charAtlasCache.push(newEntry);\n return newEntry.atlas;\n}\n\n/**\n * Removes a terminal reference from the cache, allowing its memory to be freed.\n */\nexport function removeTerminalFromCache(rendererId: number): void {\n for (let i = 0; i < charAtlasCache.length; i++) {\n const index = charAtlasCache[i].ownedBy.indexOf(rendererId);\n if (index !== -1) {\n if (charAtlasCache[i].ownedBy.length === 1) {\n // Remove the cache entry if it's the only renderer\n charAtlasCache[i].atlas.dispose();\n charAtlasCache.splice(i, 1);\n } else {\n // Remove the reference from the cache entry\n charAtlasCache[i].ownedBy.splice(index, 1);\n }\n break;\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICharAtlasConfig } from 'browser/renderer/atlas/Types';\nimport { DEFAULT_COLOR } from 'common/buffer/Constants';\nimport { IColorSet, IPartialColorSet } from 'browser/Types';\nimport { ITerminalOptions } from 'common/services/Services';\n\nexport function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig {\n // null out some fields that don't matter\n const clonedColors: IPartialColorSet = {\n foreground: colors.foreground,\n background: colors.background,\n cursor: undefined,\n cursorAccent: undefined,\n selection: undefined,\n ansi: [...colors.ansi]\n };\n return {\n devicePixelRatio: window.devicePixelRatio,\n scaledCharWidth,\n scaledCharHeight,\n fontFamily: options.fontFamily,\n fontSize: options.fontSize,\n fontWeight: options.fontWeight,\n fontWeightBold: options.fontWeightBold,\n allowTransparency: options.allowTransparency,\n colors: clonedColors\n };\n}\n\nexport function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {\n for (let i = 0; i < a.colors.ansi.length; i++) {\n if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) {\n return false;\n }\n }\n return a.devicePixelRatio === b.devicePixelRatio &&\n a.fontFamily === b.fontFamily &&\n a.fontSize === b.fontSize &&\n a.fontWeight === b.fontWeight &&\n a.fontWeightBold === b.fontWeightBold &&\n a.allowTransparency === b.allowTransparency &&\n a.scaledCharWidth === b.scaledCharWidth &&\n a.scaledCharHeight === b.scaledCharHeight &&\n a.colors.foreground === b.colors.foreground &&\n a.colors.background === b.colors.background;\n}\n\nexport function is256Color(colorCode: number): boolean {\n return colorCode < DEFAULT_COLOR;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { isFirefox } from 'common/Platform';\n\nexport const INVERTED_DEFAULT_COLOR = 257;\nexport const DIM_OPACITY = 0.5;\n// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox would\n// result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly\n// unaligned Powerline fonts (PR 3356#issuecomment-850928179).\nexport const TEXT_BASELINE: CanvasTextBaseline = isFirefox ? 'bottom' : 'ideographic';\n\nexport const CHAR_ATLAS_CELL_SPACING = 1;\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { DIM_OPACITY, INVERTED_DEFAULT_COLOR, TEXT_BASELINE } from 'browser/renderer/atlas/Constants';\nimport { IGlyphIdentifier, ICharAtlasConfig } from 'browser/renderer/atlas/Types';\nimport { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';\nimport { DEFAULT_ANSI_COLORS } from 'browser/ColorManager';\nimport { LRUMap } from 'browser/renderer/atlas/LRUMap';\nimport { isFirefox, isSafari } from 'common/Platform';\nimport { IColor } from 'browser/Types';\nimport { throwIfFalsy } from 'browser/renderer/RendererUtils';\nimport { color } from 'browser/Color';\n\n// In practice we're probably never going to exhaust a texture this large. For debugging purposes,\n// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works.\nconst TEXTURE_WIDTH = 1024;\nconst TEXTURE_HEIGHT = 1024;\n\nconst TRANSPARENT_COLOR = {\n css: 'rgba(0, 0, 0, 0)',\n rgba: 0\n};\n\n// Drawing to the cache is expensive: If we have to draw more than this number of glyphs to the\n// cache in a single frame, give up on trying to cache anything else, and try to finish the current\n// frame ASAP.\n//\n// This helps to limit the amount of damage a program can do when it would otherwise thrash the\n// cache.\nconst FRAME_CACHE_DRAW_LIMIT = 100;\n\n/**\n * The number of milliseconds to wait before generating the ImageBitmap, this is to debounce/batch\n * the operation as window.createImageBitmap is asynchronous.\n */\nconst GLYPH_BITMAP_COMMIT_DELAY = 100;\n\ninterface IGlyphCacheValue {\n index: number;\n isEmpty: boolean;\n inBitmap: boolean;\n}\n\nexport function getGlyphCacheKey(glyph: IGlyphIdentifier): number {\n // Note that this only returns a valid key when code < 256\n // Layout:\n // 0b00000000000000000000000000000001: italic (1)\n // 0b00000000000000000000000000000010: dim (1)\n // 0b00000000000000000000000000000100: bold (1)\n // 0b00000000000000000000111111111000: fg (9)\n // 0b00000000000111111111000000000000: bg (9)\n // 0b00011111111000000000000000000000: code (8)\n // 0b11100000000000000000000000000000: unused (3)\n return glyph.code << 21 | glyph.bg << 12 | glyph.fg << 3 | (glyph.bold ? 0 : 4) + (glyph.dim ? 0 : 2) + (glyph.italic ? 0 : 1);\n}\n\nexport class DynamicCharAtlas extends BaseCharAtlas {\n // An ordered map that we're using to keep track of where each glyph is in the atlas texture.\n // It's ordered so that we can determine when to remove the old entries.\n private _cacheMap: LRUMap;\n\n // The texture that the atlas is drawn to\n private _cacheCanvas: HTMLCanvasElement;\n private _cacheCtx: CanvasRenderingContext2D;\n\n // A temporary context that glyphs are drawn to before being transfered to the atlas.\n private _tmpCtx: CanvasRenderingContext2D;\n\n // The number of characters stored in the atlas by width/height\n private _width: number;\n private _height: number;\n\n private _drawToCacheCount: number = 0;\n\n // An array of glyph keys that are waiting on the bitmap to be generated.\n private _glyphsWaitingOnBitmap: IGlyphCacheValue[] = [];\n\n // The timeout that is used to batch bitmap generation so it's not requested for every new glyph.\n private _bitmapCommitTimeout: number | null = null;\n\n // The bitmap to draw from, this is much faster on other browsers than others.\n private _bitmap: ImageBitmap | null = null;\n\n constructor(document: Document, private _config: ICharAtlasConfig) {\n super();\n this._cacheCanvas = document.createElement('canvas');\n this._cacheCanvas.width = TEXTURE_WIDTH;\n this._cacheCanvas.height = TEXTURE_HEIGHT;\n // The canvas needs alpha because we use clearColor to convert the background color to alpha.\n // It might also contain some characters with transparent backgrounds if allowTransparency is\n // set.\n this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', { alpha: true }));\n\n const tmpCanvas = document.createElement('canvas');\n tmpCanvas.width = this._config.scaledCharWidth;\n tmpCanvas.height = this._config.scaledCharHeight;\n this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency }));\n\n this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth);\n this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);\n const capacity = this._width * this._height;\n this._cacheMap = new LRUMap(capacity);\n this._cacheMap.prealloc(capacity);\n\n // This is useful for debugging\n // document.body.appendChild(this._cacheCanvas);\n }\n\n public dispose(): void {\n if (this._bitmapCommitTimeout !== null) {\n window.clearTimeout(this._bitmapCommitTimeout);\n this._bitmapCommitTimeout = null;\n }\n }\n\n public beginFrame(): void {\n this._drawToCacheCount = 0;\n }\n\n public clear(): void {\n if (this._cacheMap.size > 0) {\n const capacity = this._width * this._height;\n this._cacheMap = new LRUMap(capacity);\n this._cacheMap.prealloc(capacity);\n }\n this._cacheCtx.clearRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT);\n this._tmpCtx.clearRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight);\n }\n\n public draw(\n ctx: CanvasRenderingContext2D,\n glyph: IGlyphIdentifier,\n x: number,\n y: number\n ): boolean {\n // Space is always an empty cell, special case this as it's so common\n if (glyph.code === 32) {\n return true;\n }\n\n // Exit early for uncachable glyphs\n if (!this._canCache(glyph)) {\n return false;\n }\n\n const glyphKey = getGlyphCacheKey(glyph);\n const cacheValue = this._cacheMap.get(glyphKey);\n if (cacheValue !== null && cacheValue !== undefined) {\n this._drawFromCache(ctx, cacheValue, x, y);\n return true;\n }\n if (this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {\n let index;\n if (this._cacheMap.size < this._cacheMap.capacity) {\n index = this._cacheMap.size;\n } else {\n // we're out of space, so our call to set will delete this item\n index = this._cacheMap.peek()!.index;\n }\n const cacheValue = this._drawToCache(glyph, index);\n this._cacheMap.set(glyphKey, cacheValue);\n this._drawFromCache(ctx, cacheValue, x, y);\n return true;\n }\n return false;\n }\n\n private _canCache(glyph: IGlyphIdentifier): boolean {\n // Only cache ascii and extended characters for now, to be safe. In the future, we could do\n // something more complicated to determine the expected width of a character.\n //\n // If we switch the renderer over to webgl at some point, we may be able to use blending modes\n // to draw overlapping glyphs from the atlas:\n // https://github.com/servo/webrender/issues/464#issuecomment-255632875\n // https://webglfundamentals.org/webgl/lessons/webgl-text-texture.html\n return glyph.code < 256;\n }\n\n private _toCoordinateX(index: number): number {\n return (index % this._width) * this._config.scaledCharWidth;\n }\n\n private _toCoordinateY(index: number): number {\n return Math.floor(index / this._width) * this._config.scaledCharHeight;\n }\n\n private _drawFromCache(\n ctx: CanvasRenderingContext2D,\n cacheValue: IGlyphCacheValue,\n x: number,\n y: number\n ): void {\n // We don't actually need to do anything if this is whitespace.\n if (cacheValue.isEmpty) {\n return;\n }\n const cacheX = this._toCoordinateX(cacheValue.index);\n const cacheY = this._toCoordinateY(cacheValue.index);\n ctx.drawImage(\n cacheValue.inBitmap ? this._bitmap! : this._cacheCanvas,\n cacheX,\n cacheY,\n this._config.scaledCharWidth,\n this._config.scaledCharHeight,\n x,\n y,\n this._config.scaledCharWidth,\n this._config.scaledCharHeight\n );\n }\n\n private _getColorFromAnsiIndex(idx: number): IColor {\n if (idx < this._config.colors.ansi.length) {\n return this._config.colors.ansi[idx];\n }\n return DEFAULT_ANSI_COLORS[idx];\n }\n\n private _getBackgroundColor(glyph: IGlyphIdentifier): IColor {\n if (this._config.allowTransparency) {\n // The background color might have some transparency, so we need to render it as fully\n // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice\n // around the anti-aliased edges of the glyph, and it would look too dark.\n return TRANSPARENT_COLOR;\n }\n if (glyph.bg === INVERTED_DEFAULT_COLOR) {\n return this._config.colors.foreground;\n }\n if (glyph.bg < 256) {\n return this._getColorFromAnsiIndex(glyph.bg);\n }\n return this._config.colors.background;\n }\n\n private _getForegroundColor(glyph: IGlyphIdentifier): IColor {\n if (glyph.fg === INVERTED_DEFAULT_COLOR) {\n return color.opaque(this._config.colors.background);\n }\n if (glyph.fg < 256) {\n // 256 color support\n return this._getColorFromAnsiIndex(glyph.fg);\n }\n return this._config.colors.foreground;\n }\n\n // TODO: We do this (or something similar) in multiple places. We should split this off\n // into a shared function.\n private _drawToCache(glyph: IGlyphIdentifier, index: number): IGlyphCacheValue {\n this._drawToCacheCount++;\n\n this._tmpCtx.save();\n\n // draw the background\n const backgroundColor = this._getBackgroundColor(glyph);\n // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, regardless of\n // transparency in backgroundColor\n this._tmpCtx.globalCompositeOperation = 'copy';\n this._tmpCtx.fillStyle = backgroundColor.css;\n this._tmpCtx.fillRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight);\n this._tmpCtx.globalCompositeOperation = 'source-over';\n\n // draw the foreground/glyph\n const fontWeight = glyph.bold ? this._config.fontWeightBold : this._config.fontWeight;\n const fontStyle = glyph.italic ? 'italic' : '';\n this._tmpCtx.font =\n `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;\n this._tmpCtx.textBaseline = TEXT_BASELINE;\n\n this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css;\n\n // Apply alpha to dim the character\n if (glyph.dim) {\n this._tmpCtx.globalAlpha = DIM_OPACITY;\n }\n // Draw the character\n this._tmpCtx.fillText(glyph.chars, 0, this._config.scaledCharHeight);\n\n // clear the background from the character to avoid issues with drawing over the previous\n // character if it extends past it's bounds\n let imageData = this._tmpCtx.getImageData(\n 0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight\n );\n let isEmpty = false;\n if (!this._config.allowTransparency) {\n isEmpty = clearColor(imageData, backgroundColor);\n }\n\n // If this charcater is underscore and empty, shift it up until it is visible, try for a maximum\n // of 5 pixels.\n if (isEmpty && glyph.chars === '_' && !this._config.allowTransparency) {\n for (let offset = 1; offset <= 5; offset++) {\n // Draw the character\n this._tmpCtx.fillText(glyph.chars, 0, this._config.scaledCharHeight - offset);\n\n // clear the background from the character to avoid issues with drawing over the previous\n // character if it extends past it's bounds\n imageData = this._tmpCtx.getImageData(\n 0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight\n );\n isEmpty = clearColor(imageData, backgroundColor);\n if (!isEmpty) {\n break;\n }\n }\n }\n\n this._tmpCtx.restore();\n\n // copy the data from imageData to _cacheCanvas\n const x = this._toCoordinateX(index);\n const y = this._toCoordinateY(index);\n // putImageData doesn't do any blending, so it will overwrite any existing cache entry for us\n this._cacheCtx.putImageData(imageData, x, y);\n\n // Add the glyph and queue it to the bitmap (if the browser supports it)\n const cacheValue = {\n index,\n isEmpty,\n inBitmap: false\n };\n this._addGlyphToBitmap(cacheValue);\n\n return cacheValue;\n }\n\n private _addGlyphToBitmap(cacheValue: IGlyphCacheValue): void {\n // Support is patchy for createImageBitmap at the moment, pass a canvas back\n // if support is lacking as drawImage works there too. Firefox is also\n // included here as ImageBitmap appears both buggy and has horrible\n // performance (tested on v55).\n if (!('createImageBitmap' in window) || isFirefox || isSafari) {\n return;\n }\n\n // Add the glyph to the queue\n this._glyphsWaitingOnBitmap.push(cacheValue);\n\n // Check if bitmap generation timeout already exists\n if (this._bitmapCommitTimeout !== null) {\n return;\n }\n\n this._bitmapCommitTimeout = window.setTimeout(() => this._generateBitmap(), GLYPH_BITMAP_COMMIT_DELAY);\n }\n\n private _generateBitmap(): void {\n const glyphsMovingToBitmap = this._glyphsWaitingOnBitmap;\n this._glyphsWaitingOnBitmap = [];\n window.createImageBitmap(this._cacheCanvas).then(bitmap => {\n // Set bitmap\n this._bitmap = bitmap;\n\n // Mark all new glyphs as in bitmap, excluding glyphs that came in after\n // the bitmap was requested\n for (let i = 0; i < glyphsMovingToBitmap.length; i++) {\n const value = glyphsMovingToBitmap[i];\n // It doesn't matter if the value was already evicted, it will be\n // released from memory after this block if so.\n value.inBitmap = true;\n }\n });\n this._bitmapCommitTimeout = null;\n }\n}\n\n// This is used for debugging the renderer, just swap out `new DynamicCharAtlas` with\n// `new NoneCharAtlas`.\nexport class NoneCharAtlas extends BaseCharAtlas {\n constructor(document: Document, config: ICharAtlasConfig) {\n super();\n }\n\n public draw(\n ctx: CanvasRenderingContext2D,\n glyph: IGlyphIdentifier,\n x: number,\n y: number\n ): boolean {\n return false;\n }\n}\n\n/**\n * Makes a partiicular rgb color in an ImageData completely transparent.\n * @returns True if the result is \"empty\", meaning all pixels are fully transparent.\n */\nfunction clearColor(imageData: ImageData, color: IColor): boolean {\n let isEmpty = true;\n const r = color.rgba >>> 24;\n const g = color.rgba >>> 16 & 0xFF;\n const b = color.rgba >>> 8 & 0xFF;\n for (let offset = 0; offset < imageData.data.length; offset += 4) {\n if (imageData.data[offset] === r &&\n imageData.data[offset + 1] === g &&\n imageData.data[offset + 2] === b) {\n imageData.data[offset + 3] = 0;\n } else {\n isEmpty = false;\n }\n }\n return isEmpty;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\ninterface ILinkedListNode {\n prev: ILinkedListNode | null;\n next: ILinkedListNode | null;\n key: number | null;\n value: T | null;\n}\n\nexport class LRUMap {\n private _map: { [key: number]: ILinkedListNode } = {};\n private _head: ILinkedListNode | null = null;\n private _tail: ILinkedListNode | null = null;\n private _nodePool: ILinkedListNode[] = [];\n public size: number = 0;\n\n constructor(public capacity: number) { }\n\n private _unlinkNode(node: ILinkedListNode): void {\n const prev = node.prev;\n const next = node.next;\n if (node === this._head) {\n this._head = next;\n }\n if (node === this._tail) {\n this._tail = prev;\n }\n if (prev !== null) {\n prev.next = next;\n }\n if (next !== null) {\n next.prev = prev;\n }\n }\n\n private _appendNode(node: ILinkedListNode): void {\n const tail = this._tail;\n if (tail !== null) {\n tail.next = node;\n }\n node.prev = tail;\n node.next = null;\n this._tail = node;\n if (this._head === null) {\n this._head = node;\n }\n }\n\n /**\n * Preallocate a bunch of linked-list nodes. Allocating these nodes ahead of time means that\n * they're more likely to live next to each other in memory, which seems to improve performance.\n *\n * Each empty object only consumes about 60 bytes of memory, so this is pretty cheap, even for\n * large maps.\n */\n public prealloc(count: number): void {\n const nodePool = this._nodePool;\n for (let i = 0; i < count; i++) {\n nodePool.push({\n prev: null,\n next: null,\n key: null,\n value: null\n });\n }\n }\n\n public get(key: number): T | null {\n // This is unsafe: We're assuming our keyspace doesn't overlap with Object.prototype. However,\n // it's faster than calling hasOwnProperty, and in our case, it would never overlap.\n const node = this._map[key];\n if (node !== undefined) {\n this._unlinkNode(node);\n this._appendNode(node);\n return node.value;\n }\n return null;\n }\n\n /**\n * Gets a value from a key without marking it as the most recently used item.\n */\n public peekValue(key: number): T | null {\n const node = this._map[key];\n if (node !== undefined) {\n return node.value;\n }\n return null;\n }\n\n public peek(): T | null {\n const head = this._head;\n return head === null ? null : head.value;\n }\n\n public set(key: number, value: T): void {\n // This is unsafe: See note above.\n let node = this._map[key];\n if (node !== undefined) {\n // already exists, we just need to mutate it and move it to the end of the list\n node = this._map[key];\n this._unlinkNode(node);\n node.value = value;\n } else if (this.size >= this.capacity) {\n // we're out of space: recycle the head node, move it to the tail\n node = this._head!;\n this._unlinkNode(node);\n delete this._map[node.key!];\n node.key = key;\n node.value = value;\n this._map[key] = node;\n } else {\n // make a new element\n const nodePool = this._nodePool;\n if (nodePool.length > 0) {\n // use a preallocated node if we can\n node = nodePool.pop()!;\n node.key = key;\n node.value = value;\n } else {\n node = {\n prev: null,\n next: null,\n key,\n value\n };\n }\n this._map[key] = node;\n this.size++;\n }\n this._appendNode(node);\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types';\nimport { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';\nimport { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';\nimport { Disposable } from 'common/Lifecycle';\nimport { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';\nimport { ICharSizeService } from 'browser/services/Services';\nimport { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { color } from 'browser/Color';\nimport { removeElementFromParent } from 'browser/Dom';\n\nconst TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';\nconst ROW_CONTAINER_CLASS = 'xterm-rows';\nconst FG_CLASS_PREFIX = 'xterm-fg-';\nconst BG_CLASS_PREFIX = 'xterm-bg-';\nconst FOCUS_CLASS = 'xterm-focus';\nconst SELECTION_CLASS = 'xterm-selection';\n\nlet nextTerminalId = 1;\n\n/**\n * A fallback renderer for when canvas is slow. This is not meant to be\n * particularly fast or feature complete, more just stable and usable for when\n * canvas is not an option.\n */\nexport class DomRenderer extends Disposable implements IRenderer {\n private _rowFactory: DomRendererRowFactory;\n private _terminalClass: number = nextTerminalId++;\n\n private _themeStyleElement!: HTMLStyleElement;\n private _dimensionsStyleElement!: HTMLStyleElement;\n private _rowContainer: HTMLElement;\n private _rowElements: HTMLElement[] = [];\n private _selectionContainer: HTMLElement;\n\n public dimensions: IRenderDimensions;\n\n public get onRequestRedraw(): IEvent { return new EventEmitter().event; }\n\n constructor(\n private _colors: IColorSet,\n private readonly _element: HTMLElement,\n private readonly _screenElement: HTMLElement,\n private readonly _viewportElement: HTMLElement,\n private readonly _linkifier: ILinkifier,\n private readonly _linkifier2: ILinkifier2,\n @IInstantiationService instantiationService: IInstantiationService,\n @ICharSizeService private readonly _charSizeService: ICharSizeService,\n @IOptionsService private readonly _optionsService: IOptionsService,\n @IBufferService private readonly _bufferService: IBufferService\n ) {\n super();\n this._rowContainer = document.createElement('div');\n this._rowContainer.classList.add(ROW_CONTAINER_CLASS);\n this._rowContainer.style.lineHeight = 'normal';\n this._rowContainer.setAttribute('aria-hidden', 'true');\n this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);\n this._selectionContainer = document.createElement('div');\n this._selectionContainer.classList.add(SELECTION_CLASS);\n this._selectionContainer.setAttribute('aria-hidden', 'true');\n\n this.dimensions = {\n scaledCharWidth: 0,\n scaledCharHeight: 0,\n scaledCellWidth: 0,\n scaledCellHeight: 0,\n scaledCharLeft: 0,\n scaledCharTop: 0,\n scaledCanvasWidth: 0,\n scaledCanvasHeight: 0,\n canvasWidth: 0,\n canvasHeight: 0,\n actualCellWidth: 0,\n actualCellHeight: 0\n };\n this._updateDimensions();\n this._injectCss();\n\n this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document, this._colors);\n\n this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);\n this._screenElement.appendChild(this._rowContainer);\n this._screenElement.appendChild(this._selectionContainer);\n\n this._linkifier.onShowLinkUnderline(e => this._onLinkHover(e));\n this._linkifier.onHideLinkUnderline(e => this._onLinkLeave(e));\n\n this._linkifier2.onShowLinkUnderline(e => this._onLinkHover(e));\n this._linkifier2.onHideLinkUnderline(e => this._onLinkLeave(e));\n }\n\n public dispose(): void {\n this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);\n\n // Outside influences such as React unmounts may manipulate the DOM before our disposal.\n // https://github.com/xtermjs/xterm.js/issues/2960\n removeElementFromParent(this._rowContainer, this._selectionContainer, this._themeStyleElement, this._dimensionsStyleElement);\n\n super.dispose();\n }\n\n private _updateDimensions(): void {\n this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio;\n this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);\n this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing);\n this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight);\n this.dimensions.scaledCharLeft = 0;\n this.dimensions.scaledCharTop = 0;\n this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols;\n this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._bufferService.rows;\n this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);\n this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);\n this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols;\n this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows;\n\n for (const element of this._rowElements) {\n element.style.width = `${this.dimensions.canvasWidth}px`;\n element.style.height = `${this.dimensions.actualCellHeight}px`;\n element.style.lineHeight = `${this.dimensions.actualCellHeight}px`;\n // Make sure rows don't overflow onto following row\n element.style.overflow = 'hidden';\n }\n\n if (!this._dimensionsStyleElement) {\n this._dimensionsStyleElement = document.createElement('style');\n this._screenElement.appendChild(this._dimensionsStyleElement);\n }\n\n const styles =\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +\n ` display: inline-block;` +\n ` height: 100%;` +\n ` vertical-align: top;` +\n ` width: ${this.dimensions.actualCellWidth}px` +\n `}`;\n\n this._dimensionsStyleElement.textContent = styles;\n\n this._selectionContainer.style.height = this._viewportElement.style.height;\n this._screenElement.style.width = `${this.dimensions.canvasWidth}px`;\n this._screenElement.style.height = `${this.dimensions.canvasHeight}px`;\n }\n\n public setColors(colors: IColorSet): void {\n this._colors = colors;\n this._injectCss();\n }\n\n private _injectCss(): void {\n if (!this._themeStyleElement) {\n this._themeStyleElement = document.createElement('style');\n this._screenElement.appendChild(this._themeStyleElement);\n }\n\n // Base CSS\n let styles =\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +\n ` color: ${this._colors.foreground.css};` +\n ` font-family: ${this._optionsService.options.fontFamily};` +\n ` font-size: ${this._optionsService.options.fontSize}px;` +\n `}`;\n // Text styles\n styles +=\n `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +\n ` font-weight: ${this._optionsService.options.fontWeight};` +\n `}` +\n `${this._terminalSelector} span.${BOLD_CLASS} {` +\n ` font-weight: ${this._optionsService.options.fontWeightBold};` +\n `}` +\n `${this._terminalSelector} span.${ITALIC_CLASS} {` +\n ` font-style: italic;` +\n `}`;\n // Blink animation\n styles +=\n `@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +\n ` 50% {` +\n ` box-shadow: none;` +\n ` }` +\n `}`;\n styles +=\n `@keyframes blink_block` + `_` + this._terminalClass + ` {` +\n ` 0% {` +\n ` background-color: ${this._colors.cursor.css};` +\n ` color: ${this._colors.cursorAccent.css};` +\n ` }` +\n ` 50% {` +\n ` background-color: ${this._colors.cursorAccent.css};` +\n ` color: ${this._colors.cursor.css};` +\n ` }` +\n `}`;\n // Cursor\n styles +=\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +\n ` outline: 1px solid ${this._colors.cursor.css};` +\n ` outline-offset: -1px;` +\n `}` +\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` +\n ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +\n `}` +\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +\n ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +\n `}` +\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +\n ` background-color: ${this._colors.cursor.css};` +\n ` color: ${this._colors.cursorAccent.css};` +\n `}` +\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +\n ` box-shadow: ${this._optionsService.options.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` +\n `}` +\n `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +\n ` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +\n `}`;\n // Selection\n styles +=\n `${this._terminalSelector} .${SELECTION_CLASS} {` +\n ` position: absolute;` +\n ` top: 0;` +\n ` left: 0;` +\n ` z-index: 1;` +\n ` pointer-events: none;` +\n `}` +\n `${this._terminalSelector} .${SELECTION_CLASS} div {` +\n ` position: absolute;` +\n ` background-color: ${this._colors.selectionTransparent.css};` +\n `}`;\n // Colors\n this._colors.ansi.forEach((c, i) => {\n styles +=\n `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +\n `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;\n });\n styles +=\n `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` +\n `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`;\n\n this._themeStyleElement.textContent = styles;\n }\n\n public onDevicePixelRatioChange(): void {\n this._updateDimensions();\n }\n\n private _refreshRowElements(cols: number, rows: number): void {\n // Add missing elements\n for (let i = this._rowElements.length; i <= rows; i++) {\n const row = document.createElement('div');\n this._rowContainer.appendChild(row);\n this._rowElements.push(row);\n }\n // Remove excess elements\n while (this._rowElements.length > rows) {\n this._rowContainer.removeChild(this._rowElements.pop()!);\n }\n }\n\n public onResize(cols: number, rows: number): void {\n this._refreshRowElements(cols, rows);\n this._updateDimensions();\n }\n\n public onCharSizeChanged(): void {\n this._updateDimensions();\n }\n\n public onBlur(): void {\n this._rowContainer.classList.remove(FOCUS_CLASS);\n }\n\n public onFocus(): void {\n this._rowContainer.classList.add(FOCUS_CLASS);\n }\n\n public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {\n // Remove all selections\n while (this._selectionContainer.children.length) {\n this._selectionContainer.removeChild(this._selectionContainer.children[0]);\n }\n\n // Selection does not exist\n if (!start || !end) {\n return;\n }\n\n // Translate from buffer position to viewport position\n const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;\n const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;\n const viewportCappedStartRow = Math.max(viewportStartRow, 0);\n const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);\n\n // No need to draw the selection\n if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {\n return;\n }\n\n // Create the selections\n const documentFragment = document.createDocumentFragment();\n\n if (columnSelectMode) {\n documentFragment.appendChild(\n this._createSelectionElement(viewportCappedStartRow, start[0], end[0], viewportCappedEndRow - viewportCappedStartRow + 1)\n );\n } else {\n // Draw first row\n const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;\n const endCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;\n documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));\n // Draw middle rows\n const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;\n documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount));\n // Draw final row\n if (viewportCappedStartRow !== viewportCappedEndRow) {\n // Only draw viewportEndRow if it's not the same as viewporttartRow\n const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;\n documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));\n }\n }\n this._selectionContainer.appendChild(documentFragment);\n }\n\n /**\n * Creates a selection element at the specified position.\n * @param row The row of the selection.\n * @param colStart The start column.\n * @param colEnd The end columns.\n */\n private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {\n const element = document.createElement('div');\n element.style.height = `${rowCount * this.dimensions.actualCellHeight}px`;\n element.style.top = `${row * this.dimensions.actualCellHeight}px`;\n element.style.left = `${colStart * this.dimensions.actualCellWidth}px`;\n element.style.width = `${this.dimensions.actualCellWidth * (colEnd - colStart)}px`;\n return element;\n }\n\n public onCursorMove(): void {\n // No-op, the cursor is drawn when rows are drawn\n }\n\n public onOptionsChanged(): void {\n // Force a refresh\n this._updateDimensions();\n this._injectCss();\n }\n\n public clear(): void {\n for (const e of this._rowElements) {\n e.innerText = '';\n }\n }\n\n public renderRows(start: number, end: number): void {\n const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;\n const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);\n const cursorBlink = this._optionsService.options.cursorBlink;\n\n for (let y = start; y <= end; y++) {\n const rowElement = this._rowElements[y];\n rowElement.innerText = '';\n\n const row = y + this._bufferService.buffer.ydisp;\n const lineData = this._bufferService.buffer.lines.get(row);\n const cursorStyle = this._optionsService.options.cursorStyle;\n rowElement.appendChild(this._rowFactory.createRow(lineData!, row, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols));\n }\n }\n\n private get _terminalSelector(): string {\n return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`;\n }\n\n private _onLinkHover(e: ILinkifierEvent): void {\n this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, true);\n }\n\n private _onLinkLeave(e: ILinkifierEvent): void {\n this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, false);\n }\n\n private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void {\n while (x !== x2 || y !== y2) {\n const row = this._rowElements[y];\n if (!row) {\n return;\n }\n const span = row.children[x] as HTMLElement;\n if (span) {\n span.style.textDecoration = enabled ? 'underline' : 'none';\n }\n if (++x >= cols) {\n x = 0;\n y++;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IBufferLine } from 'common/Types';\nimport { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';\nimport { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';\nimport { CellData } from 'common/buffer/CellData';\nimport { ICoreService, IOptionsService } from 'common/services/Services';\nimport { color, rgba } from 'browser/Color';\nimport { IColorSet, IColor } from 'browser/Types';\nimport { ICharacterJoinerService } from 'browser/services/Services';\nimport { JoinedCellData } from 'browser/services/CharacterJoinerService';\n\nexport const BOLD_CLASS = 'xterm-bold';\nexport const DIM_CLASS = 'xterm-dim';\nexport const ITALIC_CLASS = 'xterm-italic';\nexport const UNDERLINE_CLASS = 'xterm-underline';\nexport const STRIKETHROUGH_CLASS = 'xterm-strikethrough';\nexport const CURSOR_CLASS = 'xterm-cursor';\nexport const CURSOR_BLINK_CLASS = 'xterm-cursor-blink';\nexport const CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block';\nexport const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar';\nexport const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline';\n\nexport class DomRendererRowFactory {\n private _workCell: CellData = new CellData();\n\n constructor(\n private readonly _document: Document,\n private _colors: IColorSet,\n @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,\n @IOptionsService private readonly _optionsService: IOptionsService,\n @ICoreService private readonly _coreService: ICoreService\n ) {\n }\n\n public setColors(colors: IColorSet): void {\n this._colors = colors;\n }\n\n public createRow(lineData: IBufferLine, row: number, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, cols: number): DocumentFragment {\n const fragment = this._document.createDocumentFragment();\n\n const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);\n // Find the line length first, this prevents the need to output a bunch of\n // empty cells at the end. This cannot easily be integrated into the main\n // loop below because of the colCount feature (which can be removed after we\n // properly support reflow and disallow data to go beyond the right-side of\n // the viewport).\n let lineLength = 0;\n for (let x = Math.min(lineData.length, cols) - 1; x >= 0; x--) {\n if (lineData.loadCell(x, this._workCell).getCode() !== NULL_CELL_CODE || (isCursorRow && x === cursorX)) {\n lineLength = x + 1;\n break;\n }\n }\n\n for (let x = 0; x < lineLength; x++) {\n lineData.loadCell(x, this._workCell);\n let width = this._workCell.getWidth();\n\n // The character to the left is a wide character, drawing is owned by the char at x-1\n if (width === 0) {\n continue;\n }\n\n // If true, indicates that the current character(s) to draw were joined.\n let isJoined = false;\n let lastCharX = x;\n\n // Process any joined character ranges as needed. Because of how the\n // ranges are produced, we know that they are valid for the characters\n // and attributes of our input.\n let cell = this._workCell;\n if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {\n isJoined = true;\n const range = joinedRanges.shift()!;\n\n // We already know the exact start and end column of the joined range,\n // so we get the string and width representing it directly\n cell = new JoinedCellData(\n this._workCell,\n lineData.translateToString(true, range[0], range[1]),\n range[1] - range[0]\n );\n\n // Skip over the cells occupied by this range in the loop\n lastCharX = range[1] - 1;\n\n // Recalculate width\n width = cell.getWidth();\n }\n\n const charElement = this._document.createElement('span');\n if (width > 1) {\n charElement.style.width = `${cellWidth * width}px`;\n }\n\n if (isJoined) {\n // Ligatures in the DOM renderer must use display inline, as they may not show with\n // inline-block if they are outside the bounds of the element\n charElement.style.display = 'inline';\n\n // The DOM renderer colors the background of the cursor but for ligatures all cells are\n // joined. The workaround here is to show a cursor around the whole ligature so it shows up,\n // the cursor looks the same when on any character of the ligature though\n if (cursorX >= x && cursorX <= lastCharX) {\n cursorX = x;\n }\n }\n\n if (!this._coreService.isCursorHidden && isCursorRow && x === cursorX) {\n charElement.classList.add(CURSOR_CLASS);\n\n if (cursorBlink) {\n charElement.classList.add(CURSOR_BLINK_CLASS);\n }\n\n switch (cursorStyle) {\n case 'bar':\n charElement.classList.add(CURSOR_STYLE_BAR_CLASS);\n break;\n case 'underline':\n charElement.classList.add(CURSOR_STYLE_UNDERLINE_CLASS);\n break;\n default:\n charElement.classList.add(CURSOR_STYLE_BLOCK_CLASS);\n break;\n }\n }\n\n if (cell.isBold()) {\n charElement.classList.add(BOLD_CLASS);\n }\n\n if (cell.isItalic()) {\n charElement.classList.add(ITALIC_CLASS);\n }\n\n if (cell.isDim()) {\n charElement.classList.add(DIM_CLASS);\n }\n\n if (cell.isUnderline()) {\n charElement.classList.add(UNDERLINE_CLASS);\n }\n\n if (cell.isInvisible()) {\n charElement.textContent = WHITESPACE_CELL_CHAR;\n } else {\n charElement.textContent = cell.getChars() || WHITESPACE_CELL_CHAR;\n }\n\n if (cell.isStrikethrough()) {\n charElement.classList.add(STRIKETHROUGH_CLASS);\n }\n\n let fg = cell.getFgColor();\n let fgColorMode = cell.getFgColorMode();\n let bg = cell.getBgColor();\n let bgColorMode = cell.getBgColorMode();\n const isInverse = !!cell.isInverse();\n if (isInverse) {\n const temp = fg;\n fg = bg;\n bg = temp;\n const temp2 = fgColorMode;\n fgColorMode = bgColorMode;\n bgColorMode = temp2;\n }\n\n // Foreground\n switch (fgColorMode) {\n case Attributes.CM_P16:\n case Attributes.CM_P256:\n if (cell.isBold() && fg < 8 && this._optionsService.options.drawBoldTextInBrightColors) {\n fg += 8;\n }\n if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) {\n charElement.classList.add(`xterm-fg-${fg}`);\n }\n break;\n case Attributes.CM_RGB:\n const color = rgba.toColor(\n (fg >> 16) & 0xFF,\n (fg >> 8) & 0xFF,\n (fg ) & 0xFF\n );\n if (!this._applyMinimumContrast(charElement, this._colors.background, color)) {\n this._addStyle(charElement, `color:#${padStart(fg.toString(16), '0', 6)}`);\n }\n break;\n case Attributes.CM_DEFAULT:\n default:\n if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.foreground)) {\n if (isInverse) {\n charElement.classList.add(`xterm-fg-${INVERTED_DEFAULT_COLOR}`);\n }\n }\n }\n\n // Background\n switch (bgColorMode) {\n case Attributes.CM_P16:\n case Attributes.CM_P256:\n charElement.classList.add(`xterm-bg-${bg}`);\n break;\n case Attributes.CM_RGB:\n this._addStyle(charElement, `background-color:#${padStart(bg.toString(16), '0', 6)}`);\n break;\n case Attributes.CM_DEFAULT:\n default:\n if (isInverse) {\n charElement.classList.add(`xterm-bg-${INVERTED_DEFAULT_COLOR}`);\n }\n }\n\n fragment.appendChild(charElement);\n\n x = lastCharX;\n }\n return fragment;\n }\n\n private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor): boolean {\n if (this._optionsService.options.minimumContrastRatio === 1) {\n return false;\n }\n\n // Try get from cache first\n let adjustedColor = this._colors.contrastCache.getColor(this._workCell.bg, this._workCell.fg);\n\n // Calculate and store in cache\n if (adjustedColor === undefined) {\n adjustedColor = color.ensureContrastRatio(bg, fg, this._optionsService.options.minimumContrastRatio);\n this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null);\n }\n\n if (adjustedColor) {\n this._addStyle(element, `color:${adjustedColor.css}`);\n return true;\n }\n\n return false;\n }\n\n private _addStyle(element: HTMLElement, style: string): void {\n element.setAttribute('style', `${element.getAttribute('style') || ''}${style};`);\n }\n}\n\nfunction padStart(text: string, padChar: string, length: number): string {\n while (text.length < length) {\n text = padChar + text;\n }\n return text;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IBufferService } from 'common/services/Services';\n\n/**\n * Represents a selection within the buffer. This model only cares about column\n * and row coordinates, not wide characters.\n */\nexport class SelectionModel {\n /**\n * Whether select all is currently active.\n */\n public isSelectAllActive: boolean = false;\n\n /**\n * The minimal length of the selection from the start position. When double\n * clicking on a word, the word will be selected which makes the selection\n * start at the start of the word and makes this variable the length.\n */\n public selectionStartLength: number = 0;\n\n /**\n * The [x, y] position the selection starts at.\n */\n public selectionStart: [number, number] | undefined;\n\n /**\n * The [x, y] position the selection ends at.\n */\n public selectionEnd: [number, number] | undefined;\n\n constructor(\n private _bufferService: IBufferService\n ) {\n }\n\n /**\n * Clears the current selection.\n */\n public clearSelection(): void {\n this.selectionStart = undefined;\n this.selectionEnd = undefined;\n this.isSelectAllActive = false;\n this.selectionStartLength = 0;\n }\n\n /**\n * The final selection start, taking into consideration select all.\n */\n public get finalSelectionStart(): [number, number] | undefined {\n if (this.isSelectAllActive) {\n return [0, 0];\n }\n\n if (!this.selectionEnd || !this.selectionStart) {\n return this.selectionStart;\n }\n\n return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;\n }\n\n /**\n * The final selection end, taking into consideration select all, double click\n * word selection and triple click line selection.\n */\n public get finalSelectionEnd(): [number, number] | undefined {\n if (this.isSelectAllActive) {\n return [this._bufferService.cols, this._bufferService.buffer.ybase + this._bufferService.rows - 1];\n }\n\n if (!this.selectionStart) {\n return undefined;\n }\n\n // Use the selection start + length if the end doesn't exist or they're reversed\n if (!this.selectionEnd || this.areSelectionValuesReversed()) {\n const startPlusLength = this.selectionStart[0] + this.selectionStartLength;\n if (startPlusLength > this._bufferService.cols) {\n // Ensure the trailing EOL isn't included when the selection ends on the right edge\n if (startPlusLength % this._bufferService.cols === 0) {\n return [this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols) - 1];\n }\n return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];\n }\n return [startPlusLength, this.selectionStart[1]];\n }\n\n // Ensure the the word/line is selected after a double/triple click\n if (this.selectionStartLength) {\n // Select the larger of the two when start and end are on the same line\n if (this.selectionEnd[1] === this.selectionStart[1]) {\n return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];\n }\n }\n return this.selectionEnd;\n }\n\n /**\n * Returns whether the selection start and end are reversed.\n */\n public areSelectionValuesReversed(): boolean {\n const start = this.selectionStart;\n const end = this.selectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n * @return Whether a refresh is necessary.\n */\n public onTrim(amount: number): boolean {\n // Adjust the selection position based on the trimmed amount.\n if (this.selectionStart) {\n this.selectionStart[1] -= amount;\n }\n if (this.selectionEnd) {\n this.selectionEnd[1] -= amount;\n }\n\n // The selection has moved off the buffer, clear it.\n if (this.selectionEnd && this.selectionEnd[1] < 0) {\n this.clearSelection();\n return true;\n }\n\n // If the selection start is trimmed, ensure the start column is 0.\n if (this.selectionStart && this.selectionStart[1] < 0) {\n this.selectionStart[1] = 0;\n }\n return false;\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IOptionsService } from 'common/services/Services';\nimport { IEvent, EventEmitter } from 'common/EventEmitter';\nimport { ICharSizeService } from 'browser/services/Services';\n\nexport class CharSizeService implements ICharSizeService {\n public serviceBrand: undefined;\n\n public width: number = 0;\n public height: number = 0;\n private _measureStrategy: IMeasureStrategy;\n\n public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }\n\n private _onCharSizeChange = new EventEmitter();\n public get onCharSizeChange(): IEvent { return this._onCharSizeChange.event; }\n\n constructor(\n document: Document,\n parentElement: HTMLElement,\n @IOptionsService private readonly _optionsService: IOptionsService\n ) {\n this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);\n }\n\n public measure(): void {\n const result = this._measureStrategy.measure();\n if (result.width !== this.width || result.height !== this.height) {\n this.width = result.width;\n this.height = result.height;\n this._onCharSizeChange.fire();\n }\n }\n}\n\ninterface IMeasureStrategy {\n measure(): IReadonlyMeasureResult;\n}\n\ninterface IReadonlyMeasureResult {\n readonly width: number;\n readonly height: number;\n}\n\ninterface IMeasureResult {\n width: number;\n height: number;\n}\n\n// TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses ctx.measureText\nclass DomMeasureStrategy implements IMeasureStrategy {\n private _result: IMeasureResult = { width: 0, height: 0 };\n private _measureElement: HTMLElement;\n\n constructor(\n private _document: Document,\n private _parentElement: HTMLElement,\n private _optionsService: IOptionsService\n ) {\n this._measureElement = this._document.createElement('span');\n this._measureElement.classList.add('xterm-char-measure-element');\n this._measureElement.textContent = 'W';\n this._measureElement.setAttribute('aria-hidden', 'true');\n this._parentElement.appendChild(this._measureElement);\n }\n\n public measure(): IReadonlyMeasureResult {\n this._measureElement.style.fontFamily = this._optionsService.options.fontFamily;\n this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`;\n\n // Note that this triggers a synchronous layout\n const geometry = this._measureElement.getBoundingClientRect();\n\n // If values are 0 then the element is likely currently display:none, in which case we should\n // retain the previous value.\n if (geometry.width !== 0 && geometry.height !== 0) {\n this._result.width = geometry.width;\n this._result.height = Math.ceil(geometry.height);\n }\n\n return this._result;\n }\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IBufferLine, ICellData, CharData } from 'common/Types';\nimport { ICharacterJoiner } from 'browser/Types';\nimport { AttributeData } from 'common/buffer/AttributeData';\nimport { WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';\nimport { CellData } from 'common/buffer/CellData';\nimport { IBufferService } from 'common/services/Services';\nimport { ICharacterJoinerService } from 'browser/services/Services';\n\nexport class JoinedCellData extends AttributeData implements ICellData {\n private _width: number;\n // .content carries no meaning for joined CellData, simply nullify it\n // thus we have to overload all other .content accessors\n public content: number = 0;\n public fg: number;\n public bg: number;\n public combinedData: string = '';\n\n constructor(firstCell: ICellData, chars: string, width: number) {\n super();\n this.fg = firstCell.fg;\n this.bg = firstCell.bg;\n this.combinedData = chars;\n this._width = width;\n }\n\n public isCombined(): number {\n // always mark joined cell data as combined\n return Content.IS_COMBINED_MASK;\n }\n\n public getWidth(): number {\n return this._width;\n }\n\n public getChars(): string {\n return this.combinedData;\n }\n\n public getCode(): number {\n // code always gets the highest possible fake codepoint (read as -1)\n // this is needed as code is used by caches as identifier\n return 0x1FFFFF;\n }\n\n public setFromCharData(value: CharData): void {\n throw new Error('not implemented');\n }\n\n public getAsCharData(): CharData {\n return [this.fg, this.getChars(), this.getWidth(), this.getCode()];\n }\n}\n\nexport class CharacterJoinerService implements ICharacterJoinerService {\n public serviceBrand: undefined;\n\n private _characterJoiners: ICharacterJoiner[] = [];\n private _nextCharacterJoinerId: number = 0;\n private _workCell: CellData = new CellData();\n\n constructor(\n @IBufferService private _bufferService: IBufferService\n ) { }\n\n public register(handler: (text: string) => [number, number][]): number {\n const joiner: ICharacterJoiner = {\n id: this._nextCharacterJoinerId++,\n handler\n };\n\n this._characterJoiners.push(joiner);\n return joiner.id;\n }\n\n public deregister(joinerId: number): boolean {\n for (let i = 0; i < this._characterJoiners.length; i++) {\n if (this._characterJoiners[i].id === joinerId) {\n this._characterJoiners.splice(i, 1);\n return true;\n }\n }\n\n return false;\n }\n\n public getJoinedCharacters(row: number): [number, number][] {\n if (this._characterJoiners.length === 0) {\n return [];\n }\n\n const line = this._bufferService.buffer.lines.get(row);\n if (!line || line.length === 0) {\n return [];\n }\n\n const ranges: [number, number][] = [];\n const lineStr = line.translateToString(true);\n\n // Because some cells can be represented by multiple javascript characters,\n // we track the cell and the string indexes separately. This allows us to\n // translate the string ranges we get from the joiners back into cell ranges\n // for use when rendering\n let rangeStartColumn = 0;\n let currentStringIndex = 0;\n let rangeStartStringIndex = 0;\n let rangeAttrFG = line.getFg(0);\n let rangeAttrBG = line.getBg(0);\n\n for (let x = 0; x < line.getTrimmedLength(); x++) {\n line.loadCell(x, this._workCell);\n\n if (this._workCell.getWidth() === 0) {\n // If this character is of width 0, skip it.\n continue;\n }\n\n // End of range\n if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) {\n // If we ended up with a sequence of more than one character,\n // look for ranges to join.\n if (x - rangeStartColumn > 1) {\n const joinedRanges = this._getJoinedRanges(\n lineStr,\n rangeStartStringIndex,\n currentStringIndex,\n line,\n rangeStartColumn\n );\n for (let i = 0; i < joinedRanges.length; i++) {\n ranges.push(joinedRanges[i]);\n }\n }\n\n // Reset our markers for a new range.\n rangeStartColumn = x;\n rangeStartStringIndex = currentStringIndex;\n rangeAttrFG = this._workCell.fg;\n rangeAttrBG = this._workCell.bg;\n }\n\n currentStringIndex += this._workCell.getChars().length || WHITESPACE_CELL_CHAR.length;\n }\n\n // Process any trailing ranges.\n if (this._bufferService.cols - rangeStartColumn > 1) {\n const joinedRanges = this._getJoinedRanges(\n lineStr,\n rangeStartStringIndex,\n currentStringIndex,\n line,\n rangeStartColumn\n );\n for (let i = 0; i < joinedRanges.length; i++) {\n ranges.push(joinedRanges[i]);\n }\n }\n\n return ranges;\n }\n\n /**\n * Given a segment of a line of text, find all ranges of text that should be\n * joined in a single rendering unit. Ranges are internally converted to\n * column ranges, rather than string ranges.\n * @param line String representation of the full line of text\n * @param startIndex Start position of the range to search in the string (inclusive)\n * @param endIndex End position of the range to search in the string (exclusive)\n */\n private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] {\n const text = line.substring(startIndex, endIndex);\n // At this point we already know that there is at least one joiner so\n // we can just pull its value and assign it directly rather than\n // merging it into an empty array, which incurs unnecessary writes.\n let allJoinedRanges: [number, number][] = [];\n try {\n allJoinedRanges = this._characterJoiners[0].handler(text);\n } catch (error) {\n console.error(error);\n }\n for (let i = 1; i < this._characterJoiners.length; i++) {\n // We merge any overlapping ranges across the different joiners\n try {\n const joinerRanges = this._characterJoiners[i].handler(text);\n for (let j = 0; j < joinerRanges.length; j++) {\n CharacterJoinerService._mergeRanges(allJoinedRanges, joinerRanges[j]);\n }\n } catch (error) {\n console.error(error);\n }\n }\n this._stringRangesToCellRanges(allJoinedRanges, lineData, startCol);\n return allJoinedRanges;\n }\n\n /**\n * Modifies the provided ranges in-place to adjust for variations between\n * string length and cell width so that the range represents a cell range,\n * rather than the string range the joiner provides.\n * @param ranges String ranges containing start (inclusive) and end (exclusive) index\n * @param line Cell data for the relevant line in the terminal\n * @param startCol Offset within the line to start from\n */\n private _stringRangesToCellRanges(ranges: [number, number][], line: IBufferLine, startCol: number): void {\n let currentRangeIndex = 0;\n let currentRangeStarted = false;\n let currentStringIndex = 0;\n let currentRange = ranges[currentRangeIndex];\n\n // If we got through all of the ranges, stop searching\n if (!currentRange) {\n return;\n }\n\n for (let x = startCol; x < this._bufferService.cols; x++) {\n const width = line.getWidth(x);\n const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length;\n\n // We skip zero-width characters when creating the string to join the text\n // so we do the same here\n if (width === 0) {\n continue;\n }\n\n // Adjust the start of the range\n if (!currentRangeStarted && currentRange[0] <= currentStringIndex) {\n currentRange[0] = x;\n currentRangeStarted = true;\n }\n\n // Adjust the end of the range\n if (currentRange[1] <= currentStringIndex) {\n currentRange[1] = x;\n\n // We're finished with this range, so we move to the next one\n currentRange = ranges[++currentRangeIndex];\n\n // If there are no more ranges left, stop searching\n if (!currentRange) {\n break;\n }\n\n // Ranges can be on adjacent characters. Because the end index of the\n // ranges are exclusive, this means that the index for the start of a\n // range can be the same as the end index of the previous range. To\n // account for the start of the next range, we check here just in case.\n if (currentRange[0] <= currentStringIndex) {\n currentRange[0] = x;\n currentRangeStarted = true;\n } else {\n currentRangeStarted = false;\n }\n }\n\n // Adjust the string index based on the character length to line up with\n // the column adjustment\n currentStringIndex += length;\n }\n\n // If there is still a range left at the end, it must extend all the way to\n // the end of the line.\n if (currentRange) {\n currentRange[1] = this._bufferService.cols;\n }\n }\n\n /**\n * Merges the range defined by the provided start and end into the list of\n * existing ranges. The merge is done in place on the existing range for\n * performance and is also returned.\n * @param ranges Existing range list\n * @param newRange Tuple of two numbers representing the new range to merge in.\n * @returns The ranges input with the new range merged in place\n */\n private static _mergeRanges(ranges: [number, number][], newRange: [number, number]): [number, number][] {\n let inRange = false;\n for (let i = 0; i < ranges.length; i++) {\n const range = ranges[i];\n if (!inRange) {\n if (newRange[1] <= range[0]) {\n // Case 1: New range is before the search range\n ranges.splice(i, 0, newRange);\n return ranges;\n }\n\n if (newRange[1] <= range[1]) {\n // Case 2: New range is either wholly contained within the\n // search range or overlaps with the front of it\n range[0] = Math.min(newRange[0], range[0]);\n return ranges;\n }\n\n if (newRange[0] < range[1]) {\n // Case 3: New range either wholly contains the search range\n // or overlaps with the end of it\n range[0] = Math.min(newRange[0], range[0]);\n inRange = true;\n }\n\n // Case 4: New range starts after the search range\n continue;\n } else {\n if (newRange[1] <= range[0]) {\n // Case 5: New range extends from previous range but doesn't\n // reach the current one\n ranges[i - 1][1] = newRange[1];\n return ranges;\n }\n\n if (newRange[1] <= range[1]) {\n // Case 6: New range extends from prvious range into the\n // current range\n ranges[i - 1][1] = Math.max(newRange[1], range[1]);\n ranges.splice(i, 1);\n return ranges;\n }\n\n // Case 7: New range extends from previous range past the\n // end of the current range\n ranges.splice(i, 1);\n i--;\n }\n }\n\n if (inRange) {\n // Case 8: New range extends past the last existing range\n ranges[ranges.length - 1][1] = newRange[1];\n } else {\n // Case 9: New range starts after the last existing range\n ranges.push(newRange);\n }\n\n return ranges;\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICoreBrowserService } from './Services';\n\nexport class CoreBrowserService implements ICoreBrowserService {\n public serviceBrand: undefined;\n\n constructor(\n private _textarea: HTMLTextAreaElement\n ) {\n }\n\n public get isFocused(): boolean {\n const docOrShadowRoot = this._textarea.getRootNode ? this._textarea.getRootNode() as Document | ShadowRoot : document;\n return docOrShadowRoot.activeElement === this._textarea && document.hasFocus();\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICharSizeService, IRenderService, IMouseService } from './Services';\nimport { getCoords, getRawByteCoords } from 'browser/input/Mouse';\n\nexport class MouseService implements IMouseService {\n public serviceBrand: undefined;\n\n constructor(\n @IRenderService private readonly _renderService: IRenderService,\n @ICharSizeService private readonly _charSizeService: ICharSizeService\n ) {\n }\n\n public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {\n return getCoords(\n event,\n element,\n colCount,\n rowCount,\n this._charSizeService.hasValidSize,\n this._renderService.dimensions.actualCellWidth,\n this._renderService.dimensions.actualCellHeight,\n isSelection\n );\n }\n\n public getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined {\n const coords = this.getCoords(event, element, colCount, rowCount);\n return getRawByteCoords(coords);\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderer, IRenderDimensions } from 'browser/renderer/Types';\nimport { RenderDebouncer } from 'browser/RenderDebouncer';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { Disposable } from 'common/Lifecycle';\nimport { ScreenDprMonitor } from 'browser/ScreenDprMonitor';\nimport { addDisposableDomListener } from 'browser/Lifecycle';\nimport { IColorSet, IRenderDebouncer } from 'browser/Types';\nimport { IOptionsService, IBufferService } from 'common/services/Services';\nimport { ICharSizeService, IRenderService } from 'browser/services/Services';\n\ninterface ISelectionState {\n start: [number, number] | undefined;\n end: [number, number] | undefined;\n columnSelectMode: boolean;\n}\n\nexport class RenderService extends Disposable implements IRenderService {\n public serviceBrand: undefined;\n\n private _renderDebouncer: IRenderDebouncer;\n private _screenDprMonitor: ScreenDprMonitor;\n\n private _isPaused: boolean = false;\n private _needsFullRefresh: boolean = false;\n private _isNextRenderRedrawOnly: boolean = true;\n private _needsSelectionRefresh: boolean = false;\n private _canvasWidth: number = 0;\n private _canvasHeight: number = 0;\n private _selectionState: ISelectionState = {\n start: undefined,\n end: undefined,\n columnSelectMode: false\n };\n\n private _onDimensionsChange = new EventEmitter();\n public get onDimensionsChange(): IEvent { return this._onDimensionsChange.event; }\n private _onRender = new EventEmitter<{ start: number, end: number }>();\n public get onRenderedBufferChange(): IEvent<{ start: number, end: number }> { return this._onRender.event; }\n private _onRefreshRequest = new EventEmitter<{ start: number, end: number }>();\n public get onRefreshRequest(): IEvent<{ start: number, end: number }> { return this._onRefreshRequest.event; }\n\n public get dimensions(): IRenderDimensions { return this._renderer.dimensions; }\n\n constructor(\n private _renderer: IRenderer,\n private _rowCount: number,\n screenElement: HTMLElement,\n @IOptionsService optionsService: IOptionsService,\n @ICharSizeService private readonly _charSizeService: ICharSizeService,\n @IBufferService bufferService: IBufferService\n ) {\n super();\n\n this.register({ dispose: () => this._renderer.dispose() });\n\n this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));\n this.register(this._renderDebouncer);\n\n this._screenDprMonitor = new ScreenDprMonitor();\n this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange());\n this.register(this._screenDprMonitor);\n\n this.register(bufferService.onResize(e => this._fullRefresh()));\n this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));\n this.register(this._charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));\n\n // No need to register this as renderer is explicitly disposed in RenderService.dispose\n this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));\n\n // dprchange should handle this case, we need this as well for browsers that don't support the\n // matchMedia query.\n this.register(addDisposableDomListener(window, 'resize', () => this.onDevicePixelRatioChange()));\n\n // Detect whether IntersectionObserver is detected and enable renderer pause\n // and resume based on terminal visibility if so\n if ('IntersectionObserver' in window) {\n const observer = new IntersectionObserver(e => this._onIntersectionChange(e[e.length - 1]), { threshold: 0 });\n observer.observe(screenElement);\n this.register({ dispose: () => observer.disconnect() });\n }\n }\n\n private _onIntersectionChange(entry: IntersectionObserverEntry): void {\n this._isPaused = entry.isIntersecting === undefined ? (entry.intersectionRatio === 0) : !entry.isIntersecting;\n\n // Terminal was hidden on open\n if (!this._isPaused && !this._charSizeService.hasValidSize) {\n this._charSizeService.measure();\n }\n\n if (!this._isPaused && this._needsFullRefresh) {\n this.refreshRows(0, this._rowCount - 1);\n this._needsFullRefresh = false;\n }\n }\n\n public refreshRows(start: number, end: number, isRedrawOnly: boolean = false): void {\n if (this._isPaused) {\n this._needsFullRefresh = true;\n return;\n }\n if (!isRedrawOnly) {\n this._isNextRenderRedrawOnly = false;\n }\n this._renderDebouncer.refresh(start, end, this._rowCount);\n }\n\n private _renderRows(start: number, end: number): void {\n this._renderer.renderRows(start, end);\n\n // Update selection if needed\n if (this._needsSelectionRefresh) {\n this._renderer.onSelectionChanged(this._selectionState.start, this._selectionState.end, this._selectionState.columnSelectMode);\n this._needsSelectionRefresh = false;\n }\n\n // Fire render event only if it was not a redraw\n if (!this._isNextRenderRedrawOnly) {\n this._onRender.fire({ start, end });\n }\n this._isNextRenderRedrawOnly = true;\n }\n\n public resize(cols: number, rows: number): void {\n this._rowCount = rows;\n this._fireOnCanvasResize();\n }\n\n public changeOptions(): void {\n this._renderer.onOptionsChanged();\n this.refreshRows(0, this._rowCount - 1);\n this._fireOnCanvasResize();\n }\n\n private _fireOnCanvasResize(): void {\n // Don't fire the event if the dimensions haven't changed\n if (this._renderer.dimensions.canvasWidth === this._canvasWidth && this._renderer.dimensions.canvasHeight === this._canvasHeight) {\n return;\n }\n this._onDimensionsChange.fire(this._renderer.dimensions);\n }\n\n public dispose(): void {\n super.dispose();\n }\n\n public setRenderer(renderer: IRenderer): void {\n // TODO: RenderService should be the only one to dispose the renderer\n this._renderer.dispose();\n this._renderer = renderer;\n this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));\n\n // Force a refresh\n this._needsSelectionRefresh = true;\n this._fullRefresh();\n }\n\n private _fullRefresh(): void {\n if (this._isPaused) {\n this._needsFullRefresh = true;\n } else {\n this.refreshRows(0, this._rowCount - 1);\n }\n }\n\n public clearTextureAtlas(): void {\n this._renderer?.clearTextureAtlas?.();\n this._fullRefresh();\n }\n\n public setColors(colors: IColorSet): void {\n this._renderer.setColors(colors);\n this._fullRefresh();\n }\n\n public onDevicePixelRatioChange(): void {\n // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable\n // when devicePixelRatio changes\n this._charSizeService.measure();\n\n this._renderer.onDevicePixelRatioChange();\n this.refreshRows(0, this._rowCount - 1);\n }\n\n public onResize(cols: number, rows: number): void {\n this._renderer.onResize(cols, rows);\n this._fullRefresh();\n }\n\n // TODO: Is this useful when we have onResize?\n public onCharSizeChanged(): void {\n this._renderer.onCharSizeChanged();\n }\n\n public onBlur(): void {\n this._renderer.onBlur();\n }\n\n public onFocus(): void {\n this._renderer.onFocus();\n }\n\n public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {\n this._selectionState.start = start;\n this._selectionState.end = end;\n this._selectionState.columnSelectMode = columnSelectMode;\n this._renderer.onSelectionChanged(start, end, columnSelectMode);\n }\n\n public onCursorMove(): void {\n this._renderer.onCursorMove();\n }\n\n public clear(): void {\n this._renderer.clear();\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';\nimport { IBuffer } from 'common/buffer/Types';\nimport { IBufferLine, IDisposable } from 'common/Types';\nimport * as Browser from 'common/Platform';\nimport { SelectionModel } from 'browser/selection/SelectionModel';\nimport { CellData } from 'common/buffer/CellData';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { IMouseService, ISelectionService, IRenderService } from 'browser/services/Services';\nimport { ILinkifier2 } from 'browser/Types';\nimport { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';\nimport { getCoordsRelativeToElement } from 'browser/input/Mouse';\nimport { moveToCellSequence } from 'browser/input/MoveToCell';\nimport { Disposable } from 'common/Lifecycle';\nimport { getRangeLength } from 'common/buffer/BufferRange';\n\n/**\n * The number of pixels the mouse needs to be above or below the viewport in\n * order to scroll at the maximum speed.\n */\nconst DRAG_SCROLL_MAX_THRESHOLD = 50;\n\n/**\n * The maximum scrolling speed\n */\nconst DRAG_SCROLL_MAX_SPEED = 15;\n\n/**\n * The number of milliseconds between drag scroll updates.\n */\nconst DRAG_SCROLL_INTERVAL = 50;\n\n/**\n * The maximum amount of time that can have elapsed for an alt click to move the\n * cursor.\n */\nconst ALT_CLICK_MOVE_CURSOR_TIME = 500;\n\nconst NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);\nconst ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');\n\n/**\n * Represents a position of a word on a line.\n */\ninterface IWordPosition {\n start: number;\n length: number;\n}\n\n/**\n * A selection mode, this drives how the selection behaves on mouse move.\n */\nexport const enum SelectionMode {\n NORMAL,\n WORD,\n LINE,\n COLUMN\n}\n\n/**\n * A class that manages the selection of the terminal. With help from\n * SelectionModel, SelectionService handles with all logic associated with\n * dealing with the selection, including handling mouse interaction, wide\n * characters and fetching the actual text within the selection. Rendering is\n * not handled by the SelectionService but the onRedrawRequest event is fired\n * when the selection is ready to be redrawn (on an animation frame).\n */\nexport class SelectionService extends Disposable implements ISelectionService {\n public serviceBrand: undefined;\n\n protected _model: SelectionModel;\n\n /**\n * The amount to scroll every drag scroll update (depends on how far the mouse\n * drag is above or below the terminal).\n */\n private _dragScrollAmount: number = 0;\n\n /**\n * The current selection mode.\n */\n protected _activeSelectionMode: SelectionMode;\n\n /**\n * A setInterval timer that is active while the mouse is down whose callback\n * scrolls the viewport when necessary.\n */\n private _dragScrollIntervalTimer: number | undefined;\n\n /**\n * The animation frame ID used for refreshing the selection.\n */\n private _refreshAnimationFrame: number | undefined;\n\n /**\n * Whether selection is enabled.\n */\n private _enabled = true;\n\n private _mouseMoveListener: EventListener;\n private _mouseUpListener: EventListener;\n private _trimListener: IDisposable;\n private _workCell: CellData = new CellData();\n\n private _mouseDownTimeStamp: number = 0;\n private _oldHasSelection: boolean = false;\n private _oldSelectionStart: [number, number] | undefined = undefined;\n private _oldSelectionEnd: [number, number] | undefined = undefined;\n\n private _onLinuxMouseSelection = this.register(new EventEmitter());\n public get onLinuxMouseSelection(): IEvent { return this._onLinuxMouseSelection.event; }\n private _onRedrawRequest = this.register(new EventEmitter());\n public get onRequestRedraw(): IEvent { return this._onRedrawRequest.event; }\n private _onSelectionChange = this.register(new EventEmitter());\n public get onSelectionChange(): IEvent { return this._onSelectionChange.event; }\n private _onRequestScrollLines = this.register(new EventEmitter());\n public get onRequestScrollLines(): IEvent { return this._onRequestScrollLines.event; }\n\n constructor(\n private readonly _element: HTMLElement,\n private readonly _screenElement: HTMLElement,\n private readonly _linkifier: ILinkifier2,\n @IBufferService private readonly _bufferService: IBufferService,\n @ICoreService private readonly _coreService: ICoreService,\n @IMouseService private readonly _mouseService: IMouseService,\n @IOptionsService private readonly _optionsService: IOptionsService,\n @IRenderService private readonly _renderService: IRenderService\n ) {\n super();\n\n // Init listeners\n this._mouseMoveListener = event => this._onMouseMove(event as MouseEvent);\n this._mouseUpListener = event => this._onMouseUp(event as MouseEvent);\n this._coreService.onUserInput(() => {\n if (this.hasSelection) {\n this.clearSelection();\n }\n });\n this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._onTrim(amount));\n this.register(this._bufferService.buffers.onBufferActivate(e => this._onBufferActivate(e)));\n\n this.enable();\n\n this._model = new SelectionModel(this._bufferService);\n this._activeSelectionMode = SelectionMode.NORMAL;\n }\n\n public dispose(): void {\n this._removeMouseDownListeners();\n }\n\n public reset(): void {\n this.clearSelection();\n }\n\n /**\n * Disables the selection manager. This is useful for when terminal mouse\n * are enabled.\n */\n public disable(): void {\n this.clearSelection();\n this._enabled = false;\n }\n\n /**\n * Enable the selection manager.\n */\n public enable(): void {\n this._enabled = true;\n }\n\n public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }\n public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }\n\n /**\n * Gets whether there is an active text selection.\n */\n public get hasSelection(): boolean {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[0] !== end[0] || start[1] !== end[1];\n }\n\n /**\n * Gets the text currently selected.\n */\n public get selectionText(): string {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return '';\n }\n\n const buffer = this._bufferService.buffer;\n const result: string[] = [];\n\n if (this._activeSelectionMode === SelectionMode.COLUMN) {\n // Ignore zero width selections\n if (start[0] === end[0]) {\n return '';\n }\n\n for (let i = start[1]; i <= end[1]; i++) {\n const lineText = buffer.translateBufferLineToString(i, true, start[0], end[0]);\n result.push(lineText);\n }\n } else {\n // Get first row\n const startRowEndCol = start[1] === end[1] ? end[0] : undefined;\n result.push(buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));\n\n // Get middle rows\n for (let i = start[1] + 1; i <= end[1] - 1; i++) {\n const bufferLine = buffer.lines.get(i);\n const lineText = buffer.translateBufferLineToString(i, true);\n if (bufferLine?.isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n\n // Get final row\n if (start[1] !== end[1]) {\n const bufferLine = buffer.lines.get(end[1]);\n const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);\n if (bufferLine && bufferLine!.isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n }\n\n // Format string by replacing non-breaking space chars with regular spaces\n // and joining the array into a multi-line string.\n const formattedResult = result.map(line => {\n return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');\n }).join(Browser.isWindows ? '\\r\\n' : '\\n');\n\n return formattedResult;\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this.refresh();\n this._onSelectionChange.fire();\n }\n\n /**\n * Queues a refresh, redrawing the selection on the next opportunity.\n * @param isLinuxMouseSelection Whether the selection should be registered as a new\n * selection on Linux.\n */\n public refresh(isLinuxMouseSelection?: boolean): void {\n // Queue the refresh for the renderer\n if (!this._refreshAnimationFrame) {\n this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());\n }\n\n // If the platform is Linux and the refresh call comes from a mouse event,\n // we need to update the selection for middle click to paste selection.\n if (Browser.isLinux && isLinuxMouseSelection) {\n const selectionText = this.selectionText;\n if (selectionText.length) {\n this._onLinuxMouseSelection.fire(this.selectionText);\n }\n }\n }\n\n /**\n * Fires the refresh event, causing consumers to pick it up and redraw the\n * selection state.\n */\n private _refresh(): void {\n this._refreshAnimationFrame = undefined;\n this._onRedrawRequest.fire({\n start: this._model.finalSelectionStart,\n end: this._model.finalSelectionEnd,\n columnSelectMode: this._activeSelectionMode === SelectionMode.COLUMN\n });\n }\n\n /**\n * Checks if the current click was inside the current selection\n * @param event The mouse event\n */\n private _isClickInSelection(event: MouseEvent): boolean {\n const coords = this._getMouseBufferCoords(event);\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n\n if (!start || !end || !coords) {\n return false;\n }\n\n return this._areCoordsInSelection(coords, start, end);\n }\n\n protected _areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean {\n return (coords[1] > start[1] && coords[1] < end[1]) ||\n (start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) ||\n (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]) ||\n (start[1] < end[1] && coords[1] === start[1] && coords[0] >= start[0]);\n }\n\n /**\n * Selects word at the current mouse event coordinates.\n * @param event The mouse event.\n */\n private _selectWordAtCursor(event: MouseEvent, allowWhitespaceOnlySelection: boolean): boolean {\n // Check if there is a link under the cursor first and select that if so\n const range = this._linkifier.currentLink?.link?.range;\n if (range) {\n this._model.selectionStart = [range.start.x - 1, range.start.y - 1];\n this._model.selectionStartLength = getRangeLength(range, this._bufferService.cols);\n this._model.selectionEnd = undefined;\n return true;\n }\n\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._selectWordAt(coords, allowWhitespaceOnlySelection);\n this._model.selectionEnd = undefined;\n return true;\n }\n return false;\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n this._model.isSelectAllActive = true;\n this.refresh();\n this._onSelectionChange.fire();\n }\n\n public selectLines(start: number, end: number): void {\n this._model.clearSelection();\n start = Math.max(start, 0);\n end = Math.min(end, this._bufferService.buffer.lines.length - 1);\n this._model.selectionStart = [0, start];\n this._model.selectionEnd = [this._bufferService.cols, end];\n this.refresh();\n this._onSelectionChange.fire();\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n */\n private _onTrim(amount: number): void {\n const needsRefresh = this._model.onTrim(amount);\n if (needsRefresh) {\n this.refresh();\n }\n }\n\n /**\n * Gets the 0-based [x, y] buffer coordinates of the current mouse event.\n * @param event The mouse event.\n */\n private _getMouseBufferCoords(event: MouseEvent): [number, number] | undefined {\n const coords = this._mouseService.getCoords(event, this._screenElement, this._bufferService.cols, this._bufferService.rows, true);\n if (!coords) {\n return undefined;\n }\n\n // Convert to 0-based\n coords[0]--;\n coords[1]--;\n\n // Convert viewport coords to buffer coords\n coords[1] += this._bufferService.buffer.ydisp;\n return coords;\n }\n\n /**\n * Gets the amount the viewport should be scrolled based on how far out of the\n * terminal the mouse is.\n * @param event The mouse event.\n */\n private _getMouseEventScrollAmount(event: MouseEvent): number {\n let offset = getCoordsRelativeToElement(event, this._screenElement)[1];\n const terminalHeight = this._renderService.dimensions.canvasHeight;\n if (offset >= 0 && offset <= terminalHeight) {\n return 0;\n }\n if (offset > terminalHeight) {\n offset -= terminalHeight;\n }\n\n offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);\n offset /= DRAG_SCROLL_MAX_THRESHOLD;\n return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));\n }\n\n /**\n * Returns whether the selection manager should force selection, regardless of\n * whether the terminal is in mouse events mode.\n * @param event The mouse event.\n */\n public shouldForceSelection(event: MouseEvent): boolean {\n if (Browser.isMac) {\n return event.altKey && this._optionsService.options.macOptionClickForcesSelection;\n }\n\n return event.shiftKey;\n }\n\n /**\n * Handles te mousedown event, setting up for a new selection.\n * @param event The mousedown event.\n */\n public onMouseDown(event: MouseEvent): void {\n this._mouseDownTimeStamp = event.timeStamp;\n // If we have selection, we want the context menu on right click even if the\n // terminal is in mouse mode.\n if (event.button === 2 && this.hasSelection) {\n return;\n }\n\n // Only action the primary button\n if (event.button !== 0) {\n return;\n }\n\n // Allow selection when using a specific modifier key, even when disabled\n if (!this._enabled) {\n if (!this.shouldForceSelection(event)) {\n return;\n }\n\n // Don't send the mouse down event to the current process, we want to select\n event.stopPropagation();\n }\n\n // Tell the browser not to start a regular selection\n event.preventDefault();\n\n // Reset drag scroll state\n this._dragScrollAmount = 0;\n\n if (this._enabled && event.shiftKey) {\n this._onIncrementalClick(event);\n } else {\n if (event.detail === 1) {\n this._onSingleClick(event);\n } else if (event.detail === 2) {\n this._onDoubleClick(event);\n } else if (event.detail === 3) {\n this._onTripleClick(event);\n }\n }\n\n this._addMouseDownListeners();\n this.refresh(true);\n }\n\n /**\n * Adds listeners when mousedown is triggered.\n */\n private _addMouseDownListeners(): void {\n // Listen on the document so that dragging outside of viewport works\n if (this._screenElement.ownerDocument) {\n this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);\n this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);\n }\n this._dragScrollIntervalTimer = window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);\n }\n\n /**\n * Removes the listeners that are registered when mousedown is triggered.\n */\n private _removeMouseDownListeners(): void {\n if (this._screenElement.ownerDocument) {\n this._screenElement.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);\n this._screenElement.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);\n }\n clearInterval(this._dragScrollIntervalTimer);\n this._dragScrollIntervalTimer = undefined;\n }\n\n /**\n * Performs an incremental click, setting the selection end position to the mouse\n * position.\n * @param event The mouse event.\n */\n private _onIncrementalClick(event: MouseEvent): void {\n if (this._model.selectionStart) {\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n }\n }\n\n /**\n * Performs a single click, resetting relevant state and setting the selection\n * start position.\n * @param event The mouse event.\n */\n private _onSingleClick(event: MouseEvent): void {\n this._model.selectionStartLength = 0;\n this._model.isSelectAllActive = false;\n this._activeSelectionMode = this.shouldColumnSelect(event) ? SelectionMode.COLUMN : SelectionMode.NORMAL;\n\n // Initialize the new selection\n this._model.selectionStart = this._getMouseBufferCoords(event);\n if (!this._model.selectionStart) {\n return;\n }\n this._model.selectionEnd = undefined;\n\n // Ensure the line exists\n const line = this._bufferService.buffer.lines.get(this._model.selectionStart[1]);\n if (!line) {\n return;\n }\n\n // Return early if the click event is not in the buffer (eg. in scroll bar)\n if (line.length === this._model.selectionStart[0]) {\n return;\n }\n\n // If the mouse is over the second half of a wide character, adjust the\n // selection to cover the whole character\n if (line.hasWidth(this._model.selectionStart[0]) === 0) {\n this._model.selectionStart[0]++;\n }\n }\n\n /**\n * Performs a double click, selecting the current word.\n * @param event The mouse event.\n */\n private _onDoubleClick(event: MouseEvent): void {\n if (this._selectWordAtCursor(event, true)) {\n this._activeSelectionMode = SelectionMode.WORD;\n }\n }\n\n /**\n * Performs a triple click, selecting the current line and activating line\n * select mode.\n * @param event The mouse event.\n */\n private _onTripleClick(event: MouseEvent): void {\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._activeSelectionMode = SelectionMode.LINE;\n this._selectLineAt(coords[1]);\n }\n }\n\n /**\n * Returns whether the selection manager should operate in column select mode\n * @param event the mouse or keyboard event\n */\n public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean {\n return event.altKey && !(Browser.isMac && this._optionsService.options.macOptionClickForcesSelection);\n }\n\n /**\n * Handles the mousemove event when the mouse button is down, recording the\n * end of the selection and refreshing the selection.\n * @param event The mousemove event.\n */\n private _onMouseMove(event: MouseEvent): void {\n // If the mousemove listener is active it means that a selection is\n // currently being made, we should stop propagation to prevent mouse events\n // to be sent to the pty.\n event.stopImmediatePropagation();\n\n // Do nothing if there is no selection start, this can happen if the first\n // click in the terminal is an incremental click\n if (!this._model.selectionStart) {\n return;\n }\n\n // Record the previous position so we know whether to redraw the selection\n // at the end.\n const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;\n\n // Set the initial selection end based on the mouse coordinates\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n if (!this._model.selectionEnd) {\n this.refresh(true);\n return;\n }\n\n // Select the entire line if line select mode is active.\n if (this._activeSelectionMode === SelectionMode.LINE) {\n if (this._model.selectionEnd[1] < this._model.selectionStart[1]) {\n this._model.selectionEnd[0] = 0;\n } else {\n this._model.selectionEnd[0] = this._bufferService.cols;\n }\n } else if (this._activeSelectionMode === SelectionMode.WORD) {\n this._selectToWordAt(this._model.selectionEnd);\n }\n\n // Determine the amount of scrolling that will happen.\n this._dragScrollAmount = this._getMouseEventScrollAmount(event);\n\n // If the cursor was above or below the viewport, make sure it's at the\n // start or end of the viewport respectively. This should only happen when\n // NOT in column select mode.\n if (this._activeSelectionMode !== SelectionMode.COLUMN) {\n if (this._dragScrollAmount > 0) {\n this._model.selectionEnd[0] = this._bufferService.cols;\n } else if (this._dragScrollAmount < 0) {\n this._model.selectionEnd[0] = 0;\n }\n }\n\n // If the character is a wide character include the cell to the right in the\n // selection. Note that selections at the very end of the line will never\n // have a character.\n const buffer = this._bufferService.buffer;\n if (this._model.selectionEnd[1] < buffer.lines.length) {\n const line = buffer.lines.get(this._model.selectionEnd[1]);\n if (line && line.hasWidth(this._model.selectionEnd[0]) === 0) {\n this._model.selectionEnd[0]++;\n }\n }\n\n // Only draw here if the selection changes.\n if (!previousSelectionEnd ||\n previousSelectionEnd[0] !== this._model.selectionEnd[0] ||\n previousSelectionEnd[1] !== this._model.selectionEnd[1]) {\n this.refresh(true);\n }\n }\n\n /**\n * The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the\n * scrolling of the viewport.\n */\n private _dragScroll(): void {\n if (!this._model.selectionEnd || !this._model.selectionStart) {\n return;\n }\n if (this._dragScrollAmount) {\n this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false });\n // Re-evaluate selection\n // If the cursor was above or below the viewport, make sure it's at the\n // start or end of the viewport respectively. This should only happen when\n // NOT in column select mode.\n const buffer = this._bufferService.buffer;\n if (this._dragScrollAmount > 0) {\n if (this._activeSelectionMode !== SelectionMode.COLUMN) {\n this._model.selectionEnd[0] = this._bufferService.cols;\n }\n this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows, buffer.lines.length - 1);\n } else {\n if (this._activeSelectionMode !== SelectionMode.COLUMN) {\n this._model.selectionEnd[0] = 0;\n }\n this._model.selectionEnd[1] = buffer.ydisp;\n }\n this.refresh();\n }\n }\n\n /**\n * Handles the mouseup event, removing the mousedown listeners.\n * @param event The mouseup event.\n */\n private _onMouseUp(event: MouseEvent): void {\n const timeElapsed = event.timeStamp - this._mouseDownTimeStamp;\n\n this._removeMouseDownListeners();\n\n if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.getOption('altClickMovesCursor')) {\n if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {\n const coordinates = this._mouseService.getCoords(\n event,\n this._element,\n this._bufferService.cols,\n this._bufferService.rows,\n false\n );\n if (coordinates && coordinates[0] !== undefined && coordinates[1] !== undefined) {\n const sequence = moveToCellSequence(coordinates[0] - 1, coordinates[1] - 1, this._bufferService, this._coreService.decPrivateModes.applicationCursorKeys);\n this._coreService.triggerDataEvent(sequence, true);\n }\n }\n } else {\n this._fireEventIfSelectionChanged();\n }\n }\n\n private _fireEventIfSelectionChanged(): void {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n const hasSelection = !!start && !!end && (start[0] !== end[0] || start[1] !== end[1]);\n\n if (!hasSelection) {\n if (this._oldHasSelection) {\n this._fireOnSelectionChange(start, end, hasSelection);\n }\n return;\n }\n\n // Sanity check, these should not be undefined as there is a selection\n if (!start || !end) {\n return;\n }\n\n if (!this._oldSelectionStart || !this._oldSelectionEnd || (\n start[0] !== this._oldSelectionStart[0] || start[1] !== this._oldSelectionStart[1] ||\n end[0] !== this._oldSelectionEnd[0] || end[1] !== this._oldSelectionEnd[1])) {\n\n this._fireOnSelectionChange(start, end, hasSelection);\n }\n }\n\n private _fireOnSelectionChange(start: [number, number] | undefined, end: [number, number] | undefined, hasSelection: boolean): void {\n this._oldSelectionStart = start;\n this._oldSelectionEnd = end;\n this._oldHasSelection = hasSelection;\n this._onSelectionChange.fire();\n }\n\n private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {\n this.clearSelection();\n // Only adjust the selection on trim, shiftElements is rarely used (only in\n // reverseIndex) and delete in a splice is only ever used when the same\n // number of elements was just added. Given this is could actually be\n // beneficial to leave the selection as is for these cases.\n this._trimListener.dispose();\n this._trimListener = e.activeBuffer.lines.onTrim(amount => this._onTrim(amount));\n }\n\n /**\n * Converts a viewport column to the character index on the buffer line, the\n * latter takes into account wide characters.\n * @param coords The coordinates to find the 2 index for.\n */\n private _convertViewportColToCharacterIndex(bufferLine: IBufferLine, coords: [number, number]): number {\n let charIndex = coords[0];\n for (let i = 0; coords[0] >= i; i++) {\n const length = bufferLine.loadCell(i, this._workCell).getChars().length;\n if (this._workCell.getWidth() === 0) {\n // Wide characters aren't included in the line string so decrement the\n // index so the index is back on the wide character.\n charIndex--;\n } else if (length > 1 && coords[0] !== i) {\n // Emojis take up multiple characters, so adjust accordingly. For these\n // we don't want ot include the character at the column as we're\n // returning the start index in the string, not the end index.\n charIndex += length - 1;\n }\n }\n return charIndex;\n }\n\n public setSelection(col: number, row: number, length: number): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this._model.selectionStart = [col, row];\n this._model.selectionStartLength = length;\n this.refresh();\n }\n\n public rightClickSelect(ev: MouseEvent): void {\n if (!this._isClickInSelection(ev)) {\n if (this._selectWordAtCursor(ev, false)) {\n this.refresh(true);\n }\n this._fireEventIfSelectionChanged();\n }\n }\n\n /**\n * Gets positional information for the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition | undefined {\n // Ensure coords are within viewport (eg. not within scroll bar)\n if (coords[0] >= this._bufferService.cols) {\n return undefined;\n }\n\n const buffer = this._bufferService.buffer;\n const bufferLine = buffer.lines.get(coords[1]);\n if (!bufferLine) {\n return undefined;\n }\n\n const line = buffer.translateBufferLineToString(coords[1], false);\n\n // Get actual index, taking into consideration wide characters\n let startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);\n let endIndex = startIndex;\n\n // Record offset to be used later\n const charOffset = coords[0] - startIndex;\n let leftWideCharCount = 0;\n let rightWideCharCount = 0;\n let leftLongCharOffset = 0;\n let rightLongCharOffset = 0;\n\n if (line.charAt(startIndex) === ' ') {\n // Expand until non-whitespace is hit\n while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') {\n startIndex--;\n }\n while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') {\n endIndex++;\n }\n } else {\n // Expand until whitespace is hit. This algorithm works by scanning left\n // and right from the starting position, keeping both the index format\n // (line) and the column format (bufferLine) in sync. When a wide\n // character is hit, it is recorded and the column index is adjusted.\n let startCol = coords[0];\n let endCol = coords[0];\n\n // Consider the initial position, skip it and increment the wide char\n // variable\n if (bufferLine.getWidth(startCol) === 0) {\n leftWideCharCount++;\n startCol--;\n }\n if (bufferLine.getWidth(endCol) === 2) {\n rightWideCharCount++;\n endCol++;\n }\n\n // Adjust the end index for characters whose length are > 1 (emojis)\n const length = bufferLine.getString(endCol).length;\n if (length > 1) {\n rightLongCharOffset += length - 1;\n endIndex += length - 1;\n }\n\n // Expand the string in both directions until a space is hit\n while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine.loadCell(startCol - 1, this._workCell))) {\n bufferLine.loadCell(startCol - 1, this._workCell);\n const length = this._workCell.getChars().length;\n if (this._workCell.getWidth() === 0) {\n // If the next character is a wide char, record it and skip the column\n leftWideCharCount++;\n startCol--;\n } else if (length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n leftLongCharOffset += length - 1;\n startIndex -= length - 1;\n }\n startIndex--;\n startCol--;\n }\n while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine.loadCell(endCol + 1, this._workCell))) {\n bufferLine.loadCell(endCol + 1, this._workCell);\n const length = this._workCell.getChars().length;\n if (this._workCell.getWidth() === 2) {\n // If the next character is a wide char, record it and skip the column\n rightWideCharCount++;\n endCol++;\n } else if (length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n rightLongCharOffset += length - 1;\n endIndex += length - 1;\n }\n endIndex++;\n endCol++;\n }\n }\n\n // Incremenet the end index so it is at the start of the next character\n endIndex++;\n\n // Calculate the start _column_, converting the the string indexes back to\n // column coordinates.\n let start =\n startIndex // The index of the selection's start char in the line string\n + charOffset // The difference between the initial char's column and index\n - leftWideCharCount // The number of wide chars left of the initial char\n + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n\n // Calculate the length in _columns_, converting the the string indexes back\n // to column coordinates.\n let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols\n endIndex // The index of the selection's end char in the line string\n - startIndex // The index of the selection's start char in the line string\n + leftWideCharCount // The number of wide chars left of the initial char\n + rightWideCharCount // The number of wide chars right of the initial char (inclusive)\n - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)\n\n if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') {\n return undefined;\n }\n\n // Recurse upwards if the line is wrapped and the word wraps to the above line\n if (followWrappedLinesAbove) {\n if (start === 0 && bufferLine.getCodePoint(0) !== 32 /* ' ' */) {\n const previousBufferLine = buffer.lines.get(coords[1] - 1);\n if (previousBufferLine && bufferLine.isWrapped && previousBufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {\n const previousLineWordPosition = this._getWordAt([this._bufferService.cols - 1, coords[1] - 1], false, true, false);\n if (previousLineWordPosition) {\n const offset = this._bufferService.cols - previousLineWordPosition.start;\n start -= offset;\n length += offset;\n }\n }\n }\n }\n\n // Recurse downwards if the line is wrapped and the word wraps to the next line\n if (followWrappedLinesBelow) {\n if (start + length === this._bufferService.cols && bufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {\n const nextBufferLine = buffer.lines.get(coords[1] + 1);\n if (nextBufferLine?.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /* ' ' */) {\n const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true);\n if (nextLineWordPosition) {\n length += nextLineWordPosition.length;\n }\n }\n }\n }\n\n return { start, length };\n }\n\n /**\n * Selects the word at the coordinates specified.\n * @param coords The coordinates to get the word at.\n * @param allowWhitespaceOnlySelection If whitespace should be selected\n */\n protected _selectWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): void {\n const wordPosition = this._getWordAt(coords, allowWhitespaceOnlySelection);\n if (wordPosition) {\n // Adjust negative start value\n while (wordPosition.start < 0) {\n wordPosition.start += this._bufferService.cols;\n coords[1]--;\n }\n this._model.selectionStart = [wordPosition.start, coords[1]];\n this._model.selectionStartLength = wordPosition.length;\n }\n }\n\n /**\n * Sets the selection end to the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _selectToWordAt(coords: [number, number]): void {\n const wordPosition = this._getWordAt(coords, true);\n if (wordPosition) {\n let endRow = coords[1];\n\n // Adjust negative start value\n while (wordPosition.start < 0) {\n wordPosition.start += this._bufferService.cols;\n endRow--;\n }\n\n // Adjust wrapped length value, this only needs to happen when values are reversed as in that\n // case we're interested in the start of the word, not the end\n if (!this._model.areSelectionValuesReversed()) {\n while (wordPosition.start + wordPosition.length > this._bufferService.cols) {\n wordPosition.length -= this._bufferService.cols;\n endRow++;\n }\n }\n\n this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : wordPosition.start + wordPosition.length, endRow];\n }\n }\n\n /**\n * Gets whether the character is considered a word separator by the select\n * word logic.\n * @param char The character to check.\n */\n private _isCharWordSeparator(cell: CellData): boolean {\n // Zero width characters are never separators as they are always to the\n // right of wide characters\n if (cell.getWidth() === 0) {\n return false;\n }\n return this._optionsService.options.wordSeparator.indexOf(cell.getChars()) >= 0;\n }\n\n /**\n * Selects the line specified.\n * @param line The line index.\n */\n protected _selectLineAt(line: number): void {\n const wrappedRange = this._bufferService.buffer.getWrappedRangeForLine(line);\n this._model.selectionStart = [0, wrappedRange.first];\n this._model.selectionEnd = [this._bufferService.cols, wrappedRange.last];\n this._model.selectionStartLength = 0;\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IEvent } from 'common/EventEmitter';\nimport { IRenderDimensions, IRenderer } from 'browser/renderer/Types';\nimport { IColorSet } from 'browser/Types';\nimport { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';\nimport { createDecorator } from 'common/services/ServiceRegistry';\nimport { IDisposable } from 'common/Types';\n\nexport const ICharSizeService = createDecorator('CharSizeService');\nexport interface ICharSizeService {\n serviceBrand: undefined;\n\n readonly width: number;\n readonly height: number;\n readonly hasValidSize: boolean;\n\n readonly onCharSizeChange: IEvent;\n\n measure(): void;\n}\n\nexport const ICoreBrowserService = createDecorator('CoreBrowserService');\nexport interface ICoreBrowserService {\n serviceBrand: undefined;\n\n readonly isFocused: boolean;\n}\n\nexport const IMouseService = createDecorator('MouseService');\nexport interface IMouseService {\n serviceBrand: undefined;\n\n getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;\n getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined;\n}\n\nexport const IRenderService = createDecorator('RenderService');\nexport interface IRenderService extends IDisposable {\n serviceBrand: undefined;\n\n onDimensionsChange: IEvent;\n /**\n * Fires when buffer changes are rendered. This does not fire when only cursor\n * or selections are rendered.\n */\n onRenderedBufferChange: IEvent<{ start: number, end: number }>;\n onRefreshRequest: IEvent<{ start: number, end: number }>;\n\n dimensions: IRenderDimensions;\n\n refreshRows(start: number, end: number): void;\n clearTextureAtlas(): void;\n resize(cols: number, rows: number): void;\n changeOptions(): void;\n setRenderer(renderer: IRenderer): void;\n setColors(colors: IColorSet): void;\n onDevicePixelRatioChange(): void;\n onResize(cols: number, rows: number): void;\n // TODO: Is this useful when we have onResize?\n onCharSizeChanged(): void;\n onBlur(): void;\n onFocus(): void;\n onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;\n onCursorMove(): void;\n clear(): void;\n}\n\nexport const ISelectionService = createDecorator('SelectionService');\nexport interface ISelectionService {\n serviceBrand: undefined;\n\n readonly selectionText: string;\n readonly hasSelection: boolean;\n readonly selectionStart: [number, number] | undefined;\n readonly selectionEnd: [number, number] | undefined;\n\n readonly onLinuxMouseSelection: IEvent;\n readonly onRequestRedraw: IEvent;\n readonly onRequestScrollLines: IEvent;\n readonly onSelectionChange: IEvent;\n\n disable(): void;\n enable(): void;\n reset(): void;\n setSelection(row: number, col: number, length: number): void;\n selectAll(): void;\n selectLines(start: number, end: number): void;\n clearSelection(): void;\n rightClickSelect(event: MouseEvent): void;\n shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean;\n shouldForceSelection(event: MouseEvent): boolean;\n refresh(isLinuxMouseSelection?: boolean): void;\n onMouseDown(event: MouseEvent): void;\n}\n\nexport const ISoundService = createDecorator('SoundService');\nexport interface ISoundService {\n serviceBrand: undefined;\n\n playBellSound(): void;\n}\n\n\nexport const ICharacterJoinerService = createDecorator('CharacterJoinerService');\nexport interface ICharacterJoinerService {\n serviceBrand: undefined;\n\n register(handler: (text: string) => [number, number][]): number;\n deregister(joinerId: number): boolean;\n getJoinedCharacters(row: number): [number, number][];\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IOptionsService } from 'common/services/Services';\nimport { ISoundService } from 'browser/services/Services';\n\nexport class SoundService implements ISoundService {\n public serviceBrand: undefined;\n\n private static _audioContext: AudioContext;\n\n public static get audioContext(): AudioContext | null {\n if (!SoundService._audioContext) {\n const audioContextCtor: typeof AudioContext = (window as any).AudioContext || (window as any).webkitAudioContext;\n if (!audioContextCtor) {\n console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version');\n return null;\n }\n SoundService._audioContext = new audioContextCtor();\n }\n return SoundService._audioContext;\n }\n\n constructor(\n @IOptionsService private _optionsService: IOptionsService\n ) {\n }\n\n public playBellSound(): void {\n const ctx = SoundService.audioContext;\n if (!ctx) {\n return;\n }\n const bellAudioSource = ctx.createBufferSource();\n ctx.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._optionsService.options.bellSound)), (buffer) => {\n bellAudioSource.buffer = buffer;\n bellAudioSource.connect(ctx.destination);\n bellAudioSource.start(0);\n });\n }\n\n private _base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binaryString = window.atob(base64);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return bytes.buffer;\n }\n\n private _removeMimeType(dataURI: string): string {\n // Split the input to get the mime-type and the data itself\n const splitUri = dataURI.split(',');\n\n // Return only the data\n return splitUri[1];\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICircularList } from 'common/Types';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\n\nexport interface IInsertEvent {\n index: number;\n amount: number;\n}\n\nexport interface IDeleteEvent {\n index: number;\n amount: number;\n}\n\n/**\n * Represents a circular list; a list with a maximum size that wraps around when push is called,\n * overriding values at the start of the list.\n */\nexport class CircularList implements ICircularList {\n protected _array: (T | undefined)[];\n private _startIndex: number;\n private _length: number;\n\n public onDeleteEmitter = new EventEmitter();\n public get onDelete(): IEvent { return this.onDeleteEmitter.event; }\n public onInsertEmitter = new EventEmitter();\n public get onInsert(): IEvent { return this.onInsertEmitter.event; }\n public onTrimEmitter = new EventEmitter();\n public get onTrim(): IEvent { return this.onTrimEmitter.event; }\n\n constructor(\n private _maxLength: number\n ) {\n this._array = new Array(this._maxLength);\n this._startIndex = 0;\n this._length = 0;\n }\n\n public get maxLength(): number {\n return this._maxLength;\n }\n\n public set maxLength(newMaxLength: number) {\n // There was no change in maxLength, return early.\n if (this._maxLength === newMaxLength) {\n return;\n }\n\n // Reconstruct array, starting at index 0. Only transfer values from the\n // indexes 0 to length.\n const newArray = new Array(newMaxLength);\n for (let i = 0; i < Math.min(newMaxLength, this.length); i++) {\n newArray[i] = this._array[this._getCyclicIndex(i)];\n }\n this._array = newArray;\n this._maxLength = newMaxLength;\n this._startIndex = 0;\n }\n\n public get length(): number {\n return this._length;\n }\n\n public set length(newLength: number) {\n if (newLength > this._length) {\n for (let i = this._length; i < newLength; i++) {\n this._array[i] = undefined;\n }\n }\n this._length = newLength;\n }\n\n /**\n * Gets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index of the value to get.\n * @return The value corresponding to the index.\n */\n public get(index: number): T | undefined {\n return this._array[this._getCyclicIndex(index)];\n }\n\n /**\n * Sets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index to set.\n * @param value The value to set.\n */\n public set(index: number, value: T | undefined): void {\n this._array[this._getCyclicIndex(index)] = value;\n }\n\n /**\n * Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0\n * if the maximum length is reached.\n * @param value The value to push onto the list.\n */\n public push(value: T): void {\n this._array[this._getCyclicIndex(this._length)] = value;\n if (this._length === this._maxLength) {\n this._startIndex = ++this._startIndex % this._maxLength;\n this.onTrimEmitter.fire(1);\n } else {\n this._length++;\n }\n }\n\n /**\n * Advance ringbuffer index and return current element for recycling.\n * Note: The buffer must be full for this method to work.\n * @throws When the buffer is not full.\n */\n public recycle(): T {\n if (this._length !== this._maxLength) {\n throw new Error('Can only recycle when the buffer is full');\n }\n this._startIndex = ++this._startIndex % this._maxLength;\n this.onTrimEmitter.fire(1);\n return this._array[this._getCyclicIndex(this._length - 1)]!;\n }\n\n /**\n * Ringbuffer is at max length.\n */\n public get isFull(): boolean {\n return this._length === this._maxLength;\n }\n\n /**\n * Removes and returns the last value on the list.\n * @return The popped value.\n */\n public pop(): T | undefined {\n return this._array[this._getCyclicIndex(this._length-- - 1)];\n }\n\n /**\n * Deletes and/or inserts items at a particular index (in that order). Unlike\n * Array.prototype.splice, this operation does not return the deleted items as a new array in\n * order to save creating a new array. Note that this operation may shift all values in the list\n * in the worst case.\n * @param start The index to delete and/or insert.\n * @param deleteCount The number of elements to delete.\n * @param items The items to insert.\n */\n public splice(start: number, deleteCount: number, ...items: T[]): void {\n // Delete items\n if (deleteCount) {\n for (let i = start; i < this._length - deleteCount; i++) {\n this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)];\n }\n this._length -= deleteCount;\n this.onDeleteEmitter.fire({ index: start, amount: deleteCount });\n }\n\n // Add items\n for (let i = this._length - 1; i >= start; i--) {\n this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)];\n }\n for (let i = 0; i < items.length; i++) {\n this._array[this._getCyclicIndex(start + i)] = items[i];\n }\n if (items.length) {\n this.onInsertEmitter.fire({ index: start, amount: items.length });\n }\n\n // Adjust length as needed\n if (this._length + items.length > this._maxLength) {\n const countToTrim = (this._length + items.length) - this._maxLength;\n this._startIndex += countToTrim;\n this._length = this._maxLength;\n this.onTrimEmitter.fire(countToTrim);\n } else {\n this._length += items.length;\n }\n }\n\n /**\n * Trims a number of items from the start of the list.\n * @param count The number of items to remove.\n */\n public trimStart(count: number): void {\n if (count > this._length) {\n count = this._length;\n }\n this._startIndex += count;\n this._length -= count;\n this.onTrimEmitter.fire(count);\n }\n\n public shiftElements(start: number, count: number, offset: number): void {\n if (count <= 0) {\n return;\n }\n if (start < 0 || start >= this._length) {\n throw new Error('start argument out of range');\n }\n if (start + offset < 0) {\n throw new Error('Cannot shift elements in list beyond index 0');\n }\n\n if (offset > 0) {\n for (let i = count - 1; i >= 0; i--) {\n this.set(start + i + offset, this.get(start + i));\n }\n const expandListBy = (start + count + offset) - this._length;\n if (expandListBy > 0) {\n this._length += expandListBy;\n while (this._length > this._maxLength) {\n this._length--;\n this._startIndex++;\n this.onTrimEmitter.fire(1);\n }\n }\n } else {\n for (let i = 0; i < count; i++) {\n this.set(start + i + offset, this.get(start + i));\n }\n }\n }\n\n /**\n * Gets the cyclic index for the specified regular index. The cyclic index can then be used on the\n * backing array to get the element associated with the regular index.\n * @param index The regular index.\n * @returns The cyclic index.\n */\n private _getCyclicIndex(index: number): number {\n return (this._startIndex + index) % this._maxLength;\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n/*\n * A simple utility for cloning values\n */\nexport function clone(val: T, depth: number = 5): T {\n if (typeof val !== 'object') {\n return val;\n }\n\n // If we're cloning an array, use an array as the base, otherwise use an object\n const clonedObject: any = Array.isArray(val) ? [] : {};\n\n for (const key in val) {\n // Recursively clone eack item unless we're at the maximum depth\n clonedObject[key] = depth <= 1 ? val[key] : (val[key] && clone(val[key], depth - 1));\n }\n\n return clonedObject as T;\n}\n","/**\n * Copyright (c) 2014-2020 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n *\n * Originally forked from (with the author's permission):\n * Fabrice Bellard's javascript vt100 for jslinux:\n * http://bellard.org/jslinux/\n * Copyright (c) 2011 Fabrice Bellard\n * The original design remains. The terminal itself\n * has been extended to include xterm CSI codes, among\n * other features.\n *\n * Terminal Emulation References:\n * http://vt100.net/\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * http://invisible-island.net/vttest/\n * http://www.inwap.com/pdp10/ansicode.txt\n * http://linux.die.net/man/4/console_codes\n * http://linux.die.net/man/7/urxvt\n */\n\nimport { Disposable } from 'common/Lifecycle';\nimport { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService, LogLevelEnum, ITerminalOptions } from 'common/services/Services';\nimport { InstantiationService } from 'common/services/InstantiationService';\nimport { LogService } from 'common/services/LogService';\nimport { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';\nimport { OptionsService } from 'common/services/OptionsService';\nimport { IDisposable, IBufferLine, IAttributeData, ICoreTerminal, IKeyboardEvent, IScrollEvent, ScrollSource, ITerminalOptions as IPublicTerminalOptions } from 'common/Types';\nimport { CoreService } from 'common/services/CoreService';\nimport { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';\nimport { CoreMouseService } from 'common/services/CoreMouseService';\nimport { DirtyRowService } from 'common/services/DirtyRowService';\nimport { UnicodeService } from 'common/services/UnicodeService';\nimport { CharsetService } from 'common/services/CharsetService';\nimport { updateWindowsModeWrappedState } from 'common/WindowsMode';\nimport { IFunctionIdentifier, IParams } from 'common/parser/Types';\nimport { IBufferSet } from 'common/buffer/Types';\nimport { InputHandler } from 'common/InputHandler';\nimport { WriteBuffer } from 'common/input/WriteBuffer';\n\n// Only trigger this warning a single time per session\nlet hasWriteSyncWarnHappened = false;\n\nexport abstract class CoreTerminal extends Disposable implements ICoreTerminal {\n protected readonly _instantiationService: IInstantiationService;\n protected readonly _bufferService: IBufferService;\n protected readonly _logService: ILogService;\n protected readonly _charsetService: ICharsetService;\n protected readonly _dirtyRowService: IDirtyRowService;\n\n public readonly coreMouseService: ICoreMouseService;\n public readonly coreService: ICoreService;\n public readonly unicodeService: IUnicodeService;\n public readonly optionsService: IOptionsService;\n\n protected _inputHandler: InputHandler;\n private _writeBuffer: WriteBuffer;\n private _windowsMode: IDisposable | undefined;\n\n private _onBinary = new EventEmitter();\n public get onBinary(): IEvent { return this._onBinary.event; }\n private _onData = new EventEmitter();\n public get onData(): IEvent { return this._onData.event; }\n protected _onLineFeed = new EventEmitter();\n public get onLineFeed(): IEvent { return this._onLineFeed.event; }\n private _onResize = new EventEmitter<{ cols: number, rows: number }>();\n public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }\n protected _onScroll = new EventEmitter();\n /**\n * Internally we track the source of the scroll but this is meaningless outside the library so\n * it's filtered out.\n */\n protected _onScrollApi?: EventEmitter;\n public get onScroll(): IEvent {\n if (!this._onScrollApi) {\n this._onScrollApi = new EventEmitter();\n this.register(this._onScroll.event(ev => {\n this._onScrollApi?.fire(ev.position);\n }));\n }\n return this._onScrollApi.event;\n }\n\n public get cols(): number { return this._bufferService.cols; }\n public get rows(): number { return this._bufferService.rows; }\n public get buffers(): IBufferSet { return this._bufferService.buffers; }\n public get options(): ITerminalOptions { return this.optionsService.options; }\n public set options(options: ITerminalOptions) {\n for (const key in options) {\n this.optionsService.options[key] = options[key];\n }\n }\n\n constructor(\n options: Partial\n ) {\n super();\n\n // Setup and initialize services\n this._instantiationService = new InstantiationService();\n this.optionsService = new OptionsService(options);\n this._instantiationService.setService(IOptionsService, this.optionsService);\n this._bufferService = this.register(this._instantiationService.createInstance(BufferService));\n this._instantiationService.setService(IBufferService, this._bufferService);\n this._logService = this._instantiationService.createInstance(LogService);\n this._instantiationService.setService(ILogService, this._logService);\n this.coreService = this.register(this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()));\n this._instantiationService.setService(ICoreService, this.coreService);\n this.coreMouseService = this._instantiationService.createInstance(CoreMouseService);\n this._instantiationService.setService(ICoreMouseService, this.coreMouseService);\n this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);\n this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);\n this.unicodeService = this._instantiationService.createInstance(UnicodeService);\n this._instantiationService.setService(IUnicodeService, this.unicodeService);\n this._charsetService = this._instantiationService.createInstance(CharsetService);\n this._instantiationService.setService(ICharsetService, this._charsetService);\n\n // Register input handler and handle/forward events\n this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this.coreService, this._dirtyRowService, this._logService, this.optionsService, this.coreMouseService, this.unicodeService);\n this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed));\n this.register(this._inputHandler);\n\n // Setup listeners\n this.register(forwardEvent(this._bufferService.onResize, this._onResize));\n this.register(forwardEvent(this.coreService.onData, this._onData));\n this.register(forwardEvent(this.coreService.onBinary, this._onBinary));\n this.register(this.optionsService.onOptionChange(key => this._updateOptions(key)));\n this.register(this._bufferService.onScroll(event => {\n this._onScroll.fire({ position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL });\n this._dirtyRowService.markRangeDirty(this._bufferService.buffer.scrollTop, this._bufferService.buffer.scrollBottom);\n }));\n this.register(this._inputHandler.onScroll(event => {\n this._onScroll.fire({ position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL });\n this._dirtyRowService.markRangeDirty(this._bufferService.buffer.scrollTop, this._bufferService.buffer.scrollBottom);\n }));\n\n // Setup WriteBuffer\n this._writeBuffer = new WriteBuffer((data, promiseResult) => this._inputHandler.parse(data, promiseResult));\n }\n\n public dispose(): void {\n if (this._isDisposed) {\n return;\n }\n super.dispose();\n this._windowsMode?.dispose();\n this._windowsMode = undefined;\n }\n\n public write(data: string | Uint8Array, callback?: () => void): void {\n this._writeBuffer.write(data, callback);\n }\n\n /**\n * Write data to terminal synchonously.\n *\n * This method is unreliable with async parser handlers, thus should not\n * be used anymore. If you need blocking semantics on data input consider\n * `write` with a callback instead.\n *\n * @deprecated Unreliable, will be removed soon.\n */\n public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void {\n if (this._logService.logLevel <= LogLevelEnum.WARN && !hasWriteSyncWarnHappened) {\n this._logService.warn('writeSync is unreliable and will be removed soon.');\n hasWriteSyncWarnHappened = true;\n }\n this._writeBuffer.writeSync(data, maxSubsequentCalls);\n }\n\n public resize(x: number, y: number): void {\n if (isNaN(x) || isNaN(y)) {\n return;\n }\n\n x = Math.max(x, MINIMUM_COLS);\n y = Math.max(y, MINIMUM_ROWS);\n\n this._bufferService.resize(x, y);\n }\n\n /**\n * Scroll the terminal down 1 row, creating a blank line.\n * @param isWrapped Whether the new line is wrapped from the previous line.\n */\n public scroll(eraseAttr: IAttributeData, isWrapped: boolean = false): void {\n this._bufferService.scroll(eraseAttr, isWrapped);\n }\n\n /**\n * Scroll the display of the terminal\n * @param disp The number of lines to scroll down (negative scroll up).\n * @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used\n * to avoid unwanted events being handled by the viewport when the event was triggered from the\n * viewport originally.\n */\n public scrollLines(disp: number, suppressScrollEvent?: boolean, source?: ScrollSource): void {\n this._bufferService.scrollLines(disp, suppressScrollEvent, source);\n }\n\n /**\n * Scroll the display of the terminal by a number of pages.\n * @param pageCount The number of pages to scroll (negative scrolls up).\n */\n public scrollPages(pageCount: number): void {\n this._bufferService.scrollPages(pageCount);\n }\n\n /**\n * Scrolls the display of the terminal to the top.\n */\n public scrollToTop(): void {\n this._bufferService.scrollToTop();\n }\n\n /**\n * Scrolls the display of the terminal to the bottom.\n */\n public scrollToBottom(): void {\n this._bufferService.scrollToBottom();\n }\n\n public scrollToLine(line: number): void {\n this._bufferService.scrollToLine(line);\n }\n\n /** Add handler for ESC escape sequence. See xterm.d.ts for details. */\n public registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise): IDisposable {\n return this._inputHandler.registerEscHandler(id, callback);\n }\n\n /** Add handler for DCS escape sequence. See xterm.d.ts for details. */\n public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise): IDisposable {\n return this._inputHandler.registerDcsHandler(id, callback);\n }\n\n /** Add handler for CSI escape sequence. See xterm.d.ts for details. */\n public registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise): IDisposable {\n return this._inputHandler.registerCsiHandler(id, callback);\n }\n\n /** Add handler for OSC escape sequence. See xterm.d.ts for details. */\n public registerOscHandler(ident: number, callback: (data: string) => boolean | Promise): IDisposable {\n return this._inputHandler.registerOscHandler(ident, callback);\n }\n\n protected _setup(): void {\n if (this.optionsService.options.windowsMode) {\n this._enableWindowsMode();\n }\n }\n\n public reset(): void {\n this._inputHandler.reset();\n this._bufferService.reset();\n this._charsetService.reset();\n this.coreService.reset();\n this.coreMouseService.reset();\n }\n\n protected _updateOptions(key: string): void {\n // TODO: These listeners should be owned by individual components\n switch (key) {\n case 'scrollback':\n this.buffers.resize(this.cols, this.rows);\n break;\n case 'windowsMode':\n if (this.optionsService.options.windowsMode) {\n this._enableWindowsMode();\n } else {\n this._windowsMode?.dispose();\n this._windowsMode = undefined;\n }\n break;\n }\n }\n\n protected _enableWindowsMode(): void {\n if (!this._windowsMode) {\n const disposables: IDisposable[] = [];\n disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));\n disposables.push(this.registerCsiHandler({ final: 'H' }, () => {\n updateWindowsModeWrappedState(this._bufferService);\n return false;\n }));\n this._windowsMode = {\n dispose: () => {\n for (const d of disposables) {\n d.dispose();\n }\n }\n };\n }\n }\n}\n","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IDisposable } from 'common/Types';\n\ninterface IListener {\n (arg1: T, arg2: U): void;\n}\n\nexport interface IEvent {\n (listener: (arg1: T, arg2: U) => any): IDisposable;\n}\n\nexport interface IEventEmitter {\n event: IEvent;\n fire(arg1: T, arg2: U): void;\n dispose(): void;\n}\n\nexport class EventEmitter implements IEventEmitter {\n private _listeners: IListener[] = [];\n private _event?: IEvent;\n private _disposed: boolean = false;\n\n public get event(): IEvent {\n if (!this._event) {\n this._event = (listener: (arg1: T, arg2: U) => any) => {\n this._listeners.push(listener);\n const disposable = {\n dispose: () => {\n if (!this._disposed) {\n for (let i = 0; i < this._listeners.length; i++) {\n if (this._listeners[i] === listener) {\n this._listeners.splice(i, 1);\n return;\n }\n }\n }\n }\n };\n return disposable;\n };\n }\n return this._event;\n }\n\n public fire(arg1: T, arg2: U): void {\n const queue: IListener[] = [];\n for (let i = 0; i < this._listeners.length; i++) {\n queue.push(this._listeners[i]);\n }\n for (let i = 0; i < queue.length; i++) {\n queue[i].call(undefined, arg1, arg2);\n }\n }\n\n public dispose(): void {\n if (this._listeners) {\n this._listeners.length = 0;\n }\n this._disposed = true;\n }\n}\n\nexport function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable {\n return from(e => to.fire(e));\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n */\n\nimport { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType } from 'common/Types';\nimport { C0, C1 } from 'common/data/EscapeSequences';\nimport { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';\nimport { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';\nimport { Disposable } from 'common/Lifecycle';\nimport { concat } from 'common/TypedArrayUtils';\nimport { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder';\nimport { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';\nimport { EventEmitter, IEvent } from 'common/EventEmitter';\nimport { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types';\nimport { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content, UnderlineStyle } from 'common/buffer/Constants';\nimport { CellData } from 'common/buffer/CellData';\nimport { AttributeData } from 'common/buffer/AttributeData';\nimport { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, LogLevelEnum } from 'common/services/Services';\nimport { OscHandler } from 'common/parser/OscParser';\nimport { DcsHandler } from 'common/parser/DcsParser';\nimport { IBuffer } from 'common/buffer/Types';\nimport { parseColor } from 'common/input/XParseColor';\n\n/**\n * Map collect to glevel. Used in `selectCharset`.\n */\nconst GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2 };\n\n/**\n * VT commands done by the parser - FIXME: move this to the parser?\n */\n// @vt: #Y ESC CSI \"Control Sequence Introducer\" \"ESC [\" \"Start of a CSI sequence.\"\n// @vt: #Y ESC OSC \"Operating System Command\" \"ESC ]\" \"Start of an OSC sequence.\"\n// @vt: #Y ESC DCS \"Device Control String\" \"ESC P\" \"Start of a DCS sequence.\"\n// @vt: #Y ESC ST \"String Terminator\" \"ESC \\\" \"Terminator used for string type sequences.\"\n// @vt: #Y ESC PM \"Privacy Message\" \"ESC ^\" \"Start of a privacy message.\"\n// @vt: #Y ESC APC \"Application Program Command\" \"ESC _\" \"Start of an APC sequence.\"\n// @vt: #Y C1 CSI \"Control Sequence Introducer\" \"\\x9B\" \"Start of a CSI sequence.\"\n// @vt: #Y C1 OSC \"Operating System Command\" \"\\x9D\" \"Start of an OSC sequence.\"\n// @vt: #Y C1 DCS \"Device Control String\" \"\\x90\" \"Start of a DCS sequence.\"\n// @vt: #Y C1 ST \"String Terminator\" \"\\x9C\" \"Terminator used for string type sequences.\"\n// @vt: #Y C1 PM \"Privacy Message\" \"\\x9E\" \"Start of a privacy message.\"\n// @vt: #Y C1 APC \"Application Program Command\" \"\\x9F\" \"Start of an APC sequence.\"\n// @vt: #Y C0 NUL \"Null\" \"\\0, \\x00\" \"NUL is ignored.\"\n// @vt: #Y C0 ESC \"Escape\" \"\\e, \\x1B\" \"Start of a sequence. Cancels any other sequence.\"\n\n/**\n * Document common VT features here that are currently unsupported\n */\n// @vt: #N DCS SIXEL \"SIXEL Graphics\" \"DCS Ps ; Ps ; Ps ; q \tPt ST\" \"Draw SIXEL image starting at cursor position.\"\n// @vt: #N OSC 1 \"Set Icon Name\" \"OSC 1 ; Pt BEL\" \"Set icon name.\"\n\n/**\n * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.\n */\nconst MAX_PARSEBUFFER_LENGTH = 131072;\n\n/**\n * Limit length of title and icon name stacks.\n */\nconst STACK_LIMIT = 10;\n\n// map params to window option\nfunction paramToWindowOption(n: number, opts: IWindowOptions): boolean {\n if (n > 24) {\n return opts.setWinLines || false;\n }\n switch (n) {\n case 1: return !!opts.restoreWin;\n case 2: return !!opts.minimizeWin;\n case 3: return !!opts.setWinPosition;\n case 4: return !!opts.setWinSizePixels;\n case 5: return !!opts.raiseWin;\n case 6: return !!opts.lowerWin;\n case 7: return !!opts.refreshWin;\n case 8: return !!opts.setWinSizeChars;\n case 9: return !!opts.maximizeWin;\n case 10: return !!opts.fullscreenWin;\n case 11: return !!opts.getWinState;\n case 13: return !!opts.getWinPosition;\n case 14: return !!opts.getWinSizePixels;\n case 15: return !!opts.getScreenSizePixels;\n case 16: return !!opts.getCellSizePixels;\n case 18: return !!opts.getWinSizeChars;\n case 19: return !!opts.getScreenSizeChars;\n case 20: return !!opts.getIconTitle;\n case 21: return !!opts.getWinTitle;\n case 22: return !!opts.pushTitle;\n case 23: return !!opts.popTitle;\n case 24: return !!opts.setWinLines;\n }\n return false;\n}\n\nexport enum WindowsOptionsReportType {\n GET_WIN_SIZE_PIXELS = 0,\n GET_CELL_SIZE_PIXELS = 1\n}\n\n// create a warning log if an async handler takes longer than the limit (in ms)\nconst SLOW_ASYNC_LIMIT = 5000;\n\n/**\n * DCS subparser implementations\n */\n\n/**\n * DCS $ q Pt ST\n * DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)\n * Request Status String (DECRQSS), VT420 and up.\n * Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)\n *\n * @vt: #P[See limited support below.] DCS DECRQSS \"Request Selection or Setting\" \"DCS $ q Pt ST\" \"Request several terminal settings.\"\n * Response is in the form `ESC P 1 $ r Pt ST` for valid requests, where `Pt` contains the corresponding CSI string,\n * `ESC P 0 ST` for invalid requests.\n *\n * Supported requests and responses:\n *\n * | Type | Request | Response (`Pt`) |\n * | -------------------------------- | ----------------- | ----------------------------------------------------- |\n * | Graphic Rendition (SGR) | `DCS $ q m ST` | always reporting `0m` (currently broken) |\n * | Top and Bottom Margins (DECSTBM) | `DCS $ q r ST` | `Ps ; Ps r` |\n * | Cursor Style (DECSCUSR) | `DCS $ q SP q ST` | `Ps SP q` |\n * | Protection Attribute (DECSCA) | `DCS $ q \" q ST` | always reporting `0 \" q` (DECSCA is unsupported) |\n * | Conformance Level (DECSCL) | `DCS $ q \" p ST` | always reporting `61 ; 1 \" p` (DECSCL is unsupported) |\n *\n *\n * TODO:\n * - fix SGR report\n * - either implement DECSCA or remove the report\n * - either check which conformance is better suited or remove the report completely\n * --> we are currently a mixture of all up to VT400 but dont follow anyone strictly\n */\nclass DECRQSS implements IDcsHandler {\n private _data: Uint32Array = new Uint32Array(0);\n\n constructor(\n private _bufferService: IBufferService,\n private _coreService: ICoreService,\n private _logService: ILogService,\n private _optionsService: IOptionsService\n ) { }\n\n public hook(params: IParams): void {\n this._data = new Uint32Array(0);\n }\n\n public put(data: Uint32Array, start: number, end: number): void {\n this._data = concat(this._data, data.subarray(start, end));\n }\n\n public unhook(success: boolean): boolean {\n if (!success) {\n this._data = new Uint32Array(0);\n return true;\n }\n const data = utf32ToString(this._data);\n this._data = new Uint32Array(0);\n switch (data) {\n // valid: DCS 1 $ r Pt ST (xterm)\n case '\"q': // DECSCA\n this._coreService.triggerDataEvent(`${C0.ESC}P1$r0\"q${C0.ESC}\\\\`);\n break;\n case '\"p': // DECSCL\n this._coreService.triggerDataEvent(`${C0.ESC}P1$r61;1\"p${C0.ESC}\\\\`);\n break;\n case 'r': // DECSTBM\n const pt = '' + (this._bufferService.buffer.scrollTop + 1) +\n ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r';\n this._coreService.triggerDataEvent(`${C0.ESC}P1$r${pt}${C0.ESC}\\\\`);\n break;\n case 'm': // SGR\n // TODO: report real settings instead of 0m\n this._coreService.triggerDataEvent(`${C0.ESC}P1$r0m${C0.ESC}\\\\`);\n break;\n case ' q': // DECSCUSR\n const STYLES: { [key: string]: number } = { 'block': 2, 'underline': 4, 'bar': 6 };\n let style = STYLES[this._optionsService.options.cursorStyle];\n style -= this._optionsService.options.cursorBlink ? 1 : 0;\n this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\\\`);\n break;\n default:\n // invalid: DCS 0 $ r Pt ST (xterm)\n this._logService.debug('Unknown DCS $q %s', data);\n this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\\\`);\n }\n return true;\n }\n}\n\n/**\n * DCS Ps; Ps| Pt ST\n * DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html)\n * not supported\n *\n * @vt: #N DCS DECUDK \"User Defined Keys\" \"DCS Ps ; Ps | Pt ST\" \"Definitions for user-defined keys.\"\n */\n\n/**\n * DCS + q Pt ST (xterm)\n * Request Terminfo String\n * not implemented\n *\n * @vt: #N DCS XTGETTCAP \"Request Terminfo String\" \"DCS + q Pt ST\" \"Request Terminfo String.\"\n */\n\n/**\n * DCS + p Pt ST (xterm)\n * Set Terminfo Data\n * not supported\n *\n * @vt: #N DCS XTSETTCAP \"Set Terminfo Data\" \"DCS + p Pt ST\" \"Set Terminfo Data.\"\n */\n\n\n\n/**\n * The terminal's standard implementation of IInputHandler, this handles all\n * input from the Parser.\n *\n * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand\n * each function's header comment.\n */\nexport class InputHandler extends Disposable implements IInputHandler {\n private _parseBuffer: Uint32Array = new Uint32Array(4096);\n private _stringDecoder: StringToUtf32 = new StringToUtf32();\n private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32();\n private _workCell: CellData = new CellData();\n private _windowTitle = '';\n private _iconName = '';\n protected _windowTitleStack: string[] = [];\n protected _iconNameStack: string[] = [];\n\n private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();\n private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();\n\n private _activeBuffer: IBuffer;\n\n private _onRequestBell = new EventEmitter();\n public get onRequestBell(): IEvent { return this._onRequestBell.event; }\n private _onRequestRefreshRows = new EventEmitter();\n public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; }\n private _onRequestReset = new EventEmitter();\n public get onRequestReset(): IEvent { return this._onRequestReset.event; }\n private _onRequestSendFocus = new EventEmitter();\n public get onRequestSendFocus(): IEvent { return this._onRequestSendFocus.event; }\n private _onRequestSyncScrollBar = new EventEmitter